home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d185
/
iff.lha
/
SOURCE
/
iffr.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-12-13
|
10KB
|
323 lines
/*----------------------------------------------------------------------*
* IFFR.C Support routines for reading IFF-85 files. 1/23/86
* (IFF is Interchange Format File.)
*
* By Jerry Morrison and Steve Shaw, Electronic Arts.
* This software is in the public domain.
*
* This version for the Commodore-Amiga computer.
*
* Uses "gio". Either link with gio.c, or set the GIO_ACTIVE flag to 0
* in gio.h.
*----------------------------------------------------------------------*/
#include "iff/gio.h"
#include "iff/iff.h"
/* ----- Private subroutine FileLength() --------------------------------*/
/* Returns the length of the file or else a negative IFFP error code
* (NO_FILE or DOS_ERROR). AmigaDOS-specific implementation.
* SIDE EFFECT: Thanks to AmigaDOS, we have to change the file's position
* to find its length.
* Now if Amiga DOS maintained fh_End, we'd just do this:
* fileLength = (FileHandle *)BADDR(file)->fh_End; */
LONG FileLength(file) BPTR file; {
LONG fileLength = NO_FILE;
if (file > 0) {
GSeek(file, 0, OFFSET_END); /* Seek to end of file.*/
fileLength = GSeek(file, 0, OFFSET_CURRENT);
/* Returns position BEFORE the seek, which is #bytes in file. */
if (fileLength < 0)
fileLength = DOS_ERROR; /* DOS being absurd.*/
}
return(fileLength);
}
/* ---------- Read -----------------------------------------------------*/
/* ---------- OpenRIFF --------------------------------------------------*/
IFFP OpenRIFF(file0, new0, clientFrame)
BPTR file0; GroupContext *new0; ClientFrame *clientFrame; {
register BPTR file = file0;
register GroupContext *new = new0;
IFFP iffp = IFF_OKAY;
new->parent = NULL; /* "whole file" has no parent.*/
new->clientFrame = clientFrame;
new->file = file;
new->position = 0;
new->ckHdr.ckID = new->subtype = NULL_CHUNK;
new->ckHdr.ckSize = new->bytesSoFar = 0;
/* Set new->bound and go to the file's beginning. */
new->bound = FileLength(file);
if (new->bound < 0)
iffp = new->bound; /* File system error! */
else if ( new->bound < sizeof(ChunkHeader) )
iffp = NOT_IFF; /* Too small for an IFF file. */
else
GSeek(file, 0, OFFSET_BEGINNING); /* Go to file start. */
return(iffp);
}
/* ---------- OpenRGroup -----------------------------------------------*/
IFFP OpenRGroup(parent0, new0) GroupContext *parent0, *new0; {
register GroupContext *parent = parent0;
register GroupContext *new = new0;
IFFP iffp = IFF_OKAY;
new->parent = parent;
new->clientFrame = parent->clientFrame;
new->file = parent->file;
new->position = parent->position;
new->bound = parent->position + ChunkMoreBytes(parent);
new->ckHdr.ckID = new->subtype = NULL_CHUNK;
new->ckHdr.ckSize = new->bytesSoFar = 0;
if ( new->bound > parent->bound || IS_ODD(new->bound) )
iffp = BAD_IFF;
return(iffp);
}
/* ---------- CloseRGroup -----------------------------------------------*/
IFFP CloseRGroup(context) GroupContext *context; {
register LONG position;
if (context->parent == NULL) {
} /* Context for whole file.*/
else {
position = context->position;
context->parent->bytesSoFar += position - context->parent->position;
context->parent->position = position;
}
return(IFF_OKAY);
}
/* ---------- SkipFwd --------------------------------------------------*/
/* Skip over bytes in a context. Won't go backwards.*/
/* Updates context->position but not context->bytesSoFar.*/
/* This implementation is AmigaDOS specific.*/
IFFP SkipFwd(context, bytes) GroupContext *context; LONG bytes; {
IFFP iffp = IFF_OKAY;
if (bytes > 0) {
if (-1 == GSeek(context->file, bytes, OFFSET_CURRENT))
iffp = BAD_IFF; /* Ran out of bytes before chunk complete.*/
else
context->position += bytes;
}
return(iffp);
}
/* ---------- GetChunkHdr ----------------------------------------------*/
ID GetChunkHdr(context0) GroupContext *context0; {
register GroupContext *context = context0;
register IFFP iffp;
LONG remaining;
/* Skip remainder of previous chunk & padding. */
iffp = SkipFwd(context,
ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
CheckIFFP();
/* Set up to read the new header. */
context->ckHdr.ckID = BAD_IFF; /* Until we know it's okay, mark it BAD.*/
context->subtype = NULL_CHUNK;
context->bytesSoFar = 0;
/* Generate a psuedo-chunk if at end-of-context. */
remaining = context->bound - context->position;
if (remaining == 0) {
context->ckHdr.ckSize = 0;
context->ckHdr.ckID = END_MARK;
}
/* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
else if (sizeof(ChunkHeader) > remaining) {
context->ckHdr.ckSize = remaining;
}
/* Read the chunk header (finally). */
else {
switch (
GRead(context->file, (BYTE *)&context->ckHdr, sizeof(ChunkHeader))
) {
case -1: return(context->ckHdr.ckID = DOS_ERROR);
case 0: return(context->ckHdr.ckID = BAD_IFF);
}
/* Check: Top level chunk must be LIST or FORM or CAT. */
if (context->parent == NULL)
switch(context->ckHdr.ckID) {
case FORM: case LIST: case CAT: break;
default: return(context->ckHdr.ckID = NOT_IFF);
}
/* Update the context. */
context->position += sizeof(ChunkHeader);
remaining -= sizeof(ChunkHeader);
/* Non-positive ID values are illegal and used for error codes.*/
/* We could check for other illegal IDs...*/
if (context->ckHdr.ckID <= 0)
context->ckHdr.ckID = BAD_IFF;
/* Check: ckSize negative or larger than # bytes left in context? */
else if (context->ckHdr.ckSize < 0 ||
context->ckHdr.ckSize > remaining) {
context->ckHdr.ckSize = remaining;
context->ckHdr.ckID = BAD_IFF;
}
/* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
else switch (context->ckHdr.ckID) {
case LIST: case FORM: case PROP: case CAT: {
iffp = IFFReadBytes(context,
(BYTE *)&context->subtype,
sizeof(ID));
if (iffp != IFF_OKAY)
context->ckHdr.ckID = iffp;
break; }
}
}
return(context->ckHdr.ckID);
}
/* ---------- IFFReadBytes ---------------------------------------------*/
IFFP IFFReadBytes(context, buffer, nBytes)
GroupContext *context; BYTE *buffer; LONG nBytes; {
register IFFP iffp = IFF_OKAY;
if (nBytes < 0)
iffp = CLIENT_ERROR;
else if (nBytes > ChunkMoreBytes(context))
iffp = SHORT_CHUNK;
else if (nBytes > 0)
switch ( GRead(context->file, buffer, nBytes) ) {
case -1: {iffp = DOS_ERROR; break; }
case 0: {iffp = BAD_IFF; break; }
default: {
context->position += nBytes;
context->bytesSoFar += nBytes;
}
}
return(iffp);
}
/* ---------- SkipGroup ------------------------------------------------*/
IFFP SkipGroup(context) GroupContext *context; {
} /* Nothing to do, thanks to GetChunkHdr */
/* ---------- ReadIFF --------------------------------------------------*/
IFFP ReadIFF(file, clientFrame) BPTR file; ClientFrame *clientFrame; {
/*CompilerBug register*/ IFFP iffp;
GroupContext context;
iffp = OpenRIFF(file, &context);
context.clientFrame = clientFrame;
if (iffp == IFF_OKAY)
switch (iffp = GetChunkHdr(&context)) {
case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
case LIST: { iffp = (*clientFrame->getList)(&context); break; }
case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
/* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
}
CloseRGroup(&context);
if (iffp > 0) /* Make sure we don't return an ID.*/
iffp = NOT_IFF; /* GetChunkHdr should've caught this.*/
return(iffp);
}
/* ---------- ReadIList ------------------------------------------------*/
IFFP ReadIList(parent, clientFrame)
GroupContext *parent; ClientFrame *clientFrame; {
GroupContext listContext;
IFFP iffp;
BOOL propOk = TRUE;
iffp = OpenRGroup(parent, &listContext);
CheckIFFP();
/* One special case test lets us handle CATs as well as LISTs.*/
if (parent->ckHdr.ckID == CAT)
propOk = FALSE;
else
listContext.clientFrame = clientFrame;
do {
switch (iffp = GetChunkHdr(&listContext)) {
case PROP: {
if (propOk)
iffp = (*clientFrame->getProp)(&listContext);
else
iffp = BAD_IFF;
break;
}
case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
/* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
}
if (listContext.ckHdr.ckID != PROP)
propOk = FALSE; /* No PROPs allowed after this point.*/
} while (iffp == IFF_OKAY);
CloseRGroup(&listContext);
if (iffp > 0) /* Only chunk types above are allowed in a LIST/CAT.*/
iffp = BAD_IFF;
return(iffp == END_MARK ? IFF_OKAY : iffp);
}
/* ---------- ReadICat -------------------------------------------------*/
/* By special arrangement with the ReadIList implement'n, this is trivial.*/
IFFP ReadICat(parent) GroupContext *parent; {
return( ReadIList(parent, NULL) );
}
/* ---------- GetFChunkHdr ---------------------------------------------*/
ID GetFChunkHdr(context) GroupContext *context; {
register ID id;
id = GetChunkHdr(context);
if (id == PROP)
context->ckHdr.ckID = id = BAD_IFF;
return(id);
}
/* ---------- GetF1ChunkHdr ---------------------------------------------*/
ID GetF1ChunkHdr(context) GroupContext *context; {
register ID id;
register ClientFrame *clientFrame = context->clientFrame;
switch (id = GetChunkHdr(context)) {
case PROP: { id = BAD_IFF; break; }
case FORM: { id = (*clientFrame->getForm)(context); break; }
case LIST: { id = (*clientFrame->getList)(context); break; }
case CAT : { id = (*clientFrame->getCat )(context); break; }
/* Default: let the caller handle other chunks */
}
return(context->ckHdr.ckID = id);
}
/* ---------- GetPChunkHdr ---------------------------------------------*/
ID GetPChunkHdr(context) GroupContext *context; {
register ID id;
id = GetChunkHdr(context);
switch (id) {
case LIST: case FORM: case PROP: case CAT: {
id = context->ckHdr.ckID = BAD_IFF;
break; }
}
return(id);
}