home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2004 March / PCWELT_3_2004.ISO / pcwsoft / flaskmpeg_078_39_src.z.exe / flaskmpeg / Audio / RIFFFile.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-28  |  8.9 KB  |  374 lines

  1. /* rifffile.cpp
  2.  
  3.     Copyright (c) 1996, 1988 by Timothy J. Weber.
  4.  
  5.     See rifffile.txt for documentation.
  6. */
  7.  
  8. #include "rifffile.h"
  9.  
  10. using namespace std;
  11.  
  12. /***************************************************************************
  13.     macros and constants
  14. ***************************************************************************/
  15.  
  16. // define REVERSE_ENDIANISM if the endianism of the host platform is not Intel
  17. // (Intel is little-endian)
  18. #ifdef REVERSE_ENDIANISM
  19.  #define SWAP_32(int32) (  \
  20.     ((((DWORD) int32) & 0x000000FFL) << 24) +  \
  21.     ((((DWORD) int32) & 0x0000FF00L) << 8) +  \
  22.     ((((DWORD) int32) & 0x00FF0000L) >> 8) +  \
  23.     ((((DWORD) int32) & 0xFF000000L) >> 24))
  24. #endif
  25.  
  26. struct TypeRecord {
  27.     char* typeName;  // four-letter name
  28.     char* realName;  // English name
  29. };
  30.  
  31. const int numExtraTypes = 24;
  32. const TypeRecord extraTypes[numExtraTypes] = {
  33.     { "DISP", "Display name" },
  34.     { "IARL", "Archival location" },
  35.     { "IART", "Artist" },
  36.     { "ICMS", "Commissioned" },
  37.     { "ICMT", "Comments" },
  38.     { "ICOP", "Copyright" },
  39.     { "ICRD", "Creation date" },
  40.     { "ICRP", "Cropped" },
  41.     { "IDIM", "Dimensions" },
  42.     { "IDPI", "Dots Per Inch" },
  43.     { "IENG", "Engineer" },
  44.     { "IGNR", "Genre" },
  45.     { "IKEY", "Keywords" },
  46.     { "ILGT", "Lightness" },
  47.     { "IMED", "Medium" },
  48.     { "INAM", "Name" },
  49.     { "IPLT", "Palette Setting" },
  50.     { "IPRD", "Product" },
  51.     { "ISBJ", "Subject" },
  52.     { "ISFT", "Software" },
  53.     { "ISHP", "Sharpness" },
  54.     { "ISRC", "Source" },
  55.     { "ISRF", "Source Form" },
  56.     { "ITCH", "Technician" },
  57. };
  58.  
  59. /***************************************************************************
  60.     typedefs and class definitions
  61. ***************************************************************************/
  62.  
  63. /***************************************************************************
  64.     prototypes for static functions
  65. ***************************************************************************/
  66.  
  67. /***************************************************************************
  68.     static variables
  69. ***************************************************************************/
  70.  
  71. /***************************************************************************
  72.     member functions for RiffFile
  73. ***************************************************************************/
  74.  
  75. RiffFile::RiffFile(const char *name):
  76.     fp(fopen(name, "rb"))
  77. {
  78.     if (fp && !rewind()) {
  79.         fclose(fp);
  80.         fp = 0;
  81.     }
  82. }
  83.  
  84. RiffFile::~RiffFile()
  85. {
  86.     if (fp)
  87.         fclose(fp);
  88. }
  89.  
  90. bool RiffFile::rewind()
  91. {
  92.     // clear the chunk stack
  93.     while (!chunks.empty())
  94.         chunks.pop();
  95.  
  96.     // rewind to the start of the file
  97.     if (fseek(fp, 0, SEEK_SET))
  98.         return false;
  99.  
  100.     // look for a valid RIFF header
  101.     RiffChunk topChunk(*this);
  102.  
  103.     if (feof(fp) || strcmp(topChunk.name, "RIFF"))
  104.         return false;
  105.  
  106.     // found; push it on the stack, and leave the put pointer in the same place
  107.     // as the get pointer.
  108.     formSize = topChunk.size;
  109.     chunks.push(topChunk);
  110.     return true;
  111. }
  112.  
  113. bool RiffFile::push(const char* chunkType)
  114. {
  115.     // can't descend if we haven't started out yet.
  116.     if (chunks.empty())
  117.         return false;
  118.  
  119.     // first, go to the start of the current chunk, if we're looking for a named
  120.     // chunk.
  121.     if (chunkType)
  122.         if (fseek(fp, chunks.top().start, SEEK_SET))
  123.             return false;
  124.  
  125.     // read chunks until one matches or we exhaust this chunk
  126.     while (!feof(fp) && ftell(fp) < chunks.top().after) {
  127.         RiffChunk chunk(*this);
  128.  
  129.         if (!feof(fp)) {
  130.             // see if the subchunk type matches
  131.             if (!chunkType || strcmp(chunk.name, chunkType) == 0) {
  132.                 // found; synchronize the put pointer, push the chunk, and succeed
  133.                 chunks.push(chunk);
  134.                 return true;
  135.             } else {
  136.                 // not found; go to the next one.
  137.                 if (fseek(fp, chunk.after, SEEK_SET))
  138.                     return false;
  139.             }
  140.         }
  141.     }
  142.  
  143.     // couldn't find it; synchronize the put pointer and return error.
  144.     fseek(fp, chunks.top().start, SEEK_SET);
  145.     return false;
  146. }
  147.  
  148. bool RiffFile::pop()
  149. {
  150.     // if we've only got the top level chunk (or not even that), then we can't
  151.     // go up.
  152.     if (chunks.size() < 2)
  153.         return false;
  154.  
  155.     // Position the get and put pointers at the end of the current subchunk.
  156.     fseek(fp, chunks.top().after, SEEK_SET);
  157.  
  158.     // Pop up the stack.
  159.     chunks.pop();
  160.     return true;
  161. }
  162.  
  163. long RiffFile::chunkSize() const
  164. {
  165.     if (!chunks.empty())
  166.         return chunks.top().size;
  167.     else
  168.         return 0;
  169. }
  170.  
  171. const char* RiffFile::chunkName() const
  172. {
  173.     if (!chunks.empty())
  174.         return chunks.top().name;
  175.     else
  176.         return 0;
  177. }
  178.  
  179. const char* RiffFile::subType() const
  180. {
  181.     if (!chunks.empty() && chunks.top().subType[0])
  182.         return chunks.top().subType;
  183.     else
  184.         return 0;
  185. }
  186.  
  187. bool RiffFile::getNextExtraItem(string& type, string& value)
  188. {
  189.     // if the current chunk is LIST/INFO, then try to read another subchunk.
  190.     if (strcmp(chunkName(), "LIST") == 0
  191.         && strcmp(subType(), "INFO") == 0)
  192.     {
  193.         if (push()) {
  194.             if (readExtraItem(type, value))
  195.                 return true;
  196.             else
  197.                 // unrecognized type.  Continue on.
  198.                 return getNextExtraItem(type, value);
  199.         } else {
  200.             // got to the end of the LIST/INFO chunk.  Pop back out and continue
  201.             // looking.
  202.             pop();
  203.             return getNextExtraItem(type, value);
  204.         }
  205.     // we're not in a LIST/INFO chunk, so look for the next DISP or LIST/INFO.
  206.     } else {
  207.         push();
  208.  
  209.         if (strcmp(chunkName(), "DISP") == 0) {
  210.             // DISP chunk: read and pop back out.
  211.             return readExtraItem(type, value);
  212.         } else if (strcmp(chunkName(), "LIST") == 0
  213.             && strcmp(subType(), "INFO") == 0)
  214.         {
  215.             // LIST/INFO chunk: read first element
  216.             return getNextExtraItem(type, value);
  217.         } else {
  218.             // Some other chunk.  Pop back out and move on.
  219.             if (pop())
  220.                 return getNextExtraItem(type, value);
  221.             else
  222.                 return false;
  223.         }
  224.     }
  225. }
  226.  
  227. // Reads extra data from the current chunk, and pops out of it.
  228. bool RiffFile::readExtraItem(string& type, string& value)
  229. {
  230.     // see if it's one we recognize
  231.     bool found = false;
  232.     for (int i = 0; i < numExtraTypes; i++) {
  233.         if (strcmp(chunkName(), extraTypes[i].typeName) == 0) {
  234.             type = extraTypes[i].realName;
  235.             found = true;
  236.         }
  237.     }
  238.  
  239.     // DISP chunks skip four bytes before the display name starts.
  240.     if (strcmp(chunkName(), "DISP") == 0) {
  241.         fgetc(filep());
  242.         fgetc(filep());
  243.         fgetc(filep());
  244.         fgetc(filep());
  245.     }
  246.  
  247.     // read the value, if we recognize the type
  248.     if (found) {
  249.         int c;
  250.         value = "";
  251.         while ((c = fgetc(filep())) != '\0' && c != EOF)
  252.             value += char(c);
  253.     }
  254.  
  255.     // whether we recognize it or not, pop back out.
  256.     pop();
  257.     return found;
  258. }
  259.  
  260. /***************************************************************************
  261.     member functions for RiffChunk
  262. ***************************************************************************/
  263.  
  264. RiffChunk::RiffChunk(RiffFile& parent)
  265. {
  266.     // read the chunk name
  267.     fread(name, 1, 4, parent.filep());
  268.     name[4] = '\0';
  269.  
  270.     // read the chunk size
  271.     fread(&size, 4, 1, parent.filep());
  272.  
  273. #ifdef REVERSE_ENDIANISM
  274.     // reverse the endianism of the chunk size.
  275.     size = SWAP_32(size);
  276. #endif
  277.  
  278.     // if this is a RIFF or LIST chunk, read its subtype.
  279.     if (strcmp(name, "RIFF") == 0
  280.         || strcmp(name, "LIST") == 0)
  281.     {
  282.         fread(subType, 1, 4, parent.filep());
  283.         subType[4] = '\0';
  284.  
  285.         // subtract the subtype from the size of the data.
  286.         size -= 4;
  287.     } else
  288.         *subType = '\0';
  289.  
  290.     // the chunk starts after the name and size.
  291.     start = ftell(parent.filep());
  292.  
  293.     // the next chunk starts after this one, but starts on a word boundary.
  294.     after = start + size;
  295.     if (after % 2)
  296.         after++;
  297. }
  298.  
  299. /***************************************************************************
  300.     main()
  301. ***************************************************************************/
  302.  
  303. #ifdef TEST_RIFFFILE
  304.  
  305. #include <iostream>
  306.  
  307. static void reportProblem()
  308. {
  309.     cout << "  *** ERROR: Result incorrect." << endl;
  310. }
  311.  
  312. static void checkResult(bool got, bool expected)
  313. {
  314.     if (got)
  315.         cout << "success." << endl;
  316.     else 
  317.         cout << "fail." << endl;
  318.  
  319.     if (got != expected)
  320.         reportProblem();
  321. }
  322.  
  323. static void pause()
  324. {
  325.     cout << "Press Enter to continue." << endl;
  326.     cin.get();
  327. }
  328.  
  329. static void showChunk(RiffFile& file, int indent, bool expandLists)
  330. {
  331.     for (int i = 0; i < indent; i++)
  332.         cout << ' ';
  333.  
  334.     cout << "Chunk type: " << file.chunkName();
  335.  
  336.     if (file.subType())
  337.         cout << " (" << file.subType() << ")";
  338.  
  339.     cout << ", " << file.chunkSize() << " bytes" << endl;
  340.  
  341.     // show all subchunks
  342.     if (strcmp(file.chunkName(), "RIFF") == 0
  343.         || (expandLists && strcmp(file.chunkName(), "LIST") == 0))
  344.     {
  345.         while (file.push()) {
  346.             showChunk(file, indent + 2, expandLists);
  347.             file.pop();
  348.         }
  349.     }
  350. }
  351.  
  352. int main(int argc, const char* argv[])
  353. {
  354.     if (argc == 1)
  355.         cout << "Lists the chunks in a named RIFF file." << endl;
  356.     else {
  357.         RiffFile file(argv[1]);
  358.  
  359.         if (!file.filep()) {
  360.             cout << "No RIFF form in file " << argv[1] << "." << endl;
  361.         } else {
  362.             cout << "Without expanding lists:" << endl;
  363.             showChunk(file, 0, false);
  364.  
  365.             cout << endl << "Expanding lists:" << endl;
  366.             showChunk(file, 0, true);
  367.         }
  368.     }
  369.  
  370.     return 0;
  371. }
  372.  
  373. #endif
  374.