/* 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 #include #include #include #include #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 */