home *** CD-ROM | disk | FTP | other *** search
- /* rifffile.cpp
-
- Copyright (c) 1996, 1988 by Timothy J. Weber.
-
- See rifffile.txt for documentation.
- */
-
- #include "rifffile.h"
-
- using namespace std;
-
- /***************************************************************************
- macros and constants
- ***************************************************************************/
-
- // define REVERSE_ENDIANISM if the endianism of the host platform is not Intel
- // (Intel is little-endian)
- #ifdef REVERSE_ENDIANISM
- #define SWAP_32(int32) ( \
- ((((DWORD) int32) & 0x000000FFL) << 24) + \
- ((((DWORD) int32) & 0x0000FF00L) << 8) + \
- ((((DWORD) int32) & 0x00FF0000L) >> 8) + \
- ((((DWORD) int32) & 0xFF000000L) >> 24))
- #endif
-
- struct TypeRecord {
- char* typeName; // four-letter name
- char* realName; // English name
- };
-
- const int numExtraTypes = 24;
- const TypeRecord extraTypes[numExtraTypes] = {
- { "DISP", "Display name" },
- { "IARL", "Archival location" },
- { "IART", "Artist" },
- { "ICMS", "Commissioned" },
- { "ICMT", "Comments" },
- { "ICOP", "Copyright" },
- { "ICRD", "Creation date" },
- { "ICRP", "Cropped" },
- { "IDIM", "Dimensions" },
- { "IDPI", "Dots Per Inch" },
- { "IENG", "Engineer" },
- { "IGNR", "Genre" },
- { "IKEY", "Keywords" },
- { "ILGT", "Lightness" },
- { "IMED", "Medium" },
- { "INAM", "Name" },
- { "IPLT", "Palette Setting" },
- { "IPRD", "Product" },
- { "ISBJ", "Subject" },
- { "ISFT", "Software" },
- { "ISHP", "Sharpness" },
- { "ISRC", "Source" },
- { "ISRF", "Source Form" },
- { "ITCH", "Technician" },
- };
-
- /***************************************************************************
- typedefs and class definitions
- ***************************************************************************/
-
- /***************************************************************************
- prototypes for static functions
- ***************************************************************************/
-
- /***************************************************************************
- static variables
- ***************************************************************************/
-
- /***************************************************************************
- member functions for RiffFile
- ***************************************************************************/
-
- RiffFile::RiffFile(const char *name):
- fp(fopen(name, "rb"))
- {
- if (fp && !rewind()) {
- fclose(fp);
- fp = 0;
- }
- }
-
- RiffFile::~RiffFile()
- {
- if (fp)
- fclose(fp);
- }
-
- bool RiffFile::rewind()
- {
- // clear the chunk stack
- while (!chunks.empty())
- chunks.pop();
-
- // rewind to the start of the file
- if (fseek(fp, 0, SEEK_SET))
- return false;
-
- // look for a valid RIFF header
- RiffChunk topChunk(*this);
-
- if (feof(fp) || strcmp(topChunk.name, "RIFF"))
- return false;
-
- // found; push it on the stack, and leave the put pointer in the same place
- // as the get pointer.
- formSize = topChunk.size;
- chunks.push(topChunk);
- return true;
- }
-
- bool RiffFile::push(const char* chunkType)
- {
- // can't descend if we haven't started out yet.
- if (chunks.empty())
- return false;
-
- // first, go to the start of the current chunk, if we're looking for a named
- // chunk.
- if (chunkType)
- if (fseek(fp, chunks.top().start, SEEK_SET))
- return false;
-
- // read chunks until one matches or we exhaust this chunk
- while (!feof(fp) && ftell(fp) < chunks.top().after) {
- RiffChunk chunk(*this);
-
- if (!feof(fp)) {
- // see if the subchunk type matches
- if (!chunkType || strcmp(chunk.name, chunkType) == 0) {
- // found; synchronize the put pointer, push the chunk, and succeed
- chunks.push(chunk);
- return true;
- } else {
- // not found; go to the next one.
- if (fseek(fp, chunk.after, SEEK_SET))
- return false;
- }
- }
- }
-
- // couldn't find it; synchronize the put pointer and return error.
- fseek(fp, chunks.top().start, SEEK_SET);
- return false;
- }
-
- bool RiffFile::pop()
- {
- // if we've only got the top level chunk (or not even that), then we can't
- // go up.
- if (chunks.size() < 2)
- return false;
-
- // Position the get and put pointers at the end of the current subchunk.
- fseek(fp, chunks.top().after, SEEK_SET);
-
- // Pop up the stack.
- chunks.pop();
- return true;
- }
-
- long RiffFile::chunkSize() const
- {
- if (!chunks.empty())
- return chunks.top().size;
- else
- return 0;
- }
-
- const char* RiffFile::chunkName() const
- {
- if (!chunks.empty())
- return chunks.top().name;
- else
- return 0;
- }
-
- const char* RiffFile::subType() const
- {
- if (!chunks.empty() && chunks.top().subType[0])
- return chunks.top().subType;
- else
- return 0;
- }
-
- bool RiffFile::getNextExtraItem(string& type, string& value)
- {
- // if the current chunk is LIST/INFO, then try to read another subchunk.
- if (strcmp(chunkName(), "LIST") == 0
- && strcmp(subType(), "INFO") == 0)
- {
- if (push()) {
- if (readExtraItem(type, value))
- return true;
- else
- // unrecognized type. Continue on.
- return getNextExtraItem(type, value);
- } else {
- // got to the end of the LIST/INFO chunk. Pop back out and continue
- // looking.
- pop();
- return getNextExtraItem(type, value);
- }
- // we're not in a LIST/INFO chunk, so look for the next DISP or LIST/INFO.
- } else {
- push();
-
- if (strcmp(chunkName(), "DISP") == 0) {
- // DISP chunk: read and pop back out.
- return readExtraItem(type, value);
- } else if (strcmp(chunkName(), "LIST") == 0
- && strcmp(subType(), "INFO") == 0)
- {
- // LIST/INFO chunk: read first element
- return getNextExtraItem(type, value);
- } else {
- // Some other chunk. Pop back out and move on.
- if (pop())
- return getNextExtraItem(type, value);
- else
- return false;
- }
- }
- }
-
- // Reads extra data from the current chunk, and pops out of it.
- bool RiffFile::readExtraItem(string& type, string& value)
- {
- // see if it's one we recognize
- bool found = false;
- for (int i = 0; i < numExtraTypes; i++) {
- if (strcmp(chunkName(), extraTypes[i].typeName) == 0) {
- type = extraTypes[i].realName;
- found = true;
- }
- }
-
- // DISP chunks skip four bytes before the display name starts.
- if (strcmp(chunkName(), "DISP") == 0) {
- fgetc(filep());
- fgetc(filep());
- fgetc(filep());
- fgetc(filep());
- }
-
- // read the value, if we recognize the type
- if (found) {
- int c;
- value = "";
- while ((c = fgetc(filep())) != '\0' && c != EOF)
- value += char(c);
- }
-
- // whether we recognize it or not, pop back out.
- pop();
- return found;
- }
-
- /***************************************************************************
- member functions for RiffChunk
- ***************************************************************************/
-
- RiffChunk::RiffChunk(RiffFile& parent)
- {
- // read the chunk name
- fread(name, 1, 4, parent.filep());
- name[4] = '\0';
-
- // read the chunk size
- fread(&size, 4, 1, parent.filep());
-
- #ifdef REVERSE_ENDIANISM
- // reverse the endianism of the chunk size.
- size = SWAP_32(size);
- #endif
-
- // if this is a RIFF or LIST chunk, read its subtype.
- if (strcmp(name, "RIFF") == 0
- || strcmp(name, "LIST") == 0)
- {
- fread(subType, 1, 4, parent.filep());
- subType[4] = '\0';
-
- // subtract the subtype from the size of the data.
- size -= 4;
- } else
- *subType = '\0';
-
- // the chunk starts after the name and size.
- start = ftell(parent.filep());
-
- // the next chunk starts after this one, but starts on a word boundary.
- after = start + size;
- if (after % 2)
- after++;
- }
-
- /***************************************************************************
- main()
- ***************************************************************************/
-
- #ifdef TEST_RIFFFILE
-
- #include <iostream>
-
- static void reportProblem()
- {
- cout << " *** ERROR: Result incorrect." << endl;
- }
-
- static void checkResult(bool got, bool expected)
- {
- if (got)
- cout << "success." << endl;
- else
- cout << "fail." << endl;
-
- if (got != expected)
- reportProblem();
- }
-
- static void pause()
- {
- cout << "Press Enter to continue." << endl;
- cin.get();
- }
-
- static void showChunk(RiffFile& file, int indent, bool expandLists)
- {
- for (int i = 0; i < indent; i++)
- cout << ' ';
-
- cout << "Chunk type: " << file.chunkName();
-
- if (file.subType())
- cout << " (" << file.subType() << ")";
-
- cout << ", " << file.chunkSize() << " bytes" << endl;
-
- // show all subchunks
- if (strcmp(file.chunkName(), "RIFF") == 0
- || (expandLists && strcmp(file.chunkName(), "LIST") == 0))
- {
- while (file.push()) {
- showChunk(file, indent + 2, expandLists);
- file.pop();
- }
- }
- }
-
- int main(int argc, const char* argv[])
- {
- if (argc == 1)
- cout << "Lists the chunks in a named RIFF file." << endl;
- else {
- RiffFile file(argv[1]);
-
- if (!file.filep()) {
- cout << "No RIFF form in file " << argv[1] << "." << endl;
- } else {
- cout << "Without expanding lists:" << endl;
- showChunk(file, 0, false);
-
- cout << endl << "Expanding lists:" << endl;
- showChunk(file, 0, true);
- }
- }
-
- return 0;
- }
-
- #endif
-