home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD1.img
/
d1xx
/
d162
/
iffar
/
create.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-10-02
|
12KB
|
500 lines
/* iffar - IFF CAT archiver output and file copy rouines
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 from general purpose IFF file cracking routines for Karl's
* Audio 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 "assert.h"
#include "iff.h"
extern long lseek();
extern int verbose;
extern char *basename();
#define ID_MISC MakeID('M','I','S','C')
WriteCATheader(fd)
int fd;
{
static ULONG dummy = ID_MISC;
WriteChunk(fd,ID_CAT, &dummy, sizeof(dummy));
}
/* write a chunk header, that's the 4-byte chunk ID and a 4-byte
* chunk length
*/
WriteChunkHeader(fd,chunktype,length)
int fd;
ULONG chunktype;
long length;
{
ChunkHeader chunkheader;
chunkheader.ckID = chunktype;
chunkheader.ckSize = length;
if (write(fd,&chunkheader,sizeof(chunkheader)) == -1)
{
perror("WriteChunkHeader");
return(0);
}
return(1);
}
/* write a chunk that has a four character subtype, like FORM, CAT or LIST
*/
WriteSuperChunkHeader(fd,chunktype,subtype,length)
int fd;
ULONG chunktype, subtype;
long length;
{
if (!WriteChunkHeader(fd,chunktype,length+sizeof(subtype)))
return(0);
return(write(fd,&subtype,sizeof(subtype)) != -1);
}
/* WriteCATentry
This routine is given all of the info for a superchunk header and,
in addition, a file name. It writes the chunk header and a
FNAM chunk containing the file name out to the fd provided.
*/
WriteCATentry(fd,fname,chunktype,subtype,length)
int fd;
char *fname;
ULONG chunktype, subtype;
long length;
{
int fnamelen;
int calc_chunk_length;
/* Figure out the length of the file name. Remember that
* it should be even. (I should use a cool macro to force
* that, but I don't)
* Add the size of the FNAM chunk we're going to write to our
* calculated chunk length.
*/
fnamelen = strlen(fname);
calc_chunk_length = fnamelen;
if (fnamelen & 1)
calc_chunk_length++;
calc_chunk_length += length + sizeof(ChunkHeader);
WriteSuperChunkHeader(fd,chunktype,subtype,calc_chunk_length);
if (!WriteChunk(fd,ID_FNAM,fname,strlen(fname)))
return(0);
return(1);
}
/* write a chunk header and body, that's the 8-byte header and the data
* to match the specified length
*/
WriteChunk(fd,chunktype,data,length)
int fd;
ULONG chunktype;
char *data;
long length;
{
static char zero = '\0';
if (!WriteChunkHeader(fd,chunktype,length))
return(0);
/* if there's a body, write it out */
if (length != 0)
if (write(fd,data,length) == -1)
{
perror("WriteChunk");
return(0);
}
/* if the length is odd, write out an additional zero byte */
if (length & 1)
if (write(fd,&zero,1) == -1)
{
perror("WriteChunk");
return(0);
}
return(1);
}
/* checknew - given fname, this routine prints "Creating IFF CAT archive "
* and the fname if file fname does not exist
*/
checknew(fname)
char *fname;
{
extern int suppress_creation_message;
int fd;
if (!suppress_creation_message)
{
if ((fd = open(fname,O_RDONLY)) < 0)
{
fprintf(stderr,"Creating IFF CAT archive '%s'\n",fname);
}
else
close(fd);
}
}
/* open_quick_append
* open the archive for append, verifying that it is IFF, subtype CAT,
* that the length in the header matches the length of the file and
* such. Note that this has been wrapped into a better OpenCAT routine
* but I have not fixed open_quick_append to call it yet.
*/
int open_quick_append(fname)
char *fname;
{
ChunkHeader mychunkheader;
long filesize;
int fd;
/* If I can't open the archive read only, it doesn't exist, so
* create it
*/
if ((fd = open(fname,O_RDONLY)) < 0)
{
if ((fd = create_archive(fname,ID_MISC)) < 0)
{
perror(fname);
return(-1);
}
}
else
{
/* read the IFF header and validate we've got a CAT */
if (read(fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
{
perror(fname);
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",fname);
return(-1);
}
/* verify that the header's filesize matches the file's size */
if ((filesize = lseek(fd,0,2)) == -1)
{
perror(fname);
return(-1);
}
if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
{
fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",fname);
fprintf(stderr,"I'm assuming it's OK and using file size.\n");
}
/* ok, reopen the file for append and return the fd */
close(fd);
if ((fd = open(fname,O_RDWR|O_APPEND)) < 0)
{
perror(fname);
return(-1);
}
}
return(fd);
}
#define COPY_BUFFER_SIZE 32768
char *copy_buffer = 0;
/* append_file_to_archive
*
* this routine copies IFF file named by fname to the CAT archive known
* by it's open-for-append fd.
*/
append_file_to_archive(fname,archive_fd)
char *fname;
int archive_fd;
{
char *basename_ptr;
int bytes, i;
int infd, fnamelen, basenamelen;
ULONG chunkid, subtype;
long chunksize, new_chunk_address;
ULONG subchunkid;
long subchunksize, placeholder, calculated_chunk_size, inputfilesize;
/* seek to the end of the archive */
lseek(archive_fd,0,2);
/* open the file to appended to the archive, read only */
if ((infd = open(fname,O_RDONLY)) == -1)
{
perror(fname);
return(0);
}
/* get the filesize of the input file and relocate back to the start
* of it */
inputfilesize = lseek(infd,0,2);
lseek(infd,0,0);
/* get the ID and size of the next chunk */
if ((chunkid = nextchunk(infd,&chunksize,&inputfilesize)) == 0)
{
fprintf(stderr,"couldn't get header chunk from file %s\n",fname);
close(infd);
return(0);
}
/* if the header isn't CAT, FORM or LIST, don't copy it */
if (chunkid != ID_CAT && chunkid != ID_FORM && chunkid != ID_LIST)
{
fprintf(stderr,"file %s is not an IFF CAT, FORM or LIST, ignored\n",fname);
close(infd);
return(0);
}
/* kludgily get the subtype - for FORMs, LISTs & CATs it's the
* first 4 bytes of the chunk data. These are included in chunksize
*/
if (read(infd,&subtype,4) != 4)
{
perror("copy subtype");
return(0);
}
inputfilesize -= 4;
/* record where we are in the archive so we can rewrite the header
* which we'll need to do if we add a FNAM chunk */
new_chunk_address = lseek(archive_fd,0L,1) + 4;
/* write in the chunk ID and the subtype - the program will
* rewrite the length when we know for sure how long the
* chunk is */
if (!WriteChunk(archive_fd,chunkid,&subtype,4))
{
perror("append WriteChunk");
return(0);
}
/* keep track of the size of the FORM, CAT or LIST we're reading
* through. We start with 4, the size of subtype written above */
calculated_chunk_size = 4;
fnamelen = strlen(fname);
/* if the filename includes a path, use only the base portion */
basename_ptr = basename(fname);
basenamelen = strlen(basename);
/* write a FNAM chunk, it's the basename determined above,
* and our handle for it */
if (!WriteChunk(archive_fd,ID_FNAM,basename_ptr,basenamelen))
return(0);
/* add size of the chunk header and the length of the chunk
* body to the calculated chunk size. If the number is odd,
* increment it by one as the IFF spec requires odd-sized
* chunks to be one-null-padded to make them even. Note
* that WriteChunk took care of it for the actual data written
*/
calculated_chunk_size += sizeof(ChunkHeader) + basenamelen;
if (basenamelen & 1)
calculated_chunk_size++;
/* for all remaining chunks inside the FORM, LIST or CAT that
* we're adding to the archive, */
while ((subchunkid = nextchunk(infd,&subchunksize,&inputfilesize)) != 0)
{
/* if it's an FNAM chunk, discard it */
if (subchunkid == ID_FNAM)
skipchunk(infd,subchunksize,&inputfilesize);
else
{
calculated_chunk_size += subchunksize + sizeof(ChunkHeader);
if (subchunksize & 1)
calculated_chunk_size++;
/* write the chunk header for the embedded chunk we're copying */
if (!WriteChunkHeader(archive_fd,subchunkid,subchunksize))
return(0);
/* now copy the embedded chunk's data */
copychunkbytes(infd,archive_fd,subchunksize,&inputfilesize);
}
}
/* get current position in the archive, seek back to the header of the
* FORM, CAT or LIST we just copied (into the archive) and write in the
* correct chunk size, then seek back to where we were
*/
placeholder = lseek(archive_fd,0L,1);
lseek(archive_fd,new_chunk_address,0);
if (write(archive_fd,&calculated_chunk_size,4) != 4)
{
perror("archive subheader rewrite");
fprintf(stderr,"archive is blown.\n");
close(infd);
return(0);
}
/* return to previous place in archive, close file we copied and
* return 'success' */
lseek(archive_fd,placeholder,0);
close(infd);
return(1);
}
/* rewrite_archive_header - write (filesize - sizeof(ChunkHeader)) into
* CAT archive's header, assumes file is open for write
*/
rewrite_archive_header(fd)
{
long filesize;
/* get filesize by seeking to EOF */
if ((filesize = lseek(fd,0,2)) == -1)
{
perror("archive");
return(0);
}
/* go back to the beginning of the file */
if (lseek(fd,0,0) == -1)
{
perror("archive cleanup seek");
return(0);
}
if (!WriteChunkHeader(fd,ID_CAT,(filesize - sizeof(ChunkHeader))))
{
perror("archive cleanup");
return(0);
}
return(1);
}
/* the copy buffer cleanup routine, it frees the copy buffer memory if
* the buffer has been allocated
*/
void cleanup_copy_buffer()
{
if (copy_buffer)
FreeMem(copy_buffer,COPY_BUFFER_SIZE);
}
/* copychunkbytes
*
* copy nbytes from infd to outfd, subtracting that amount from
* the number of filebytes left within the virtual chunk, as given
* by the address of that variable
*/
copychunkbytes(infd,outfd,nbytes,filebytes_left_ptr)
int infd, outfd;
long nbytes, *filebytes_left_ptr;
{
int copysize, odd;
/* if we haven't allocated copy_buffer, allocate it and add it's cleanup
* routine (cleanup_copy_buffer) to the cleanup list */
if (!copy_buffer)
{
if ((copy_buffer = (char *)AllocMem(COPY_BUFFER_SIZE,MEMF_FAST)) == (char *)NULL)
panic("couldn't allocate copy buffer");
add_cleanup(cleanup_copy_buffer);
}
/* if nbytes of copying requested exceeds the virtual EOF (end of
* the chunk's metachunk), truncate the chunk
*/
if (nbytes > *filebytes_left_ptr)
{
fprintf(stderr,"copychunkbytes: chunk size exceeds size of superchunk - truncating\n");
nbytes = *filebytes_left_ptr;
}
/* find out if the length of the chunk is odd or not - we'll need
* it later to see if we need to write a pad byte
*/
odd = (nbytes & 1);
/* do the copy, breaking it up into multiple COPY_BUFFER_SIZE sized
* portions, if neccessary */
while (nbytes > 0)
{
copysize = (nbytes > COPY_BUFFER_SIZE) ? COPY_BUFFER_SIZE : nbytes;
if (read(infd,copy_buffer,copysize) != copysize)
{
perror("copybytes input");
fprintf(stderr,"archive is blown.\n");
close(infd);
return(0);
}
if (write(outfd,copy_buffer,copysize) != copysize)
{
perror("copybytes output");
fprintf(stderr,"archive is blown.\n");
close(infd);
return(0);
}
/* update bytes left in chunk and in chunk's metachunk */
nbytes -= copysize;
*filebytes_left_ptr -= copysize;
}
/* done with copy - if number of bytes copied was odd, read and
* discard the pad byte, write out a pad byte and update the
* virtual EOF (end of chunk) */
if (odd)
{
if (read(infd,copy_buffer,1) != 1)
perror("copychunkbytes: failed to skip input byte");
assert(*copy_buffer == '\0');
write(outfd,copy_buffer,1);
(*filebytes_left_ptr)--;
}
}
/* create an archive by opening it, writing a CAT header, and returning the
* file descriptor if it succeeded or -1 otherwise
*/
int create_archive(archive_name,subtype)
char *archive_name;
ULONG subtype;
{
int archive_fd;
if ((archive_fd = open(archive_name, O_RDWR|O_CREAT|O_TRUNC)) == -1)
{
perror(archive_name);
return(-1);
}
if (!WriteCATheader(archive_fd))
{
fprintf("create_archive: couldn't write CAT chunkheader of new archive\n");
return(-1);
}
/* success, return the archive fd */
return(archive_fd);
}
/* end of create.c */