home *** CD-ROM | disk | FTP | other *** search
- /* iffar - IFF CAT archiver, IFF support functions
-
- By Karl Lehenbauer, version 1.2, release date 5/9/88
- This code is released to the public domain.
- See the README file for more information.
-
- */
-
- /* culled general purpose IFF file cracking routines for Karl's
- * IFF Stuff by Karl Lehenbauer, based originally on public domain IFF
- * code from Electronic Arts, 2/24/88
- */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <stdio.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include "assert.h"
-
- #include "iff.h"
-
- extern long lseek();
-
- extern ULONG nextchunk();
-
- /* print a chunkID to stderr */
- PutID(id)
- ID id;
- {
- fprintf(stderr,"%c%c%c%c",
- (char)((id>>24L) & 0x7f),
- (char)((id>>16L) & 0x7f),
- (char)((id>>8) & 0x7f),
- (char)(id & 0x7f) );
- }
-
- UBYTE *MyAllocMem(bytes, type)
- ULONG bytes, type;
- {
- UBYTE *tmp;
- UBYTE *AllocMem();
- tmp = AllocMem(bytes, type);
- return tmp;
- }
-
- /* return chunktype of next chunk */
- /* every time nextchunk is executed and returns that it found a chunk,
- * either readchunk or skipchunk must be called and only one time!
- */
- ULONG nextchunk(fd,chunksize,chunk_bytes_left)
- int fd;
- long *chunksize, *chunk_bytes_left;
- {
- int sawsize, i, blown = 0;
- ChunkHeader mychunkheader;
- char checkchar;
-
- /* if chunk_bytes_left is zero, we obey it as a virtual EOF, so
- * return 0 */
- if (*chunk_bytes_left == 0)
- return(0);
-
- /* read the next chunk header */
- if ((sawsize = read(fd,&mychunkheader,sizeof(mychunkheader))) !=
- sizeof(mychunkheader))
- {
- if (sawsize != 0)
- fprintf(stderr,"Something's wrong with nextchunk! (sawsize %d)\n", sawsize);
- *chunksize = 0;
- return(0);
- }
-
- #ifdef MAJORDEBUG
- fputs("nextchunk: next chunk '",stderr);
- PutID(mychunkheader.ckID);
- fprintf(stderr,"', size %d, parent bytes left %d\n",mychunkheader.ckSize,*chunk_bytes_left);
- #endif
-
- *chunksize = mychunkheader.ckSize;
-
- /* see if chunk ID looks OK */
- for (i = 0; i < 4; i++)
- {
- checkchar = (mychunkheader.ckID >> (i * 8)) & 0xff;
- if (!isprint(checkchar))
- {
- if (!blown)
- {
- blown = 1;
- fprintf(stderr,"nextchunk: chunk ID contains an unprintable character (0x%x)\n",checkchar);
- }
- break;
- }
- }
-
- /* see if chunk length is reasonable */
- if ((mychunkheader.ckSize < 0) || (mychunkheader.ckSize > MAXCHUNKSIZE))
- {
- fprintf(stderr,"nextchunk: chunk length of %ld is unreasonable\n",mychunkheader.ckSize);
- blown = 1;
- }
-
- if (blown)
- {
- fprintf(stderr,"nextchunk: I either got lost or the archive is blown\n");
- return(0);
- }
-
- /* square up the bytes left in the chunk by the size of a chunk header,
- * eight bytes. We leave it to the caller to subtract the size of the
- * body of the chunk by calling skipchunk or readchunk
- */
- *chunk_bytes_left -= sizeof(mychunkheader);
-
- if (*chunk_bytes_left < 0)
- {
- fprintf("nextchunk: chunk overran its parent by %d bytes\n",(0-*chunk_bytes_left));
- *chunksize = 0;
- *chunk_bytes_left = 0;
- return(0);
- }
-
- return(mychunkheader.ckID);
- }
-
- /* read next chunk into buffer supplied, size must be value returned by
- * nextchunk
- * zero is returned on failure, one on success
- */
- readchunk(fd,buf,size,chunk_bytes_left)
- int fd;
- char *buf;
- LONG size, *chunk_bytes_left;
- {
- *chunk_bytes_left -= size;
-
- if (*chunk_bytes_left < 0)
- {
- fprintf(stderr,"readchunk: chunk requested passed the end of its parent chunk\n");
- *chunk_bytes_left = 0;
- return(0);
- }
-
- if (read(fd,buf,size) != size)
- {
- perror("smus file");
- fputs("LoadSMUS: read of IFF chunk failed\n",stderr);
- return(0);
- }
-
- /* odd-length chunks have a trailer byte - skip it */
- if (size & 1)
- {
- lseek(fd,1L,1);
- (*chunk_bytes_left)--;
- }
- return(1);
- }
-
- /* skip non-header portion of chunk, chunksize must have been returned
- * by nextchunk
- * returns 1 on success, 0 on failure
- */
- skipchunk(fd,chunksize,chunk_bytes_left)
- int fd;
- LONG chunksize, *chunk_bytes_left;
- {
- *chunk_bytes_left -= chunksize;
- if (chunksize & 1)
- (*chunk_bytes_left)--;
- if (*chunk_bytes_left < 0)
- {
- fprintf(stderr,"skipchunk: chunk size passes end of parent chunk's data by %d bytes\n",0 - *chunk_bytes_left);
- return(0);
- }
- /* skip over chunk data and skip an extra byte if length is odd */
- lseek(fd,(long)chunksize,1);
- if (chunksize & 1)
- lseek(fd,1L,1);
- return(1);
- }
-
- /* OpenIFF
- * given file name, open the IFF file.
- * read the header, return failure if it's not a FORM
- * (someday we'll handle the more complex types)
- * read the form type, return failure if it's not the type requested
- * success, return the file descriptor
- */
-
- int OpenIFF(fname,expected_formtype,length_ptr)
- char *fname;
- LONG expected_formtype;
- LONG *length_ptr;
- {
- int iffile;
- ChunkHeader chunkhead;
- LONG formtype;
-
- /* open the file */
- if ((iffile = open(fname, O_RDONLY)) < 0)
- {
- fprintf(stderr,"OpenIFF: can't open IFF SMUS file %s\n",fname);
- perror(fname);
- return(-1);
- }
-
- /* get the length */
- *length_ptr = lseek(iffile,0,2);
- lseek(iffile,0,0);
-
- /* read the header chunk */
- if (read(iffile, &chunkhead, sizeof(chunkhead)) < 0)
- {
- fprintf(stderr,"OpenIFF: initial read from IFF file %s failed!\n",fname);
- return(-1);
- }
-
- /* return if the header chunk doesn't say it's IFF FORM */
- if (chunkhead.ckID != ID_FORM)
- {
- fprintf(stderr,"OpenIFF: File %s isn't IFF, is too complex, or doesn't start with FORM\n",fname);
- return(-1);
- }
- /* fprintf(stderr,"OpenIFF: FORM found, size is %d\n",chunkhead.ckSize); */
-
- /* read the form type */
- read(iffile, &formtype, sizeof(formtype));
-
- /* return if the form type isn't the type requested */
- if (formtype != expected_formtype)
- {
- fprintf(stderr,"OpenIFF: File %s is IFF ");
- PutID(formtype);
- fprintf(stderr," rather than the requested ");
- PutID(expected_formtype);
- fprintf(stderr,"\n");
- return(-1);
- }
- return(iffile);
- }
-
- /* read chunks until one of type chunktype is found or EOF
- * note that after a successful call to chunkuntil,
- * skipchunk or readchunk must be performed or the IFF reading
- * software will get lost on the next nextchunk
- * chunksize is returned on success, -1 otherwise
- * The caller should probably check the return explicitly for -1.
- * If checking only for less than zero, chunks larger than
- * two gigabytes will cause your code to break.
- */
-
- LONG chunkuntil(fd,chunktype,file_bytes_left)
- int fd;
- ULONG chunktype;
- long *file_bytes_left;
- {
- ULONG currentchunk;
- LONG chunksize;
-
- while ((currentchunk = nextchunk(fd,&chunksize,file_bytes_left)) != NULL)
- {
- if (currentchunk == chunktype)
- return(chunksize);
- skipchunk(fd,chunksize,file_bytes_left);
- }
- return(0);
- }
-
- /* OpenCAT - Open an IFF CAT archive */
-
- /* OpenCAT
- * Open an IFF CAT archive, insuring that the file starts with an
- * IFF CAT header and that the length in the header is valid.
- * Return the CAT subtype, file descriptor and length, leaving the
- * file pointed at the start of the first subchunk
- */
-
- int OpenCAT(archive_name,subtype_ptr,length_ptr)
- char *archive_name;
- ULONG *subtype_ptr, *length_ptr;
- {
- ChunkHeader mychunkheader;
- int archive_fd;
- long start_of_body, filesize;
- long placeholder;
-
- if ((archive_fd = open(archive_name,O_RDONLY)) == -1)
- {
- /* fprintf(stderr,"Can't open archive '%s'\n",archive_name); */
- return(-1);
- }
-
- if (read(archive_fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
- {
- perror(archive_name);
- fprintf(stderr,"couldn't read chunk header\n");
- return(-1);
- }
-
- if (mychunkheader.ckID != ID_CAT)
- {
- fprintf(stderr,"file '%s' is not an IFF CAT archive\n",archive_name);
- return(-1);
- }
-
- if (read(archive_fd,subtype_ptr,sizeof(subtype_ptr)) != sizeof(subtype_ptr))
- {
- fprintf(stderr,"error reading archive header - subtype\n");
- return(-1);
- }
-
- /* save location of current start of body */
- if ((start_of_body = lseek(archive_fd,0,1)) == -1)
- {
- perror(archive_name);
- return(-1);
- }
-
- /* seek to the end to get the size */
- if ((filesize = lseek(archive_fd,0,2)) == -1)
- {
- perror(archive_name);
- return(-1);
- }
-
- /* see if the shoe fits */
- if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
- {
- fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",archive_name);
- fprintf(stderr,"I'm assuming it's blown.\n");
- return(-1);
- }
-
- /* go back to the start of the IFF CAT archive's data */
- if (lseek(archive_fd,start_of_body,0) == -1)
- {
- perror(archive_name);
- return(-1);
- }
-
- /* it worked store filesize in location pointed to by 'length'
- * and return the archive file's file descriptor
- */
- *length_ptr = filesize;
- return(archive_fd);
- }
-
- /* end of OpenCAT */
-
- /* nextcat - read header info for the next entry in an IFF CAT */
-
- /* nextCATchunk
- *
- * given fd, read into IFF file.
- * if we're not at a FORM, CAT or LIST, print the chunk type if verbose,
- * then skip the chunk
- * if we are at a FORM, CAT or LIST, read the subtype and return it
- * via the argument subtype_ptr.
- * if the next chunk within the embedded FORM, CAT or LIST is FNAM,
- * read the text in the FNAM chunk (file name) and write it into space
- * pointed to by argument fname_ptr.
- * return the size of the chunk in argument chunk_length_ptr.
- * update the space left in the metachunk (usually the file) of argument
- * metachunk_length_ptr
- */
-
- ULONG nextCATchunk(fd,subtype_ptr,fname_ptr,chunk_length_ptr,metachunk_length_ptr)
- int fd;
- ULONG *subtype_ptr;
- char *fname_ptr;
- LONG *chunk_length_ptr, *metachunk_length_ptr;
- {
- ULONG cat_type, chunkid, innerchunkid;
- long chunksize, innerchunkposition, innerchunksize, filesize;
- int odd;
-
- /* null out the returned subtype and fnam */
- *subtype_ptr = 0L;
- *fname_ptr = '\0';
-
- if ((chunkid = nextchunk(fd,chunk_length_ptr,metachunk_length_ptr)) == 0L)
- return(0L);
-
- /* if the chunk type isn't FORM, CAT or LIST, return the chunkid
- */
- if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST)
- return(chunkid);
-
- /* get the chunk subtype */
- if (read(fd,subtype_ptr,4) != 4)
- {
- perror("reading subtype");
- return(0);
- }
-
- /* reduce chunksize and metachunksize by the size of the subtype */
- *chunk_length_ptr -= sizeof(ULONG);
- *metachunk_length_ptr -= sizeof(ULONG);
-
- /* sneak a peek into the embedded FORM, CAT or LIST to see
- * if the next chunk is an FNAM chunk */
-
- assert(*chunk_length_ptr > 0);
-
- /* fetch the current location in the file - we'll restore it
- * if we don't find this next chunk to be a FNAM one
- */
- innerchunkposition = lseek(fd,0L,1);
-
- /* get the type and size of the inner chunk */
- chunksize = *chunk_length_ptr;
- innerchunkid = nextchunk(fd,&innerchunksize,&chunksize);
-
- /* if it's not an fname chunk, seek back to the start of the
- * chunk and return the chunk id - master length should be OK
- */
- if (innerchunkid != ID_FNAM)
- {
- lseek(fd,innerchunkposition,0);
- return(chunkid);
- }
-
- odd = innerchunksize & 1;
-
- /* read and zero-terminate the file name (contents of FNAM chunk) */
- if (!readchunk(fd,fname_ptr,innerchunksize,&chunksize))
- {
- fprintf(stderr,"nextCATchunk: got into trouble reading chunk text\n");
- return(0);
- }
- *(fname_ptr + innerchunksize) = '\0';
-
- /* update the length of the chunk and its parent & return the chunk id
- * (nextchunk normally handles updating the length but we used different
- * variables to make restoring (in case we don't find an FNAM chunk)
- * easier
- */
- *chunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
- *metachunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
- if (odd)
- {
- (*chunk_length_ptr)--;
- (*metachunk_length_ptr)--;
- }
- return(chunkid);
- }
-
- /* end of nextCATchunk */
-
- /* end of iff.c */
-