/*************************************************************************/
/*                                                                       */
/*                Centre for Speech Technology Research                  */
/*                     University of Edinburgh, UK                       */
/*                   Copyright (c) 1994,1995,1996                        */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK        */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE     */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*                    Author :  Alan W Black (and Paul Taylor)           */
/*                    Date   :  June 1996                                */
/*-----------------------------------------------------------------------*/
/*  These routines form a basis for reading and writing Entropic's ESPS  */
/*  headered files.  The reason we wrote them was to avoid including     */
/*  Entropic's own (large and cumbersome) code into all our programs.    */
/*  No Entropic proprietary code is included in this code which means    */
/*  you do not needs an Entropic licence to use it.                      */
/*                                                                       */
/*  However this should not be seen as anti-Entropic in anyway it is for */
/*  our and your convenience.  We would like to specifically thank       */
/*  Rodney Johnson of Entropic for giving us help and confirming to us   */
/*  the header format is in fact more complex than one can imagine,      */
/*  mostly for very bad reasons, (backward compatibility cripples all    */
/*  software in the long run).  Hence this code is almost definitely     */
/*  incomplete and is not guaranteed to read or create all ESPS files    */
/*  properly but it is adequate for many tasks.                          */
/*                                                                       */
/*  Also thanks go to Peter Kabal from McGill University whose AF code   */
/*  showed me this might be worth attempting, his code gave me something */
/*  to look at to start with.                                            */
/*                                                                       */
/*  I should add, this wasn't easy to write, though I'm much better at   */
/*  octal and hex dumps now.                                             */
/*=======================================================================*/

#include <stdio.h>
#include <stdlib.h>
#include "EST_unix.h"
#include <string.h>
#include <time.h>
#include "EST_wave_utils.h"
#include "esps_utils.h"

/* First you must realise there is in fact a number of very similar but */
/* subtly different header formats that appear on ESPS files.           */
/* ESPS_FEA and ESPS_SD (others for filters, spectrograms, etc)         */
/* The basic format is                                                  */
/*     preamble                                                         */
/*     fixed header                                                     */
/*     variable header                                                  */
/*        field descriptions (type and dimensions)                      */
/*        field names                                                   */
/*        header FEAs (maybe with values)                               */
/*        old and foreign headers (mostly ignored here)                 */
/*     data records themselves                                          */

/* esps_fea contain a name and possibly a value.  They appear in the    */
/* variable part of the header                                          */
esps_fea new_esps_fea()
{
    esps_fea r = walloc(struct ESPS_FEA_struct,1);
    r->type = 0;
    r->clength = 0;
    r->name = NULL;
    r->dtype = 0;
    r->count = 0;
    r->v.ival = NULL;
    return r;
}

void delete_esps_fea(esps_fea r)
{
    esps_fea t,u;

    for (t=r; t != NULL; t=u)
    {
	if (t->clength != 0)
	    wfree(t->name);
	if (t->count != 0) /* this wont work if fields in v aren't aligned */
	    wfree(t->v.ival);
	u = t->next;
	wfree(t);
    }
}

void print_esps_fea(esps_fea r)
{
    /* Print out the information in the FEA record */
    int i;
    
    fprintf(stdout,"type:  %d\n",r->type);
    fprintf(stdout,"name:  %s\n",r->name);
    fprintf(stdout,"size:  %d\n",r->count);
    fprintf(stdout,"dtype: %d\n",r->dtype);
    for (i=0; i<r->count; i++)
	switch (r->dtype)
	{
	  case ESPS_DOUBLE:
	    fprintf(stdout," %d: %g\n",i,r->v.dval[i]); break;
	  case ESPS_FLOAT:
	    fprintf(stdout," %d: %f\n",i,r->v.fval[i]); break;
	  case ESPS_INT:
	    fprintf(stdout," %d: %d\n",i,r->v.ival[i]); break;
	  case ESPS_SHORT:
	    fprintf(stdout," %d: %d\n",i,r->v.sval[i]); break;
	  case ESPS_CHAR:
	    fprintf(stdout," %d: %d\n",i,r->v.cval[i]); break;
	  default:
	    fprintf(stdout," %d: unknown\n",i);
	}
}

void add_field(esps_hdr hdr, const char *name, int type, int dimension)
{
    /* Add a new field to the record */
    char **names = hdr->field_name;
    short *types = hdr->field_type;
    int *dims = hdr->field_dimension;
    int i;

    hdr->field_name = walloc(char *,hdr->num_fields+1);
    hdr->field_type = walloc(short,hdr->num_fields+1);
    hdr->field_dimension = walloc(int,hdr->num_fields+1);
    for (i=0; i < hdr->num_fields; i++)
    {
	hdr->field_name[i] = names[i];
	hdr->field_type[i] = types[i];
	hdr->field_dimension[i] = dims[i];
    }
    wfree(names);
    wfree(types);
    wfree(dims);
    hdr->field_name[hdr->num_fields] = wstrdup(name);
    hdr->field_type[hdr->num_fields] = type;
    hdr->field_dimension[hdr->num_fields] = dimension;
    hdr->num_fields++;

    return;

}

