home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------*
- * 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 "gio.h"
- #include "iff.h"
- #include "intuall.h"
- #include "stdio.h"
- #include "functions.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) {
- Seek(file, 0L, (long)OFFSET_END); /* Seek to end of file.*/
- fileLength = Seek(file, 0L, (long)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, 0L, (long)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, (long)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,
- (long)(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, (long)sizeof(ChunkHeader))
- ) {
- case -1: return((ID)(context->ckHdr.ckID = DOS_ERROR));
- case 0: return((ID)(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((ID)(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,
- (long)sizeof(ID));
- if (iffp != IFF_OKAY)
- context->ckHdr.ckID = iffp;
- break; }
- }
-
- }
- return((long)(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 > 0) {
- if (nBytes > ChunkMoreBytes(context)) nBytes = ChunkMoreBytes(context);
- 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;
- }
- }
- }
- if (iffp != IFF_OKAY) printf("IFFReadBytes: %ld\n",(long)iffp);
- 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, (long)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((long)(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);
- }
-
-