Amiga IFF examples (1 of 2)

Mike Farren farren at well.UUCP
Sat Jan 11 07:52:16 AEST 1986


    This is the first of two postings containing the Electronic Arts
    public domain examples for the IFF file interchange format.  This
    posting contains examples of reading and writing IFF files.
      The document describing the IFF formats will be included in the
    version 1.1 Rom Kernal Manual, which should be available soon.

      Briefly, an IFF file consists of one or more "chunks", each of
    which begins with a header consisting of a four-byte type identifier
    (such as "FORM", "ILBM", "PROP", etc.) followed by a long integer
    containing the size of the chunk. The data that make up the chunk
    follows as a string of bytes.  Each chunk is padded to end on an
    even word boundary (a la 68000).  Each chunk may contain other
    chunks.  The contents of each chunk is determined by its type, as
    specified in the identifier.

******************************* CUT HERE! *******************************
#!/bin/sh
#
#    This is a shell archive.  To unpack the files, delete everything
#    before "#!/bin/sh", and then pipe the body through the "sh" command.
#    This archive contains the following files:
#
#    iff.h - The header file for the IFF programs
#    iffr.c - The IFF read routines
#    iffw.c - The IFF write routines
#    iffcheck.c - A program demonstrating the use of the above routines.
#    iffcheck.lnk - The Alink "WITH" file for iffcheck
#
echo 'Start of IFF files'
echo 'x - iff.h'
sed 's/^X//' > iff.h << '/'
X#ifndef IFF_H
X#define IFF_H
X/*----------------------------------------------------------------------*/
X/* IFF.H  defs for IFF-85 Interchange Format Files.            11/15/85 */
X/*                                                                      */
X/* By Jerry Morrison and Steve Shaw, Electronic Arts.                   */
X/* This software is in the public domain.                               */
X/*----------------------------------------------------------------------*/
X
X#ifndef EXEC_TYPES_H
X#include "exec/types.h"
X#endif
X
X#ifndef LIBRARIES_DOS_H
X#include "libraries/dos.h"
X#endif
X
Xtypedef LONG IFFP;      /* Status code result from an IFF procedure */
X        /* LONG, because must be type compatable with ID for GetChunkHdr.*/
X        /* Note that the error codes below are not legal IDs.*/
X#define IFF_OKAY  0     /* Keep going...*/
X#define END_MARK  -1    /* As if there was a chunk at end of group.*/
X#define IFF_DONE  -2    /* clientProc returns this when it has READ enough.
X                         * It means return thru all levels. File is Okay.*/
X#define DOS_ERROR -3
X#define NOT_IFF   -4    /* not an IFF file.*/
X#define NO_FILE   -5    /* Tried to open file, DOS didn't find it.*/
X#define CLIENT_ERROR -6 /* Client made invalid request, for instance, write a
X                         * negative size chunk.*/
X#define BAD_FORM  -7    /* A client read proc complains about FORM semantics;
X                         * e.g. valid IFF, but missing a required chunk.*/
X#define SHORT_CHUNK -8  /* Client asked to IFFReadBytes more bytes than left
X                         * in the chunk. Could be client bug or bad form.*/
X#define BAD_IFF   -9    /* mal-formed IFF file. [TBD] Expand this into a
X                         * range of error codes.*/
X#define LAST_ERROR BAD_IFF
X
X/* This MACRO is used to RETURN immediately when a termination condition is
X * found. This is a pretty weird macro. It requires the caller to declare a
X * local "IFFP iffp" and assign it. This wouldn't work as a subroutine since
X * it returns for it's caller. */
X#define CheckIFFP()   { if (iffp != IFF_OKAY) return(iffp); }
X
X
X/* ---------- ID -------------------------------------------------------*/
X
Xtypedef LONG ID;        /* An ID is four printable ASCII chars but
X                         * stored as a LONG for efficient copy & compare.*/
X
X/* Four-character IDentifier builder.*/
X#define MakeID(a,b,c,d)  ( (a)<<24 | (b)<<16 | (c)<<8 | (d) )
X
X/* Standard group IDs.  A chunk with one of these IDs contains a
X   SubTypeID followed by zero or more chunks.*/
X#define FORM MakeID('F','O','R','M')
X#define PROP MakeID('P','R','O','P')
X#define LIST MakeID('L','I','S','T')
X#define CAT  MakeID('C','A','T',' ')
X#define FILLER MakeID(' ',' ',' ',' ')
X/* The IDs "FOR1".."FOR9", "LIS1".."LIS9", & "CAT1".."CAT9" are reserved
X * for future standardization.*/
X
X/* Pseudo-ID used internally by chunk reader and writer.*/
X#define NULL_CHUNK 0L          /* No current chunk.*/
X
X
X/* ---------- Chunk ----------------------------------------------------*/
X
X/* All chunks start with a type ID and a count of the data bytes that
X   follow--the chunk's "logical size" or "data size". If that number is odd,
X   a 0 pad byte is written, too. */
Xtypedef struct {
X    ID    ckID;
X    LONG  ckSize;
X    } ChunkHeader;
X
Xtypedef struct {
X    ID    ckID;
X    LONG  ckSize;
X    UBYTE ckData[ 1 /*REALLY: ckSize*/ ];
X    } Chunk;
X
X/* Pass ckSize = szNotYetKnown to the writer to mean "compute the size".*/
X#define szNotYetKnown 0x80000001L
X
X/* Need to know whether a value is odd so can word-align.*/
X#define IS_ODD(a)   ((a) & 1)
X
X/* This macro rounds up to an even number. */
X#define WordAlign(size)   ((size+1)&~1)
X
X/* ALL CHUNKS MUST BE PADDED TO EVEN NUMBER OF BYTES.
X * ChunkPSize computes the total "physical size" of a padded chunk from
X * its "data size" or "logical size". */
X#define ChunkPSize(dataSize)  (WordAlign(dataSize) + sizeof(ChunkHeader))
X
X/* The Grouping chunks (LIST, FORM, PROP, & CAT) contain concatenations of
X * chunks after a subtype ID that identifies the content chunks.
X * "FORM type XXXX", "LIST of FORM type XXXX", "PROPerties associated
X * with FORM type XXXX", or "conCATenation of XXXX".*/
Xtypedef struct {
X    ID    ckID;
X    LONG  ckSize;       /* this ckSize includes "grpSubID".*/
X    ID    grpSubID;
X    } GroupHeader;
X
Xtypedef struct {
X    ID    ckID;
X    LONG  ckSize;
X    ID    grpSubID;
X    UBYTE grpData[ 1 /*REALLY: ckSize-sizeof(grpSubID)*/ ];
X    } GroupChunk;
X
X
X/* ---------- IFF Reader -----------------------------------------------*/
X
X/******** Routines to support a stream-oriented IFF file reader *******
X *
X * These routines handle lots of details like error checking and skipping
X * over padding. They're also careful not to read past any containing context.
X *
X * These routines ASSUME they're the only ones reading from the file.
X * Client should check IFFP error codes. Don't press on after an error!
X * These routines try to have no side effects in the error case, except
X * partial I/O is sometimes unavoidable.
X *
X * All of these routines may return DOS_ERROR. In that case, ask DOS for the
X * specific error code.
X *
X * The overall scheme for the low level chunk reader is to open a "group read
X * context" with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
X * (and its kin) and IFFReadBytes, and close the context with CloseRGroup.
X *
X * The overall scheme for reading an IFF file is to use ReadIFF, ReadIList,
X * and ReadICat to scan the file. See those procedures, ClientProc (below),
X * and the skeleton IFF reader. */
X
X/* Client passes ptrs to procedures of this type to ReadIFF which call them
X * back to handle LISTs, FORMs, CATs, and PROPs.
X *
X * Use the GroupContext ptr when calling reader routines like GetChunkHdr.
X * Look inside the GroupContext ptr for your ClientFrame ptr. You'll
X * want to type cast it into a ptr to your containing struct to get your
X * private contextual data (stacked property settings). See below. */
Xtypedef IFFP ClientProc(struct _GroupContext *);
X
X/* Client's context for reading an IFF file or a group.
X * Client should actually make this the first component of a larger struct
X * (it's personal stack "frame") that has a field to store each "interesting"
X * property encountered.
X * Either initialize each such field to a global default or keep a boolean
X * indicating if you've read a property chunk into that field.
X * Your getList and getForm procs should allocate a new "frame" and copy the
X * parent frame's contents. The getProp procedure should store into the frame
X * allocated by getList for the containing LIST. */
Xtypedef struct _ClientFrame {
X    ClientProc *getList, *getProp, *getForm, *getCat;
X    /* client's own data follows; place to stack property settings */
X    } ClientFrame;
X
X/* Our context for reading a group chunk. */
Xtypedef struct _GroupContext {
X    struct _GroupContext *parent; /* Containing group; NULL => whole file. */
X    ClientFrame *clientFrame;     /* Reader data & client's context state. */
X    BPTR file;          /* Byte-stream file handle. */
X    LONG position;      /* The context's logical file position. */
X    LONG bound;         /* File-absolute context bound
X                         * or szNotYetKnown (writer only). */
X    ChunkHeader ckHdr;  /* Current chunk header. ckHdr.ckSize = szNotYetKnown
X                         * means we need to go back and set the size (writer only).
X                         * See also Pseudo-IDs, above. */
X    ID subtype;         /* Group's subtype ID when reading. */
X    LONG bytesSoFar;    /* # bytes read/written of current chunk's data. */
X    } GroupContext;
X
X/* Computes the number of bytes not yet read from the current chunk, given
X * a group read context gc. */
X#define ChunkMoreBytes(gc)  ((gc)->ckHdr.ckSize - (gc)->bytesSoFar)
X
X
X/***** Low Level IFF Chunk Reader *****/
X
X/* Given an open file, open a read context spanning the whole file.
X * This is normally only called by ReadIFF.
X * This sets new->clientFrame = clientFrame.
X * ASSUME context allocated by caller but not initialized.
X * ASSUME caller doesn't deallocate the context before calling CloseRGroup.
X * NOT_IFF ERROR if the file is too short for even a chunk header.*/
Xextern IFFP OpenRIFF(BPTR, GroupContext *, ClientFrame *);
X                 /*  file, new,            clientFrame  */
X
X/* Open the remainder of the current chunk as a group read context.
X * This will be called just after the group's subtype ID has been read
X * (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
X * remainder is a sequence of chunks.
X * This sets new->clientFrame = parent->clientFrame. The caller should repoint
X * it at a new clientFrame if opening a LIST context so it'll have a "stack
X * frame" to store PROPs for the LIST. (It's usually convenient to also
X * allocate a new Frame when you encounter FORM of the right type.)
X *
X * ASSUME new context allocated by caller but not initialized.
X * ASSUME caller doesn't deallocate the context or access the parent context
X * before calling CloseRGroup.
X * BAD_IFF ERROR if context end is odd or extends past parent. */
Xextern IFFP OpenRGroup(GroupContext *, GroupContext *);
X                   /*  parent,         new  */
X
X/* Close a group read context, updating its parent context.
X * After calling this, the old context may be deallocated and the parent
X * context can be accessed again. It's okay to call this particular procedure
X * after an error has occurred reading the group.
X * This always returns IFF_OKAY. */
Xextern IFFP CloseRGroup(GroupContext *);
X                    /*  old  */
X
X/* Skip any remaining bytes of the previous chunk and any padding, then
X * read the next chunk header into context.ckHdr.
X * If the ckID is LIST, FORM, CAT, or PROP, this automatically reads the
X * subtype ID into context->subtype.
X * Caller should dispatch on ckID (and subtype) to an appropriate handler.
X *
X * RETURNS context.ckHdr.ckID (the ID of the new chunk header); END_MARK
X * if there are no more chunks in this context; or NOT_IFF if the top level
X * file chunk isn't a FORM, LIST, or CAT; or BAD_IFF if malformed chunk, e.g.
X * ckSize is negative or too big for containing context, ckID isn't positive,
X * or we hit end-of-file.
X *
X * See also GetFChunkHdr, GetF1ChunkHdr, and GetPChunkHdr, below.*/
Xextern ID       GetChunkHdr(GroupContext *);
X  /*  context.ckHdr.ckID    context  */
X
X/* Read nBytes number of data bytes of current chunk. (Use OpenGroup, etc.
X * instead to read the contents of a group chunk.) You can call this several
X * times to read the data piecemeal.
X * CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > ChunkMoreBytes(context)
X * which could be due to a client bug or a chunk that's shorter than it
X * ought to be (bad form). (on either CLIENT_ERROR or SHORT_CHUNK,
X * IFFReadBytes won't read any bytes.) */
Xextern IFFP IFFReadBytes(GroupContext *, BYTE *, LONG);
X                     /*  context,        buffer, nBytes  */
X
X
X/***** IFF File Reader *****/
X
X/* This is a noop ClientProc that you can use for a getList, getForm, getProp,
X * or getCat procedure that just skips the group. A simple reader might just
X * implement getForm, store &ReadICat in the getCat field of clientFrame, and
X * use &SkipGroup for the getList and getProp procs.*/
Xextern IFFP SkipGroup(GroupContext *);
X
X/* IFF file reader.
X * Given an open file, allocate a group context and use it to read the FORM,
X * LIST, or CAT and it's contents. The idea is to parse the file's contents,
X * and for each FORM, LIST, CAT, or PROP encountered, call the getForm,
X * getList, getCat, or getProp procedure in clientFrame, passing the
X * GroupContext ptr.
X * This is achieved with the aid of ReadIList (which your getList should
X * call) and ReadICat (which your getCat should call, if you don't just use
X * &ReadICat for your getCat). If you want to handle FORMs, LISTs, and CATs
X * nested within FORMs, the getForm procedure must dispatch to getForm,
X * getList, and getCat (it can use GetF1ChunkHdr to make this easy).
X *
X * Normal return is IFF_OKAY (if whole file scanned) or IFF_DONE (if a client
X * proc said "done" first).
X * See the skeletal getList, getForm, getCat, and getProp procedures. */
Xextern IFFP ReadIFF(BPTR, ClientFrame *);
X                /*  file, clientFrame  */
X
X/* IFF LIST reader.
X * Your "getList" procedure should allocate a ClientFrame, copy the parent's
X * ClientFrame, and then call this procedure to do all the work.
X *
X * Normal return is IFF_OKAY (if whole LIST scanned) or IFF_DONE (if a client
X * proc said "done" first).
X * BAD_IFF ERROR if a PROP appears after a non-PROP. */
Xextern IFFP ReadIList(GroupContext *, ClientFrame *);
X                  /*  parent,         clientFrame  */
X
X/* IFF CAT reader.
X * Most clients can simply use this to read their CATs. If you must do extra
X * setup work, put a ptr to your getCat procedure in the clientFrame, and
X * have that procedure call ReadICat to do the detail work.
X *
X * Normal return is IFF_OKAY (if whole CAT scanned) or IFF_DONE (if a client
X * proc said "done" first).
X * BAD_IFF ERROR if a PROP appears in the CAT. */
Xextern IFFP ReadICat(GroupContext *);
X                 /*  parent  */
X
X/* Call GetFChunkHdr instead of GetChunkHdr to read each chunk inside a FORM.
X * It just calls GetChunkHdr and returns BAD_IFF if it gets a PROP chunk. */
Xextern ID       GetFChunkHdr(GroupContext *);
X  /*  context.ckHdr.ckID    context  */
X
X/* GetF1ChunkHdr is like GetFChunkHdr, but it automatically dispatches to the
X * getForm, getList, and getCat procedure (and returns the result) if it
X * encounters a FORM, LIST, or CAT. */
Xextern ID       GetF1ChunkHdr(GroupContext *);
X  /*  context.ckHdr.ckID    context  */
X
X/* Call GetPChunkHdr instead of GetChunkHdr to read each chunk inside a PROP.
X * It just calls GetChunkHdr and returns BAD_IFF if it gets a group chunk. */
Xextern ID       GetPChunkHdr(GroupContext *);
X  /*  context.ckHdr.ckID    context  */
X
X
X/* ---------- IFF Writer -----------------------------------------------*/
X
X/******* Routines to support a stream-oriented IFF file writer *******
X *
X * These routines will random access back to set a chunk size value when the
X * caller doesn't know it ahead of time. They'll also do things automatically
X * like padding and error checking.
X *
X * These routines ASSUME they're the only ones writing to the file.
X * Client should check IFFP error codes. Don't press on after an error!
X * These routines try to have no side effects in the error case, except that
X * partial I/O is sometimes unavoidable.
X *
X * All of these routines may return DOS_ERROR. In that case, ask DOS for the
X * specific error code.
X *
X * The overall scheme is to open an output GroupContext via OpenWIFF or
X * OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
X * each chunk, then use CloseWGroup to close the GroupContext.
X *
X * To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
X * its chunks, then call EndWGroup. StartWGroup automatically writes the
X * group header and opens a nested context for writing the contents.
X * EndWGroup closes the nested context and completes the group chunk. */
X
X
X/* Given a file open for output, open a write context.
X * The "limit" arg imposes a fence or upper limit on the logical file
X * position for writing data in this context. Pass in szNotYetKnown to be
X * bounded only by disk capacity.
X * ASSUME new context structure allocated by caller but not initialized.
X * ASSUME caller doesn't deallocate the context before calling CloseWGroup.
X * The caller is only allowed to write out one FORM, LIST, or CAT in this top
X * level context (see StartWGroup and PutCkHdr).
X * CLIENT_ERROR if limit is odd.*/
Xextern IFFP OpenWIFF(BPTR, GroupContext *, LONG);
X                 /*  file, new,            limit {file position}  */
X
X/* Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
X * nested context. The groupSize includes all nested chunks + the subtype ID.
X *
X * The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
X * in FILLER if it's a mixture of different kinds.
X *
X * This writes the chunk header via PutCkHdr, writes the subtype ID via
X * IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
X * chunks and finish by calling EndWGroup.
X * The OpenWGroup call sets new->clientFrame = parent->clientFrame.
X *
X * ASSUME new context structure allocated by caller but not initialized.
X * ASSUME caller doesn't deallocate the context or access the parent context
X * before calling CloseWGroup.
X * ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup. */
Xextern IFFP StartWGroup(GroupContext *, ID, LONG, ID, GroupContext *);
X                    /*  parent, groupType, groupSize, subtype, new  */
X
X/* End a group started by StartWGroup.
X * This just calls CloseWGroup and PutCkEnd.
X * ERROR conditions: See CloseWGroup and PutCkEnd. */
Xextern IFFP EndWGroup(GroupContext *);
X                    /*  old  */
X
X/* Open the remainder of the current chunk as a group write context.
X * This is normally only called by StartWGroup.
X *
X * Any fixed limit to this group chunk or a containing context will impose
X * a limit on the new context.
X * This will be called just after the group's subtype ID has been written
X * so the remaining contents will be a sequence of chunks.
X * This sets new->clientFrame = parent->clientFrame.
X * ASSUME new context structure allocated by caller but not initialized.
X * ASSUME caller doesn't deallocate the context or access the parent context
X * before calling CloseWGroup.
X * CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first. */
Xextern IFFP OpenWGroup(GroupContext *, GroupContext *);
X                   /*  parent,         new  */
X
X/* Close a write context and update its parent context.
X * This is normally only called by EndWGroup.
X *
X * If this is a top level context (created by OpenWIFF) we'll set the file's
X * EOF (end of file) but won't close the file.
X * After calling this, the old context may be deallocated and the parent
X * context can be accessed again.
X *
X * Amiga DOS Note: There's no call to set the EOF. We just position to the
X * desired end and return. Caller must Close file at that position.
X * CLIENT_ERROR if PutCkEnd wasn't called first. */
Xextern IFFP CloseWGroup(GroupContext *);
X                    /*  old  */
X
X/* Write a whole chunk to a GroupContext. This writes a chunk header, ckSize
X * data bytes, and (if needed) a pad byte. It also updates the GroupContext.
X * CLIENT_ERROR if ckSize == szNotYetKnown. See also PutCkHdr errors. */
Xextern IFFP PutCk(GroupContext *, ID,   LONG,   BYTE *);
X              /*  context,        ckID, ckSize, *data  */
X
X/* Write just a chunk header. Follow this will any number of calls to
X * IFFWriteBytes and finish with PutCkEnd.
X * If you don't yet know how big the chunk is, pass in ckSize = szNotYetKnown,
X * then PutCkEnd will set the ckSize for you later.
X * Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
X * number of bytes get written.
X * CLIENT_ERROR if the chunk would overflow the GroupContext's bound, if
X * PutCkHdr was previously called without a matching PutCkEnd, if ckSize < 0
X * (except szNotYetKnown), if you're trying to write something other
X * than one FORM, LIST, or CAT in a top level (file level) context, or
X * if ckID <= 0 (these illegal ID values are used for error codes). */
Xextern IFFP PutCkHdr(GroupContext *, ID,   LONG);
X                 /*  context,        ckID, ckSize  */
X
X/* Write nBytes number of data bytes for the current chunk and update
X * GroupContext.
X * CLIENT_ERROR if this would overflow the GroupContext's limit or the
X * current chunk's ckSize, or if PutCkHdr wasn't called first, or if
X * nBytes < 0. */
Xextern IFFP IFFWriteBytes(GroupContext *, BYTE *, LONG);
X                      /*  context,        *data,  nBytes  */
X
X/* Complete the current chunk, write a pad byte if needed, and update
X * GroupContext.
X * If current chunk's ckSize = szNotYetKnown, this goes back and sets the
X * ckSize in the file.
X * CLIENT_ERROR if PutCkHdr wasn't called first, or if client hasn't
X * written 'ckSize' number of bytes with IFFWriteBytes. */
Xextern IFFP PutCkEnd(GroupContext *);
X                 /*  context  */
X
X#endif
/
echo 'x - iffr.c'
sed 's/^X//' > iffr.c << '/'
X/*----------------------------------------------------------------------*
X * IFFR.C  Support routines for reading IFF-85 files.          11/15/85
X * (IFF is Interchange Format File.)
X *
X * By Jerry Morrison and Steve Shaw, Electronic Arts.
X * This software is in the public domain.
X *
X * This version for the Commodore-Amiga computer.
X *----------------------------------------------------------------------*/
X#include "iff/iff.h"
X
X/* ---------- Read -----------------------------------------------------*/
X
X/* ---------- OpenRIFF --------------------------------------------------*/
XIFFP OpenRIFF(file0, new0, clientFrame)
X        BPTR file0;   GroupContext *new0;  ClientFrame *clientFrame; {
X    register BPTR file = file0;
X    register GroupContext *new = new0;
X    IFFP iffp = IFF_OKAY;
X
X    new->parent       = NULL;           /* "whole file" has no parent.*/
X    new->clientFrame  = clientFrame;
X    new->file         = file;
X    new->position     = 0;
X    new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
X    new->ckHdr.ckSize = new->bytesSoFar = 0;
X
X    /* Set new->bound. AmigaDOS specific code.*/
X    if (file <= 0)   return(NO_FILE);
X    Seek(file, 0, OFFSET_END);                  /* Seek to end of file.*/
X    new->bound = Seek(file, 0, OFFSET_CURRENT); /* Pos'n == #bytes in file.*/
X    if (new->bound < 0)   return(DOS_ERROR);    /* DOS being absurd.*/
X    Seek(file, 0, OFFSET_BEGINNING);            /* Go to file start.*/
X    /* Would just do this if Amiga DOS maintained fh_End: */
X    /* new->bound = (FileHandle *)BADDR(file)->fh_End; */
X
X    if ( new->bound < sizeof(ChunkHeader) )
X        iffp = NOT_IFF;
X    return(iffp);
X    }
X
X/* ---------- OpenRGroup -----------------------------------------------*/
XIFFP OpenRGroup(parent0, new0)   GroupContext *parent0, *new0; {
X    register GroupContext *parent = parent0;
X    register GroupContext *new    = new0;
X    IFFP iffp = IFF_OKAY;
X
X    new->parent       = parent;
X    new->clientFrame  = parent->clientFrame;
X    new->file         = parent->file;
X    new->position     = parent->position;
X    new->bound        = parent->position + ChunkMoreBytes(parent);
X    new->ckHdr.ckID   = new->subtype    = NULL_CHUNK;
X    new->ckHdr.ckSize = new->bytesSoFar = 0;
X
X    if ( new->bound > parent->bound  ||  IS_ODD(new->bound) )
X        iffp = BAD_IFF;
X    return(iffp);
X    }
X
X/* ---------- CloseRGroup -----------------------------------------------*/
XIFFP CloseRGroup(context)   GroupContext *context; {
X    register LONG position;
X
X    if (context->parent == NULL) {
X        }  /* Context for whole file.*/
X    else {
X        position = context->position;
X        context->parent->bytesSoFar += position - context->parent->position;
X        context->parent->position = position;
X        }
X    return(IFF_OKAY);
X    }
X
X/* ---------- SkipFwd --------------------------------------------------*/
X/* Skip over bytes in a context. Won't go backwards.*/
X/* Updates context->position but not context->bytesSoFar.*/
X/* This implementation is AmigaDOS specific.*/
XIFFP SkipFwd(context, bytes)   GroupContext *context;  LONG bytes; {
X    IFFP iffp = IFF_OKAY;
X
X    if (bytes > 0) {
X        if (-1 == Seek(context->file, bytes, OFFSET_CURRENT))
X            iffp = BAD_IFF;     /* Ran out of bytes before chunk complete.*/
X        else
X            context->position += bytes;
X        }
X    return(iffp);
X    }
X
X/* ---------- GetChunkHdr ----------------------------------------------*/
XID GetChunkHdr(context0)   GroupContext *context0;  {
X    register GroupContext *context = context0;
X    register IFFP iffp;
X    LONG remaining;
X
X    /* Skip remainder of previous chunk & padding. */
X    iffp = SkipFwd(context,
X        ChunkMoreBytes(context) + IS_ODD(context->ckHdr.ckSize));
X    CheckIFFP();
X
X    /* Set up to read the new header. */
X    context->ckHdr.ckID = BAD_IFF;      /* Until we know it's okay, mark it BAD.*/
X    context->subtype    = NULL_CHUNK;
X    context->bytesSoFar = 0;
X
X    /* Generate a psuedo-chunk if at end-of-context. */
X    remaining = context->bound - context->position;
X    if (remaining == 0) {
X        context->ckHdr.ckSize = 0;
X        context->ckHdr.ckID   = END_MARK;
X        }
X
X    /* BAD_IFF if not enough bytes in the context for a ChunkHeader.*/
X    else if (sizeof(ChunkHeader) > remaining) {
X        context->ckHdr.ckSize = remaining;
X        }
X
X    /* Read the chunk header (finally). */
X    else {
X        switch (Read(context->file, &context->ckHdr, sizeof(ChunkHeader))) {
X            case -1: return(context->ckHdr.ckID = DOS_ERROR);
X            case 0:  return(context->ckHdr.ckID = BAD_IFF);
X            }
X
X        /* Check: Top level chunk must be LIST or FORM or CAT. */
X        if (context->parent == NULL)
X            switch(context->ckHdr.ckID) {
X                case FORM:  case LIST:  case CAT:  break;
X                default:    return(context->ckHdr.ckID = NOT_IFF);
X                }
X
X        /* Update the context. */
X        context->position += sizeof(ChunkHeader);
X        remaining         -= sizeof(ChunkHeader);
X
X        /* Non-positive ID values are illegal and used for error codes.*/
X        /* We could check for other illegal IDs...*/
X        if (context->ckHdr.ckID <= 0)
X             context->ckHdr.ckID = BAD_IFF;
X
X        /* Check: ckSize negative or larger than # bytes left in context? */
X        else if (context->ckHdr.ckSize < 0  ||
X                 context->ckHdr.ckSize > remaining) {
X            context->ckHdr.ckSize = remaining;
X            context->ckHdr.ckID   = BAD_IFF;
X            }
X
X        /* Automatically read the LIST, FORM, PROP, or CAT subtype ID */
X        else switch (context->ckHdr.ckID) {
X            case LIST:  case FORM:  case PROP:  case CAT:  {
X                iffp = IFFReadBytes(context,
X                                    (BYTE *)&context->subtype,
X                                    sizeof(ID));
X                if (iffp != IFF_OKAY)
X                    context->ckHdr.ckID = iffp;
X                break; }
X            }
X
X        }
X    return(context->ckHdr.ckID);
X    }
X
X/* ---------- IFFReadBytes ---------------------------------------------*/
XIFFP IFFReadBytes(context, buffer, nBytes)
X    GroupContext *context;   BYTE *buffer;   LONG nBytes; {
X    register IFFP iffp = IFF_OKAY;
X
X    if (nBytes < 0)
X        iffp = CLIENT_ERROR;
X    else if (nBytes > ChunkMoreBytes(context))
X        iffp = SHORT_CHUNK;
X    else if (nBytes > 0)
X        switch ( Read(context->file, buffer, nBytes) ) {
X            case -1: {iffp = DOS_ERROR; break; }
X            case 0:  {iffp = BAD_IFF;   break; }
X            default: {
X                context->position   += nBytes;
X                context->bytesSoFar += nBytes;
X                }
X            }
X
X    return(iffp);
X    }
X
X/* ---------- SkipGroup ------------------------------------------------*/
XIFFP SkipGroup(context)  GroupContext *context;  {
X    }   /* Nothing to do, thanks to GetChunkHdr */
X
X/* ---------- ReadIFF --------------------------------------------------*/
XIFFP ReadIFF(file, clientFrame)  BPTR file;  ClientFrame *clientFrame;  {
X    /*CompilerBug register*/ IFFP iffp;
X    GroupContext context;
X
X    iffp = OpenRIFF(file, &context);
X    context.clientFrame = clientFrame;
X
X    if (iffp == IFF_OKAY)
X        switch (iffp = GetChunkHdr(&context)) {
X            case FORM: { iffp = (*clientFrame->getForm)(&context); break; }
X            case LIST: { iffp = (*clientFrame->getList)(&context); break; }
X            case CAT : { iffp = (*clientFrame->getCat )(&context); break; }
X            /* default: Includes IFF_DONE, BAD_IFF, NOT_IFF... */
X            }
X
X    CloseRGroup(&context);
X
X    if (iffp > 0)               /* Make sure we don't return an ID.*/
X        iffp = NOT_IFF;         /* GetChunkHdr should've caught this.*/
X    return(iffp);
X    }
X
X/* ---------- ReadIList ------------------------------------------------*/
XIFFP ReadIList(parent, clientFrame)
X    GroupContext *parent;  ClientFrame *clientFrame; {
X    GroupContext listContext;
X    IFFP iffp;
X    BOOL propOk = TRUE;
X
X    iffp = OpenRGroup(parent, &listContext);
X    CheckIFFP();
X
X    /* One special case test lets us handle CATs as well as LISTs.*/
X    if (parent->ckHdr.ckID == CAT)
X        propOk = FALSE;
X    else
X        listContext.clientFrame = clientFrame;
X
X    do {
X        switch (iffp = GetChunkHdr(&listContext)) {
X            case PROP: {
X                if (propOk)
X                    iffp = (*clientFrame->getProp)(&listContext);
X                else
X                    iffp = BAD_IFF;
X                break;
X                }
X            case FORM: { iffp = (*clientFrame->getForm)(&listContext); break; }
X            case LIST: { iffp = (*clientFrame->getList)(&listContext); break; }
X            case CAT : { iffp = (*clientFrame->getCat )(&listContext); break; }
X            /* default: Includes END_MARK, IFF_DONE, BAD_IFF, NOT_IFF... */
X            }
X        if (listContext.ckHdr.ckID != PROP)
X            propOk = FALSE;     /* No PROPs allowed after this point.*/
X        } while (iffp == IFF_OKAY);
X
X    CloseRGroup(&listContext);
X
X    if (iffp > 0)       /* Only chunk types above are allowed in a LIST/CAT.*/
X        iffp = BAD_IFF;
X    return(iffp == END_MARK ? IFF_OKAY : iffp);
X    }
X
X/* ---------- ReadICat -------------------------------------------------*/
X/* By special arrangement with the ReadIList implement'n, this is trivial.*/
XIFFP ReadICat(parent)  GroupContext *parent;  {
X    return( ReadIList(parent, NULL) );
X    }
X
X/* ---------- GetFChunkHdr ---------------------------------------------*/
XID GetFChunkHdr(context)   GroupContext *context; {
X    register ID id;
X
X    id = GetChunkHdr(context);
X    if (id == PROP)
X        context->ckHdr.ckID = id = BAD_IFF;
X    return(id);
X    }
X
X/* ---------- GetF1ChunkHdr ---------------------------------------------*/
XID GetF1ChunkHdr(context)   GroupContext *context; {
X    register ID id;
X    register ClientFrame *clientFrame = context->clientFrame;
X
X    switch (id = GetChunkHdr(context))  {
X        case PROP: { id = BAD_IFF; break; }
X        case FORM: { id = (*clientFrame->getForm)(context); break; }
X        case LIST: { id = (*clientFrame->getList)(context); break; }
X        case CAT : { id = (*clientFrame->getCat )(context); break; }
X        /* Default: let the caller handle other chunks */
X        }
X    return(context->ckHdr.ckID = id);
X    }
X
X/* ---------- GetPChunkHdr ---------------------------------------------*/
XID GetPChunkHdr(context)   GroupContext *context; {
X    register ID id;
X
X    id = GetChunkHdr(context);
X    switch (id) {
X        case LIST:  case FORM:  case PROP:  case CAT:  {
X            id = context->ckHdr.ckID = BAD_IFF;
X            break; }
X        }
X    return(id);
X    }
X
/
echo 'x - iffw.c'
sed 's/^X//' > iffw.c << '/'
X/*----------------------------------------------------------------------*
X * IFFW.C  Support routines for writing IFF-85 files.          11/15/85
X * (IFF is Interchange Format File.)
X *
X * By Jerry Morrison and Steve Shaw, Electronic Arts.
X * This software is in the public domain.
X *
X * This version for the Commodore-Amiga computer.
X *----------------------------------------------------------------------*/
X#include "iff/iff.h"
X
X/* ---------- IFF Writer -----------------------------------------------*/
X
X/* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
X#define Known(size)   ( (size) != szNotYetKnown )
X
X/* Yet another weird macro to make the source code simpler...*/
X#define IfIffp(expr)  {if (iffp == IFF_OKAY)  iffp = (expr);}
X
X
X/* ---------- OpenWIFF -------------------------------------------------*/
XIFFP OpenWIFF(file, new0, limit)  BPTR file; GroupContext *new0; LONG limit; {
X    register GroupContext *new = new0;
X    register IFFP iffp = IFF_OKAY;
X
X    new->parent       = NULL;
X    new->clientFrame  = NULL;
X    new->file         = file;
X    new->position     = 0;
X    new->bound        = limit;
X    new->ckHdr.ckID   = NULL_CHUNK;  /* indicates no current chunk */
X    new->ckHdr.ckSize = new->bytesSoFar = 0;
X
X    if (0 > Seek(file, 0, OFFSET_BEGINNING))    /* Go to start of the file.*/
X        iffp = DOS_ERROR;
X    else if ( Known(limit) && IS_ODD(limit) )
X        iffp = CLIENT_ERROR;
X    return(iffp);
X    }
X
X/* ---------- StartWGroup ----------------------------------------------*/
XIFFP StartWGroup(parent, groupType, groupSize, subtype, new)
X      GroupContext *parent, *new; ID groupType, subtype; LONG groupSize;  {
X    register IFFP iffp;
X
X    iffp = PutCkHdr(parent, groupType, groupSize);
X    IfIffp( IFFWriteBytes(parent, (BYTE *)&subtype, sizeof(ID)) );
X    IfIffp( OpenWGroup(parent, new) );
X    return(iffp);
X    }
X
X/* ---------- OpenWGroup -----------------------------------------------*/
XIFFP OpenWGroup(parent0, new0)  GroupContext *parent0, *new0; {
X    register GroupContext *parent = parent0;
X    register GroupContext *new    = new0;
X    register LONG ckEnd;
X    register IFFP iffp = IFF_OKAY;
X
X    new->parent       = parent;
X    new->clientFrame  = parent->clientFrame;
X    new->file         = parent->file;
X    new->position     = parent->position;
X    new->bound        = parent->bound;
X    new->ckHdr.ckID   = NULL_CHUNK;
X    new->ckHdr.ckSize = new->bytesSoFar = 0;
X
X    if ( Known(parent->ckHdr.ckSize) ) {
X        ckEnd = new->position + ChunkMoreBytes(parent);
X        if ( new->bound == szNotYetKnown || new->bound > ckEnd )
X            new->bound = ckEnd;
X        };
X
X    if ( parent->ckHdr.ckID == NULL_CHUNK || /* not currently writing a chunk*/
X         IS_ODD(new->position) ||
X         (Known(new->bound) && IS_ODD(new->bound)) )
X        iffp = CLIENT_ERROR;
X    return(iffp);
X    }
X
X/* ---------- CloseWGroup ----------------------------------------------*/
XIFFP CloseWGroup(old0)  GroupContext *old0; {
X    register GroupContext *old = old0;
X
X    if ( old->ckHdr.ckID != NULL_CHUNK )  /* didn't close the last chunk */
X        return(CLIENT_ERROR);
X    if ( old->parent == NULL ) {          /* top level file context */
X        /* [TBD] set logical EOF */
X        }
X    else {
X        old->parent->bytesSoFar += old->position - old->parent->position;
X        old->parent->position = old->position;
X        };
X    return(IFF_OKAY);
X    }
X
X/* ---------- EndWGroup ------------------------------------------------*/
XIFFP EndWGroup(old)  GroupContext *old;  {
X    register GroupContext *parent = old->parent;
X    register IFFP iffp;
X
X    iffp = CloseWGroup(old);
X    IfIffp( PutCkEnd(parent) );
X    return(iffp);
X    }
X
X/* ---------- PutCk ----------------------------------------------------*/
XIFFP PutCk(context, ckID, ckSize, data)
X      GroupContext *context; ID ckID; LONG ckSize; BYTE *data; {
X    register IFFP iffp = IFF_OKAY;
X
X    if ( ckSize == szNotYetKnown )
X        iffp = CLIENT_ERROR;
X    IfIffp( PutCkHdr(context, ckID, ckSize) );
X    IfIffp( IFFWriteBytes(context, data, ckSize) );
X    IfIffp( PutCkEnd(context) );
X    return(iffp);
X    }
X
X/* ---------- PutCkHdr -------------------------------------------------*/
XIFFP PutCkHdr(context0, ckID, ckSize)
X      GroupContext *context0;  ID ckID;  LONG ckSize; {
X    register GroupContext *context = context0;
X    LONG minPSize = sizeof(ChunkHeader); /* physical chunk >= minPSize bytes*/
X
X    /* CLIENT_ERROR if we're already inside a chunk or asked to write
X     * other than one FORM, LIST, or CAT at the top level of a file */
X    /* Also, non-positive ID values are illegal and used for error codes.*/
X    /* (We could check for other illegal IDs...)*/
X    if ( context->ckHdr.ckID != NULL_CHUNK  ||  ckID <= 0 )
X        return(CLIENT_ERROR);
X    else if (context->parent == NULL)  {
X        switch (ckID)  {
X            case FORM:  case LIST:  case CAT:  break;
X            default: return(CLIENT_ERROR);
X            }
X        if (context->position != 0)
X            return(CLIENT_ERROR);
X        }
X
X    if ( Known(ckSize) ) {
X        if ( ckSize < 0 )
X            return(CLIENT_ERROR);
X        minPSize += ckSize;
X        };
X    if ( Known(context->bound)  &&
X         context->position + minPSize > context->bound )
X        return(CLIENT_ERROR);
X
X    context->ckHdr.ckID   = ckID;
X    context->ckHdr.ckSize = ckSize;
X    context->bytesSoFar   = 0;
X    if (0 > Write(context->file, &context->ckHdr, sizeof(ChunkHeader)))
X        return(DOS_ERROR);
X    context->position += sizeof(ChunkHeader);
X    return(IFF_OKAY);
X    }
X
X/* ---------- IFFWriteBytes ---------------------------------------------*/
XIFFP IFFWriteBytes(context0, data, nBytes)
X      GroupContext *context0;  BYTE *data;  LONG nBytes; {
X    register GroupContext *context = context0;
X
X    if ( context->ckHdr.ckID == NULL_CHUNK  ||  /* not in a chunk */
X         nBytes < 0  ||                         /* negative nBytes */
X         (Known(context->bound)  &&             /* overflow context */
X            context->position + nBytes > context->bound)  ||
X         (Known(context->ckHdr.ckSize)  &&      /* overflow chunk */
X            context->bytesSoFar + nBytes > context->ckHdr.ckSize) )
X        return(CLIENT_ERROR);
X
X    if (0 > Write(context->file, data, nBytes))
X        return(DOS_ERROR);
X
X    context->bytesSoFar += nBytes;
X    context->position   += nBytes;
X    return(IFF_OKAY);
X    }
X
X/* ---------- PutCkEnd -------------------------------------------------*/
XIFFP PutCkEnd(context0)  GroupContext *context0; {
X    register GroupContext *context = context0;
X    WORD zero = 0;      /* padding source */
X
X    if ( context->ckHdr.ckID == NULL_CHUNK )  /* not in a chunk */
X        return(CLIENT_ERROR);
X
X    if ( context->ckHdr.ckSize == szNotYetKnown ) {
X        /* go back and set the chunk size to bytesSoFar */
X        if ( 0 > Seek(context->file,
X                      -(context->bytesSoFar + sizeof(LONG)),
X                      OFFSET_CURRENT)  ||
X             0 > Write(context->file, &context->bytesSoFar, sizeof(LONG))  ||
X             0 > Seek(context->file, context->bytesSoFar, OFFSET_CURRENT)  )
X            return(DOS_ERROR);
X        }
X    else {  /* make sure the client wrote as many bytes as planned */
X        if ( context->ckHdr.ckSize != context->bytesSoFar )
X            return(CLIENT_ERROR);
X        };
X
X    /* Write a pad byte if needed to bring us up to an even boundary.
X     * Since the context end must be even, and since we haven't
X     * overwritten the context, if we're on an odd position there must
X     * be room for a pad byte. */
X    if ( IS_ODD(context->bytesSoFar) ) {
X        if ( 0 > Write(context->file, &zero, 1) )
X            return(DOS_ERROR);
X        context->position += 1;
X        };
X
X    context->ckHdr.ckID   = NULL_CHUNK;
X    context->ckHdr.ckSize = context->bytesSoFar = 0;
X    return(IFF_OKAY);
X    }
X
/
echo 'x - iffcheck.c'
sed 's/^X//' > iffcheck.c << '/'
X/*---------------------------------------------------------------------*
X * IFFCheck.C  Print out the structure of an IFF-85 file,      11/19/85
X * checking for structural errors.
X *
X * DO NOT USE THIS AS A SKELETAL PROGRAM FOR AN IFF READER!
X * See ShowILBM.C for a skeletal example.
X *
X * This version for the Commodore-Amiga computer.
X *----------------------------------------------------------------------*/
X#include "iff/iff.h"
X
X
X/* ---------- IFFCheck -------------------------------------------------*/
X/* [TBD] More extensive checking could be done on the IDs encountered in the
X * file. Check that the reserved IDs "FOR1".."FOR9", "LIS1".."LIS9", and
X * "CAT1".."CAT9" aren't used. Check that reserved IDs aren't used as Form
X * types. Check that all IDs are made of 4 printable characters (trailing
X * spaces ok). */
X
Xtypedef struct {
X    ClientFrame clientFrame;
X    int levels;         /* # groups currently nested within.*/
X    } Frame;
X
Xchar MsgOkay[] = { "----- (IFF_OKAY) A good IFF file." };
Xchar MsgEndMark[] = {"----- (END_MARK) How did you get this message??" };
Xchar MsgDone[] = { "----- (IFF_DONE) How did you get this message??" };
Xchar MsgDos[] = { "----- (DOS_ERROR) The DOS gave back an error." };
Xchar MsgNot[] = { "----- (NOT_IFF) not an IFF file." };
Xchar MsgNoFile[] = { "----- (NO_FILE) no such file found." };
Xchar MsgClientError[] = {"----- (CLIENT_ERROR) IFF Checker bug."};
Xchar MsgForm[] = { "----- (BAD_FORM) How did you get this message??" };
Xchar MsgShort[] = { "----- (SHORT_CHUNK) How did you get this message??" };
Xchar MsgBad[] = { "----- (BAD_IFF) a mangled IFF file." };
X
X/* MUST GET THESE IN RIGHT ORDER!!*/
Xchar *IFFPMessages[-LAST_ERROR+1] = {
X    /*IFF_OKAY*/  MsgOkay,
X    /*END_MARK*/  MsgEndMark,
X    /*IFF_DONE*/  MsgDone,
X    /*DOS_ERROR*/ MsgDos,
X    /*NOT_IFF*/   MsgNot,
X    /*NO_FILE*/   MsgNoFile,
X    /*CLIENT_ERROR*/ MsgClientError,
X    /*BAD_FORM*/  MsgForm,
X    /*SHORT_CHUNK*/  MsgShort,
X    /*BAD_IFF*/   MsgBad
X    };
X
X/* FORWARD REFERENCES */
Xextern IFFP GetList(GroupContext *);
Xextern IFFP GetForm(GroupContext *);
Xextern IFFP GetProp(GroupContext *);
Xextern IFFP GetCat (GroupContext *);
X
Xvoid IFFCheck(name)  char *name; {
X    IFFP iffp;
X    BPTR file = Open(name, MODE_OLDFILE);
X    Frame frame;
X
X    frame.levels = 0;
X    frame.clientFrame.getList = GetList;
X    frame.clientFrame.getForm = GetForm;
X    frame.clientFrame.getProp = GetProp;
X    frame.clientFrame.getCat  = GetCat ;
X
X    printf("----- Checking file '%s' -----\n", name);
X    if (file == 0)
X        iffp = NO_FILE;
X    else
X        iffp = ReadIFF(file, (ClientFrame *)&frame);
X
X    Close(file);
X    printf("%s\n", IFFPMessages[-iffp]);
X    }
X
Xmain(argc, argv)   int argc;  char **argv; {
X    if (argc != 1+1) {
X        printf("Usage: 'iffcheck filename'\n");
X        exit(0);
X        }
X    IFFCheck(argv[1]);
X    }
X
X/* ---------- Put... ---------------------------------------------------*/
X
XPutLevels(count)   int count; {
X    for ( ;  count > 0;  --count) {
X        printf(".");
X        }
X    }
X
XPutID(id)  ID id; {
X    printf("%c%c%c%c", (id>>24)&0x7f, (id>>16)&0x7f, (id>>8)&0x7f, id&0x7f);
X    }
X
XPutN(n)   int n; {
X    printf(" %d ", n);
X    }
X
X/* Put something like "...BMHD 14" or "...LIST 14 PLBM". */
XPutHdr(context)  GroupContext *context; {
X    PutLevels( ((Frame *)context->clientFrame)->levels );
X    PutID(context->ckHdr.ckID);
X    PutN(context->ckHdr.ckSize);
X
X    if (context->subtype != NULL_CHUNK)
X        PutID(context->subtype);
X
X    printf("\n");
X    }
X
X/* ---------- AtLeaf ---------------------------------------------------*/
X
X/* At Leaf chunk.  That is, a chunk which does NOT contain other chunks.
X * Print "ID size".*/
XIFFP AtLeaf(context)  GroupContext *context; {
X
X    PutHdr(context);
X    /* A typical reader would read the chunk's contents, using the "Frame"
X     * for local data, esp. shared property settings (PROP).*/
X    /* IFFReadBytes(context, ...buffer, context->ckHdr->ckSize); */
X    return(IFF_OKAY);
X    }
X
X/* ---------- GetList --------------------------------------------------*/
X/* Handle a LIST chunk.  Print "LIST size subTypeID".
X * Then dive into it.*/
XIFFP GetList(parent)  GroupContext *parent; {
X    Frame newFrame;
X
X    newFrame = *(Frame *)parent->clientFrame;  /* copy parent's frame*/
X    newFrame.levels++;
X
X    PutHdr(parent);
X
X    return( ReadIList(parent, (ClientFrame *)&newFrame) );
X    }
X
X/* ---------- GetForm --------------------------------------------------*/
X/* Handle a FORM chunk.  Print "FORM size subTypeID".
X * Then dive into it.*/
XIFFP GetForm(parent)   GroupContext *parent; {
X    /*CompilerBug register*/ IFFP iffp;
X    GroupContext new;
X    Frame newFrame;
X
X    newFrame = *(Frame *)parent->clientFrame;  /* copy parent's frame*/
X    newFrame.levels++;
X
X    PutHdr(parent);
X
X    iffp = OpenRGroup(parent, &new);
X    CheckIFFP();
X    new.clientFrame = (ClientFrame *)&newFrame;
X
X    /* FORM reader for Checker. */
X    /* LIST, FORM, PROP, CAT already handled by GetF1ChunkHdr. */
X    do {if ( (iffp = GetF1ChunkHdr(&new)) > 0 )
X            iffp = AtLeaf(&new);
X        } while (iffp >= IFF_OKAY);
X
X    CloseRGroup(&new);
X    return(iffp == END_MARK ? IFF_OKAY : iffp);
X    }
X
X/* ---------- GetProp --------------------------------------------------*/
X/* Handle a PROP chunk.  Print "PROP size subTypeID".
X * Then dive into it.*/
XIFFP GetProp(listContext)  GroupContext *listContext; {
X    /*CompilerBug register*/ IFFP iffp;
X    GroupContext new;
X
X    PutHdr(listContext);
X
X    iffp = OpenRGroup(listContext, &new);
X    CheckIFFP();
X
X    /* PROP reader for Checker. */
X    ((Frame *)listContext->clientFrame)->levels++;
X
X    do {if ( (iffp = GetPChunkHdr(&new)) > 0 )
X            iffp = AtLeaf(&new);
X        } while (iffp >= IFF_OKAY);
X
X    ((Frame *)listContext->clientFrame)->levels--;
X
X    CloseRGroup(&new);
X    return(iffp == END_MARK ? IFF_OKAY : iffp);
X    }
X
X/* ---------- GetCat ---------------------------------------------------*/
X/* Handle a CAT chunk.  Print "CAT size subTypeID".
X * Then dive into it.*/
XIFFP GetCat(parent)  GroupContext *parent;  {
X    IFFP iffp;
X
X    ((Frame *)parent->clientFrame)->levels++;
X
X    PutHdr(parent);
X
X    iffp = ReadICat(parent);
X
X    ((Frame *)parent->clientFrame)->levels--;
X    return(iffp);
X    }
X
/
echo 'x - iffcheck.lnk'
sed 's/^X//' > iffcheck.lnk << '/'
XFROM startup.o,iffcheck.o,iffr.o
XLIBRARY lc.lib,amiga.lib,debug.lib
XTO iffcheck
/
exit
----------------
Have fun!
-- 
           Mike Farren
           uucp: {your favorite backbone site}!hplabs!well!farren
           Fido: Sci-Fido, Fidonode 125/84, (415)655-0667



More information about the Comp.sources.unix mailing list