Updated original EA-IFF-85 files to read and write Interchange File Format files.

Plan is to convert COLLADA XML into IFF, for faster, more compact and easier to read and parse files.
Reading/writing code is a few hundred lines of code, instead of COLLADA-DOM and libxml combo.
So it would be suitable as a run-time format for platforms ranging from iPhone to PlayStation 3.
Relevant physics data (and perhaps other data) will be converted from COLLADA -> IFF. 
We could call the resulting files COLLADA binary IFF -> CLIFF.
This commit is contained in:
erwin.coumans
2008-10-26 23:04:43 +00:00
parent 92c69ddefa
commit 2a721c7489
6 changed files with 2973 additions and 0 deletions

1423
Extras/iff/IFF.txt Normal file

File diff suppressed because it is too large Load Diff

468
Extras/iff/iff.h Normal file
View File

@@ -0,0 +1,468 @@
#ifndef IFF_H
#define IFF_H
/*----------------------------------------------------------------------*/
/* IFF.H defs for IFF-85 Interchange Format Files. 10/8/85 */
/* */
/* By Jerry Morrison and Steve Shaw, Electronic Arts. */
/* This software is in the public domain. */
/* Original version was for the Commodore-Amiga computer. */
/* This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008*/
/*----------------------------------------------------------------------*/
#define NL 0L /** A ver of NULL So Manx will like it **/
#include <stdio.h> //printf debugging
typedef int LONG;
typedef unsigned char UBYTE;
typedef void* BPTR;
typedef unsigned short int UWORD;
typedef short int WORD;
typedef char BYTE;
#define LOCAL
#define TRUE 1
#define FALSE 0
typedef int BOOL;
#define OFFSET_END SEEK_END
#define OFFSET_CURRENT SEEK_CUR
#define OFFSET_BEGINNING SEEK_SET
#define Seek fseek
#define Write fwrite
#define Read(file,buffer,nbytes) fread(buffer,1,nbytes,file)
#define GWriteFlush(file) (0)
#define GWrite(file, buffer, nBytes) fwrite(buffer,1,nBytes,file)
#define GSeek(file, position, mode) Seek(file, position, mode)
typedef LONG IFFP; /* Status code result from an IFF procedure */
/* LONG, because must be type compatable with ID for GetChunkHdr.*/
/* Note that the error codes below are not legal IDs.*/
#define IFF_OKAY 0L /* Keep going...*/
#define END_MARK -1L /* As if there was a chunk at end of group.*/
#define IFF_DONE -2L /* clientProc returns this when it has READ enough.
* It means return thru all levels. File is Okay.*/
#define DOS_ERROR -3L
#define NOT_IFF -4L /* not an IFF file.*/
#define NO_FILE -5L /* Tried to open file, DOS didn't find it.*/
#define CLIENT_ERROR -6L /* Client made invalid request, for instance, asking
* for more bytes than existed in chunk.*/
#define BAD_FORM -7L /* A client read proc complains about FORM semantics;
* e.g. valid IFF, but missing a required chunk.*/
#define SHORT_CHUNK -8L /* Client asked to IFFReadBytes more bytes than left
* in the chunk. Could be client bug or bad form.*/
#define BAD_IFF -9L /* mal-formed IFF file. [TBD] Expand this into a
* range of error codes.*/
#define LAST_ERROR (short)BAD_IFF
/* This MACRO is used to RETURN immediately when a termination condition is
* found. This is a pretty weird macro. It requires the caller to declare a
* local "IFFP iffp" and assign it. This wouldn't work as a subroutine since
* it returns for it's caller. */
#define CheckIFFP() { if (iffp != IFF_OKAY) return(iffp); }
/* ---------- ID -------------------------------------------------------*/
typedef LONG ID; /* An ID is four printable ASCII chars but
* stored as a LONG for efficient copy & compare.*/
/* Four-character IDentifier builder.*/
#define MakeID(a,b,c,d) (((long)(a)) | ((long)(b))<<8L | (c)<<16L | (d)<<24L)
//#define MakeID(a,b,c,d) (((long)(a))<<24L | ((long)(b))<<16L | (c)<<8 | (d))
/* Standard group IDs. A chunk with one of these IDs contains a
SubTypeID followed by zero or more chunks.*/
#define FORM MakeID('F','O','R','M')
#define PROP MakeID('P','R','O','P')
#define LIST MakeID('L','I','S','T')
#define CAT MakeID('C','A','T',' ')
#define FILLER MakeID(' ',' ',' ',' ')
/* The IDs "FOR1".."FOR9", "LIS1".."LIS9", & "CAT1".."CAT9" are reserved
* for future standardization.*/
/* Pseudo-ID used internally by chunk reader and writer.*/
#define NULL_CHUNK 0L /* No current chunk.*/
/* ---------- Chunk ----------------------------------------------------*/
/* All chunks start with a type ID and a count of the data bytes that
follow--the chunk's "logical size" or "data size". If that number is odd,
a 0 pad byte is written, too. */
typedef struct {
ID ckID;
LONG ckSize;
} ChunkHeader;
typedef struct {
ID ckID;
LONG ckSize;
UBYTE ckData[ 1 /*REALLY: ckSize*/ ];
} Chunk;
/* Pass ckSize = szNotYetKnown to the writer to mean "compute the size".*/
#define szNotYetKnown 0x80000001L
/* Need to know whether a value is odd so can word-align.*/
#define IS_ODD(a) ((a) & 1)
/* This macro rounds up to an even number. */
#define WordAlign(size) ((size+1)&~1)
/* ALL CHUNKS MUST BE PADDED TO EVEN NUMBER OF BYTES.
* ChunkPSize computes the total "physical size" of a padded chunk from
* its "data size" or "logical size". */
#define ChunkPSize(dataSize) (WordAlign(dataSize) + (long)sizeof(ChunkHeader))
/* The Grouping chunks (LIST, FORM, PROP, & CAT) contain concatenations of
* chunks after a subtype ID that identifies the content chunks.
* "FORM type XXXX", "LIST of FORM type XXXX", "PROPerties associated
* with FORM type XXXX", or "conCATenation of XXXX".*/
typedef struct {
ID ckID;
LONG ckSize; /* this ckSize includes "grpSubID".*/
ID grpSubID;
} GroupHeader;
typedef struct {
ID ckID;
LONG ckSize;
ID grpSubID;
UBYTE grpData[ 1 /*REALLY: ckSize-sizeof(grpSubID)*/ ];
} GroupChunk;
/* ---------- IFF Reader -----------------------------------------------*/
/******** Routines to support a stream-oriented IFF file reader *******
*
* These routines handle lots of details like error checking and skipping
* over padding. They're also careful not to read past any containing context.
*
* These routines ASSUME they're the only ones reading from the file.
* Client should check IFFP error codes. Don't press on after an error!
* These routines try to have no side effects in the error case, except
* partial I/O is sometimes unavoidable.
*
* All of these routines may return DOS_ERROR. In that case, ask DOS for the
* specific error code.
*
* The overall scheme for the low level chunk reader is to open a "group read
* context" with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
* (and its kin) and IFFReadBytes, and close the context with CloseRGroup.
*
* The overall scheme for reading an IFF file is to use ReadIFF, ReadIList,
* and ReadICat to scan the file. See those procedures, ClientProc (below),
* and the skeleton IFF reader. */
/* Client passes ptrs to procedures of this type to ReadIFF which call them
* back to handle LISTs, FORMs, CATs, and PROPs.
*
* Use the GroupContext ptr when calling reader routines like GetChunkHdr.
* Look inside the GroupContext ptr for your ClientFrame ptr. You'll
* want to type cast it into a ptr to your containing struct to get your
* private contextual data (stacked property settings). See below. */
typedef IFFP ClientProc( struct _GroupContext * );
/* Client's context for reading an IFF file or a group.
* Client should actually make this the first component of a larger struct
* (it's personal stack "frame") that has a field to store each "interesting"
* property encountered.
* Either initialize each such field to a global default or keep a boolean
* indicating if you've read a property chunk into that field.
* Your getList and getForm procs should allocate a new "frame" and copy the
* parent frame's contents. The getProp procedure should store into the frame
* allocated by getList for the containing LIST. */
typedef struct _ClientFrame {
ClientProc *getList, *getProp, *getForm, *getCat;
/* client's own data follows; place to stack property settings */
} ClientFrame;
/* Our context for reading a group chunk. */
typedef struct _GroupContext {
struct _GroupContext *parent; /* Containing group; NULL => whole file. */
ClientFrame *clientFrame; /* Reader data & client's context state. */
BPTR file; /* Byte-stream file handle. */
LONG position; /* The context's logical file position. */
LONG bound; /* File-absolute context bound
* or szNotYetKnown (writer only). */
ChunkHeader ckHdr; /* Current chunk header. ckHdr.ckSize = szNotYetKnown
* means we need to go back and set the size (writer onl
y).
* See also Pseudo-IDs, above. */
ID subtype; /* Group's subtype ID when reading. */
LONG bytesSoFar; /* # bytes read/written of current chunk's data. */
} GroupContext;
/* Computes the number of bytes not yet read from the current chunk, given
* a group read context gc. */
#define ChunkMoreBytes(gc) ((gc)->ckHdr.ckSize - (gc)->bytesSoFar)
/***** Low Level IFF Chunk Reader *****/
/* Given an open file, open a read context spanning the whole file.
* This is normally only called by ReadIFF.
* This sets new->clientFrame = clientFrame.
* ASSUME context allocated by caller but not initialized.
* ASSUME caller doesn't deallocate the context before calling CloseRGroup.
* NOT_IFF ERROR if the file is too short for even a chunk header.*/
extern IFFP OpenRIFF(/* BPTR, GroupContext *, ClientFrame * */);
/* file, new, clientFrame */
/* Open the remainder of the current chunk as a group read context.
* This will be called just after the group's subtype ID has been read
* (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
* remainder is a sequence of chunks.
* This sets new->clientFrame = parent->clientFrame. The caller should repoint
* it at a new clientFrame if opening a LIST context so it'll have a "stack
* frame" to store PROPs for the LIST. (It's usually convenient to also
* allocate a new Frame when you encounter FORM of the right type.)
*
* ASSUME new context allocated by caller but not initialized.
* ASSUME caller doesn't deallocate the context or access the parent context
* before calling CloseRGroup.
* BAD_IFF ERROR if context end is odd or extends past parent. */
extern IFFP OpenRGroup(GroupContext *, GroupContext * );
/* parent, new */
/* Close a group read context, updating its parent context.
* After calling this, the old context may be deallocated and the parent
* context can be accessed again. It's okay to call this particular procedure
* after an error has occurred reading the group.
* This always returns IFF_OKAY. */
extern IFFP CloseRGroup( GroupContext * );
/* old */
/* Skip any remaining bytes of the previous chunk and any padding, then
* read the next chunk header into context.ckHdr.
* If the ckID is LIST, FORM, CAT, or PROP, this automatically reads the
* subtype ID into context->subtype.
* Caller should dispatch on ckID (and subtype) to an appropriate handler.
*
* RETURNS context.ckHdr.ckID (the ID of the new chunk header); END_MARK
* if there are no more chunks in this context; or NOT_IFF if the top level
* file chunk isn't a FORM, LIST, or CAT; or BAD_IFF if malformed chunk, e.g.
* ckSize is negative or too big for containing context, ckID isn't positive,
* or we hit end-of-file.
*
* See also GetFChunkHdr, GetF1ChunkHdr, and GetPChunkHdr, below.*/
extern ID GetChunkHdr(/* GroupContext * */);
/* context.ckHdr.ckID context */
/* Read nBytes number of data bytes of current chunk. (Use OpenGroup, etc.
* instead to read the contents of a group chunk.) You can call this several
* times to read the data piecemeal.
* CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > ChunkMoreBytes(context)
* which could be due to a client bug or a chunk that's shorter than it
* ought to be (bad form). (on either CLIENT_ERROR or SHORT_CHUNK,
* IFFReadBytes won't read any bytes.) */
extern IFFP IFFReadBytes( GroupContext *, BYTE *, LONG );
/* context, buffer, nBytes */
/***** IFF File Reader *****/
/* This is a noop ClientProc that you can use for a getList, getForm, getProp,
* or getCat procedure that just skips the group. A simple reader might just
* implement getForm, store &ReadICat in the getCat field of clientFrame, and
* use &SkipGroup for the getList and getProp procs.*/
extern IFFP SkipGroup( GroupContext* );
/* IFF file reader.
* Given an open file, allocate a group context and use it to read the FORM,
* LIST, or CAT and it's contents. The idea is to parse the file's contents,
* and for each FORM, LIST, CAT, or PROP encountered, call the getForm,
* getList, getCat, or getProp procedure in clientFrame, passing the
* GroupContext ptr.
* This is achieved with the aid of ReadIList (which your getList should
* call) and ReadICat (which your getCat should call, if you don't just use
* &ReadICat for your getCat). If you want to handle FORMs, LISTs, and CATs
* nested within FORMs, the getForm procedure must dispatch to getForm,
* getList, and getCat (it can use GetF1ChunkHdr to make this easy).
*
* Normal return is IFF_OKAY (if whole file scanned) or IFF_DONE (if a client
* proc said "done" first).
* See the skeletal getList, getForm, getCat, and getProp procedures. */
extern IFFP ReadIFF( BPTR, ClientFrame * );
/* file, clientFrame */
/* IFF LIST reader.
* Your "getList" procedure should allocate a ClientFrame, copy the parent's
* ClientFrame, and then call this procedure to do all the work.
*
* Normal return is IFF_OKAY (if whole LIST scanned) or IFF_DONE (if a client
* proc said "done" first).
* BAD_IFF ERROR if a PROP appears after a non-PROP. */
extern IFFP ReadIList(/* GroupContext *, ClientFrame * */);
/* parent, clientFrame */
/* IFF CAT reader.
* Most clients can simply use this to read their CATs. If you must do extra
* setup work, put a ptr to your getCat procedure in the clientFrame, and
* have that procedure call ReadICat to do the detail work.
*
* Normal return is IFF_OKAY (if whole CAT scanned) or IFF_DONE (if a client
* proc said "done" first).
* BAD_IFF ERROR if a PROP appears in the CAT. */
extern IFFP ReadICat(/* GroupContext * */);
/* parent */
/* Call GetFChunkHdr instead of GetChunkHdr to read each chunk inside a FORM.
* It just calls GetChunkHdr and returns BAD_IFF if it gets a PROP chunk. */
extern ID GetFChunkHdr( GroupContext * );
/* context.ckHdr.ckID context */
/* GetF1ChunkHdr is like GetFChunkHdr, but it automatically dispatches to the
* getForm, getList, and getCat procedure (and returns the result) if it
* encounters a FORM, LIST, or CAT. */
extern ID GetF1ChunkHdr(/* GroupContext * */);
/* context.ckHdr.ckID context */
/* Call GetPChunkHdr instead of GetChunkHdr to read each chunk inside a PROP.
* It just calls GetChunkHdr and returns BAD_IFF if it gets a group chunk. */
extern ID GetPChunkHdr(/* GroupContext * */);
/* context.ckHdr.ckID context */
/** endianSwap32/endianSwap16 convert integers from big endian to little endian on little-endian platforms.
* They have no effect on big-endian platforms
**/
extern int endianSwap32(int value);
extern short int endianSwap16(short int value);
/* ---------- IFF Writer -----------------------------------------------*/
/******* Routines to support a stream-oriented IFF file writer *******
*
* These routines will random access back to set a chunk size value when the
* caller doesn't know it ahead of time. They'll also do things automatically
* like padding and error checking.
*
* These routines ASSUME they're the only ones writing to the file.
* Client should check IFFP error codes. Don't press on after an error!
* These routines try to have no side effects in the error case, except that
* partial I/O is sometimes unavoidable.
*
* All of these routines may return DOS_ERROR. In that case, ask DOS for the
* specific error code.
*
* The overall scheme is to open an output GroupContext via OpenWIFF or
* OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
* each chunk, then use CloseWGroup to close the GroupContext.
*
* To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
* its chunks, then call EndWGroup. StartWGroup automatically writes the
* group header and opens a nested context for writing the contents.
* EndWGroup closes the nested context and completes the group chunk. */
/* Given a file open for output, open a write context.
* The "limit" arg imposes a fence or upper limit on the logical file
* position for writing data in this context. Pass in szNotYetKnown to be
* bounded only by disk capacity.
* ASSUME new context structure allocated by caller but not initialized.
* ASSUME caller doesn't deallocate the context before calling CloseWGroup.
* The caller is only allowed to write out one FORM, LIST, or CAT in this top
* level context (see StartWGroup and PutCkHdr).
* CLIENT_ERROR if limit is odd.*/
extern IFFP OpenWIFF( BPTR, GroupContext *, LONG );
/* file, new, limit {file position} */
/* Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
* nested context. The groupSize includes all nested chunks + the subtype ID.
*
* The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
* in FILLER if it's a mixture of different kinds.
*
* This writes the chunk header via PutCkHdr, writes the subtype ID via
* IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
* chunks and finish by calling EndWGroup.
* The OpenWGroup call sets new->clientFrame = parent->clientFrame.
*
* ASSUME new context structure allocated by caller but not initialized.
* ASSUME caller doesn't deallocate the context or access the parent context
* before calling CloseWGroup.
* ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup. */
extern IFFP StartWGroup( GroupContext *, ID, LONG, ID, GroupContext * );
/* parent, groupType, groupSize, subtype, new */
/* End a group started by StartWGroup.
* This just calls CloseWGroup and PutCkEnd.
* ERROR conditions: See CloseWGroup and PutCkEnd. */
extern IFFP EndWGroup( GroupContext * );
/* old */
/* Open the remainder of the current chunk as a group write context.
* This is normally only called by StartWGroup.
*
* Any fixed limit to this group chunk or a containing context will impose
* a limit on the new context.
* This will be called just after the group's subtype ID has been written
* so the remaining contents will be a sequence of chunks.
* This sets new->clientFrame = parent->clientFrame.
* ASSUME new context structure allocated by caller but not initialized.
* ASSUME caller doesn't deallocate the context or access the parent context
* before calling CloseWGroup.
* CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first. */
extern IFFP OpenWGroup(/* GroupContext *, GroupContext * */);
/* parent, new */
/* Close a write context and update its parent context.
* This is normally only called by EndWGroup.
*
* If this is a top level context (created by OpenWIFF) we'll set the file's
* EOF (end of file) but won't close the file.
* After calling this, the old context may be deallocated and the parent
* context can be accessed again.
*
* Amiga DOS Note: There's no call to set the EOF. We just position to the
* desired end and return. Caller must Close file at that position.
* CLIENT_ERROR if PutCkEnd wasn't called first. */
extern IFFP CloseWGroup( GroupContext * );
/* old */
/* Write a whole chunk to a GroupContext. This writes a chunk header, ckSize
* data bytes, and (if needed) a pad byte. It also updates the GroupContext.
* CLIENT_ERROR if ckSize == szNotYetKnown. See also PutCkHdr errors. */
extern IFFP PutCk( GroupContext *, ID, LONG, BYTE * );
/* context, ckID, ckSize, *data */
/* Write just a chunk header. Follow this will any number of calls to
* IFFWriteBytes and finish with PutCkEnd.
* If you don't yet know how big the chunk is, pass in ckSize = szNotYetKnown,
* then PutCkEnd will set the ckSize for you later.
* Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
* number of bytes get written.
* CLIENT_ERROR if the chunk would overflow the GroupContext's bound, if
* PutCkHdr was previously called without a matching PutCkEnd, if ckSize < 0
* (except szNotYetKnown), if you're trying to write something other
* than one FORM, LIST, or CAT in a top level (file level) context, or
* if ckID <= 0 (these illegal ID values are used for error codes). */
extern IFFP PutCkHdr(/* GroupContext *, ID, LONG */);
/* context, ckID, ckSize */
/* Write nBytes number of data bytes for the current chunk and update
* GroupContext.
* CLIENT_ERROR if this would overflow the GroupContext's limit or the
* current chunk's ckSize, or if PutCkHdr wasn't called first, or if
* nBytes < 0. */
extern IFFP IFFWriteBytes( GroupContext *, BYTE *, LONG );
/* context, *data, nBytes */
/* Complete the current chunk, write a pad byte if needed, and update
* GroupContext.
* If current chunk's ckSize = szNotYetKnown, this goes back and sets the
* ckSize in the file.
* CLIENT_ERROR if PutCkHdr wasn't called first, or if client hasn't
* written 'ckSize' number of bytes with IFFWriteBytes. */
extern IFFP PutCkEnd(/* GroupContext * */);
/* context */
#endif

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------*
* IFFCheck.C Print out the structure of an IFF-85 file, 11/19/85
* checking for structural errors.
*
* DO NOT USE THIS AS A SKELETAL PROGRAM FOR AN IFF READER!
* See ShowILBM.C for a skeletal example.
*
* Original version was for the Commodore-Amiga computer.
/* This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008
*----------------------------------------------------------------------*/
#include "iff.h"
#include "stdlib.h" //for exit
/* ---------- IFFCheck -------------------------------------------------*/
/* [TBD] More extensive checking could be done on the IDs encountered in the
* file. Check that the reserved IDs "FOR1".."FOR9", "LIS1".."LIS9", and
* "CAT1".."CAT9" aren't used. Check that reserved IDs aren't used as Form
* types. Check that all IDs are made of 4 printable characters (trailing
* spaces ok). */
typedef struct {
ClientFrame clientFrame;
int levels; /* # groups currently nested within.*/
} Frame;
char MsgOkay[] = { "----- (IFF_OKAY) A good IFF file." };
char MsgEndMark[] = {"----- (END_MARK) How did you get this message??" };
char MsgDone[] = { "----- (IFF_DONE) How did you get this message??" };
char MsgDos[] = { "----- (DOS_ERROR) The DOS gave back an error." };
char MsgNot[] = { "----- (NOT_IFF) not an IFF file." };
char MsgNoFile[] = { "----- (NO_FILE) no such file found." };
char MsgClientError[] = {"----- (CLIENT_ERROR) IFF Checker bug."};
char MsgForm[] = { "----- (BAD_FORM) How did you get this message??" };
char MsgShort[] = { "----- (SHORT_CHUNK) How did you get this message??" };
char MsgBad[] = { "----- (BAD_IFF) a mangled IFF file." };
/* MUST GET THESE IN RIGHT ORDER!!*/
char *IFFPMessages[-LAST_ERROR+1] = {
/*IFF_OKAY*/ MsgOkay,
/*END_MARK*/ MsgEndMark,
/*IFF_DONE*/ MsgDone,
/*DOS_ERROR*/ MsgDos,
/*NOT_IFF*/ MsgNot,
/*NO_FILE*/ MsgNoFile,
/*CLIENT_ERROR*/ MsgClientError,
/*BAD_FORM*/ MsgForm,
/*SHORT_CHUNK*/ MsgShort,
/*BAD_IFF*/ MsgBad
};
/* FORWARD REFERENCES */
extern IFFP GetList(GroupContext*);
extern IFFP GetForm(GroupContext*);
extern IFFP GetProp(GroupContext*);
extern IFFP GetCat (GroupContext*);
void IFFCheck(name) char *name; {
IFFP iffp;
BPTR file = fopen(name,"rb");//Open(name, MODE_OLDFILE);
Frame frame;
frame.levels = 0;
frame.clientFrame.getList = GetList;
frame.clientFrame.getForm = GetForm;
frame.clientFrame.getProp = GetProp;
frame.clientFrame.getCat = GetCat ;
printf("----- Checking file '%s' -----\n", name);
if (file == 0)
iffp = NO_FILE;
else
iffp = ReadIFF(file, (ClientFrame *)&frame);
fclose(file);
printf("%s\n", IFFPMessages[-iffp]);
}
void main(int argc, char **argv)
{
if (argc != 1+1) {
printf("Usage: 'iffcheck filename'\n");
exit(0);
}
IFFCheck(argv[1]);
}
/* ---------- Put... ---------------------------------------------------*/
PutLevels(count)
int count;
{
for ( ; count > 0; --count) {
printf(".");
}
}
PutID(id)
ID id;
{
long int i = 1;
const char *p = (const char *) &i;
if (p[0] == 1) // Lowest address contains the least significant byte
{
//little endian machine
printf("%c%c%c%c ", (char)(id&0x7f) , (char)(id>>8)&0x7f,
(char)(id>>16)&0x7f, (char)(id>>24)&0x7f);
} else
{
//big endian machine
printf("%c%c%c%c ", (char)(id>>24)&0x7f, (char)(id>>16)&0x7f,
(char)(id>>8)&0x7f, (char)(id&0x7f));
}
/* printf("id = %lx", id); */
}
PutN(n)
long n;
{
printf(" %ld ", n);
}
/* Put something like "...BMHD 14" or "...LIST 14 PLBM". */
PutHdr(context) GroupContext *context; {
PutLevels( ((Frame *)context->clientFrame)->levels );
PutID(context->ckHdr.ckID);
PutN(context->ckHdr.ckSize);
if (context->subtype != NULL_CHUNK)
PutID(context->subtype);
printf("\n");
}
/* ---------- AtLeaf ---------------------------------------------------*/
/* At Leaf chunk. That is, a chunk which does NOT contain other chunks.
* Print "ID size".*/
IFFP AtLeaf(context) GroupContext *context; {
PutHdr(context);
/* A typical reader would read the chunk's contents, using the "Frame"
* for local data, esp. shared property settings (PROP).*/
/* IFFReadBytes(context, ...buffer, context->ckHdr->ckSize); */
return(IFF_OKAY);
}
/* ---------- GetList --------------------------------------------------*/
/* Handle a LIST chunk. Print "LIST size subTypeID".
* Then dive into it.*/
IFFP GetList(parent) GroupContext *parent; {
Frame newFrame;
newFrame = *(Frame *)parent->clientFrame; /* copy parent's frame*/
newFrame.levels++;
PutHdr(parent);
return( ReadIList(parent, (ClientFrame *)&newFrame) );
}
/* ---------- GetForm --------------------------------------------------*/
/* Handle a FORM chunk. Print "FORM size subTypeID".
* Then dive into it.*/
IFFP GetForm(parent) GroupContext *parent; {
/*CompilerBug register*/ IFFP iffp;
GroupContext new;
Frame newFrame;
newFrame = *(Frame *)parent->clientFrame; /* copy parent's frame*/
newFrame.levels++;
PutHdr(parent);
iffp = OpenRGroup(parent, &new);
CheckIFFP();
new.clientFrame = (ClientFrame *)&newFrame;
/* FORM reader for Checker. */
/* LIST, FORM, PROP, CAT already handled by GetF1ChunkHdr. */
do {if ( (iffp = GetF1ChunkHdr(&new)) > 0 )
iffp = AtLeaf(&new);
} while (iffp >= IFF_OKAY);
CloseRGroup(&new);
return(iffp == END_MARK ? IFF_OKAY : iffp);
}
/* ---------- GetProp --------------------------------------------------*/
/* Handle a PROP chunk. Print "PROP size subTypeID".
* Then dive into it.*/
IFFP GetProp(listContext) GroupContext *listContext; {
/*CompilerBug register*/ IFFP iffp;
GroupContext new;
PutHdr(listContext);
iffp = OpenRGroup(listContext, &new);
CheckIFFP();
/* PROP reader for Checker. */
((Frame *)listContext->clientFrame)->levels++;
do {if ( (iffp = GetPChunkHdr(&new)) > 0 )
iffp = AtLeaf(&new);
} while (iffp >= IFF_OKAY);
((Frame *)listContext->clientFrame)->levels--;
CloseRGroup(&new);
return(iffp == END_MARK ? IFF_OKAY : iffp);
}
/* ---------- GetCat ---------------------------------------------------*/
/* Handle a CAT chunk. Print "CAT size subTypeID".
* Then dive into it.*/
IFFP GetCat(parent) GroupContext *parent; {
IFFP iffp;
((Frame *)parent->clientFrame)->levels++;
PutHdr(parent);
iffp = ReadICat(parent);
((Frame *)parent->clientFrame)->levels--;
return(iffp);
}

