/*
 * $Id: readics.cxx,v 4.0 2003/04/28 14:44:34 hut66au Exp $
 *
 * Imview, the portable image analysis application
 * http://www.cmis.csiro.au/Hugues.Talbot/imview
 * ----------------------------------------------------------
 *
 *  Imview is an attempt to provide an image display application
 *  suitable for professional image analysis. It was started in
 *  1997 and is mostly the result of the efforts of Hugues Talbot,
 *  Image Analysis Project, CSIRO Mathematical and Information
 *  Sciences, with help from others (see the CREDITS files for
 *  more information)
 *
 *  Imview is Copyrighted (C) 1997-2001 by Hugues Talbot and was
 *  supported in parts by the Australian Commonwealth Science and 
 *  Industry Research Organisation. Please see the COPYRIGHT file 
 *  for full details. Imview also includes the contributions of 
 *  many others. Please see the CREDITS file for full details.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU 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 General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
 * */

/* 
 * 	readics ()  - reads in ICS images
 *
 *      Hugues Talbot	26 Mar 1998, based on
 *      liar code by same author 18 Dec 1997
 *      itself based on Z-IMAGE code by Leanne Bischof
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "imunistd.h"
#include "imview.hxx"
#include "imageIO.hxx"
#include "ics.hxx"
#include "readics.hxx"

#define TRUE 1
#define FALSE 0

extern imageIO *IOBlackBox;

/*--declarations of locally defined functions--*/
static int read_ics_header(char *filename, ICS *icsheader);
static int read_ids_char(char *filename, unsigned char *dest, int length);
static int read_ids_double(char *filename, double *dest, int length, int need_to_swap);
static int read_ids_short(char *filename, short *dest, int length, int need_to_swap);
static void swap_short(char *from, char *to, int nbytes);
static void swap_float(char *from, char *to, int nbytes);


int readICSImage(const char *name)
{
    void   *p, **pp;
    int     res, nx, ny, nz;
    pixtype thepixtype;
    
    // call the low-level TIFF reader

    res = load_ics(name, &p, &thepixtype, &nx, &ny, &nz);

    pp = (void **)malloc(sizeof(void *));
    pp[0] = p;
    
    if (res == 0) { // all went well
	// now reprocess that according to content
	
	// the new buffer needs to be set first before
	// the dimensions are changed because the previous buffer
	// might be freed, and we will need its dimensions.
	IOBlackBox->setCurrBuffp(pp);
	IOBlackBox->setCurrImgWidth(nx);
	IOBlackBox->setCurrImgHeight(ny);
	IOBlackBox->setCurrImgThickness(nz);
	IOBlackBox->setXOffset(0);
	IOBlackBox->setYOffset(0);
	IOBlackBox->setZOffset(0);
	IOBlackBox->setCurrImgType(IM_SPECTRUM);
	IOBlackBox->setCurrPixType((pixtype)thepixtype);
	IOBlackBox->setCurrImgNbComps(1); // best default here
	IOBlackBox->setCurrImgNbSamples(1);
	IOBlackBox->setCurrImgColourMap(0);
	IOBlackBox->setCurrImgNbColours(0);
	IOBlackBox->setImgDesc("ICS");
    }	
	
    return res;
}

// returns the number of frames in an ICS file
int icsnbsubfiles(const char *)
{
    return 1; // that was easy.
}

/* check endianness */
static int check_endian(void)	 /* no argument  */
{
/** returns the endianness of the current CPU

    RETURN VALUE:	int
    Returns IS_BIGENDIAN (1) if the CPU is big-endian (Sun, SGI)
    Returns IS_LITTLEENDIAN (2) if the CUP is little-endian (DEC, Intel)

    DESCRIPTION:
    Returns the endianness of the current CPU
    
    HISTORY:
    Written by Hugues Talbot	22 Dec 1997

    TESTS:
    
    REFERENCES:
    
    KEYWORDS:

**/

    union {
	long l;
	char c[sizeof (long)];
    } u;
    u.l = 1;
    if (u.c[sizeof (long) - 1] == 1)
	return IS_BIGENDIAN;
    else
	return IS_LITTLEENDIAN;
}
    
