home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frozen Fish 1: Amiga
/
FrozenFish-Apr94.iso
/
bbs
/
alib
/
d1xx
/
d162
/
iffar.lha
/
Iffar
/
iff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-10-02
|
12KB
|
453 lines
/* 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 */