/*  readbsq.c -- Read and unpack band-sequential binary array

Purpose:

    This function extracts a specified window (rectangular subset) from
    a band-sequential (BSQ) binary data array and places it into an
    array of short integers specified by the calling program.  In a
    band-sequential array, all data for the first band come first,
    followed by all data for the second band, and so on until the last
    band is complete.  Within each band, the data are organized by rows.

    The 'bands' of the array may represent spectral bands, geographic
    layers, successive time intervals, or the like.

    This subroutine may be used to unpack a two-dimensional binary array
    by specifying inband = 1.

    For multi-byte data values, this subroutine assumes that the input
    array is in "big endian" format; i.e., the most-significant byte of
    each value precedes the least-significant byte.  If the host
    computer being used to read the data uses "little endian" format,
    the constant BIGEND should be changed -- see the #define statement
    for BIGEND.

    This subroutine opens and closes the input file and allocates a
    temporary input buffer each time it is called.  If it is to be
    called repeatedly from a main program using different window
    specifications (e.g., to read in a small number of rows at a time),
    it will be more efficient to move the open(), close(), malloc(), and
    free() functions into the calling program and replace the filename
    argument with pointers to the opened file and the input buffer.

Usage:
    
    int readbsq (char* filename, int inband, int inrow, int incol,
		  int inbyte, int iband, int irow, int icol,
		  int nband, int nrow, int ncol, short outarray[])

Arguments:
    filename	(in)	Name of file containing the BSQ data array.
    inband	(in)	Number of bands in input data array; for
			two-dimensional array, inband = 1.
    inrow	(in)	Number of rows in input data array.
    incol	(in)	Number of columns per row in input data	array.
    inbyte	(in)	Number of bytes per input data value.  Allowed
			values are 1 and 2.
    iband	(in)	Initial band for which data are desired -- bands
			are numbered from 1 to inband.
    irow	(in)	Initial row for which data are desired -- rows
			are numbered from top to bottom (north to
			south), beginning with row 1.
    icol	(in)	Initial column for which data are desired --
			columns are numbered from left to right (west to
			east), beginning with column 1.
    nband	(in)	Number of consecutive bands to be extracted from
			input data array.
    nrow	(in)	Number of consecutive rows to be extracted from
			each band of data array.
    ncol	(in)	Number of consecutive columns to be extracted
			from each row of data array.
    outarray	(out)	Array to receive the data from input file.  For
			a three-dimesnional array, outarray should be
			dimensioned as outarray[nband][nrow][ncol].  If
			nband = 1, outarray[nrow][ncol] is acceptable.

Return Value:
    0	Normal completion.
    1	Error occurred.

Data Files Accessed:
    <filename>	READ to extract data for requested window.

Program History:
    01-96	Initial version, R. A. White, PSU/ESSC			*/

/***************   End of Prolog for readbsq()   ***************/

#include <stdio.h>
#include <stdlib.h>
									/*
/*  Use BIGEND = 0 for big-endian hosts such as Sun SPARC.  Set
    BIGEND = 1 for little-endian hosts, such as VAX and Intel 
    80x86-compatible PCs  */

#define BIGEND 0

/***************   Start of Function readbsq()   ***************/

int readbsq (
    char    *filename,	/* Name of file containing the BSQ data array */
    int	    inband,	/* Number of bands in input data array */
    int	    inrow,	/* Number of rows in input data array */
    int	    incol,	/* Number of columns per row in input array */
    int	    inbyte,	/* Number of bytes per input data value */
    int	    iband,	/* Initial band for which data are desired */
    int	    irow,	/* Initial row for which data are desired */
    int	    icol,	/* Initial column for which data are desired */
    int	    nband,	/* Number of bands in window */
    int	    nrow,	/* Number of rows in window */
    int	    ncol,	/* Number of columns in window */
    short   outarray[])	/* Array to receive the data from input file */
    {
				    			   		/*
Local Variables and Arrays:						*/

    int		band;		/* Current input band */
    int		col;		/* Current input column */
    char	errbuf[256];	/* Buffer for error message */
    FILE	*fptr;		/* File pointer for input */
    unsigned char *inbuf;	/* Buffer to receive one input record */
    unsigned char *inptr;	/* Current position in input buffer */
    short	*outptr;	/* Current position in outarray */
    int		rcoffset;	/* Offset to first byte to be read
				   within each band */
    int		reclen;		/* Length of input record, bytes */
    int		row;		/* Current input row */
    short	*shortptr;	/* Pointer to inbuf, cast to type short */
    unsigned char swapbyte;	/* Temporary storage for swapped byte */

/*  Open input array and allocate buffer  */

    fptr = fopen (filename, "r");
    if (fptr == NULL)
	{
	sprintf (errbuf, "Error opening input file %s", filename);
	perror (errbuf);
	return 1;
	}
    reclen = ncol * inbyte;
    inbuf = (unsigned char *) malloc (reclen);

/*  Read requested columns of one row at a time  */

    rcoffset = inbyte * ((icol -1) + (irow - 1) * incol);
    outptr = outarray;
    for (band = iband; band < iband + nband; band++)
	{
	if (fseek (fptr, inbyte * (band - 1) * inrow * incol +
	   rcoffset, SEEK_SET) != 0)
	    {
	    sprintf (errbuf, "Error seeking to band %d", band);
	    perror (errbuf);
	    return 1;
	    }
	for (row = irow; row < irow + nrow; row++)
	    {
	    if ((row > irow) && (ncol < incol))
		{
		if (fseek (fptr, inbyte * (incol - ncol), SEEK_CUR)
		  != 0) 
		    {
		    sprintf (errbuf, "Error seeking to row %d, band %d",
		      row, band);
		    perror (errbuf);
		    return 1;
		    }
		}
	    if (fread (inbuf, 1, reclen, fptr) != reclen)
		{
		sprintf (errbuf, "Error reading row %d, band %d", row,
		  band);
		perror (errbuf);
		return 1;
		}

/*  Move data for row into output array  */

	    inptr = inbuf;
	    shortptr = (short *) inbuf;
	    for (col = icol; col < icol + ncol; col++)
		{
		if (inbyte == 1)
		    {
		    *(outptr++) = *(inptr++);
		    }
		else
		    {
#if BIGEND==1
		    swapbyte = *inptr;
		    *inptr = *(inptr + 1);
		    *(inptr+1) = swapbyte;
		    inptr += 2;
#endif
		    *(outptr++) = *(shortptr++);
		    }
		}
	    }
	}

/*  Close file and return  */

    fclose (fptr);
    free(inbuf);
    return;

    }	/*  End of Function readbsq()  */