/* SWAP_SHORT ---------------------------------------------------------------
|
| Description of sub-routine function...
|
|----------------------------------------------------------------------------*/

/* nbytes should be divisible by 2 */

void swap_short(char *from, char *to, int nbytes)
/*
char    *from;
char    *to;
int      nbytes;
*/
{
        register int i;
	register char c;

        for (i = 0; i < nbytes; i+=2)
        {
	    c = from[i];
            to[i] = from[i+1];
            to[i+1] = c;
        }
}



/* SWAP_FLOAT ---------------------------------------------------------------
|
| Description of sub-routine function...
|
|----------------------------------------------------------------------------*/

/* nbytes should be divisible by 4 */

static void swap_float(char *from, char *to, int nbytes)
/*
char    *from;
char    *to;
int      nbytes;
*/
{
        register int i;
	register char c;

        for (i = 0; i < nbytes; i+=4)
        {
            c = from[i];
            to[i] = from[i+3];
            to[i+3] = c;
            c = from[i+1];
            to[i+1] = from[i+2];
	    to[i+2] = c;
        }
}


static int read_ics_header(char *filename,	 /* input filename  */
		    ICS *icsheader)	 /* ICS header      */
{
/** Reads the ICS header

    RETURN VALUE:	int 

    DESCRIPTION:
    Read the the ICS header file into the dataset description
    structure.
    char *filename : name of the file to be read
    ICS *icsheader : pointer to the dataset description structure
    int err :  0 = success
    -1 = allocation error
    -2 = file open error
    -3 = file read error
    -4 = not an ICS file
    -5 = illegal order in ICS file
    (layout variables missing or not defined at top)
    -6 = too high dimensional data

    HISTORY:
    Damir Sudar
    Copyright (c) University of California San Francisco

    TESTS:

    REFERENCES:

    KEYWORDS:

**/

    int fd, cat;
    unsigned int length, n_read, ui;
    char *buffer1, *buffer2, *end;
    char temp1[100],temp2[100];
    int i;
    char *t, *tg, *bp;
    char delim1, delim2;
    static int kwrds = 14;
    static char *keywrdtable[] =
    {
	"parameters",
	"order",
	"sizes",
	"coordinates",
	"significant_bits",
	"format",
	"sign",
	"compression",
	"origin",
	"scale",
	"labels",
	"units",
	"byte_order",
	"SCIL_TYPE"
    };
  
    /* make the proper filename and open the ICS file */
    strcpy(temp1,filename);
    strcat(temp1,".ics");
    if ((fd = open(temp1,O_RDONLY)) < 0) return(-2);
   
    /* get the length of the ICS file and rewind */
    length = (unsigned int)lseek(fd,0L,2);
    lseek(fd,0L,0);
   
  /* allocate space for all data from the ICS file */
    if ((buffer1 = (char *)malloc(length)) == NULL)
    {
	close(fd);
	return(-1);
    }
  
    /* allocate space for the unresolved data from the ICS file */
    if ((buffer2 = (char *)malloc(length)) == NULL)
    {
	close(fd);
	free(buffer1);
	return(-1);
    }
  
    /* read the data into buffer1 and close the file */
    if ((n_read = read(fd,buffer1,length)) != length)
    {
	close(fd);
	free(buffer1);
	free(buffer2);
	return(-3);
    }
    close(fd);
  
  /* initialisations */
    icsheader->valid_filename = FALSE;
    icsheader->valid_parameters = FALSE;
    icsheader->valid_order = FALSE;
    icsheader->valid_sizes = FALSE;
    icsheader->valid_sigbits = FALSE;
    icsheader->valid_origin = FALSE;
    icsheader->valid_scale = FALSE;
    icsheader->valid_label = FALSE;
    icsheader->valid_units = FALSE;
    icsheader->valid_coord = FALSE;
    icsheader->valid_format = FALSE;
    icsheader->valid_sign = FALSE;
    icsheader->valid_compression = FALSE;
    icsheader->valid_byteorder = FALSE;
    icsheader->valid_SCIL_TYPE = FALSE;
    bp = buffer1;
    end = bp + length;	/* EOF */
    *buffer2 = '\0';	/* initially empty */
    delim1 = *bp++;	/* field delimiter */
    delim2 = *bp++;	/* record delimiter */
    t = temp1;
 
    /* check if written by ICS */
    while (*bp != delim2)
	*t++ = *bp++;
    bp++;
    *t = '\0';
    if (strncmp(temp1,"ICS",3) && strncmp(temp1,"ics",3))
    {
	free(buffer1);
	free(buffer2);
	return(-4);
    }

    /* get the filename from the ICS file */

    t = temp1;
    while (*bp != delim2)
	*t++ = *bp++;
    bp++;
    *t = '\0';

    t = strchr(temp1,delim1);
    strcpy(icsheader->filename,t);
    *t = '\0';

    if (strcmp(temp1,"filename"))
    {
	free(buffer1);
	free(buffer2);
	return(-5);
    }
    icsheader->valid_filename = TRUE;

  /* check the following records one by one and try to resolve
     the information per record */
    while (bp < end)	/* until EOF */
    {
	/* get the next record into temp1 */
	t = temp1;
	while (*bp != delim2 && bp < end)	/* dont read beyond EOF */
	    *t++ = *bp++;
	bp++;
	*t = '\0';
      
      /* get the category into temp2 */
	t = temp1;
	tg = temp2;
	while (*t != delim1)
	    *tg++ = *t++;
	t++;
	*tg = '\0';

      /* check if it is one of the decodable categories */
	cat = 0;
	if (!strcmp(temp2,"layout")) cat = 1;
	if (!strcmp(temp2,"representation")) cat = 2;
	if (!strcmp(temp2,"parameter")) cat = 3;

	if (cat == 0)
	{
	    /* if not concatenate record to buffer2 */
	    strcat(buffer2,temp1);
	    strcat(buffer2,"\n");
	    continue;
	}
	/* get the next field from this record */
	tg = temp2;
	while (*t != delim1)
	    *tg++ = *t++;
	t++;
	*tg = '\0';

      /* find this item in the keyword table */
	for (i = 0; i < kwrds; i++)
	    if (!strcmp(temp2,keywrdtable[i])) break;
	if (i == kwrds)
	{ /* not found in the keyword table */
	    /* concatenate this record to buffer2 */
	    strcat(buffer2,temp1);
	    strcat(buffer2,"\n");
	    continue;
	}

	/* switch to the right value handling routine */
	switch (i)
	{
	  case 0: /* parameters */
	    if (cat != 1)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    icsheader->parameters = atoi(temp2);
	    if (icsheader->parameters > MAXDIM)
	    {  /* if necessary change MAXDIM in ics.h */
		free(buffer1);
		free(buffer2);
		return(-6);
	    }
	    icsheader->valid_parameters = TRUE;
	    break;
	  case 1: /* order */
	    if (cat != 1)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		strcpy(icsheader->order[i],temp2);
	    }
	    icsheader->valid_order = TRUE;
	    break;
	  case 2: /* sizes */
	    if (cat != 1)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		icsheader->sizes[i] = atoi(temp2);
	    }
	    icsheader->valid_sizes = TRUE;
	    break;
	  case 3: /* coord */
	    if (cat != 1)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    strcpy(icsheader->coord,temp2);
	    icsheader->valid_coord = TRUE;
	    break;
	  case 4: /* significant bits */
	    if (cat != 1)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    icsheader->sigbits = atoi(temp2);
	    icsheader->valid_sigbits = TRUE;
	    break;
	  case 5: /* format */
	    if (cat != 2)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    strcpy(icsheader->format,temp2);
	    icsheader->valid_format = TRUE;
	    break;
	  case 6: /* signed or unsigned */
	    if (cat != 2)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    if (!strcmp(temp2,"unsigned"))
		icsheader->sign = UNSIGNED;
	    else icsheader->sign = SIGNED;
	    icsheader->valid_sign = TRUE;
	    break;
	  case 7: /* packing */
	    if (cat != 2)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    strcpy(icsheader->compression,temp2);
	    icsheader->valid_compression = TRUE;
	    break;
	  case 8: /* origin */
	    if (cat != 3)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		icsheader->origin[i] = (float)atof(temp2);
	    }
	    icsheader->valid_origin = TRUE;
	    break;
	  case 9: /* scale */
	    if (cat != 3)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		icsheader->scale[i] = (float)atof(temp2);
	    }
	    icsheader->valid_scale = TRUE;
	    break;
	  case 10: /* labels */
	    if (cat != 3)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		strcpy(icsheader->label[i],temp2);
	    }
	    icsheader->valid_label = TRUE;
	    break;
	  case 11: /* units */
	    if (cat != 3)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->parameters == 0)
	    { /* need to know the number of parameters first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    for (i = 0; i < icsheader->parameters; i++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		strcpy(icsheader->units[i],temp2);
	    }
	    icsheader->valid_units = TRUE;
	    break;
	  case 12: /* byteorder */
	    if (cat != 2)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    if (icsheader->sizes[0] == 0)
	    { /* need to know the number of bits first */
		free(buffer1);
		free(buffer2);
		return(-5);
	    }
	    length = icsheader->sizes[0] / 8;
	    for (ui = 0; ui < length; ui++)
	    {
		tg = temp2;
		while (*t != delim1 && *t != '\0')
		    *tg++ = *t++;
		*tg = '\0';
		t++;
		icsheader->byteorder[ui] = atoi(temp2);
	    }
	    icsheader->valid_byteorder = TRUE;
	    break;
	  case 13: /* SCIL_TYPE */
	    if (cat != 2)
	    { /* wrong category concatenate this record to buffer2 */
		strcat(buffer2,temp1);
		strcat(buffer2,"\n");
		break;
	    }
	    tg = temp2;
	    while (*t != '\0')
		*tg++ = *t++;
	    *tg = '\0';
	    t++;
	    strcpy(icsheader->SCIL_TYPE,temp2);
	    icsheader->valid_SCIL_TYPE = TRUE;
	    break;
	  default:
	    /* unknown category concatenate this record to buffer2 */
	    strcat(buffer2,temp1);
	    strcat(buffer2,"\n");
	    break;
	}
    }
  
    /* clean up and return */
    free(buffer1);
    free(buffer2);
    return(0);
}

