Initialize libbullet git in 2.0_beta.
[platform/upstream/libbullet.git] / Extras / iff / iffr.cpp
1 /*----------------------------------------------------------------------*
2 * IFFR.C  Support routines for reading IFF-85 files.          11/15/85
3 * (IFF is Interchange Format File.)
4 *
5 * By Jerry Morrison and Steve Shaw, Electronic Arts.
6 * This software is in the public domain.
7 *
8 * Original version was for the Commodore-Amiga computer.
9 * This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008
10 *----------------------------------------------------------------------*/
11
12 #include "iff.h"
13 /*  #include "DF1:iff/gio.h" */
14 /*  #define OFFSET_BEGINNING OFFSET_BEGINING */
15
16 /**  Manx expects INTs as 16 bits,  This wont matter on LAttice ***/
17
18
19
20 /* ---------- Read -----------------------------------------------------*/
21
22 extern int PutID(); /**  Added as a diagnostic aid, will remove later ***/
23
24 /* ---------- OpenRIFF --------------------------------------------------*/
25 IFFP OpenRIFF(BPTR file0, GroupContext *new0,ClientFrame *clientFrame)
26 {
27         register BPTR file = file0;
28         register GroupContext *newtmp = new0;
29         IFFP iffp = IFF_OKAY;
30
31         newtmp->parent       = NL;      /* "whole file" has no parent.*/
32         newtmp->clientFrame  = clientFrame;
33         newtmp->file         = file;
34         newtmp->position     = 0;
35         newtmp->ckHdr.ckID   = newtmp->subtype    = NULL_CHUNK;
36         newtmp->ckHdr.ckSize = newtmp->bytesSoFar = 0;
37
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; */
46
47         if ( newtmp->bound < (long)sizeof(ChunkHeader) )
48                 iffp = NOT_IFF;
49         return(iffp);
50 }
51
52 /* ---------- OpenRGroup -----------------------------------------------*/
53 IFFP OpenRGroup(GroupContext* parent0,GroupContext* new0)
54 {
55         register GroupContext *parent = parent0;
56         register GroupContext *newtmp    = new0;
57         IFFP iffp = IFF_OKAY;
58
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;
66
67         if ( newtmp->bound > parent->bound  ||  IS_ODD(newtmp->bound) )
68                 iffp = BAD_IFF;
69         return(iffp);
70 }
71
72 /* ---------- CloseRGroup -----------------------------------------------*/
73 IFFP CloseRGroup(GroupContext *context)
74 {
75         register int position;
76
77         if (context->parent == NL) {
78         }  /* Context for whole file.*/
79         else {
80                 position = context->position;
81                 context->parent->bytesSoFar += position - context->parent->position;
82                 context->parent->position = position;
83         }
84         return(IFF_OKAY);
85 }
86
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)
92 {
93         IFFP iffp = IFF_OKAY;
94
95         if (bytes > 0) {
96                 if (-1 == Seek(context->file, bytes, OFFSET_CURRENT))
97                         iffp = BAD_IFF;   /* Ran out of bytes before chunk complete.*/
98                 else
99                         context->position += bytes;
100         }
101         return(iffp);
102 }
103
104 short int endianSwap16(short int val)
105 {
106    long int i = 1;
107    const char *p = (const char *) &i;
108    if (p[0] == 1)  // Lowest address contains the least significant byte
109    {
110            return (((val & 0xff00) >> 8) | ((val & 0x00ff) << 8));
111    }
112    return val;
113 }
114
115
116
117 int endianSwap32(int val)
118 {
119    long int i = 1;
120    const char *p = (const char *) &i;
121    if (p[0] == 1)  // Lowest address contains the least significant byte
122    {
123            return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8)  | ((val & 0x000000ff) << 24));
124    }
125    return val;
126 }
127
128
129
130
131
132 /* ---------- GetChunkHdr ----------------------------------------------*/
133 int GetChunkHdr(GroupContext *context0)
134 {
135         register GroupContext *context = context0;
136         register IFFP iffp;
137         int remaining;
138
139         /* Skip remainder of previous chunk & padding. */
140         iffp = SkipFwd(context,
141                 ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
142         CheckIFFP();
143
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;
148
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;
154         }
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;
158         }
159
160         /* Read the chunk header (finally). */
161         else {
162                 switch (Read(context->file,
163                         &context->ckHdr, (long)sizeof(ChunkHeader)))
164                 {
165                 case -1: return(context->ckHdr.ckID = DOS_ERROR);
166                 case 0:  return(context->ckHdr.ckID = BAD_IFF);
167                 }
168                 //swap endian-ness of ckSize on little endian machines
169                 context->ckHdr.ckSize = endianSwap32(context->ckHdr.ckSize);
170
171
172
173
174                 /*** $$$ ***
175                 PutID(context->ckHdr.ckID);
176                 printf("\n");
177                 printf("id = %lx\n", context->ckHdr.ckID);
178                 **/
179
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);
186                 }
187
188                 /* Update the context. */
189                 context->position += (long)sizeof(ChunkHeader);
190                 remaining         -= (long)sizeof(ChunkHeader);
191
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;
196
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;
202                 }
203
204                 /* Automatically read the LIST, FORM, PROP, or CAT subtype int */
205                 else {
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,
211                                                 (long)sizeof(int));
212                                         if (iffp != IFF_OKAY )
213                                                 context->ckHdr.ckID = iffp;
214                         }
215                 }
216         }
217         return(context->ckHdr.ckID);
218 }
219
220 /* ---------- IFFReadBytes ---------------------------------------------*/
221 IFFP IFFReadBytes(GroupContext *context,BYTE *buffer, int nBytes)
222 {
223         register IFFP iffp = IFF_OKAY;
224
225         if (nBytes < 0)
226                 iffp = CLIENT_ERROR;
227
228         else if (nBytes > ChunkMoreBytes(context))
229                 iffp = SHORT_CHUNK;
230
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; }
235                  default: {
236                          context->position   += nBytes;
237                          context->bytesSoFar += nBytes;
238                                   }
239         }
240         return(iffp);
241 }
242
243 /* ---------- SkipGroup ------------------------------------------------*/
244 IFFP SkipGroup( GroupContext* context) 
245 {
246         return 0;
247 }   /* Nothing to do, thanks to GetChunkHdr */
248
249 /* ---------- ReadIFF --------------------------------------------------*/
250 IFFP ReadIFF(BPTR file,ClientFrame *clientFrame)
251 {
252         /*CompilerBug register*/ IFFP iffp;
253         GroupContext context;
254
255         iffp = OpenRIFF(file, &context,clientFrame);
256         context.clientFrame = clientFrame;
257
258         if (iffp == IFF_OKAY) {
259                 iffp = GetChunkHdr(&context);
260
261                 if (iffp == FORM)
262                         iffp = (*clientFrame->getForm)(&context);
263
264                 else if (iffp == LIST)
265                         iffp = (*clientFrame->getList)(&context);
266
267                 else if (iffp == CAT)
268                         iffp = (*clientFrame->getCat)(&context);
269         }
270         CloseRGroup(&context);
271
272         if (iffp > 0 )           /* Make sure we don't return an int.*/
273                 iffp = NOT_IFF;      /* GetChunkHdr should've caught this.*/
274         return(iffp);
275 }
276
277 /* ---------- ReadIList ------------------------------------------------*/
278 IFFP ReadIList(GroupContext *parent,ClientFrame *clientFrame)
279 {
280         GroupContext listContext;
281         IFFP iffp;
282         BOOL propOk = TRUE;
283
284         iffp = OpenRGroup(parent, &listContext);
285         CheckIFFP();
286
287         /* One special case test lets us handle CATs as well as LISTs.*/
288         if (parent->ckHdr.ckID == CAT)
289                 propOk = FALSE;
290         else
291                 listContext.clientFrame = clientFrame;
292
293         do {
294                 iffp = GetChunkHdr(&listContext);
295                 if (iffp == PROP) {
296                         if (propOk)
297                                 iffp = (*clientFrame->getProp)(&listContext);
298                         else
299                                 iffp = BAD_IFF;
300                 }
301                 else if (iffp == FORM)
302                         iffp = (*clientFrame->getForm)(&listContext);
303
304                 else if (iffp == LIST)
305                         iffp = (*clientFrame->getList)(&listContext);
306
307                 else if (iffp == CAT)
308                         iffp = (*clientFrame->getList)(&listContext);
309
310                 if (listContext.ckHdr.ckID != PROP)
311                         propOk = FALSE;   /* No PROPs allowed after this point.*/
312         } while (iffp == IFF_OKAY);
313
314         CloseRGroup(&listContext);
315
316         if (iffp > 0 )   /* Only chunk types above are allowed in a LIST/CAT.*/
317                 iffp = BAD_IFF;
318         return(iffp == END_MARK ? IFF_OKAY : iffp);
319 }
320
321 /* ---------- ReadICat -------------------------------------------------*/
322 /* By special arrangement with the ReadIList implement'n, this is trivial.*/
323 IFFP ReadICat(GroupContext *parent)
324 {
325         return( ReadIList(parent, parent->clientFrame));//NL) );
326 }
327
328 /* ---------- GetFChunkHdr ---------------------------------------------*/
329 int GetFChunkHdr(GroupContext *context)
330 {
331         register int id;
332
333         id = GetChunkHdr(context);
334         if (id == PROP)
335                 context->ckHdr.ckID = id = BAD_IFF;
336         return(id);
337 }
338
339 /* ---------- GetF1ChunkHdr ---------------------------------------------*/
340 int GetF1ChunkHdr(GroupContext *context)
341 {
342         register int id;
343         register ClientFrame *clientFrame = context->clientFrame;
344
345         id = GetChunkHdr(context);
346         if (id == PROP)
347                 id = BAD_IFF;
348
349         else if (id == FORM)
350                 id = (*clientFrame->getForm)(context);
351
352         else if (id == LIST)
353                 id = (*clientFrame->getForm)(context);
354
355         else if (id == CAT)
356                 id = (*clientFrame->getCat)(context);
357
358         return(context->ckHdr.ckID = id);
359 }
360
361 /* ---------- GetPChunkHdr ---------------------------------------------*/
362 int GetPChunkHdr(GroupContext *context)
363 {
364         register int id;
365
366         id = GetChunkHdr(context);
367         if (id == LIST || id == FORM || id == PROP || id == CAT )
368                 id = context->ckHdr.ckID = BAD_IFF;
369         return(id);
370 }