home *** CD-ROM | disk | FTP | other *** search
- /* 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 an
- IFAR 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 IFAR 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_IFAR,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 an IFAR 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_ptr);
-
- /* write an IFAR chunk, it's the basename determined above,
- * and our handle for it */
- if (!WriteChunk(archive_fd,ID_IFAR,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 IFAR chunk, discard it */
- if (subchunkid == ID_IFAR)
- 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 */
-
-