/* im_binfile.c --- load a raw binary file
 *
 * Author: N. Dessipris
 * Written on: 31/7/91
 * Modified on:
 * 15/6/93 JC
 *	- includes fixed
 *	- externs fixed
 * 7/6/94 JC
 *	- im_outcheck() added
 * 13/12/94 JC
 *	- now uses im_mapfile(), rather than read() and copy
 * 	- hence returns a new IMAGE descriptor
 * 25/5/95 JC
 *	- oops! mess up with fd on errors
 * 5/2/04 JC
 *	- added offset param
 * 26/5/04
 *	- data init was broken by im_mapfile() change
 * 30/9/05
 * 	- use int64 for size calcs so we can map >31 bit files on 64 bit
 * 	  machines
 * 	- delay mmap() for large files, cf. im_openin()
 * 12/5/09
 *	- fix signed/unsigned warnings
 */

/*

    This file is part of VIPS.
    
    VIPS is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 */

/*

    These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#include <vips/vips.h>
#include <vips/internal.h>

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/

/**
 * im_binfile:
 * @name: filename to open
 * @xsize: image width
 * @ysize: image height
 * @bands: image bands (or bytes per pixel)
 * @offset: bytes to skip at start of file
 *
 * This function maps the named file and returns an #IMAGE you can use to
 * read it.
 *
 * It returns an 8-bit image with @bands bands. If the image is not 8-bit, use 
 * im_copy_set() to transform the descriptor after loading it.
 *
 * See also: im_copy_set(), im_raw2vips(), im_open().
 *
 * Returns: the new #IMAGE, or NULL on error.
 */
IMAGE *
im_binfile( const char *name, int xsize, int ysize, int bands, int offset )
{
	IMAGE *im;
	gint64 psize;
	gint64 rsize;

	/* Check parameters.
	 */
	if( xsize <= 0 || ysize <= 0 || bands <= 0 ) {
		im_error( "im_binfile", 
			"%s", _( "bad parameters" ) );
		return( NULL );
	}

	/* Make new output image for us.
	 */
	if( !(im = im_init( name )) )
		return( NULL );
	if( (im->fd = im__open_image_file( name )) == -1 ) {
		im_close( im );
		return( NULL );
	}
	im->dtype = IM_OPENIN;
	im->sizeof_header = offset;

	/* Predict file size.
	 */
	psize = (gint64) xsize * ysize * bands + offset;

	/* Read the real file length and check against what we think 
	 * the size should be.
	 */
	if( (rsize = im_file_length( im->fd )) == -1 ) {
		im_close( im );
		return( NULL );
	}
	im->file_length = rsize;

	/* Very common, so special message.
	 */
	if( psize > rsize ) {
		im_error( "im_binfile", _( "unable to open %s: "
			"file has been truncated" ), im->filename );
		im_close( im );
		return( NULL );
	}

	/* Just wierd. Only print a warning for this, since we should
	 * still be able to process it without coredumps.
	 */
	if( psize < rsize )
		im_warn( "im_binfile", _( "%s is longer than expected" ),
			im->filename );

	/* Set header fields.
	 */
	im->Xsize = xsize;
	im->Ysize = ysize;
	im->Bands = bands;

	/* Set others to standard values.
	 */
	im->BandFmt = IM_BANDFMT_UCHAR;
	im->Bbits = im_bits_of_fmt( im->BandFmt );
	im->Coding = IM_CODING_NONE;

	if( bands == 1 )
		im->Type = IM_TYPE_B_W;
	else if( bands == 3 )
		im->Type = IM_TYPE_RGB;
	else 
		im->Type = IM_TYPE_MULTIBAND;

	im->Xres = 1.0;
	im->Yres = 1.0;

	im->Length = 0;
	im->Compression = 0;
	im->Level = 0;

	im->Xoffset = 0;
	im->Yoffset = 0;

	/* Init others too.
	 */
	im->dhint = IM_THINSTRIP;

	return( im );
}
