/* general purpose IFF file cracking routines for Karl's 
 * Audio Stuff by Karl Lehenbauer, based originally on public domain IFF 
 * code from Electronic Arts, 2/24/88
 */

#include <exec/types.h>
#include <functions.h>
#include <exec/memory.h>
#include <stdio.h>
#include <fcntl.h>

#include "assert.h"

#include "iff.h"

PutID(id)
ID id;
{
    fprintf(stderr,"%c%c%c%c",
	   (char)((id>>24L) & 0x7f),
	   (char)((id>>16L) & 0x7f),
	   (char)((id>>8)   & 0x7f),
	   (char)(id        & 0x7f) );
}

UBYTE *MyAllocMem(bytes, type)
ULONG bytes, type;
{
	UBYTE *tmp;

		tmp = AllocMem(bytes, type);
	return tmp;
}

/* return chunktype of next chunk */
/* every time nextchunk is executed and returns that it found a chunk,
 * either readchunk or skipchunk must be called and only one time!
 */
ULONG nextchunk(fd,chunksize)
int fd;
int *chunksize;
{
	int sawsize;
	ChunkHeader mychunkheader;

	if ((sawsize = read(fd,&mychunkheader,sizeof(mychunkheader))) != 
		sizeof(mychunkheader))
	{
		if (sawsize != 0)
			fprintf(stderr,"Something's wrong with nextchunk! (sawsize %d)\n", sawsize);
		*chunksize = 0;
		return(0);
	}
#ifdef MAJORDEBUG
	fputs("nextchunk: checking ",stderr);
	PutID(mychunkheader.ckID);
	fprintf(stderr,", size is %d\n",mychunkheader.ckSize);
#endif

	*chunksize = mychunkheader.ckSize;
	return(mychunkheader.ckID);
}

/* read next chunk into buffer supplied, size must be value returned by
 * nextchunk
 * zero is returned on failure, one on success
 */
readchunk(fd,buf,size)
int fd;
char *buf;
LONG size;
{
	if (read(fd,buf,size) != size) 
	{
		fputs("readchunk: read of IFF chunk failed\n",stderr);
		return(0);
	}
	if (size & 1)
		lseek(fd,1L,1);
	return(1);
}

/* skip non-header portion of chunk, chunksize must have been returned
 * by nextchunk
 */
skipchunk(fd,chunksize)
int fd;
LONG chunksize;
{
	/* skip over chunk data and skip an extra byte if length is odd */
	lseek(fd,(long)chunksize,1);
	if (chunksize & 1)
		lseek(fd,1L,1);
}

/* OpenIFF
 * given file name, open the IFF file.
 * read the header, return failure if it's not a FORM
 * (someday we'll handle the more complex types)
 * read the form type, return failure if it's not the type requested
 * success, return the file descriptor
 */

int OpenIFF(fname,expected_formtype)
char *fname;
LONG expected_formtype;
{
	int iffile;
	ChunkHeader chunkhead;
	LONG formtype;

	/* open the file */
	if ((iffile = open(fname, O_RDONLY)) < 0)
		return(-1);

	/* read the header chunk */
	if (read(iffile, &chunkhead, sizeof(chunkhead)) < 0)
	{
		fprintf(stderr,"OpenIFF: initial read from IFF file %s failed!\n",fname);
		return(-1);
	}

	/* return if the header chunk doesn't say it's IFF FORM */
	if (chunkhead.ckID != ID_FORM)
	{
		fprintf(stderr,"OpenIFF: File %s isn't IFF, is too complex, or doesn't start with FORM\n",fname);
		return(-1);
	}
#ifdef DEBUG
	fprintf(stderr,"OpenIFF: FORM found, size is %d\n",chunkhead.ckSize);
#endif

	/* read the form type */
	read(iffile, &formtype, sizeof(formtype));

	/* return if the form type isn't the type requested */
	if (formtype != expected_formtype)
	{
		fprintf(stderr,"OpenIFF: File %s is IFF ");
		PutID(formtype);
		fprintf(stderr," rather than the requested ");
		PutID(expected_formtype);
		fprintf(stderr,"\n");
		return(-1);
	}
	return(iffile);
}

/* read chunks until one of type chunktype is found or EOF
 * note that after a successful call to chunkuntil,
 * skipchunk or readchunk must be performed or the IFF reading
 * software will get lost on the next nextchunk
 * chunksize is returned on success, -1 otherwise
 * The caller should probably check the return explicitly for -1.
 * If checking only for less than zero, chunks larger than
 * two gigabytes will cause your code to break.
 */

LONG chunkuntil(fd,chunktype)
int fd;
ULONG chunktype;
{
	ULONG currentchunk;
	LONG chunksize;

	while ((currentchunk = nextchunk(fd,&chunksize)) != NULL)
	{
		if (currentchunk == chunktype)
			return(chunksize);
		skipchunk(fd,chunksize);
	}
	return(-1);
}

/* end of iff.c */
