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