/*---------------------------------------------------------------------------*/


/*---------------------------------------------------------------------------*/

static int read_ids_char(char *filename, unsigned char *dest, int length)
{
  int fd, tot, num;
  char *dp;
  char temp1[100];

  /* make the proper filename and open the IDS file */
  strcpy(temp1,filename);
  strcat(temp1,".ids");
  if ((fd = open(temp1,O_RDONLY)) < 0) return(-2);
  
  /* get the length of the IDS file and rewind */
  tot = lseek(fd,0L,2);
  lseek(fd,0L,0);
  
  /* check if lengths are the same */
  if (tot > length) tot = length;
  
  dp = (char *)dest;
  /* read the data into destination */
  while ((num = read(fd,dp,512)) > 0)
  {
      dp += num;
      tot -= num;
      if (tot < 512) break;
  }
  if ((num = read(fd,dp,tot)) != tot)
  {
      close(fd);
      return(-3);
  }
  
  close(fd);
  return(0);
}

/*---------------------------------------------------------------------------*/
static int read_ids_short(char *filename, short *dest, int length, int need_to_swap)
{
    int i, fd, tot, num;
    short int *temp, *t;
    char temp1[100];
    
    /* make the proper filename and open the IDS file */
    strcpy(temp1,filename);
    strcat(temp1,".ids");
    if ((fd = open(temp1,O_RDONLY)) < 0) return(-2);
    
    /* get the length of the IDS file and rewind */
    tot = lseek(fd,0L,2);
    lseek(fd,0L,0);
    
    /* check if lengths are the same */
    if (tot > length) tot = length*sizeof(short int);
    
    temp = (short int *) calloc(sizeof(short int),512);
    
    /* read the data into destination */
    while ((num = read(fd,temp,512*sizeof(short int))) > 0)
    {
	if (need_to_swap)
	    swap_short((char*)temp,(char*)temp,num);
	t = temp;
	tot -= num;
	for (i=0; i<(int)(num/sizeof(short int)); i++)
	    *dest++ = (short)(*t++);
	if (tot < (int)(512*sizeof(short int))) break;
    }
    
    if ((num = read(fd,temp,tot)) == tot) 
    {
	if (need_to_swap)
	    swap_short((char*)temp,(char*)temp,num);
	t = temp;
	for (i=0; i<(int)(num/sizeof(short int)); i++)
	    *dest++ = (short)(*t++);
    }
    else {
	free(temp);
	close(fd);
	return(-3);
    }
    
    free(temp);
    close(fd);
    return(0);
}

					   
/*---------------------------------------------------------------------------*/

