1 /*----------------------------------------------------------------------*
2 * IFFR.C Support routines for reading IFF-85 files. 11/15/85
3 * (IFF is Interchange Format File.)
5 * By Jerry Morrison and Steve Shaw, Electronic Arts.
6 * This software is in the public domain.
8 * Original version was for the Commodore-Amiga computer.
9 * This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008
10 *----------------------------------------------------------------------*/
13 /* #include "DF1:iff/gio.h" */
14 /* #define OFFSET_BEGINNING OFFSET_BEGINING */
16 /** Manx expects INTs as 16 bits, This wont matter on LAttice ***/
20 /* ---------- Read -----------------------------------------------------*/
22 extern int PutID(); /** Added as a diagnostic aid, will remove later ***/
24 /* ---------- OpenRIFF --------------------------------------------------*/
25 IFFP OpenRIFF(BPTR file0, GroupContext *new0,ClientFrame *clientFrame)
27 register BPTR file = file0;
28 register GroupContext *newtmp = new0;
31 newtmp->parent = NL; /* "whole file" has no parent.*/
32 newtmp->clientFrame = clientFrame;
35 newtmp->ckHdr.ckID = newtmp->subtype = NULL_CHUNK;
36 newtmp->ckHdr.ckSize = newtmp->bytesSoFar = 0;
38 /* Set newtmp->bound. AmigaDOS specific code.*/
39 if (file <= 0) return(NO_FILE);
40 Seek(file, 0L, OFFSET_END); /* Seek to end of file.*/
41 newtmp->bound = ftell(file);//Seek(file, 0L, OFFSET_CURRENT); /* Pos'n == #bytes in file.*/
42 if (newtmp->bound < 0) return(DOS_ERROR); /* DOS being absurd.*/
43 Seek(file, 0L, OFFSET_BEGINNING); /* Go to file start.*/
44 /* Would just do this if Amiga DOS maintained fh_End: */
45 /* newtmp->bound = (FileHandle *)BADDR(file)->fh_End; */
47 if ( newtmp->bound < (long)sizeof(ChunkHeader) )
52 /* ---------- OpenRGroup -----------------------------------------------*/
53 IFFP OpenRGroup(GroupContext* parent0,GroupContext* new0)
55 register GroupContext *parent = parent0;
56 register GroupContext *newtmp = new0;
59 newtmp->parent = parent;
60 newtmp->clientFrame = parent->clientFrame;
61 newtmp->file = parent->file;
62 newtmp->position = parent->position;
63 newtmp->bound = parent->position + ChunkMoreBytes(parent);
64 newtmp->ckHdr.ckID = newtmp->subtype = NULL_CHUNK;
65 newtmp->ckHdr.ckSize = newtmp->bytesSoFar = 0;
67 if ( newtmp->bound > parent->bound || IS_ODD(newtmp->bound) )
72 /* ---------- CloseRGroup -----------------------------------------------*/
73 IFFP CloseRGroup(GroupContext *context)
75 register int position;
77 if (context->parent == NL) {
78 } /* Context for whole file.*/
80 position = context->position;
81 context->parent->bytesSoFar += position - context->parent->position;
82 context->parent->position = position;
87 /* ---------- SkipFwd --------------------------------------------------*/
88 /* Skip over bytes in a context. Won't go backwards.*/
89 /* Updates context->position but not context->bytesSoFar.*/
90 /* This implementation is AmigaDOS specific.*/
91 IFFP SkipFwd(GroupContext *context,int bytes)
96 if (-1 == Seek(context->file, bytes, OFFSET_CURRENT))
97 iffp = BAD_IFF; /* Ran out of bytes before chunk complete.*/
99 context->position += bytes;
104 short int endianSwap16(short int val)
107 const char *p = (const char *) &i;
108 if (p[0] == 1) // Lowest address contains the least significant byte
110 return (((val & 0xff00) >> 8) | ((val & 0x00ff) << 8));
117 int endianSwap32(int val)
120 const char *p = (const char *) &i;
121 if (p[0] == 1) // Lowest address contains the least significant byte
123 return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24));
132 /* ---------- GetChunkHdr ----------------------------------------------*/
133 int GetChunkHdr(GroupContext *context0)
135 register GroupContext *context = context0;
139 /* Skip remainder of previous chunk & padding. */
140 iffp = SkipFwd(context,
141 ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
144 /* Set up to read the newtmp header. */
145 context->ckHdr.ckID = BAD_IFF; /* Until we know it's okay, mark it BAD.*/
146 context->subtype = NULL_CHUNK;
147 context->bytesSoFar = 0;
149 /* Generate a psuedo-chunk if at end-of-context. */
150 remaining = context->bound - context->position;
151 if (remaining == 0 ) {
152 context->ckHdr.ckSize = 0;
153 context->ckHdr.ckID = END_MARK;
155 /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
156 else if ((long)sizeof(ChunkHeader) > remaining) {
157 context->ckHdr.ckSize = remaining;
160 /* Read the chunk header (finally). */
162 switch (Read(context->file,
163 &context->ckHdr, (long)sizeof(ChunkHeader)))
165 case -1: return(context->ckHdr.ckID = DOS_ERROR);
166 case 0: return(context->ckHdr.ckID = BAD_IFF);
168 //swap endian-ness of ckSize on little endian machines
169 context->ckHdr.ckSize = endianSwap32(context->ckHdr.ckSize);
175 PutID(context->ckHdr.ckID);
177 printf("id = %lx\n", context->ckHdr.ckID);
180 /* Check: Top level chunk must be LIST or FORM or CAT. */
181 if (context->parent == NL) {
182 if (context->ckHdr.ckID != FORM &&
183 context->ckHdr.ckID != LIST &&
184 context->ckHdr.ckID != CAT )
185 return(context->ckHdr.ckID = NOT_IFF);
188 /* Update the context. */
189 context->position += (long)sizeof(ChunkHeader);
190 remaining -= (long)sizeof(ChunkHeader);
192 /* Non-positive int values are illegal and used for error codes.*/
193 /* We could check for other illegal IDs...*/
194 if (context->ckHdr.ckID <= 0 )
195 context->ckHdr.ckID = BAD_IFF;
197 /* Check: ckSize negative or larger than # bytes left in context? */
198 else if (context->ckHdr.ckSize < 0 ||
199 context->ckHdr.ckSize > remaining) {
200 context->ckHdr.ckSize = remaining;
201 context->ckHdr.ckID = BAD_IFF;
204 /* Automatically read the LIST, FORM, PROP, or CAT subtype int */
206 if (context->ckHdr.ckID == LIST ||
207 context->ckHdr.ckID == FORM ||
208 context->ckHdr.ckID == PROP ||
209 context->ckHdr.ckID == CAT) {
210 iffp = IFFReadBytes(context, (BYTE *)&context->subtype,
212 if (iffp != IFF_OKAY )
213 context->ckHdr.ckID = iffp;
217 return(context->ckHdr.ckID);
220 /* ---------- IFFReadBytes ---------------------------------------------*/
221 IFFP IFFReadBytes(GroupContext *context,BYTE *buffer, int nBytes)
223 register IFFP iffp = IFF_OKAY;
228 else if (nBytes > ChunkMoreBytes(context))
231 else if (nBytes > 0 )
232 switch ( Read(context->file, buffer, nBytes) ) {
233 case -1: {iffp = DOS_ERROR; break; }
234 case 0: {iffp = BAD_IFF; break; }
236 context->position += nBytes;
237 context->bytesSoFar += nBytes;
243 /* ---------- SkipGroup ------------------------------------------------*/
244 IFFP SkipGroup( GroupContext* context)
247 } /* Nothing to do, thanks to GetChunkHdr */
249 /* ---------- ReadIFF --------------------------------------------------*/
250 IFFP ReadIFF(BPTR file,ClientFrame *clientFrame)
252 /*CompilerBug register*/ IFFP iffp;
253 GroupContext context;
255 iffp = OpenRIFF(file, &context,clientFrame);
256 context.clientFrame = clientFrame;
258 if (iffp == IFF_OKAY) {
259 iffp = GetChunkHdr(&context);
262 iffp = (*clientFrame->getForm)(&context);
264 else if (iffp == LIST)
265 iffp = (*clientFrame->getList)(&context);
267 else if (iffp == CAT)
268 iffp = (*clientFrame->getCat)(&context);
270 CloseRGroup(&context);
272 if (iffp > 0 ) /* Make sure we don't return an int.*/
273 iffp = NOT_IFF; /* GetChunkHdr should've caught this.*/
277 /* ---------- ReadIList ------------------------------------------------*/
278 IFFP ReadIList(GroupContext *parent,ClientFrame *clientFrame)
280 GroupContext listContext;
284 iffp = OpenRGroup(parent, &listContext);
287 /* One special case test lets us handle CATs as well as LISTs.*/
288 if (parent->ckHdr.ckID == CAT)
291 listContext.clientFrame = clientFrame;
294 iffp = GetChunkHdr(&listContext);
297 iffp = (*clientFrame->getProp)(&listContext);
301 else if (iffp == FORM)
302 iffp = (*clientFrame->getForm)(&listContext);
304 else if (iffp == LIST)
305 iffp = (*clientFrame->getList)(&listContext);
307 else if (iffp == CAT)
308 iffp = (*clientFrame->getList)(&listContext);
310 if (listContext.ckHdr.ckID != PROP)
311 propOk = FALSE; /* No PROPs allowed after this point.*/
312 } while (iffp == IFF_OKAY);
314 CloseRGroup(&listContext);
316 if (iffp > 0 ) /* Only chunk types above are allowed in a LIST/CAT.*/
318 return(iffp == END_MARK ? IFF_OKAY : iffp);
321 /* ---------- ReadICat -------------------------------------------------*/
322 /* By special arrangement with the ReadIList implement'n, this is trivial.*/
323 IFFP ReadICat(GroupContext *parent)
325 return( ReadIList(parent, parent->clientFrame));//NL) );
328 /* ---------- GetFChunkHdr ---------------------------------------------*/
329 int GetFChunkHdr(GroupContext *context)
333 id = GetChunkHdr(context);
335 context->ckHdr.ckID = id = BAD_IFF;
339 /* ---------- GetF1ChunkHdr ---------------------------------------------*/
340 int GetF1ChunkHdr(GroupContext *context)
343 register ClientFrame *clientFrame = context->clientFrame;
345 id = GetChunkHdr(context);
350 id = (*clientFrame->getForm)(context);
353 id = (*clientFrame->getForm)(context);
356 id = (*clientFrame->getCat)(context);
358 return(context->ckHdr.ckID = id);
361 /* ---------- GetPChunkHdr ---------------------------------------------*/
362 int GetPChunkHdr(GroupContext *context)
366 id = GetChunkHdr(context);
367 if (id == LIST || id == FORM || id == PROP || id == CAT )
368 id = context->ckHdr.ckID = BAD_IFF;