View File

@@ -0,0 +1,239 @@
/*
Test read and write IFF-85, Interchange Format File
Copyright (c) 2008 Erwin Coumans http://bulletphysics.com
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
* IFF Support routines for writing IFF-85 files. 12/02/85
* (IFF is Interchange Format File.)
* By Jerry Morrison and Steve Shaw, Electronic Arts.
* This software is in the public domain.
*
*/
#include <stdio.h>
extern "C"
{
#include "iff.h"
}
IFFP MyReadICat(GroupContext *parent)
{
printf("Found and skipped a CAT\n");
return 0;
}
IFFP MySkipGroup( GroupContext * )
{
printf("Found and skipped a LIST\n");
return 0;
}
typedef UBYTE Masking; /* Choice of masking technique.*/
typedef UBYTE Compression; /* Choice of compression algorithm applied to
/* A BitMapHeader is stored in a BMHD chunk. */
typedef struct {
UWORD w, h; /* raster width & height in pixels */
WORD x, y; /* position for this image */
UBYTE nPlanes; /* # source bitplanes */
Masking masking; /* masking technique */
Compression compression; /* compression algoithm */
UBYTE pad1; /* UNUSED. For consistency, put 0 here.*/
UWORD transparentColor; /* transparent "color number" */
UBYTE xAspect, yAspect; /* aspect ratio, a rational number x/y */
WORD pageWidth, pageHeight; /* source "page" size in pixels */
} BitMapHeader;
#define bufSz 512
BYTE bodyBuffer[bufSz];
static void btSwap(char* a, char* b)
{
char tmp = *a;
*a = *b;
*b = tmp;
};
#define ID_ILBM MakeID('I','L','B','M')
#define ID_BMHD MakeID('B','M','H','D')
#define ID_CMAP MakeID('C','M','A','P')
#define ID_BODY MakeID('B','O','D','Y')
#define ID_DYNAWORLD MakeID('B','T','D','W')
#define ID_RIGIDBODY MakeID('B','T','R','B')
#define ID_SID MakeID('S','I','D',' ')
#define ID_MASS MakeID('M','A','S','S')
#define ID_SHAPE MakeID('S','H','A','P')
#define ID_COLOBJ MakeID('C','O','B','J')
#define ID_CUBE MakeID('C','U','B','E')
#define ID_DIMENSIONS MakeID('D','I','M','E')
IFFP MyProcessGroup(GroupContext *parent)
{
/*compilerBug register*/ IFFP iffp;
GroupContext rigidbodyContext;
BitMapHeader bmHeader;
bool foundBMHD = false;
if (parent->subtype != ID_ILBM)
return(IFF_OKAY); /* just continue scaning the file */
iffp = OpenRGroup(parent, &rigidbodyContext);
CheckIFFP();
do {
iffp = GetFChunkHdr(&rigidbodyContext);
if (iffp == ID_BMHD) {
printf("found ID_BMHD\n");
foundBMHD = true;
iffp = IFFReadBytes(&rigidbodyContext, (BYTE *)&bmHeader, (long)sizeof(BitMapHeader));
//do endian swap
bmHeader.w = endianSwap16(bmHeader.w);
bmHeader.h = endianSwap16(bmHeader.h);
bmHeader.pageWidth = endianSwap16(bmHeader.pageWidth);
bmHeader.pageHeight = endianSwap16(bmHeader.pageHeight);
}
else if (iffp == ID_CMAP) {
printf("found ID_CMAP\n");
// ilbmFrame.nColorRegs = maxColorReg; /* we have room for this many */
// iffp = GetCMAP(
// &rigidbodyContext, (WORD *)&ilbmFrame.colorMap, &ilbmFrame.nColorRegs);
}
else if (iffp == ID_BODY)
{
printf("found ID_BODY\n");
if (!foundBMHD)
return BAD_FORM;
// if (!ilbmFrame.foundBMHD) return(BAD_FORM); /* No BMHD chunk! */
int moreBytes = ChunkMoreBytes(&rigidbodyContext);
while (moreBytes>0)
{
int curRead = moreBytes > bufSz? bufSz : moreBytes;
//read
iffp = IFFReadBytes(&rigidbodyContext, bodyBuffer, curRead);
moreBytes -= curRead;
}
printf("remaining=%d\n",moreBytes);
if (iffp == IFF_OKAY)
iffp = IFF_DONE; /* Eureka */
// nPlanes = MIN(ilbmFrame.bmHdr.nPlanes, EXDepth);
}
else if (iffp == END_MARK)
iffp = BAD_FORM;
} while (iffp >= IFF_OKAY); /* loop if valid ID of ignored chunk or a
* subroutine returned IFF_OKAY (no errors).*/
if (iffp != IFF_DONE) return(iffp);
/* If we get this far, there were no errors. */
CloseRGroup(&rigidbodyContext);
return(iffp);
}
#define CkErr(expression) {if (ifferr == IFF_OKAY) ifferr = (expression);}
int main(int argc, char* argv[])
{
FILE* file = 0;
{
//Create and write an IFF file from scratch
file = fopen("test.iff","wb");
GroupContext fileContext;
GroupContext catContext;
IFFP ifferr=0;
ifferr = OpenWIFF(file, &fileContext, szNotYetKnown) ;
//ifferr = StartWGroup(&fileContext, CAT, szNotYetKnown, ID_DYNAWORLD, &catContext);
ifferr = StartWGroup(&fileContext, LIST, szNotYetKnown, ID_DYNAWORLD, &catContext);
{
GroupContext rigidbodyPropContext;
ifferr = StartWGroup(&catContext, PROP, szNotYetKnown, ID_MASS, &rigidbodyPropContext);
float mass = 0.1f;
PutCk(&rigidbodyPropContext, ID_MASS, 4,(char*)&mass);
ifferr = EndWGroup(&rigidbodyPropContext) ;
for (int i=0;i<3;i++)
{
GroupContext rigidbodyContext;
ifferr = StartWGroup(&catContext, FORM, szNotYetKnown, ID_RIGIDBODY, &rigidbodyContext);
char sidbuffer[]="rb1";
float dimensions[3] = {2,2,2};
PutCk(&rigidbodyContext, ID_SID, 3,sidbuffer);
{
GroupContext shapeContext;
ifferr = StartWGroup(&rigidbodyContext, FORM, szNotYetKnown, ID_SHAPE, &shapeContext);
PutCk(&shapeContext, ID_CUBE, 4,(char*)&mass);
PutCk(&shapeContext, ID_DIMENSIONS, sizeof(dimensions),(char*)&dimensions);
ifferr = EndWGroup(&shapeContext) ;
}
ifferr = EndWGroup(&rigidbodyContext) ;
}
}
ifferr = EndWGroup(&catContext) ;
ifferr = CloseWGroup(&fileContext);
fclose(file);
}
{
//show a very simple way to skim through an ILBM or general IFF file
//for more verbose feedback, use iffcheck.c
IFFP result;
//file = fopen("pe_3000_fall.iff","rb");
file = fopen("test.iff","rb");
ClientFrame clientFrame;
clientFrame.getList = MySkipGroup;
clientFrame.getProp = MySkipGroup;
clientFrame.getForm = MyProcessGroup;
clientFrame.getCat = MyReadICat ;
result = ReadIFF(file,&clientFrame);
fclose(file);
}
return 0;
}

