home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 138.lha / Iff_Archiver / iff.c < prev    next >
C/C++ Source or Header  |  1986-11-20  |  12KB  |  453 lines

  1. /* iffar - IFF CAT archiver, IFF support functions
  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 general purpose IFF file cracking routines for Karl's 
  10.  * IFF 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 <ctype.h>
  19. #include "assert.h"
  20.  
  21. #include "iff.h"
  22.  
  23. extern long lseek();
  24.  
  25. extern ULONG nextchunk();
  26.  
  27. /* print a chunkID to stderr */
  28. PutID(id)
  29. ID id;
  30. {
  31.     fprintf(stderr,"%c%c%c%c",
  32.        (char)((id>>24L) & 0x7f),
  33.        (char)((id>>16L) & 0x7f),
  34.        (char)((id>>8)   & 0x7f),
  35.        (char)(id        & 0x7f) );
  36. }
  37.  
  38. UBYTE *MyAllocMem(bytes, type)
  39. ULONG bytes, type;
  40. {
  41.     UBYTE *tmp;
  42.     UBYTE *AllocMem();
  43.         tmp = AllocMem(bytes, type);
  44.     return tmp;
  45. }
  46.  
  47. /* return chunktype of next chunk */
  48. /* every time nextchunk is executed and returns that it found a chunk,
  49.  * either readchunk or skipchunk must be called and only one time!
  50.  */
  51. ULONG nextchunk(fd,chunksize,chunk_bytes_left)
  52. int fd;
  53. long *chunksize, *chunk_bytes_left;
  54. {
  55.     int sawsize, i, blown = 0;
  56.     ChunkHeader mychunkheader;
  57.     char checkchar;
  58.  
  59.     /* if chunk_bytes_left is zero, we obey it as a virtual EOF, so
  60.      * return 0 */
  61.      if (*chunk_bytes_left == 0)
  62.          return(0);
  63.  
  64.     /* read the next chunk header */
  65.     if ((sawsize = read(fd,&mychunkheader,sizeof(mychunkheader))) != 
  66.         sizeof(mychunkheader))
  67.     {
  68.         if (sawsize != 0)
  69.             fprintf(stderr,"Something's wrong with nextchunk! (sawsize %d)\n", sawsize);
  70.         *chunksize = 0;
  71.         return(0);
  72.     }
  73.  
  74. #ifdef MAJORDEBUG
  75.     fputs("nextchunk: next chunk '",stderr);
  76.     PutID(mychunkheader.ckID);
  77.     fprintf(stderr,"', size %d, parent bytes left %d\n",mychunkheader.ckSize,*chunk_bytes_left);
  78. #endif
  79.  
  80.     *chunksize = mychunkheader.ckSize;
  81.  
  82.     /* see if chunk ID looks OK */
  83.     for (i = 0; i < 4; i++)
  84.     {
  85.         checkchar = (mychunkheader.ckID >> (i * 8)) & 0xff;
  86.         if (!isprint(checkchar))
  87.         {
  88.             if (!blown)
  89.             {
  90.                 blown = 1;
  91.                 fprintf(stderr,"nextchunk: chunk ID contains an unprintable character (0x%x)\n",checkchar);
  92.             }
  93.             break;
  94.         }
  95.     }
  96.  
  97.     /* see if chunk length is reasonable */
  98.     if ((mychunkheader.ckSize < 0) || (mychunkheader.ckSize > MAXCHUNKSIZE))
  99.     {
  100.         fprintf(stderr,"nextchunk: chunk length of %ld is unreasonable\n",mychunkheader.ckSize);
  101.         blown = 1;
  102.     }
  103.  
  104.     if (blown)
  105.     {
  106.         fprintf(stderr,"nextchunk: I either got lost or the archive is blown\n");
  107.         return(0);
  108.     }
  109.  
  110.     /* square up the bytes left in the chunk by the size of a chunk header,
  111.      * eight bytes.  We leave it to the caller to subtract the size of the
  112.      * body of the chunk by calling skipchunk or readchunk
  113.      */
  114.     *chunk_bytes_left -= sizeof(mychunkheader);
  115.  
  116.     if (*chunk_bytes_left < 0)
  117.     {
  118.         fprintf("nextchunk: chunk overran its parent by %d bytes\n",(0-*chunk_bytes_left));
  119.         *chunksize = 0;
  120.         *chunk_bytes_left = 0;
  121.         return(0);
  122.     }
  123.  
  124.     return(mychunkheader.ckID);
  125. }
  126.  
  127. /* read next chunk into buffer supplied, size must be value returned by
  128.  * nextchunk
  129.  * zero is returned on failure, one on success
  130.  */
  131. readchunk(fd,buf,size,chunk_bytes_left)
  132. int fd;
  133. char *buf;
  134. LONG size, *chunk_bytes_left;
  135. {
  136.     *chunk_bytes_left -= size;
  137.  
  138.     if (*chunk_bytes_left < 0)
  139.     {
  140.         fprintf(stderr,"readchunk: chunk requested passed the end of its parent chunk\n");
  141.         *chunk_bytes_left = 0;
  142.         return(0);
  143.     }
  144.  
  145.     if (read(fd,buf,size) != size) 
  146.     {
  147.         perror("smus file");
  148.         fputs("LoadSMUS: read of IFF chunk failed\n",stderr);
  149.         return(0);
  150.     }
  151.  
  152.     /* odd-length chunks have a trailer byte - skip it */
  153.     if (size & 1)
  154.     {
  155.         lseek(fd,1L,1);
  156.         (*chunk_bytes_left)--;
  157.     }
  158.     return(1);
  159. }
  160.  
  161. /* skip non-header portion of chunk, chunksize must have been returned
  162.  * by nextchunk
  163.  * returns 1 on success, 0 on failure
  164.  */
  165. skipchunk(fd,chunksize,chunk_bytes_left)
  166. int fd;
  167. LONG chunksize, *chunk_bytes_left;
  168. {
  169.     *chunk_bytes_left -= chunksize;
  170.     if (chunksize & 1)
  171.         (*chunk_bytes_left)--;
  172.     if (*chunk_bytes_left < 0)
  173.     {
  174.         fprintf(stderr,"skipchunk: chunk size passes end of parent chunk's data by %d bytes\n",0 - *chunk_bytes_left);
  175.         return(0);
  176.     }
  177.     /* skip over chunk data and skip an extra byte if length is odd */
  178.     lseek(fd,(long)chunksize,1);
  179.     if (chunksize & 1)
  180.         lseek(fd,1L,1);
  181.     return(1);
  182. }
  183.  
  184. /* OpenIFF
  185.  * given file name, open the IFF file.
  186.  * read the header, return failure if it's not a FORM
  187.  * (someday we'll handle the more complex types)
  188.  * read the form type, return failure if it's not the type requested
  189.  * success, return the file descriptor
  190.  */
  191.  
  192. int OpenIFF(fname,expected_formtype,length_ptr)
  193. char *fname;
  194. LONG expected_formtype;
  195. LONG *length_ptr;
  196. {
  197.     int iffile;
  198.     ChunkHeader chunkhead;
  199.     LONG formtype;
  200.  
  201.     /* open the file */
  202.     if ((iffile = open(fname, O_RDONLY)) < 0)
  203.     {
  204.         fprintf(stderr,"OpenIFF: can't open IFF SMUS file %s\n",fname);
  205.         perror(fname);
  206.         return(-1);
  207.     }
  208.  
  209.     /* get the length */
  210.     *length_ptr = lseek(iffile,0,2);
  211.     lseek(iffile,0,0);
  212.  
  213.     /* read the header chunk */
  214.     if (read(iffile, &chunkhead, sizeof(chunkhead)) < 0)
  215.     {
  216.         fprintf(stderr,"OpenIFF: initial read from IFF file %s failed!\n",fname);
  217.         return(-1);
  218.     }
  219.  
  220.     /* return if the header chunk doesn't say it's IFF FORM */
  221.     if (chunkhead.ckID != ID_FORM)
  222.     {
  223.         fprintf(stderr,"OpenIFF: File %s isn't IFF, is too complex, or doesn't start with FORM\n",fname);
  224.         return(-1);
  225.     }
  226.     /* fprintf(stderr,"OpenIFF: FORM found, size is %d\n",chunkhead.ckSize); */
  227.  
  228.     /* read the form type */
  229.     read(iffile, &formtype, sizeof(formtype));
  230.  
  231.     /* return if the form type isn't the type requested */
  232.     if (formtype != expected_formtype)
  233.     {
  234.         fprintf(stderr,"OpenIFF: File %s is IFF ");
  235.         PutID(formtype);
  236.         fprintf(stderr," rather than the requested ");
  237.         PutID(expected_formtype);
  238.         fprintf(stderr,"\n");
  239.         return(-1);
  240.     }
  241.     return(iffile);
  242. }
  243.  
  244. /* read chunks until one of type chunktype is found or EOF
  245.  * note that after a successful call to chunkuntil,
  246.  * skipchunk or readchunk must be performed or the IFF reading
  247.  * software will get lost on the next nextchunk
  248.  * chunksize is returned on success, -1 otherwise
  249.  * The caller should probably check the return explicitly for -1.
  250.  * If checking only for less than zero, chunks larger than
  251.  * two gigabytes will cause your code to break.
  252.  */
  253.  
  254. LONG chunkuntil(fd,chunktype,file_bytes_left)
  255. int fd;
  256. ULONG chunktype;
  257. long *file_bytes_left;
  258. {
  259.     ULONG currentchunk;
  260.     LONG chunksize;
  261.  
  262.     while ((currentchunk = nextchunk(fd,&chunksize,file_bytes_left)) != NULL)
  263.     {
  264.         if (currentchunk == chunktype)
  265.             return(chunksize);
  266.         skipchunk(fd,chunksize,file_bytes_left);
  267.     }
  268.     return(0);
  269. }
  270.  
  271. /* OpenCAT - Open an IFF CAT archive */
  272.  
  273. /* OpenCAT
  274.  * Open an IFF CAT archive, insuring that the file starts with an
  275.  * IFF CAT header and that the length in the header is valid.
  276.  * Return the CAT subtype, file descriptor and length, leaving the
  277.  * file pointed at the start of the first subchunk
  278.  */
  279.  
  280. int OpenCAT(archive_name,subtype_ptr,length_ptr)
  281. char *archive_name;
  282. ULONG *subtype_ptr, *length_ptr;
  283. {
  284.     ChunkHeader mychunkheader;
  285.     int archive_fd;
  286.     long start_of_body, filesize;
  287.     long placeholder;
  288.  
  289.     if ((archive_fd = open(archive_name,O_RDONLY)) == -1)
  290.     {
  291.         /* fprintf(stderr,"Can't open archive '%s'\n",archive_name); */
  292.         return(-1);
  293.     }
  294.  
  295.     if (read(archive_fd,&mychunkheader,sizeof(mychunkheader)) != sizeof(mychunkheader))
  296.     {
  297.         perror(archive_name);
  298.         fprintf(stderr,"couldn't read chunk header\n");
  299.         return(-1);
  300.     }
  301.  
  302.     if (mychunkheader.ckID != ID_CAT)
  303.     {
  304.         fprintf(stderr,"file '%s' is not an IFF CAT archive\n",archive_name);
  305.         return(-1);
  306.     }
  307.  
  308.     if (read(archive_fd,subtype_ptr,sizeof(subtype_ptr)) != sizeof(subtype_ptr))
  309.     {
  310.         fprintf(stderr,"error reading archive header - subtype\n");
  311.         return(-1);
  312.     }
  313.  
  314.     /* save location of current start of body */
  315.     if ((start_of_body = lseek(archive_fd,0,1)) == -1)
  316.     {
  317.         perror(archive_name);
  318.         return(-1);
  319.     }
  320.  
  321.     /* seek to the end to get the size */
  322.     if ((filesize = lseek(archive_fd,0,2)) == -1)
  323.     {
  324.         perror(archive_name);
  325.         return(-1);
  326.     }
  327.  
  328.     /* see if the shoe fits */
  329.     if ((filesize - sizeof(ChunkHeader)) != mychunkheader.ckSize)
  330.     {
  331.         fprintf(stderr,"archive %s's CAT chunk size does not equal the file's size.\n",archive_name);
  332.         fprintf(stderr,"I'm assuming it's blown.\n");
  333.         return(-1);
  334.     }
  335.  
  336.     /* go back to the start of the IFF CAT archive's data */
  337.     if (lseek(archive_fd,start_of_body,0) == -1)
  338.     {
  339.         perror(archive_name);
  340.         return(-1);
  341.     }
  342.  
  343.     /* it worked store filesize in location pointed to by 'length' 
  344.      * and return the archive file's file descriptor
  345.      */
  346.     *length_ptr = filesize;
  347.     return(archive_fd);
  348. }
  349.  
  350. /* end of OpenCAT */
  351.  
  352. /* nextcat - read header info for the next entry in an IFF CAT */
  353.  
  354. /* nextCATchunk
  355.  *
  356.  * given fd, read into IFF file.
  357.  * if we're not at a FORM, CAT or LIST, print the chunk type if verbose,
  358.  *    then skip the chunk
  359.  * if we are at a FORM, CAT or LIST, read the subtype and return it
  360.  * via the argument subtype_ptr.
  361.  * if the next chunk within the embedded FORM, CAT or LIST is FNAM,
  362.  * read the text in the FNAM chunk (file name) and write it into space
  363.  * pointed to by argument fname_ptr.
  364.  * return the size of the chunk in argument chunk_length_ptr.
  365.  * update the space left in the metachunk (usually the file) of argument
  366.  * metachunk_length_ptr
  367.  */
  368.  
  369. ULONG nextCATchunk(fd,subtype_ptr,fname_ptr,chunk_length_ptr,metachunk_length_ptr)
  370. int fd;
  371. ULONG *subtype_ptr;
  372. char *fname_ptr;
  373. LONG *chunk_length_ptr, *metachunk_length_ptr;
  374. {
  375.     ULONG cat_type, chunkid, innerchunkid;
  376.     long chunksize, innerchunkposition, innerchunksize, filesize;
  377.     int odd;
  378.  
  379.     /* null out the returned subtype and fnam */
  380.     *subtype_ptr = 0L;
  381.     *fname_ptr = '\0';
  382.  
  383.     if ((chunkid = nextchunk(fd,chunk_length_ptr,metachunk_length_ptr)) == 0L)
  384.         return(0L);
  385.  
  386.     /* if the chunk type isn't FORM, CAT or LIST, return the chunkid
  387.      */
  388.     if (chunkid != ID_FORM && chunkid != ID_CAT && chunkid != ID_LIST)
  389.         return(chunkid);
  390.  
  391.     /* get the chunk subtype */
  392.     if (read(fd,subtype_ptr,4) != 4)
  393.     {
  394.         perror("reading subtype");
  395.         return(0);
  396.     }
  397.  
  398.     /* reduce chunksize and metachunksize by the size of the subtype */
  399.     *chunk_length_ptr -= sizeof(ULONG);
  400.     *metachunk_length_ptr -= sizeof(ULONG);
  401.  
  402.     /* sneak a peek into the embedded FORM, CAT or LIST to see
  403.      * if the next chunk is an FNAM chunk */
  404.  
  405.      assert(*chunk_length_ptr > 0);
  406.  
  407.     /* fetch the current location in the file - we'll restore it
  408.      * if we don't find this next chunk to be a FNAM one
  409.      */
  410.     innerchunkposition = lseek(fd,0L,1);
  411.  
  412.     /* get the type and size of the inner chunk */
  413.     chunksize = *chunk_length_ptr;
  414.     innerchunkid = nextchunk(fd,&innerchunksize,&chunksize);
  415.  
  416.     /* if it's not an fname chunk, seek back to the start of the
  417.      * chunk and return the chunk id - master length should be OK
  418.      */
  419.     if (innerchunkid != ID_FNAM)
  420.     {
  421.         lseek(fd,innerchunkposition,0);
  422.         return(chunkid);
  423.     }
  424.  
  425.     odd = innerchunksize & 1;
  426.  
  427.     /* read and zero-terminate the file name (contents of FNAM chunk) */
  428.     if (!readchunk(fd,fname_ptr,innerchunksize,&chunksize))
  429.     {
  430.         fprintf(stderr,"nextCATchunk: got into trouble reading chunk text\n");
  431.         return(0);
  432.     }
  433.     *(fname_ptr + innerchunksize) = '\0';
  434.  
  435.     /* update the length of the chunk and its parent &  return the chunk id
  436.      * (nextchunk normally handles updating the length but we used different
  437.      * variables to make restoring (in case we don't find an FNAM chunk)
  438.      * easier
  439.      */
  440.     *chunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
  441.     *metachunk_length_ptr -= (sizeof(ChunkHeader) + innerchunksize);
  442.     if (odd)
  443.     {
  444.         (*chunk_length_ptr)--;
  445.         (*metachunk_length_ptr)--;
  446.     }
  447.     return(chunkid);
  448. }
  449.  
  450. /* end of nextCATchunk */
  451.  
  452. /* end of iff.c */
  453.