static int read_ids_double(char *filename, double *dest, int length, int need_to_swap)
{
    int i, fd, tot, num;
    float *temp, *t;
    char temp1[100];
    
    /* make the proper filename and open the IDS file */
    strcpy(temp1,filename);
    strcat(temp1,".ids");
    if ((fd = open(temp1,O_RDONLY)) < 0) return(-2);
    
    /* get the length of the IDS file and rewind */
    tot = lseek(fd,0L,2);
    lseek(fd,0L,0);
    
    /* check if lengths are the same */
    if (tot > length) tot = length*sizeof(float);
    
    temp = (float *) calloc(sizeof(float),512);
    
    /* read the data into destination */
    while ((num = read(fd,temp,512*sizeof(float))) > 0)
    {
	if (need_to_swap)
	    swap_float((char*)temp,(char*)temp,num);
	t = temp;
	tot -= num;
	for (i=0; i<(int)(num/sizeof(float)); i++)
	    *dest++ = (double)(*t++);
	if (tot < (int)(512*sizeof(float))) break;
    }
    
    if ((num = read(fd,temp,tot)) == tot) 
    {
	if (need_to_swap)
	    swap_float((char*)temp,(char*)temp,num);
	t = temp;
	for (i=0; i<(int)(num/sizeof(float)); i++)
	    *dest++ = (double)(*t++);
    }
    else {
	free(temp);
	close(fd);
	return(-3);
    }
    
    free(temp);
    close(fd);
    return(0);
}
					   