void add_fea_d(esps_hdr hdr,const char *name, int pos, double d)
{
    /* Add a double FEA field to the header */
    esps_fea t = new_esps_fea();
    int i;

    t->type = 13;  /* must be lucky for some !! */
    t->clength = strlen(name);
    t->name = wstrdup(name);
    if (t->count < pos+1)
    {
	double *dval = t->v.dval;
	t->v.dval = walloc(double,pos+1);
	for (i=0; i<t->count; i++)
	    t->v.dval[i] = dval[i];
	for (; i < pos+1; i++)
	    t->v.dval[i] = 0.0;
	wfree(dval);
	t->count = pos+1;
    }
    t->dtype = ESPS_DOUBLE;
    t->v.dval[pos] = d;

    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

void add_fea_f(esps_hdr hdr,const char *name, int pos, float d)
{
    /* Add a float FEA field to the header */
    esps_fea t = new_esps_fea();
    int i;

    t->type = 13;
    t->clength = strlen(name);
    t->name = wstrdup(name);
    if (t->count < pos+1)
    {
	float *fval = t->v.fval;
	t->v.fval = walloc(float,pos+1);
	for (i=0; i<t->count; i++)
	    t->v.fval[i] = fval[i];
	for (; i < pos+1; i++)
	    t->v.fval[i] = 0.0;
	wfree(fval);
	t->count = pos+1;
    }
    t->dtype = ESPS_FLOAT;
    t->v.fval[pos] = d;

    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

void add_fea_i(esps_hdr hdr,const char *name, int pos, int d)
{
    /* Add an int FEA field to the header */
    esps_fea t = new_esps_fea();
    int i;

    t->type = 13;
    t->clength = strlen(name);
    t->name = wstrdup(name);
    if (t->count < pos+1)
    {
	int *ival = t->v.ival;
	t->v.ival = walloc(int,pos+1);
	for (i=0; i<t->count; i++)
	    t->v.ival[i] = ival[i];
	for (; i < pos+1; i++)
	    t->v.ival[i] = 0;
	wfree(ival);
	t->count = pos+1;
    }
    t->dtype = ESPS_INT;
    t->v.ival[pos] = d;

    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

void add_fea_s(esps_hdr hdr,const char *name, int pos, short d)
{
    /* Add a short FEA field to the header */
    esps_fea t = new_esps_fea();
    int i;

    t->type = 13;
    t->clength = strlen(name);
    t->name = wstrdup(name);
    if (t->count < pos+1)
    {
	short *sval = t->v.sval;
	t->v.sval = walloc(short,pos+1);
	for (i=0; i<t->count; i++)
	    t->v.sval[i] = sval[i];
	for (; i < pos+1; i++)
	    t->v.sval[i] = (short)0;
	wfree(sval);
	t->count = pos+1;
    }
    t->dtype = ESPS_SHORT;
    t->v.sval[pos] = d;

    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

void add_fea_c(esps_hdr hdr,const char *name, int pos, char d)
{
    /* Add a char FEA field to the header */
    esps_fea t = new_esps_fea();
    int i;

    t->type = 13;
    t->clength = strlen(name);
    t->name = wstrdup(name);
    if (t->count < pos+1)
    {
	char *cval = t->v.cval;
	t->v.cval = walloc(char,pos+1);
	for (i=0; i<t->count; i++)
	    t->v.cval[i] = cval[i];
	for (; i < pos+1; i++)
	    t->v.cval[i] = (char)0;
	wfree(cval);
	t->count = pos+1;
    }
    t->dtype = ESPS_CHAR;
    t->v.cval[pos] = d;

    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

void add_fea_special(esps_hdr hdr,int type,const char *name)
{
    /* Add a special FEA field to the header */
    esps_fea t = new_esps_fea();

    t->type = type;
    t->clength = strlen(name);
    t->name = wstrdup(name);
    t->count = 0;
    
    t->next = hdr->fea;
    hdr->fea = t;

    return;
}

int fea_value_d(const char *name,int pos,esps_hdr hdr, double *d)
{
    /* Get value of double FEA */
    esps_fea t;
    
    for (t=hdr->fea; t != NULL; t=t->next)
	if (streq(name,t->name))
	{
	    if (t->dtype != ESPS_DOUBLE)
	    {
		fprintf(stderr,"ESPS hdr: access non-double field \"%s\" as double\n",
			name);
		return -1;
	    }
	    *d = t->v.dval[pos];
	    return 0;
	}

    return -1;  /* failed to find it */
}

int fea_value_f(const char *name,int pos,esps_hdr hdr, float *d)
{
    /* Get value of float FEA */
    esps_fea t;
    
    for (t=hdr->fea; t != NULL; t=t->next)
	if (streq(name,t->name))
	{
	    if (t->dtype != ESPS_FLOAT)
	    {
		fprintf(stderr,"ESPS hdr: access non-float field \"%s\" as float\n",
			name);
		return -1;
	    }
	    *d = t->v.fval[pos];
	    return 0;
	}

    return -1;  /* failed to find it */
}

int fea_value_s(const char *name,int pos,esps_hdr hdr, short *d)
{
    /* Get value of short FEA */
    esps_fea t;
    
    for (t=hdr->fea; t != NULL; t=t->next)
	if (streq(name,t->name))
	{
	    if (t->dtype != ESPS_SHORT)
	    {
		fprintf(stderr,"ESPS hdr: access non-short field \"%s\" as short\n",
			name);
		return -1;
	    }
	    *d = t->v.sval[pos];
	    return 0;
	}

    return -1;  /* failed to find it */
}

int fea_value_i(const char *name,int pos,esps_hdr hdr, int *d)
{
    /* Get value of int FEA */
    esps_fea t;
    
    for (t=hdr->fea; t != NULL; t=t->next)
	if (streq(name,t->name))
	{
	    if (t->dtype != ESPS_INT)
	    {
		fprintf(stderr,"ESPS hdr: access non-int field \"%s\" as int\n",
			name);
		return -1;
	    }
	    *d = t->v.ival[pos];
	    return 0;
	}

    return -1;  /* failed to find it */
}

int fea_value_c(const char *name,int pos,esps_hdr hdr, char *d)
{
    /* Get value of int FEA */
    esps_fea t;
    
    for (t=hdr->fea; t != NULL; t=t->next)
	if (streq(name,t->name))
	{
	    if (t->dtype != ESPS_CHAR)
	    {
		fprintf(stderr,"ESPS hdr: access non-char field \"%s\" as char\n",
			name);
		return -1;
	    }
	    *d = t->v.cval[pos];
	    return 0;
	}

    return -1;  /* failed to find it */
}

static int esps_alloc_fea(esps_fea r)
{
    switch (r->dtype)
    {
      case 0: /* nothing */
	break;
      case ESPS_DOUBLE:
	r->v.dval = walloc(double,r->count); break;
      case ESPS_FLOAT:
	r->v.fval = walloc(float,r->count); break;
      case ESPS_INT:
	r->v.ival = walloc(int,r->count); break;
      case ESPS_SHORT:
	r->v.sval = walloc(short,r->count); break;
      case ESPS_CHAR:
	r->v.cval = walloc(char,r->count); break;
      default:
	fprintf(stderr,"ESPS file: unsupported FEA dtype\n");
	return -1;
    }

    return 0;
}

void write_esps_fea(FILE *fd, esps_fea t,esps_hdr hdr)
{
    /* write out this fea */
    (void)hdr;
    short clength;
    char *nspace;
    int i;

    fwrite(&t->type,2,1,fd);
    clength = (strlen(t->name)+3)/4;
    fwrite(&clength,2,1,fd);
    nspace = walloc(char, clength*4);
    memset(nspace,0,clength*4);
    memmove(nspace,t->name,strlen(t->name));
    fwrite(nspace,1,clength*4,fd);
    wfree(nspace);
    if ((t->type == 11) ||
	(t->type == 1) ||
	(t->type == 15))
	return;
    fwrite(&t->count,4,1,fd);
    fwrite(&t->dtype,2,1,fd);
    
    for (i=0; i<t->count; i++)
    {
	switch(t->dtype)
	{
	  case ESPS_DOUBLE: 
	    fwrite(&t->v.dval[i],8,1,fd); break;
	  case ESPS_FLOAT: 
	    fwrite(&t->v.fval[i],4,1,fd); break;
	  case ESPS_INT: 
	    fwrite(&t->v.ival[i],4,1,fd); break;
	  case ESPS_SHORT: 
	    fwrite(&t->v.sval[i],2,1,fd); break;
	  case ESPS_CHAR: 
	    fwrite(&t->v.cval[i],1,1,fd); break;
	  default:
	    fprintf(stderr,"ESPS write_hdr: unsupported FEA dtype %d\n",
		    t->dtype);
	}
    }
    return;
}

int write_esps_rec(esps_rec r, esps_hdr h, FILE *fd)
{
    /* will have to worry about swap someday */
    (void)h;
    int i;

    for (i=0; i < r->num_fields; i++)
    {
	switch(r->field[i]->type)
	{
	  case ESPS_DOUBLE:
	    fwrite(r->field[i]->v.dval,8,r->field[i]->dimension,fd);
	    break;
	  case ESPS_FLOAT:
	    fwrite(r->field[i]->v.fval,4,r->field[i]->dimension,fd);
	    break;
	  case ESPS_INT:
	    fwrite(r->field[i]->v.ival,4,r->field[i]->dimension,fd);
	    break;
	  case ESPS_SHORT:
	    fwrite(r->field[i]->v.sval,2,r->field[i]->dimension,fd);
	    break;
	  case ESPS_CHAR:
	    fwrite(r->field[i]->v.cval,1,r->field[i]->dimension,fd);
	    break;
	  case ESPS_CODED:
	    fwrite(r->field[i]->v.sval,2,r->field[i]->dimension,fd);
	    break;

	  default:
	    fprintf(stderr,"ESPS file: unsupported field type %d\n",
		    r->field[i]->type);
	}
    }
    return 0;
}

esps_fea read_esps_fea(FILE *fd, esps_hdr hdr)
{
    /* read next FEA record at point */
    esps_fea r = new_esps_fea();
    short sdata;
    int i;
    int idata;
    float fdata;
    double ddata;
    char cdata;

    fread(&sdata,2,1,fd);
    if (hdr->swapped) sdata = SWAPSHORT(sdata);
    r->type = sdata;
    if (r->type == 0)              /* a field name */
    {   /* next short is the size in bytes */
	fread(&sdata,2,1,fd);
	if (hdr->swapped) sdata = SWAPSHORT(sdata);
	r->clength = sdata;
    }
    else if ((r->type == 13) ||   /* a feature and value */
             (r->type == 11) ||   /* a single string (comment ?) */
             (r->type == 1)  ||   /* a filename */
             (r->type == 4)  ||   /* a filename */
	     (r->type == 15))     /* directory name */
    {                 
	fread(&sdata,2,1,fd);
	if (hdr->swapped) sdata = SWAPSHORT(sdata);
	r->clength = sdata * 4;
    }
    else
    {
	fprintf(stderr,"ESPS: fea record unknown type\n");
	wfree(r);
	return NULL;
    }
    r->name = walloc(char,r->clength+1);
    fread(r->name,1,r->clength,fd);
    r->name[r->clength] = '\0';
    if ((r->type == 11) ||       /* a single string */
	(r->type == 1)  ||       /* a filename */
	(r->type == 15))         /* directory name */
	return r;  
    fread(&idata,4,1,fd);
    if (hdr->swapped) idata = SWAPINT(idata);
    r->count = idata;
    fread(&sdata,2,1,fd);
    if (hdr->swapped) sdata = SWAPSHORT(sdata);
    r->dtype = sdata;
    if (esps_alloc_fea(r) == -1)
	return NULL;
    for (i=0; i<r->count; i++)
    {
	switch (r->dtype)
	{
	  case ESPS_DOUBLE:
	    fread(&ddata,8,1,fd);
	    if (hdr->swapped) swapdouble(&ddata);
	    r->v.dval[i] = ddata;
	    break;
	  case ESPS_FLOAT:
	    fread(&fdata,4,1,fd);
	    if (hdr->swapped) swapfloat(&fdata);
	    r->v.fval[i] = fdata;
	    break;
	  case ESPS_INT:
	    fread(&idata,4,1,fd);
	    if (hdr->swapped) idata = SWAPINT(idata);
	    r->v.ival[i] = idata;
	    break;
	  case ESPS_SHORT:
	    fread(&sdata,2,1,fd);
	    if (hdr->swapped) sdata = SWAPSHORT(sdata);
	    r->v.sval[i] = sdata;
	    break;
	  case ESPS_CHAR:
	    fread(&cdata,1,1,fd);
	    r->v.cval[i] = cdata;
	    break;
	  default:
	    fprintf(stderr,"ESPS read_hdr: unsupported FEA dtype %d\n",r->dtype);
	    wfree(r);
	    return NULL;
	}
    }

    return r;
}    

static char *esps_get_field_name(FILE *fd, esps_hdr hdr, int expect_source)
{
    /* read the next field name */
  short size=0;  /* bet its really a short */
  char *name;
  
  if (fread(&size,2,1,fd) != 1)
    {
      fputs("error reading field name size\n", stderr);
      return wstrdup("ERROR");
    }
  if (hdr->swapped) size = SWAPSHORT(size);
  name = walloc(char,size+1);
  if (fread(name,1,size,fd) != (unsigned)size)
    {
      fputs("error reading field name\n", stderr);
      strncpy(name, "ERROR", size);
    }
  name[size] = '\0';
  if (hdr->file_type == ESPS_SD || expect_source)
    fseek(fd,6,SEEK_CUR);  /* skip some zeroes */
  else
    fseek(fd,2,SEEK_CUR);

  if (expect_source)
    {
      fread(&size,2,1,fd);
      if (hdr->swapped) size = SWAPSHORT(size);
      fseek(fd,size,SEEK_CUR);
    }

  return name;
}

static void esps_put_field_name(char *name,FILE *fd, esps_hdr hdr)
{
    /* write the next field name */
    short size = strlen(name);
    short shortdata;

    shortdata = 0;
    fwrite(&shortdata,2,1,fd);
    fwrite(&size,2,1,fd);
    fwrite(name,1,size,fd);
    if (hdr->file_type == ESPS_SD)
    {
	shortdata = 0;
	fwrite(&shortdata,2,1,fd);
	fwrite(&shortdata,2,1,fd);
	fwrite(&shortdata,2,1,fd);
    }
    return;
}

esps_hdr new_esps_hdr(void)
{
    esps_hdr h = walloc(struct ESPS_HDR_struct,1);
    h->file_type = ESPS_FEA;
    h->swapped = FALSE;
    h->num_records = 0;
    h->num_fields = 0;
    h->field_name = NULL;
    h->field_type = NULL;
    h->field_dimension = NULL;
    h->fea = NULL;
    return h;
}

void delete_esps_hdr(esps_hdr h)
{
    int i;
    if (h != NULL)
    {
	if (h->field_name != NULL)
	{
	    for (i=0; i < h->num_fields; i++)
		wfree(h->field_name[i]);
	    wfree(h->field_name);
	}
	delete_esps_fea(h->fea);
    }
}

esps_rec new_esps_rec(esps_hdr hdr)
{
    /* New esps record */
    esps_rec r = walloc(struct ESPS_REC_struct,1);
    int i,size;

    r->field = walloc(esps_field,hdr->num_fields);
    for (size=0,i=0; i < hdr->num_fields; i++)
    {
	r->field[i]=walloc(struct ESPS_FIELD_struct,1);
	r->field[i]->type = hdr->field_type[i];
	r->field[i]->dimension = hdr->field_dimension[i];
	switch(r->field[i]->type)
	{
	  case ESPS_DOUBLE:
	    r->field[i]->v.dval = walloc(double,r->field[i]->dimension);
	    size += 8;
	    break;
	  case ESPS_FLOAT:
	    r->field[i]->v.fval = walloc(float,r->field[i]->dimension);
	    size += 4;
	    break;
	  case ESPS_INT:
	    r->field[i]->v.ival = walloc(int,r->field[i]->dimension);
	    size += 4;
	    break;
	  case ESPS_SHORT:
	    r->field[i]->v.sval = walloc(short,r->field[i]->dimension);
	    size += 2;
	    break;
	  case ESPS_CHAR:
	    r->field[i]->v.cval = walloc(char,r->field[i]->dimension);
	    size += 1;
	    break;
	  case ESPS_CODED:
	    r->field[i]->v.sval = walloc(short,r->field[i]->dimension);
	    size += 2;
	    break;
	  default:
	    fprintf(stderr,"ESPS file: unsupported field type %d\n",
		    r->field[i]->type);
	}
    }
    r->num_fields = hdr->num_fields;
    r->size = size;
    return r;

}

void delete_esps_rec(esps_rec r)
{
    int i;

    for (i=0; i<r->num_fields; i++)
    {
	wfree(r->field[i]->v.ival);
	wfree(r->field[i]);
    }
    wfree(r->field);
    return;
}

int read_esps_rec(esps_rec r, esps_hdr hdr, FILE *fd)
{
    /* read the next record at point */
    int i,j;
    double doubledata;
    float floatdata;
    int intdata;
    short shortdata;
    
    for (i=0; i< r->num_fields; i++)
    {
	switch (r->field[i]->type)
	{
	  case ESPS_DOUBLE:
	    for(j=0; j < r->field[i]->dimension; j++)
	    {
		if (fread(&doubledata,8,1,fd) == 0) return EOF;
		if (hdr->swapped) swapdouble(&doubledata);
		r->field[i]->v.dval[j] = doubledata;
	    }
	    break;
	  case ESPS_FLOAT:
	    for(j=0; j < r->field[i]->dimension; j++)
	    {
		if (fread(&floatdata,4,1,fd) == 0) return EOF;
		if (hdr->swapped) swapfloat(&floatdata);
		r->field[i]->v.fval[j] = floatdata;
	    }
	    break;
	  case ESPS_INT:
	    for(j=0; j < r->field[i]->dimension; j++)
	    {
		if (fread(&intdata,4,1,fd) == 0) return EOF;
		if (hdr->swapped) intdata = SWAPINT(intdata);
		r->field[i]->v.ival[j] = intdata;
	    }
	    break;
	  case ESPS_SHORT:
	    for(j=0; j < r->field[i]->dimension; j++)
	    {
		if (fread(&shortdata,2,1,fd) == 0) return EOF;
		if (hdr->swapped) shortdata = SWAPSHORT(shortdata);
		r->field[i]->v.sval[j] = shortdata;
	    }
	    break;
	  case ESPS_CHAR:
	    if (fread(r->field[i]->v.cval,1,r->field[i]->dimension,fd) !=
		(unsigned)r->field[i]->dimension) return EOF;
	    break;
	  case ESPS_CODED:
	    for(j=0; j < r->field[i]->dimension; j++)
	    {
		if (fread(&shortdata,2,1,fd) == 0) return EOF;
		if (hdr->swapped) shortdata = SWAPSHORT(shortdata);
		r->field[i]->v.sval[j] = shortdata;
	    }
	    break;
	  default:
	    fprintf(stderr,"ESPS file: unsupported field type %d\n",
		    r->field[i]->type);
	    return EOF;
	}

    }
    
    return 0;

}

double get_field_d(esps_rec r, int field, int pos)
{
    return r->field[field]->v.dval[pos];
}
float get_field_f(esps_rec r, int field, int pos)
{
    return r->field[field]->v.fval[pos];
}
int get_field_i(esps_rec r, int field, int pos)
{
    return r->field[field]->v.ival[pos];
}
short get_field_s(esps_rec r, int field, int pos)
{
    return r->field[field]->v.sval[pos];
}
char get_field_c(esps_rec r, int field, int pos)
{
    return r->field[field]->v.cval[pos];
}
void set_field_d(esps_rec r, int field, int pos, double d)
{
    r->field[field]->v.dval[pos] = d;
}
void set_field_f(esps_rec r, int field, int pos, float d)
{
    r->field[field]->v.fval[pos] = d;
}
void set_field_i(esps_rec r, int field, int pos, int d)
{
    r->field[field]->v.ival[pos] = d;
}
void set_field_s(esps_rec r, int field, int pos, short d)
{
    r->field[field]->v.sval[pos] = d;
}
void set_field_c(esps_rec r, int field, int pos, char d)
{
    r->field[field]->v.cval[pos] = d;
}

int esps_record_size(esps_hdr hdr)
{
    /* works out the number of bytes in a record */
    esps_rec r = new_esps_rec(hdr);
    int size = r->size;
    delete_esps_rec(r);

    return size;
}

static int esps_num_of_type(int type,esps_hdr hdr)
{
    /* counts up the number of occurrences of fields of type in a record */
    int i;
    int sum;

    for (sum=i=0; i < hdr->num_fields; i++)
	if (hdr->field_type[i] == type)
	    sum++;
    return sum;
}    


esps_hdr make_esps_sd_hdr(void)
{
    /* returns a basic header for an ESPS_SD file */
    esps_hdr hdr = new_esps_hdr();
    hdr->file_type = ESPS_SD;
    return hdr;

}

esps_hdr make_esps_hdr(void)
{
    /* returns a basic header for an ESPS_SD file */
    esps_hdr hdr = new_esps_hdr();
    hdr->file_type = ESPS_FEA;
    return hdr;

}

enum EST_read_status read_esps_hdr(esps_hdr *uhdr,FILE *fd)
{
    /* reads an ESPS header from fd at point (should be position 0) */
    /* leaves point at start of data (immediately after header)     */
    struct ESPS_PREAMBLE preamble;
    struct ESPS_FIXED_HDR fhdr;
    esps_hdr hdr;
    int end,pos,intdata,i;
    short shortdata;
    double sd_sample_rate;
    int typematch;
    int swap;
    short name_flag;

    fread(&preamble,sizeof(preamble),1,fd);
    if (preamble.check == ESPS_MAGIC)
	swap = FALSE;
    else if (preamble.check == SWAPINT(ESPS_MAGIC))
	swap = TRUE;
    else
	return wrong_format;

    hdr = new_esps_hdr();
    hdr->swapped = swap;
    fread(&fhdr,sizeof(fhdr),1,fd);
    if (hdr->swapped) 
    {
	preamble.data_offset = SWAPINT(preamble.data_offset);
	preamble.record_size = SWAPINT(preamble.record_size);
	fhdr.num_samples = SWAPINT(fhdr.num_samples);
	fhdr.num_doubles = SWAPINT(fhdr.num_doubles);
	fhdr.num_floats = SWAPINT(fhdr.num_floats);
	fhdr.num_ints = SWAPINT(fhdr.num_ints);
	fhdr.num_shorts = SWAPINT(fhdr.num_shorts);
	fhdr.num_chars = SWAPINT(fhdr.num_chars);
	fhdr.fea_type = SWAPSHORT(fhdr.fea_type);
	fhdr.num_fields = SWAPSHORT(fhdr.num_fields);
    }
    pos = ftell(fd);
    if (fhdr.num_samples == 0)  /* has to be derived from the file size */
    {
	pos = ftell(fd);
	fseek(fd,0,SEEK_END);
	end = ftell(fd);
	fseek(fd,pos,SEEK_SET);
	fhdr.num_samples = (end - preamble.data_offset)/preamble.record_size;
    }
    hdr->num_records = fhdr.num_samples;
    hdr->num_fields = fhdr.num_fields;
    hdr->hdr_size = preamble.data_offset;
    if (fhdr.thirteen == 9)
    {   /* esps identifies such files are as Sample Data Files */
	hdr->file_type = ESPS_SD;
	/* fake the rest to make it appear like other SD files */
	hdr->num_fields = 1;
	hdr->field_dimension = walloc(int,hdr->num_fields);
	hdr->field_dimension[0] = 1;
	hdr->field_type = walloc(short,hdr->num_fields);
	hdr->field_type[0] = ESPS_SHORT;
	hdr->field_name = walloc(char *,1);
	hdr->field_name[0] = wstrdup("samples");
	fseek(fd,hdr->hdr_size,SEEK_SET);
	/* In this cases its just in the header as a float */
	sd_sample_rate = *((float *)&fhdr.fil4[0]);
	add_fea_d(hdr,"record_freq",0,(double)sd_sample_rate);
	*uhdr = hdr;
	return format_ok;
    }
    else if ((fhdr.fea_type == 8) &&
	     (hdr->num_fields == 1) &&
	     ((fhdr.num_shorts*2) == preamble.record_size))
	hdr->file_type = ESPS_SD;  /* this is a heuristic */
    else
	hdr->file_type = ESPS_FEA;
    /* Now we have the field descriptions */
    
    /* 0000 0001 dimensions */
    hdr->field_dimension = walloc(int,hdr->num_fields);
    for (i=0; i<hdr->num_fields; i++)                   
    {
	fread(&intdata,4,1,fd);                         /* dimensions */
	if (hdr->swapped) intdata = SWAPINT(intdata);
	hdr->field_dimension[i] = intdata;
    }
    /* 0 -> num_fields-1 -- probably ordering information */
    fseek(fd,hdr->num_fields*4,SEEK_CUR);               /* ordering info */
    fseek(fd,hdr->num_fields*2,SEEK_CUR);               /* zeros */
    hdr->field_type = walloc(short,hdr->num_fields);    
    for (i=0; i<hdr->num_fields; i++)
    {
	fread(&shortdata,2,1,fd);                       /* field types */  
	if (hdr->swapped) shortdata = SWAPSHORT(shortdata);
	hdr->field_type[i] = shortdata;
    }
    typematch = TRUE;
    fread(&intdata,4,1,fd);                             /* number of doubles */
    if (hdr->swapped) intdata = SWAPINT(intdata);
    if (fhdr.num_doubles != intdata) typematch = FALSE;
    fread(&intdata,4,1,fd);                             /* number of floats */
    if (hdr->swapped) intdata = SWAPINT(intdata);
    if (fhdr.num_floats != intdata) typematch = FALSE;
    fread(&intdata,4,1,fd);                             /* number of ints */
    if (hdr->swapped) intdata = SWAPINT(intdata);
    if (fhdr.num_ints != intdata) typematch = FALSE;
    fread(&intdata,4,1,fd);                             /* number of shorts */
    if (hdr->swapped) intdata = SWAPINT(intdata);
    if (fhdr.num_shorts != intdata) typematch = FALSE;
    fread(&intdata,4,1,fd);                             /* number of chars */
    if (hdr->swapped) intdata = SWAPINT(intdata);
    if (fhdr.num_chars != intdata) typematch = FALSE;
    if ((hdr->file_type != ESPS_SD) && (typematch == FALSE))
    {
	fprintf(stderr,"ESPS hdr: got lost in the header (record description)\n");
	delete_esps_hdr(hdr);
	return misc_read_error;
    }
    /* other types ... */
    fseek(fd,9*2,SEEK_CUR);                             /* other types */
    fseek(fd,hdr->num_fields*2,SEEK_CUR);               /* zeros */
    /* Now we can read the field names */
    hdr->field_name = walloc(char *,hdr->num_fields);

    fread(&name_flag, 2, 1, fd);
    if (hdr->swapped) name_flag = SWAPSHORT(name_flag);

    for (i=0; i < hdr->num_fields; i++)
	hdr->field_name[i] = esps_get_field_name(fd,hdr,name_flag); /* field names */
    if (hdr->file_type == ESPS_SD)
    {   /* Only one field 'samples' */
	if (!streq(hdr->field_name[0],"samples"))
	{
	    fprintf(stderr,"ESPS hdr: guessed wrong about FEA_SD file (no 'samples' field)\n");
	    delete_esps_hdr(hdr);
	    return misc_read_error;
	}
    }

    /* Now fea, feature and value -- but how many are there ? */
    while (ftell(fd) < preamble.data_offset-4)
    {
	esps_fea r = read_esps_fea(fd,hdr);              /* feas */
	if (r == NULL) break;
/*	print_esps_fea(r); */
	r->next = hdr->fea;
	hdr->fea = r;
	if (r->type == 1) 
	    break; /* I think this (filename) is last FEA */
    }
    /* There's other gunk after this but I think I've done enough */
    /* The rest seems to be mostly previous headers               */

    fseek(fd,hdr->hdr_size,SEEK_SET); /* skip the rest of the header */
    *uhdr = hdr;
	
    return format_ok;
}


enum EST_write_status write_esps_hdr(esps_hdr hdr,FILE *fd)
{
    /* well here's the scary part, try to write a valid file hdr to */
    /* the file                                                     */
    struct ESPS_PREAMBLE preamble;
    struct ESPS_FIXED_HDR fhdr;
    time_t tx = time(0);
    esps_fea t;
    int i,intdata;
    short shortdata;

    memset(&preamble,0,sizeof(preamble));
    memset(&fhdr,0,sizeof(fhdr));
    /* I can't really make the machine code work properly, so I'll  */
    /* just fix it for the two major byte orders to Sun and Suni386 */
    if (EST_NATIVE_BO == bo_big)
	preamble.machine_code = 4;  /* a sun */
    else
	preamble.machine_code = 6;  /* a suni386 */
    preamble.check_code  = 3000;  /* ? */
    preamble.data_offset = 0;  /* will come back and fix this later */
    preamble.record_size = esps_record_size(hdr);
    preamble.check = ESPS_MAGIC;
    preamble.edr = 0;
    preamble.fil1 = 0;
    preamble.foreign_hd = 0; /* docs say it should be -1, but its always 0 */

    fhdr.thirteen = 13;      /* must be for luck */
    fhdr.sdr_size = 0;
    fhdr.magic = ESPS_MAGIC;
    strncpy(fhdr.date,ctime(&tx),26);
    sprintf(fhdr.version,"1.91");  /* that's what all the others have */
    sprintf(fhdr.prog,"EDST");
    sprintf(fhdr.vers,"0.1");
    strncpy(fhdr.progcompdate,ctime(&tx),26);
    fhdr.num_samples = hdr->num_records;
    fhdr.filler = 0;
    /* in each record */
    fhdr.num_doubles = esps_num_of_type(ESPS_DOUBLE,hdr);
    fhdr.num_floats  = esps_num_of_type(ESPS_FLOAT,hdr);
    fhdr.num_ints    = esps_num_of_type(ESPS_INT,hdr);
    fhdr.num_shorts  = esps_num_of_type(ESPS_SHORT,hdr);
    fhdr.num_chars   = esps_num_of_type(ESPS_CHAR,hdr);
    fhdr.fsize = 40;
    fhdr.hsize = 0;    /* given value below on second shot */
    if (hdr->file_type == ESPS_SD)
	fhdr.fea_type = 8;
    else
	fhdr.fea_type = 0;
    fhdr.num_fields = hdr->num_fields;

    fwrite(&preamble,sizeof(preamble),1,fd);
    fwrite(&fhdr,sizeof(fhdr),1,fd);
    /* The following cover dimensions, type and ordering info */
    for (i=0; i < hdr->num_fields; i++)
    {   /* Dimensions (i.e. number of channels) */
	intdata = 1;
	fwrite(&intdata,4,1,fd);                        /* dimensions */
    }
    for (i=0; i < hdr->num_fields; i++)                 /* ordering info (?) */
	fwrite(&i,4,1,fd);
    if (hdr->file_type == ESPS_SD)  /* zeros hmm should be zeroes only */
	shortdata = 1;              /* is FEA case, 1 in ESPS_SD case  */
    else                            /* fixed 24/7/98                   */
	shortdata = 0;
    for (i=0; i < hdr->num_fields; i++)
	fwrite(&shortdata,2,1,fd);     
    for (i=0; i < hdr->num_fields; i++)    
    {
	shortdata = hdr->field_type[0];                 /* field types */
	fwrite(&shortdata,2,1,fd);
    }
    intdata = fhdr.num_doubles;                        /* number of doubles */
    fwrite(&intdata,4,1,fd);
    intdata = fhdr.num_floats;                         /* number of floats */
    fwrite(&intdata,4,1,fd);
    intdata = fhdr.num_ints;                           /* number of ints */
    fwrite(&intdata,4,1,fd);
    intdata = fhdr.num_shorts;                         /* number of shorts */
    fwrite(&intdata,4,1,fd);
    intdata = fhdr.num_chars;                          /* number of chars */
    fwrite(&intdata,4,1,fd);
    shortdata = 0;
    for (i=0; i < 9; i++)
	fwrite(&shortdata,2,1,fd);                      /* other types */
    for (i=0; i < hdr->num_fields; i++)
	fwrite(&shortdata,2,1,fd);                      /* zeros */
    /* Now dump the filednames */
    for (i=0; i < hdr->num_fields; i++)
	esps_put_field_name(hdr->field_name[i],fd,hdr); /* field names */
    if (hdr->file_type != ESPS_SD)
	fwrite(&shortdata,2,1,fd);   /* another 0 */
    /* Now the feas */
    for (t=hdr->fea; t != NULL; t=t->next)
	write_esps_fea(fd,t,hdr);                       /* feas */
    /* now have to go back and fix the header size */
    intdata = 0;
    fwrite(&intdata,4,1,fd);
    preamble.data_offset = ftell(fd);
    fhdr.hsize = (preamble.data_offset-249)/2;
    if (fseek(fd,0,SEEK_SET) == -1)
    {
	fprintf(stderr,"esps write header: can't fseek to start of file\n");
	return misc_write_error;
    }
    fwrite(&preamble,sizeof(preamble),1,fd);
    fwrite(&fhdr,sizeof(fhdr),1,fd);
    fseek(fd,preamble.data_offset,SEEK_SET);
    
    return write_ok;
}
