/*----------------------------------------------------------------------* * IFFR.C Support routines for reading IFF-85 files. 11/15/85 * (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. *----------------------------------------------------------------------*/ #include "iff.h" /* #include "DF1:iff/gio.h" */ /* #define OFFSET_BEGINNING OFFSET_BEGINING */ /** Manx expects INTs as 16 bits, This wont matter on LAttice ***/ extern LONG Seek(); extern LONG Read(); /* ---------- Read -----------------------------------------------------*/ extern LONG PutID(); /** Added as a diagnostic aid, will remove later ***/ /* ---------- 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 = NL; /* "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. AmigaDOS specific code.*/ if (file <= 0) return(NO_FILE); Seek(file, 0L, OFFSET_END); /* Seek to end of file.*/ new->bound = Seek(file, 0L, OFFSET_CURRENT); /* Pos'n == #bytes in file.*/ if (new->bound < 0) return(DOS_ERROR); /* DOS being absurd.*/ Seek(file, 0L, OFFSET_BEGINNING); /* Go to file start.*/ /* Would just do this if Amiga DOS maintained fh_End: */ /* new->bound = (FileHandle *)BADDR(file)->fh_End; */ if ( new->bound < (long)sizeof(ChunkHeader) ) iffp = NOT_IFF; 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 == NL) { } /* 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 == Seek(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 ((long)sizeof(ChunkHeader) > remaining) { context->ckHdr.ckSize = remaining; } /* Read the chunk header (finally). */ else { switch (Read(context->file, &context->ckHdr, (long)sizeof(ChunkHeader))) { case -1: return(context->ckHdr.ckID = DOS_ERROR); case 0: return(context->ckHdr.ckID = BAD_IFF); } /*** $$$ *** PutID(context->ckHdr.ckID); printf("\n"); printf("id = %lx\n", context->ckHdr.ckID); **/ /* Check: Top level chunk must be LIST or FORM or CAT. */ if (context->parent == NL) { if (context->ckHdr.ckID != FORM && context->ckHdr.ckID != LIST && context->ckHdr.ckID != CAT ) return(context->ckHdr.ckID = NOT_IFF); } /* Update the context. */ context->position += (long)sizeof(ChunkHeader); remaining -= (long)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 { if (context->ckHdr.ckID == LIST || context->ckHdr.ckID == FORM || context->ckHdr.ckID == PROP || context->ckHdr.ckID == CAT) { iffp = IFFReadBytes(context, (BYTE *)&context->subtype, (long)sizeof(ID)); if (iffp != IFF_OKAY ) context->ckHdr.ckID = iffp; } } } 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 ( Read(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) { iffp = GetChunkHdr(&context); if (iffp == FORM) iffp = (*clientFrame->getForm)(&context); else if (iffp == LIST) iffp = (*clientFrame->getList)(&context); else if (iffp == CAT) iffp = (*clientFrame->getCat)(&context); } 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 { iffp = GetChunkHdr(&listContext); if (iffp == PROP) { if (propOk) iffp = (*clientFrame->getProp)(&listContext); else iffp = BAD_IFF; } else if (iffp == FORM) iffp = (*clientFrame->getForm)(&listContext); else if (iffp == LIST) iffp = (*clientFrame->getList)(&listContext); else if (iffp == CAT) iffp = (*clientFrame->getList)(&listContext); 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, NL) ); } /* ---------- 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; id = GetChunkHdr(context); if (id == PROP) id = BAD_IFF; else if (id == FORM) id = (*clientFrame->getForm)(context); else if (id == LIST) id = (*clientFrame->getForm)(context); else if (id == CAT) id = (*clientFrame->getCat)(context); return(context->ckHdr.ckID = id); } /* ---------- GetPChunkHdr ---------------------------------------------*/ ID GetPChunkHdr(context) GroupContext *context; { register ID id; id = GetChunkHdr(context); if (id == LIST || id == FORM || id == PROP || id == CAT ) id = context->ckHdr.ckID = BAD_IFF; return(id); }