/*---------------------------------------------------------------------------*/
/* front-end code for the LIAR, Hugues Talbot	18 Dec 1997 */

int load_ics(const char *fname, void **imdata, pixtype *datatype, int *nx, int *ny, int *nz)
{
    int res = 0, size,  need_to_swap=-1; 
    /*char text[81];*/
    static char tmpfname[1024];
    static char tmp1[1024];
    char *tp;
    int   err;
    ICS   icsheader;


    dbgprintf("Loading %s as an ICS image", fname);
    /* Check if IDS data file exists */

    strcpy(tmpfname, fname);
    if (((tp = strrchr(tmpfname, '.')) != 0)
	 && ((strncmp(tp, ".ics", 4) == 0)
	     || (strncmp(tp, ".ids", 4) == 0))) {
	// remove this extension, which we shall add later!
	tp[0] = '\0';
    } 
    
    /* make the proper filename and open (& close) the IDS file */
    strcpy(tmp1, tmpfname);
    strcat(tmp1,".ids");
    if ( (access(tmp1,R_OK)) != 0) { /* we can't read the data ! */
        errprintf("IDS file \"%s.ids\" is unreadable:\n%s.",tmpfname, strerror(errno));
	return (20);
    }

    /* read ICS header file */
    err = (int) read_ics_header(tmpfname,&icsheader);
    if (err == -2){
        errprintf("ICS file \"%s.ics\" does not exist.",tmpfname);
	return (21);
    }
    else if (err!=0){
	errprintf("Error %d in reading %s.ics",err,tmpfname);
	return (22);
    }

    /* Test if header values are valid - at present can only read
                                         ^^^^^^^^^ <- you can tell this is Leanne writing here...
       8bit unsigned & 16bit signed integers, & 32bit float files */

    if (!((icsheader.valid_parameters) && (icsheader.valid_sizes) && 
	  (icsheader.valid_format) && (icsheader.valid_sign))) {
	errprintf("ICS header file contains invalid parameters.");
	return 23;
    }
    
    if (icsheader.parameters == 3) {
	*nx = icsheader.sizes[1];
	*ny = icsheader.sizes[2];
	*nz = 1;
    } else if (icsheader.parameters == 4) {
	*nx = icsheader.sizes[1];
	*ny = icsheader.sizes[2];
	*nz = icsheader.sizes[3];
    } else {
	errprintf("ICS header file indicates data file is not a 2D or 3D image.");
	return 24;
    }

    if ((icsheader.sizes[0]==8) && (!strcmp(icsheader.format,"integer")) 
	&& (icsheader.sign==UNSIGNED)) *datatype = IM_UINT1;
    else if((icsheader.sizes[0]==16)&&(!strcmp(icsheader.format,"integer")) 
	    && (icsheader.sign==SIGNED)) *datatype = IM_INT2;
    else if((icsheader.sizes[0]==32) && (!strcmp(icsheader.format,"real"))
	    && (icsheader.sign==SIGNED)) *datatype = IM_DOUBLE;
    else {
        errprintf("Data type must be 8bit unsigned int, 16bit signed int or 32bit float.");
	return 25;
    }

    if ((*datatype == IM_INT2) || (*datatype == IM_DOUBLE)) {
	need_to_swap = FALSE;
	/* work out if we need to swap the data */
	if (check_endian() == IS_LITTLEENDIAN) {
	    /* machine is a DEC or a PC, say */
	    if (icsheader.byteorder[0] != 1) {
		need_to_swap = TRUE;
		dbgprintf("Need to swap data endianness");
	    }
	} else {
	    /* machine is a Sun or an SGI, say */
	    if (icsheader.byteorder[0] == 1) {
		need_to_swap = TRUE;
		dbgprintf("Need to swap data endianness");
	    }
	}
    }

    size = (*nx) * (*ny) * (*nz);

    switch(*datatype) {
      case IM_UINT1:
	size *= sizeof(char);
	(*imdata) = (char *)malloc(size * sizeof(char));
        read_ids_char(tmpfname,(unsigned char *)*imdata,size); 
        break;
      case IM_INT2:
	size *= sizeof(short);
	(*imdata) = (short *)malloc(size * sizeof(short));
        read_ids_short(tmpfname,(short *)(*imdata),size,need_to_swap); 
        break;
      case IM_DOUBLE:
	size *= sizeof(double);
	(*imdata) = (double *)malloc(size * sizeof(double));
        read_ids_double(tmpfname,(double *)(*imdata),size,need_to_swap); 
        break;
      default:
	errprintf("ICS reader: pixel type %d not handled yet\n", *datatype);
	return 30;
    }

    return res;
}

