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:
1423
Extras/iff/IFF.txt
Normal file
1423
Extras/iff/IFF.txt
Normal file
File diff suppressed because it is too large
Load Diff
468
Extras/iff/iff.h
Normal file
468
Extras/iff/iff.h
Normal 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
|
||||||
228
Extras/iff/iffCheck/iffcheck.c
Normal file
228
Extras/iff/iffCheck/iffcheck.c
Normal 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);
|
||||||
|
}
|
||||||
239
Extras/iff/iffCreateTest/main.cpp
Normal file
239
Extras/iff/iffCreateTest/main.cpp
Normal 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
377
Extras/iff/iffr.c
Normal 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
238
Extras/iff/iffw.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user