home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 500-599 / ff588.lza / MandelSquare / Source / PlayAnim / iffr.c < prev    next >
C/C++ Source or Header  |  1992-01-04  |  10KB  |  312 lines

  1. /*----------------------------------------------------------------------*
  2.  * IFFR.C  Support routines for reading IFF-85 files.          1/23/86
  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.  * This version for the Commodore-Amiga computer.
  9.  *
  10.  * Uses "gio".  Either link with gio.c, or set the GIO_ACTIVE flag to 0
  11.  * in gio.h.
  12.  *----------------------------------------------------------------------*/
  13.  
  14. #include <libraries/dosextens.h>
  15.  
  16. #include "gio.h"
  17. #include "iff.h"
  18.  
  19. /* ----- Private subroutine FileLength() --------------------------------*/
  20. /* Returns the length of the file or else a negative IFFP error code
  21.  * (NO_FILE or DOS_ERROR). AmigaDOS-specific implementation.
  22.  * SIDE EFFECT: Thanks to AmigaDOS, we have to change the file's position
  23.  * to find its length.
  24.  * Now if Amiga DOS maintained fh_End, we'd just do this:
  25.  *    fileLength = (FileHandle *)BADDR(file)->fh_End; */
  26. STATIC LONG FileLength(file)  BPTR file;  {
  27.     LONG fileLength = NO_FILE;
  28.  
  29.     if (file > 0)  {
  30.     Seek(file, 0, OFFSET_END);    /* Seek to end of file.*/
  31.     fileLength = Seek(file, 0, OFFSET_CURRENT);
  32.         /* Returns position BEFORE the seek, which is #bytes in file. */
  33.     if (fileLength < 0)
  34.         fileLength = DOS_ERROR;    /* DOS being absurd.*/
  35.     }
  36.     return(fileLength);
  37.     }
  38.  
  39. /* ---------- Read -----------------------------------------------------*/
  40.  
  41. /* ---------- OpenRIFF --------------------------------------------------*/
  42. STATIC IFFP OpenRIFF(file, new, clientFrame)
  43.     BPTR file;   GroupContext *new;  ClientFrame *clientFrame; {
  44.     IFFP iffp = IFF_OKAY;
  45.  
  46.     new->parent       = NULL;        /* "whole file" has no parent.*/
  47.     new->clientFrame  = clientFrame;
  48.     new->file         = file;
  49.     new->position     = 0;
  50.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  51.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  52.  
  53.     /* Set new->bound and go to the file's beginning. */
  54.     new->bound = FileLength(file);
  55.     if (new->bound < 0)
  56.     iffp = new->bound;           /* File system error! */
  57.     else if ( new->bound < sizeof(ChunkHeader) )
  58.     iffp = NOT_IFF;               /* Too small for an IFF file. */
  59.     else
  60.     GSeek(file, 0, OFFSET_BEGINNING);  /* Go to file start. */
  61.     return(iffp);
  62.     }
  63.  
  64. /* ---------- SkipFwd --------------------------------------------------*/
  65. /* Skip over bytes in a context. Won't go backwards.*/
  66. /* Updates context->position but not context->bytesSoFar.*/
  67. /* This implementation is AmigaDOS specific.*/
  68. STATIC IFFP SkipFwd(context, bytes)   GroupContext *context;  LONG bytes; {
  69.     IFFP iffp = IFF_OKAY;
  70.  
  71.     if (bytes > 0) {
  72.     if (-1 == GSeek(context->file, bytes, OFFSET_CURRENT))
  73.         iffp = BAD_IFF;    /* Ran out of bytes before chunk complete.*/
  74.     else
  75.         context->position += bytes;
  76.     }
  77.     return(iffp);
  78.     }
  79.  
  80. /* ---------- OpenRGroup -----------------------------------------------*/
  81. IFFP OpenRGroup(parent, new)   GroupContext *parent, *new; {
  82.     IFFP iffp = IFF_OKAY;
  83.  
  84.     new->parent       = parent;
  85.     new->clientFrame  = parent->clientFrame;
  86.     new->file         = parent->file;
  87.     new->position     = parent->position;
  88.     new->bound        = parent->position + ChunkMoreBytes(parent);
  89.     new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
  90.     new->ckHdr.ckSize = new->bytesSoFar = 0;
  91.  
  92.     if ( new->bound > parent->bound  ||  IS_ODD(new->bound) )
  93.     iffp = BAD_IFF;
  94.     return(iffp);
  95.     }
  96.  
  97. /* ---------- CloseRGroup -----------------------------------------------*/
  98. IFFP CloseRGroup(context)   GroupContext *context; {
  99.     LONG position;
  100.  
  101.     if (context->parent == NULL) {
  102.     }  /* Context for whole file.*/
  103.     else {
  104.     position = context->position;
  105.     context->parent->bytesSoFar += position - context->parent->position;
  106.     context->parent->position = position;
  107.     }
  108.     return(IFF_OKAY);
  109.     }
  110.  
  111. /* ---------- GetChunkHdr ----------------------------------------------*/
  112. ID GetChunkHdr(context)   GroupContext *context;  {
  113.     IFFP iffp;
  114.     LONG remaining;
  115.  
  116.     /* Skip remainder of previous chunk & padding. */
  117.     iffp = SkipFwd(context,
  118.     (ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize)));
  119.     CheckIFFP();
  120.  
  121.     /* Set up to read the new header. */
  122.     context->ckHdr.ckID = BAD_IFF;    /* Until we know it's okay, mark it BAD.*/
  123.     context->subtype    = NULL_CHUNK;
  124.     context->bytesSoFar = 0;
  125.  
  126.     /* Generate a psuedo-chunk if at end-of-context. */
  127.     remaining = context->bound - context->position;
  128.     if (remaining == 0) {
  129.     context->ckHdr.ckSize = 0;
  130.     context->ckHdr.ckID   = END_MARK;
  131.     }
  132.  
  133.     /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
  134.     else if (sizeof(ChunkHeader) > remaining) {
  135.     context->ckHdr.ckSize = remaining;
  136.     }
  137.  
  138.     /* Read the chunk header (finally). */
  139.     else {
  140.         switch (
  141.         GRead(context->file, (BYTE *)&context->ckHdr, sizeof(ChunkHeader))
  142.         ) {
  143.         case -1: return((ID)(context->ckHdr.ckID = DOS_ERROR));
  144.         case 0:  return((ID)(context->ckHdr.ckID = BAD_IFF));
  145.         }
  146.  
  147.     /* Check: Top level chunk must be LIST or FORM or CAT. */
  148.     if (context->parent == NULL)
  149.         switch(context->ckHdr.ckID) {
  150.         case FORM:  case LIST:  case CAT:  break;
  151.         default:    return((ID)(context->ckHdr.ckID = NOT_IFF));
  152.         }
  153.  
  154.     /* Update the context. */
  155.     context->position += sizeof(ChunkHeader);
  156.     remaining         -= sizeof(ChunkHeader);
  157.  
  158.     /* Non-positive ID values are illegal and used for error codes.*/
  159.     /* We could check for other illegal IDs...*/
  160.     if (context->ckHdr.ckID <= 0)
  161.          context->ckHdr.ckID = BAD_IFF;
  162.  
  163.     /* Check: ckSize negative or larger than # bytes left in context? */
  164.     else if (context->ckHdr.ckSize < 0  ||
  165.          context->ckHdr.ckSize > remaining) {
  166.         context->ckHdr.ckSize = remaining;
  167.         context->ckHdr.ckID   = BAD_IFF;
  168.         }
  169.  
  170.     /* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
  171.     else switch (context->ckHdr.ckID) {
  172.         case LIST:  case FORM:  case PROP:  case CAT:  {
  173.         iffp = IFFReadBytes(context,
  174.                     (BYTE *)&context->subtype,
  175.                     sizeof(ID));
  176.         if (iffp != IFF_OKAY)
  177.             context->ckHdr.ckID = iffp;
  178.         break; }
  179.         }
  180.  
  181.     }
  182.     return(context->ckHdr.ckID);
  183.     }
  184.  
  185. /* ---------- IFFReadBytes ---------------------------------------------*/
  186. IFFP IFFReadBytes(context, buffer, nBytes)
  187.     GroupContext *context;   BYTE *buffer;   LONG nBytes; {
  188.     IFFP iffp = IFF_OKAY;
  189.  
  190.     if (nBytes < 0)
  191.     iffp = CLIENT_ERROR;
  192.     else if (nBytes > 0) {
  193.         if (nBytes > ChunkMoreBytes(context)) nBytes = ChunkMoreBytes(context);
  194.     switch ( GRead(context->file, buffer, nBytes) ) {
  195.         case -1: {iffp = DOS_ERROR; break; }
  196.         case 0:  {iffp = BAD_IFF;   break; }
  197.         default: {
  198.         context->position   += nBytes;
  199.         context->bytesSoFar += nBytes;
  200.         }
  201.         }
  202.     }
  203.     return(iffp);
  204.     }
  205.  
  206. /* ---------- ReadIFF --------------------------------------------------*/
  207. IFFP ReadIFF(file, clientFrame)  BPTR file;  ClientFrame *clientFrame;  {
  208.     IFFP iffp;
  209.     GroupContext context;
  210.  
  211.     iffp = OpenRIFF(file, &context,clientFrame);
  212.  
  213.     if (iffp == IFF_OKAY)
  214.     switch (iffp = GetChunkHdr(&context)) {
  215.         case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
  216.         case LIST: { iffp = (*clientFrame->getList)(&context); break; }
  217.         case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
  218.         /* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
  219.         }
  220.  
  221.     CloseRGroup(&context);
  222.  
  223.     if (iffp > 0)        /* Make sure we don't return an ID.*/
  224.     iffp = NOT_IFF;        /* GetChunkHdr should've caught this.*/
  225.     return(iffp);
  226.     }
  227.  
  228. /* ---------- ReadIList ------------------------------------------------*/
  229. IFFP ReadIList(parent, clientFrame)
  230.     GroupContext *parent;  ClientFrame *clientFrame; {
  231.     GroupContext listContext;
  232.     IFFP iffp;
  233.     BOOL propOk = TRUE;
  234.  
  235.     iffp = OpenRGroup(parent, &listContext);
  236.     CheckIFFP();
  237.  
  238.     /* One special case test lets us handle CATs as well as LISTs.*/
  239.     if (parent->ckHdr.ckID == CAT)
  240.     propOk = FALSE;
  241.     else
  242.     listContext.clientFrame = clientFrame;
  243.  
  244.     do {
  245.     switch (iffp = GetChunkHdr(&listContext)) {
  246.         case PROP: {
  247.         if (propOk)
  248.             iffp = (*clientFrame->getProp)(&listContext);
  249.         else
  250.             iffp = BAD_IFF;
  251.         break;
  252.         }
  253.         case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
  254.         case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
  255.         case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
  256.         /* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
  257.         }
  258.     if (listContext.ckHdr.ckID != PROP)
  259.         propOk = FALSE;    /* No PROPs allowed after this point.*/
  260.     } while (iffp == IFF_OKAY);
  261.  
  262.     CloseRGroup(&listContext);
  263.  
  264.     if (iffp > 0)    /* Only chunk types above are allowed in a LIST/CAT.*/
  265.     iffp = BAD_IFF;
  266.     return(iffp == END_MARK ? IFF_OKAY : iffp);
  267.     }
  268.  
  269. /* ---------- ReadICat -------------------------------------------------*/
  270. /* By special arrangement with the ReadIList implement'n, this is trivial.*/
  271. IFFP ReadICat(parent)  GroupContext *parent;  {
  272.     return( ReadIList(parent, NULL) );
  273.     }
  274.  
  275. /* ---------- GetFChunkHdr ---------------------------------------------*/
  276. ID GetFChunkHdr(context)   GroupContext *context; {
  277.     ID id;
  278.  
  279.     id = GetChunkHdr(context);
  280.     if (id == PROP)
  281.     context->ckHdr.ckID = id = BAD_IFF;
  282.     return(id);
  283.     }
  284.  
  285. /* ---------- GetF1ChunkHdr ---------------------------------------------*/
  286. ID GetF1ChunkHdr(context)   GroupContext *context; {
  287.     ID id;
  288.     ClientFrame *clientFrame = context->clientFrame;
  289.  
  290.     switch (id = GetChunkHdr(context))  {
  291.     case PROP: { id = BAD_IFF; break; }
  292.     case FORM: { id = (*clientFrame->getForm)(context); break; }
  293.     case LIST: { id = (*clientFrame->getList)(context); break; }
  294.     case CAT : { id = (*clientFrame->getCat )(context); break; }
  295.     /* Default: let the caller handle other chunks */
  296.     }
  297.     return(context->ckHdr.ckID = id);
  298.     }
  299.  
  300. /* ---------- GetPChunkHdr ---------------------------------------------*/
  301. ID GetPChunkHdr(context)   GroupContext *context; {
  302.     ID id;
  303.  
  304.     id = GetChunkHdr(context);
  305.     switch (id) {
  306.     case LIST:  case FORM:  case PROP:  case CAT:  {
  307.         id = context->ckHdr.ckID = BAD_IFF;
  308.         break; }
  309.     }
  310.     return(id);
  311.     }
  312.