377
Extras/iff/iffr.c Normal file
View File

@@ -0,0 +1,377 @@
/*----------------------------------------------------------------------*
* 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.
*
* Original version was for the Commodore-Amiga computer.
/* This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008
*----------------------------------------------------------------------*/
#include "iff.h"
/* #include "DF1:iff/gio.h" */
/* #define OFFSET_BEGINNING OFFSET_BEGINING */
/** Manx expects INTs as 16 bits, This wont matter on LAttice ***/
/* ---------- 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 = ftell(file);//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);
}
short int endianSwap16(short int val)
{
long int i = 1;
const char *p = (const char *) &i;
if (p[0] == 1) // Lowest address contains the least significant byte
{
return (((val & 0xff00) >> 8) | ((val & 0x00ff) << 8));
}
return val;
}
int endianSwap32(int val)
{
long int i = 1;
const char *p = (const char *) &i;
if (p[0] == 1) // Lowest address contains the least significant byte
{
return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24));
}
return val;
}
/* ---------- GetChunkHdr ----------------------------------------------*/
ID GetChunkHdr(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);
}
//swap endian-ness of ckSize on little endian machines
context->ckHdr.ckSize = endianSwap32(context->ckHdr.ckSize);
/*** $$$ ***
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( GroupContext* context)
{
return 0;
} /* 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, parent->clientFrame));//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);
}

238
Extras/iff/iffw.c Normal file
View File

@@ -0,0 +1,238 @@
/*----------------------------------------------------------------------*
* IFFW.C Support routines for writing IFF-85 files. 12/02/85
* (IFF is Interchange Format File.)
*
* By Jerry Morrison and Steve Shaw, Electronic Arts.
* This software is in the public domain.
*
* Original version was for the Commodore-Amiga computer.
/* This version is compatible with PC, OSX, PS3, Wii, iPhone. 10/26/2008
*----------------------------------------------------------------------*/
#include "iff.h"
/* ---------- IFF Writer -----------------------------------------------*/
/* A macro to test if a chunk size is definite, i.e. not szNotYetKnown.*/
#define Known(size) ( (size) != szNotYetKnown )
/* Yet another weird macro to make the source code simpler...*/
#define IfIffp(expr) {if (iffp == IFF_OKAY) iffp = (expr);}
/* ---------- OpenWIFF -------------------------------------------------*/
IFFP OpenWIFF(file, new0, limit) BPTR file; GroupContext *new0; LONG limit; {
register GroupContext *new = new0;
register IFFP iffp = IFF_OKAY;
new->parent = NULL;
new->clientFrame = NULL;
new->file = file;
new->position = 0;
new->bound = limit;
new->ckHdr.ckID = NULL_CHUNK; /* indicates no current chunk */
new->ckHdr.ckSize = new->bytesSoFar = 0;
if (0 > Seek(file, 0, OFFSET_BEGINNING)) /* Go to start of the file.*/
iffp = DOS_ERROR;
else if ( Known(limit) && IS_ODD(limit) )
iffp = CLIENT_ERROR;
return(iffp);
}
/* ---------- StartWGroup ----------------------------------------------*/
IFFP StartWGroup(parent, groupType, groupSize, subtype, new)
GroupContext *parent, *new; ID groupType, subtype; LONG groupSize; {
register IFFP iffp;
iffp = PutCkHdr(parent, groupType, groupSize);
IfIffp( IFFWriteBytes(parent, (BYTE *)&subtype, sizeof(ID)) );
IfIffp( OpenWGroup(parent, new) );
return(iffp);
}
/* ---------- OpenWGroup -----------------------------------------------*/
IFFP OpenWGroup(parent0, new0) GroupContext *parent0, *new0; {
register GroupContext *parent = parent0;
register GroupContext *new = new0;
register LONG ckEnd;
register IFFP iffp = IFF_OKAY;
new->parent = parent;
new->clientFrame = parent->clientFrame;
new->file = parent->file;
new->position = parent->position;
new->bound = parent->bound;
new->ckHdr.ckID = NULL_CHUNK;
new->ckHdr.ckSize = new->bytesSoFar = 0;
if ( Known(parent->ckHdr.ckSize) ) {
ckEnd = new->position + ChunkMoreBytes(parent);
if ( new->bound == szNotYetKnown || new->bound > ckEnd )
new->bound = ckEnd;
};
if ( parent->ckHdr.ckID == NULL_CHUNK || /* not currently writing a chunk*/
IS_ODD(new->position) ||
(Known(new->bound) && IS_ODD(new->bound)) )
iffp = CLIENT_ERROR;
return(iffp);
}
/* ---------- CloseWGroup ----------------------------------------------*/
IFFP CloseWGroup(old0) GroupContext *old0; {
register GroupContext *old = old0;
if ( old->ckHdr.ckID != NULL_CHUNK ) /* didn't close the last chunk */
return(CLIENT_ERROR);
if ( old->parent == NULL ) { /* top level file context */
GWriteFlush(old->file);
}
else {
old->parent->bytesSoFar += old->position - old->parent->position;
old->parent->position = old->position;
};
return(IFF_OKAY);
}
/* ---------- EndWGroup ------------------------------------------------*/
IFFP EndWGroup(old) GroupContext *old; {
register GroupContext *parent = old->parent;
register IFFP iffp;
iffp = CloseWGroup(old);
IfIffp( PutCkEnd(parent) );
return(iffp);
}
/* ---------- PutCk ----------------------------------------------------*/
IFFP PutCk(context, ckID, ckSize, data)
GroupContext *context; ID ckID; LONG ckSize; BYTE *data; {
register IFFP iffp = IFF_OKAY;
if ( ckSize == szNotYetKnown )
iffp = CLIENT_ERROR;
IfIffp( PutCkHdr(context, ckID, ckSize) );
IfIffp( IFFWriteBytes(context, data, ckSize) );
IfIffp( PutCkEnd(context) );
return(iffp);
}
/* ---------- PutCkHdr -------------------------------------------------*/
IFFP PutCkHdr(context0, ckID, ckSize)
GroupContext *context0; ID ckID; LONG ckSize; {
register GroupContext *context = context0;
LONG minPSize = sizeof(ChunkHeader); /* physical chunk >= minPSize bytes*/
/* CLIENT_ERROR if we're already inside a chunk or asked to write
* other than one FORM, LIST, or CAT at the top level of a file */
/* Also, non-positive ID values are illegal and used for error codes.*/
/* (We could check for other illegal IDs...)*/
if ( context->ckHdr.ckID != NULL_CHUNK || ckID <= 0 )
return(CLIENT_ERROR);
else if (context->parent == NULL) {
switch (ckID) {
case FORM: case LIST: case CAT: break;
default: return(CLIENT_ERROR);
}
if (context->position != 0)
return(CLIENT_ERROR);
}
if ( Known(ckSize) ) {
if ( ckSize < 0 )
return(CLIENT_ERROR);
minPSize += ckSize;
};
if ( Known(context->bound) &&
context->position + minPSize > context->bound )
return(CLIENT_ERROR);
context->ckHdr.ckID = ckID;
context->ckHdr.ckSize = ckSize;
context->bytesSoFar = 0;
{
context->ckHdr.ckSize = endianSwap32(context->ckHdr.ckSize);
if (0 > GWrite(context->file, &context->ckHdr, sizeof(ChunkHeader)))
return(DOS_ERROR);
context->ckHdr.ckSize = endianSwap32(context->ckHdr.ckSize);
}
context->position += sizeof(ChunkHeader);
return(IFF_OKAY);
}
/* ---------- IFFWriteBytes ---------------------------------------------*/
IFFP IFFWriteBytes(context0, data, nBytes)
GroupContext *context0; BYTE *data; LONG nBytes; {
register GroupContext *context = context0;
if ( context->ckHdr.ckID == NULL_CHUNK || /* not in a chunk */
nBytes < 0 || /* negative nBytes */
(Known(context->bound) && /* overflow context */
context->position + nBytes > context->bound) ||
(Known(context->ckHdr.ckSize) && /* overflow chunk */
context->bytesSoFar + nBytes > context->ckHdr.ckSize) )
{
return(CLIENT_ERROR);
}
if (0 > GWrite(context->file, data, nBytes))
return(DOS_ERROR);
context->bytesSoFar += nBytes;
context->position += nBytes;
return(IFF_OKAY);
}
/* ---------- PutCkEnd -------------------------------------------------*/
IFFP PutCkEnd(context0) GroupContext *context0; {
register GroupContext *context = context0;
WORD zero = 0; /* padding source */
if ( context->ckHdr.ckID == NULL_CHUNK ) /* not in a chunk */
return(CLIENT_ERROR);
if ( context->ckHdr.ckSize == szNotYetKnown ) {
/* go back and set the chunk size to bytesSoFar */
int offset = context->bytesSoFar+sizeof(LONG);
if ( 0 > GSeek(context->file,
-(offset),
OFFSET_CURRENT))
{
return(DOS_ERROR);
} else
{
context->bytesSoFar = endianSwap32(context->bytesSoFar);
if (0 > GWrite(context->file, &context->bytesSoFar, sizeof(LONG)))
return (DOS_ERROR);
context->bytesSoFar = endianSwap32(context->bytesSoFar);
if (0 > GSeek(context->file, context->bytesSoFar, OFFSET_CURRENT) )
return (DOS_ERROR);
}
}
else { /* make sure the client wrote as many bytes as planned */
if ( context->ckHdr.ckSize != context->bytesSoFar )
return(CLIENT_ERROR);
};
/* Write a pad byte if needed to bring us up to an even boundary.
* Since the context end must be even, and since we haven't
* overwritten the context, if we're on an odd position there must
* be room for a pad byte. */
if ( IS_ODD(context->bytesSoFar) ) {
if ( 0 > GWrite(context->file, &zero, 1) )
return(DOS_ERROR);
context->position += 1;
};
context->ckHdr.ckID = NULL_CHUNK;
context->ckHdr.ckSize = context->bytesSoFar = 0;
return(IFF_OKAY);
}