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

  1. /* iffar - IFF CAT archiver output and file copy rouines
  2.  
  3.    By Karl Lehenbauer, version 1.2, release date 5/9/88.
  4.    This code is released to the public domain.
  5.    See the README file for more information.
  6.  
  7. */
  8.  
  9. /* culled from general purpose IFF file cracking routines for Karl's 
  10.  * Audio Stuff by Karl Lehenbauer, based originally on public domain IFF 
  11.  * code from Electronic Arts, 2/24/88
  12.  */
  13.  
  14. #include <exec/types.h>
  15. #include <exec/memory.h>
  16. #include <stdio.h>
  17. #include <fcntl.h>
  18. #include "assert.h"
  19.  
  20. #include "iff.h"
  21.  
  22. extern long lseek();
  23.  
  24. extern int verbose;
  25.  
  26. extern char *basename();
  27.  
  28. #define ID_MISC MakeID('M','I','S','C')
  29.  
  30. WriteCATheader(fd)
  31. int fd;
  32. {
  33.     static ULONG dummy = ID_MISC;
  34.     WriteChunk(fd,ID_CAT, &dummy, sizeof(dummy));
  35. }
  36.  
  37. /* write a chunk header, that's the 4-byte chunk ID and a 4-byte 
  38.  * chunk length 
  39.  */
  40. WriteChunkHeader(fd,chunktype,length)
  41. int fd;
  42. ULONG chunktype;
  43. long length;
  44. {
  45.     ChunkHeader chunkheader;
  46.  
  47.     chunkheader.ckID = chunktype;
  48.     chunkheader.ckSize = length;
  49.  
  50.     if (write(fd,&chunkheader,sizeof(chunkheader)) == -1)
  51.     {
  52.         perror("WriteChunkHeader");
  53.         return(0);
  54.     }
  55.     return(1);
  56. }
  57.  
  58. /* write a chunk that has a four character subtype, like FORM, CAT or LIST
  59. */
  60. WriteSuperChunkHeader(fd,chunktype,subtype,length)
  61. int fd;
  62. ULONG chunktype, subtype;
  63. long length;
  64. {
  65.     if (!WriteChunkHeader(fd,chunktype,length+sizeof(subtype)))
  66.         return(0);
  67.     return(write(fd,&subtype,sizeof(subtype)) != -1);
  68. }
  69.  
  70. /* WriteCATentry
  71.     This routine is given all of the info for a superchunk header and,
  72.     in addition, a file name.  It writes the chunk header and a
  73.     FNAM chunk containing the file name out to the fd provided.
  74. */
  75. WriteCATentry(fd,fname,chunktype,subtype,length)
  76. int fd;
  77. char *fname;
  78. ULONG chunktype, subtype;
  79. long length;
  80. {
  81.     int fnamelen;
  82.     int calc_chunk_length;
  83.  
  84.     /* Figure out the length of the file name.  Remember that
  85.      * it should be even.  (I should use a cool macro to force
  86.      * that, but I don't)
  87.      * Add the size of the FNAM chunk we're going to write to our
  88.      * calculated chunk length.
  89.      */
  90.     fnamelen = strlen(fname);
  91.     calc_chunk_length = fnamelen;
  92.     if (fnamelen & 1)
  93.         calc_chunk_length++;
  94.     calc_chunk_length += length + sizeof(ChunkHeader);
  95.  
  96.     WriteSuperChunkHeader(fd,chunktype,subtype,calc_chunk_length);
  97.     if (!WriteChunk(fd,ID_FNAM,fname,strlen(fname)))
  98.         return(0);
  99.     return(1);
  100. }
  101.  
  102. /* write a chunk header and body, that's the 8-byte header and the data
  103.  * to match the specified length
  104.  */
  105. WriteChunk(fd,chunktype,data,length)
  106. int fd;
  107. ULONG chunktype;
  108. char *data;
  109. long length;
  110. {
  111.     static char zero = '\0';
  112.  
  113.     if (!WriteChunkHeader(fd,chunktype,length))
  114.         return(0);
  115.  
  116.     /* if there's a body, write it out */
  117.     if (length != 0)
  118.         if (write(fd,data,length) == -1)
  119.         {
  120.             perror("WriteChunk");
  121.             return(0);
  122.         }
  123.  
  124.     /* if the length is odd, write out an additional zero byte */
  125.     if (length & 1)
  126.         if (write(fd,&zero,1) == -1)
  127.         {
  128.             perror("WriteChunk");
  129.             return(0);
  130.         }
  131.     return(1);
  132. }
  133.  
  134. /* checknew - given fname, this routine prints "Creating IFF CAT archive "
  135.  * and the fname if file fname does not exist
  136.  */
  137. checknew(fname)
  138. char *fname;
  139. {
  140.     extern int suppress_creation_message;
  141.     int fd;
  142.  
  143.     if (!suppress_creation_message)
  144.     {
  145.         if ((fd = open(fname,O_RDONLY)) < 0)
  146.         {
  147.             fprintf(stderr,"Creating IFF CAT archive '%s'\n",fname);
  148.         }
  149.         else
  150.             close(fd);
  151.     }
  152. }
  153.  
  154. /* open_quick_append
  155.  * open the archive for append, verifying that it is IFF, subtype CAT,
  156.  * that the length in the header matches the length of the file and
  157.  * such.  Note that this has been wrapped into a better OpenCAT routine
  158.  * but I have not fixed open_quick_append to call it yet.
  159.  */
  160. int open_quick_append(fname)
  161. char *fname;
  162. {
  163.     ChunkHeader mychunkheader;
  164.     long filesize;
  165.     int fd;
  166.  
  167.     /* If I can't open the archive read only, it doesn't exist, so
  168.      * create it
  169.      */
  170.     if ((fd = open(fname,O_RDONLY)) < 0)
  171.     {
  172.         if ((fd = create_archive(fname,ID_MISC)) < 0)
  173.         {
  174.             perror(fname);
  175.             return(-1);
  176.         }
  177.     }
  178.     else
  179.     {
  180.         /* read the IFF header and validate we've got a CAT */
  181.         if (read(fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
  182.         {
  183.             perror(fname);
  184.             fprintf(stderr,"couldn't read chunk header\n");
  185.             return(-1);
  186.         }
  187.  
  188.         if (mychunkheader.ckID != ID_CAT)
  189.         {
  190.             fprintf(stderr,"file '%s' is not an IFF CAT archive\n",fname);
  191.             return(-1);
  192.         }
  193.  
  194.         /* verify that the header's filesize matches the file's size */
  195.         if ((filesize = lseek(fd,0,2)) == -1)
  196.         {
  197.             perror(fname);
  198.             return(-1);
  199.         }
  200.  
  201.         if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
  202.         {
  203.             fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",fname);
  204.             fprintf(stderr,"I'm assuming it's OK and using file size.\n");
  205.         }
  206.  
  207.         /* ok, reopen the file for append and return the fd */
  208.         close(fd);
  209.         if ((fd = open(fname,O_RDWR|O_APPEND)) < 0)
  210.         {
  211.             perror(fname);
  212.             return(-1);
  213.         }
  214.     }
  215.     return(fd);
  216. }
  217.  
  218. #define COPY_BUFFER_SIZE 32768
  219.  
  220. char *copy_buffer = 0;
  221.  
  222. /* append_file_to_archive
  223.  *
  224.  * this routine copies IFF file named by fname to the CAT archive known
  225.  * by it's open-for-append fd.
  226.  */
  227. append_file_to_archive(fname,archive_fd)
  228. char *fname;
  229. int archive_fd;
  230. {
  231.     char *basename_ptr;
  232.     int bytes, i;
  233.     int infd, fnamelen, basenamelen;
  234.     ULONG chunkid, subtype;
  235.     long chunksize, new_chunk_address;
  236.     ULONG subchunkid;
  237.     long subchunksize, placeholder, calculated_chunk_size, inputfilesize;
  238.  
  239.     /* seek to the end of the archive */
  240.     lseek(archive_fd,0,2);
  241.  
  242.     /* open the file to appended to the archive, read only */
  243.     if ((infd = open(fname,O_RDONLY)) == -1)
  244.     {
  245.         perror(fname);
  246.         return(0);
  247.     }
  248.  
  249.     /* get the filesize of the input file and relocate back to the start
  250.      * of it */
  251.     inputfilesize = lseek(infd,0,2);
  252.     lseek(infd,0,0);
  253.  
  254.     /* get the ID and size of the next chunk */
  255.     if ((chunkid = nextchunk(infd,&chunksize,&inputfilesize)) == 0)
  256.     {
  257.         fprintf(stderr,"couldn't get header chunk from file %s\n",fname);
  258.         close(infd);
  259.         return(0);
  260.     }
  261.  
  262.     /* if the header isn't CAT, FORM or LIST, don't copy it */
  263.     if (chunkid != ID_CAT && chunkid != ID_FORM && chunkid != ID_LIST)
  264.     {
  265.         fprintf(stderr,"file %s is not an IFF CAT, FORM or LIST, ignored\n",fname);
  266.         close(infd);
  267.         return(0);
  268.     }
  269.  
  270.     /* kludgily get the subtype - for FORMs, LISTs & CATs it's the
  271.      * first 4 bytes of the chunk data.  These are included in chunksize
  272.      */
  273.     if (read(infd,&subtype,4) != 4)
  274.     {
  275.         perror("copy subtype");
  276.         return(0);
  277.     }
  278.     inputfilesize -= 4;
  279.  
  280.     /* record where we are in the archive so we can rewrite the header
  281.      * which we'll need to do if we add a FNAM chunk */
  282.      new_chunk_address = lseek(archive_fd,0L,1) + 4;
  283.  
  284.     /* write in the chunk ID and the subtype - the program will
  285.      * rewrite the length when we know for sure how long the
  286.      * chunk is */
  287.     if (!WriteChunk(archive_fd,chunkid,&subtype,4))
  288.     {
  289.         perror("append WriteChunk");
  290.         return(0);
  291.     }
  292.  
  293.     /* keep track of the size of the FORM, CAT or LIST we're reading
  294.      * through.  We start with 4, the size of subtype written above */
  295.     calculated_chunk_size = 4;
  296.  
  297.     fnamelen = strlen(fname);
  298.  
  299.     /* if the filename includes a path, use only the base portion */
  300.     basename_ptr = basename(fname);
  301.     basenamelen = strlen(basename);
  302.  
  303.     /* write a FNAM chunk, it's the basename determined above,
  304.      * and our handle for it */
  305.     if (!WriteChunk(archive_fd,ID_FNAM,basename_ptr,basenamelen))
  306.         return(0);
  307.  
  308.     /* add size of the chunk header and the length of the chunk
  309.      * body to the calculated chunk size.  If the number is odd,
  310.      * increment it by one as the IFF spec requires odd-sized
  311.      * chunks to be one-null-padded to make them even.  Note
  312.      * that WriteChunk took care of it for the actual data written
  313.      */
  314.     calculated_chunk_size += sizeof(ChunkHeader) + basenamelen;
  315.     if (basenamelen & 1)
  316.         calculated_chunk_size++;
  317.  
  318.     /* for all remaining chunks inside the FORM, LIST or CAT that
  319.      * we're adding to the archive, */
  320.     while ((subchunkid = nextchunk(infd,&subchunksize,&inputfilesize)) != 0)
  321.     {
  322.         /* if it's an FNAM chunk, discard it */
  323.         if (subchunkid == ID_FNAM)
  324.             skipchunk(infd,subchunksize,&inputfilesize);
  325.         else
  326.         {
  327.             calculated_chunk_size += subchunksize + sizeof(ChunkHeader);
  328.             if (subchunksize & 1)
  329.                 calculated_chunk_size++;
  330.  
  331.             /* write the chunk header for the embedded chunk we're copying */
  332.             if (!WriteChunkHeader(archive_fd,subchunkid,subchunksize))
  333.                 return(0);
  334.  
  335.             /* now copy the embedded chunk's data */
  336.             copychunkbytes(infd,archive_fd,subchunksize,&inputfilesize);
  337.         }
  338.     }
  339.     /* get current position in the archive, seek back to the header of the
  340.      * FORM, CAT or LIST we just copied (into the archive) and write in the 
  341.      * correct chunk size, then seek back to where we were
  342.      */
  343.  
  344.     placeholder = lseek(archive_fd,0L,1);
  345.     lseek(archive_fd,new_chunk_address,0);
  346.     if (write(archive_fd,&calculated_chunk_size,4) != 4)
  347.     {
  348.         perror("archive subheader rewrite");
  349.         fprintf(stderr,"archive is blown.\n");
  350.         close(infd);
  351.         return(0);
  352.     }
  353.     /* return to previous place in archive, close file we copied and
  354.      * return 'success' */
  355.     lseek(archive_fd,placeholder,0);
  356.     close(infd);
  357.     return(1);
  358. }
  359.  
  360. /* rewrite_archive_header - write (filesize - sizeof(ChunkHeader)) into
  361.  * CAT archive's header, assumes file is open for write
  362.  */
  363. rewrite_archive_header(fd)
  364. {
  365.     long filesize;
  366.  
  367.     /* get filesize by seeking to EOF */
  368.     if ((filesize = lseek(fd,0,2)) == -1)
  369.     {
  370.         perror("archive");
  371.         return(0);
  372.     }
  373.  
  374.     /* go back to the beginning of the file */
  375.     if (lseek(fd,0,0) == -1)
  376.     {
  377.         perror("archive cleanup seek");
  378.         return(0);
  379.     }
  380.  
  381.     if (!WriteChunkHeader(fd,ID_CAT,(filesize - sizeof(ChunkHeader))))
  382.     {
  383.         perror("archive cleanup");
  384.         return(0);
  385.     }
  386.  
  387.     return(1);
  388. }
  389.  
  390. /* the copy buffer cleanup routine, it frees the copy buffer memory if
  391.  * the buffer has been allocated
  392.  */
  393. void cleanup_copy_buffer()
  394. {
  395.     if (copy_buffer)
  396.         FreeMem(copy_buffer,COPY_BUFFER_SIZE);
  397. }
  398.  
  399. /* copychunkbytes
  400.  *
  401.  * copy nbytes from infd to outfd, subtracting that amount from
  402.  * the number of filebytes left within the virtual chunk, as given
  403.  * by the address of that variable
  404.  */
  405. copychunkbytes(infd,outfd,nbytes,filebytes_left_ptr)
  406. int infd, outfd;
  407. long nbytes, *filebytes_left_ptr;
  408. {
  409.     int copysize, odd;
  410.  
  411.     /* if we haven't allocated copy_buffer, allocate it and add it's cleanup
  412.      * routine (cleanup_copy_buffer) to the cleanup list */
  413.     if (!copy_buffer)
  414.     {
  415.         if ((copy_buffer = (char *)AllocMem(COPY_BUFFER_SIZE,MEMF_FAST)) == (char *)NULL)
  416.             panic("couldn't allocate copy buffer");
  417.  
  418.         add_cleanup(cleanup_copy_buffer);
  419.     }
  420.  
  421.     /* if nbytes of copying requested exceeds the virtual EOF (end of
  422.      * the chunk's metachunk), truncate the chunk
  423.      */
  424.     if (nbytes > *filebytes_left_ptr)
  425.     {
  426.         fprintf(stderr,"copychunkbytes: chunk size exceeds size of superchunk - truncating\n");
  427.         nbytes = *filebytes_left_ptr;
  428.     }
  429.  
  430.     /* find out if the length of the chunk is odd or not - we'll need
  431.      * it later to see if we need to write a pad byte
  432.      */
  433.      odd = (nbytes & 1);
  434.  
  435.     /* do the copy, breaking it up into multiple COPY_BUFFER_SIZE sized
  436.      * portions, if neccessary */
  437.     while (nbytes > 0)
  438.     {
  439.         copysize = (nbytes > COPY_BUFFER_SIZE) ? COPY_BUFFER_SIZE : nbytes;
  440.  
  441.         if (read(infd,copy_buffer,copysize) != copysize)
  442.         {
  443.             perror("copybytes input");
  444.             fprintf(stderr,"archive is blown.\n");
  445.             close(infd);
  446.             return(0);
  447.         }
  448.         if (write(outfd,copy_buffer,copysize) != copysize)
  449.         {
  450.             perror("copybytes output");
  451.             fprintf(stderr,"archive is blown.\n");
  452.             close(infd);
  453.             return(0);
  454.         }
  455.         /* update bytes left in chunk and in chunk's metachunk */
  456.         nbytes -= copysize;
  457.         *filebytes_left_ptr -= copysize;
  458.     }
  459.  
  460.     /* done with copy - if number of bytes copied was odd, read and
  461.      * discard the pad byte, write out a pad byte and update the
  462.      * virtual EOF (end of chunk) */
  463.     if (odd)
  464.     {
  465.         if (read(infd,copy_buffer,1) != 1)
  466.             perror("copychunkbytes: failed to skip input byte");
  467.         assert(*copy_buffer == '\0');
  468.         write(outfd,copy_buffer,1);
  469.         (*filebytes_left_ptr)--;
  470.     }
  471. }
  472.  
  473. /* create an archive by opening it, writing a CAT header, and returning the
  474.  * file descriptor if it succeeded or -1 otherwise
  475.  */
  476. int create_archive(archive_name,subtype)
  477. char *archive_name;
  478. ULONG subtype;
  479. {
  480.     int archive_fd;
  481.  
  482.     if ((archive_fd = open(archive_name, O_RDWR|O_CREAT|O_TRUNC)) == -1)
  483.     {
  484.         perror(archive_name);
  485.         return(-1);
  486.     }
  487.  
  488.     if (!WriteCATheader(archive_fd))
  489.     {
  490.         fprintf("create_archive: couldn't write CAT chunkheader of new archive\n");
  491.         return(-1);
  492.     }
  493.  
  494.     /* success, return the archive fd */
  495.     return(archive_fd);
  496. }
  497.  
  498. /* end of create.c */
  499.  
  500.