| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // DataChunk.cpp
- // Implementation of Data Chunk save/load system
- // Author: Michael S. Booth, October 2000
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "stdlib.h"
- #include "string.h"
- #include "Compression.h"
- #include "Common/DataChunk.h"
- #include "Common/File.h"
- #include "Common/FileSystem.h"
- // If verbose, lots of debug logging.
- #define not_VERBOSE
- CachedFileInputStream::CachedFileInputStream(void):m_buffer(NULL),m_size(0)
- {
- }
- CachedFileInputStream::~CachedFileInputStream(void)
- {
- if (m_buffer) {
- delete[] m_buffer;
- m_buffer=NULL;
- }
- }
- Bool CachedFileInputStream::open(AsciiString path)
- {
- File *file=TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
- m_size = 0;
- if (file) {
- m_size=file->size();
- if (m_size) {
- m_buffer = file->readEntireAndClose();
- file = NULL;
- }
- m_pos=0;
- }
- if (CompressionManager::isDataCompressed(m_buffer, m_size) == 0)
- {
- //DEBUG_LOG(("CachedFileInputStream::open() - file %s is uncompressed at %d bytes!\n", path.str(), m_size));
- }
- else
- {
- Int uncompLen = CompressionManager::getUncompressedSize(m_buffer, m_size);
- //DEBUG_LOG(("CachedFileInputStream::open() - file %s is compressed! It should go from %d to %d\n", path.str(),
- // m_size, uncompLen));
- char *uncompBuffer = NEW char[uncompLen];
- Int actualLen = CompressionManager::decompressData(m_buffer, m_size, uncompBuffer, uncompLen);
- if (actualLen == uncompLen)
- {
- //DEBUG_LOG(("Using uncompressed data\n"));
- delete[] m_buffer;
- m_buffer = uncompBuffer;
- m_size = uncompLen;
- }
- else
- {
- //DEBUG_LOG(("Decompression failed - using compressed data\n"));
- // decompression failed. Maybe we invalidly thought it was compressed?
- delete[] uncompBuffer;
- }
- }
- //if (m_size >= 4)
- //{
- // DEBUG_LOG(("File starts as '%c%c%c%c'\n", m_buffer[0], m_buffer[1],
- // m_buffer[2], m_buffer[3]));
- //}
- if (file)
- {
- file->close();
- }
- return m_size != 0;
- }
- void CachedFileInputStream::close(void)
- {
- if (m_buffer) {
- delete[] m_buffer;
- m_buffer=NULL;
- }
- m_pos=0;
- m_size=0;
- }
- Int CachedFileInputStream::read(void *pData, Int numBytes)
- {
- if (m_buffer) {
- if ((numBytes+m_pos)>m_size) {
- numBytes=m_size-m_pos;
- }
- if (numBytes) {
- memcpy(pData,m_buffer+m_pos,numBytes);
- m_pos+=numBytes;
- }
- return(numBytes);
- }
- return 0;
- }
- UnsignedInt CachedFileInputStream::tell(void)
- {
- return m_pos;
- }
- Bool CachedFileInputStream::absoluteSeek(UnsignedInt pos)
- {
- if (pos<0) return false;
- if (pos>m_size) {
- pos=m_size;
- }
- m_pos=pos;
- return true;
- }
- Bool CachedFileInputStream::eof(void)
- {
- return m_size==m_pos;
- }
- void CachedFileInputStream::rewind()
- {
- m_pos=0;
- }
- // -----------------------------------------------------------
- //
- // FileInputStream - helper class. Used to read in data using a FILE *
- //
- /*
- FileInputStream::FileInputStream(void):m_file(NULL)
- {
- }
- FileInputStream::~FileInputStream(void)
- {
- if (m_file != NULL) {
- m_file->close();
- m_file = NULL;
- }
- }
- Bool FileInputStream::open(AsciiString path)
- {
- m_file = TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
- return m_file==NULL?false:true;
- }
- void FileInputStream::close(void)
- {
- if (m_file != NULL) {
- m_file->close();
- m_file = NULL;
- }
- }
- Int FileInputStream::read(void *pData, Int numBytes)
- {
- int bytesRead = 0;
- if (m_file != NULL) {
- bytesRead = m_file->read(pData, numBytes);
- }
- return(bytesRead);
- }
- UnsignedInt FileInputStream::tell(void)
- {
- UnsignedInt pos = 0;
- if (m_file != NULL) {
- pos = m_file->position();
- }
- return(pos);
- }
- Bool FileInputStream::absoluteSeek(UnsignedInt pos)
- {
- if (m_file != NULL) {
- return (m_file->seek(pos, File::START) != -1);
- }
- return(false);
- }
- Bool FileInputStream::eof(void)
- {
- if (m_file != NULL) {
- return (m_file->size() == m_file->position());
- }
- return(true);
- }
- void FileInputStream::rewind()
- {
- if (m_file != NULL) {
- m_file->seek(0, File::START);
- }
- }
- */
- //----------------------------------------------------------------------
- // DataChunkOutput
- // Data will be stored to a temporary m_tmp_file until the DataChunkOutput
- // object is destroyed. At that time, the actual output m_tmp_file will
- // be written, including a table of m_contents.
- //----------------------------------------------------------------------
- #define TEMP_FILENAME "_tmpChunk.dat"
- DataChunkOutput::DataChunkOutput( OutputStream *pOut ) :
- m_pOut(pOut)
- {
- AsciiString tmpFileName = TheGlobalData->getPath_UserData();
- tmpFileName.concat(TEMP_FILENAME);
- m_tmp_file = ::fopen( tmpFileName.str(), "wb" );
- // Added Sadullah Nader
- // Initializations missing and needed
- m_chunkStack = NULL;
-
- // End Add
- }
- DataChunkOutput::~DataChunkOutput()
- {
- // store the table of m_contents
- m_contents.write(*m_pOut);
- // Rewind the temp m_tmp_file
- ::fclose(m_tmp_file);
- AsciiString tmpFileName = TheGlobalData->getPath_UserData();
- tmpFileName.concat(TEMP_FILENAME);
- m_tmp_file = ::fopen( tmpFileName.str(), "rb" );
- ::fseek(m_tmp_file, 0, SEEK_SET);
- // append the temp m_tmp_file m_contents
- char buffer[256];
- int len = 256;
- while( len == 256 )
- {
- // copy data from the temp m_tmp_file to the output m_tmp_file
- len = ::fread( buffer, 1, 256, m_tmp_file );
- m_pOut->write( buffer, len );
- }
- ::fclose(m_tmp_file);
- }
- void DataChunkOutput::openDataChunk( char *name, DataChunkVersionType ver )
- {
- // allocate (or get existing) ID from the table of m_contents
- UnsignedInt id = m_contents.allocateID( AsciiString(name) );
- // allocate a new chunk and place it on top of the chunk stack
- OutputChunk *c = newInstance(OutputChunk);
- c->next = m_chunkStack;
- m_chunkStack = c;
- m_chunkStack->id = id;
- // store the chunk ID
- ::fwrite( (const char *)&id, sizeof(UnsignedInt), 1, m_tmp_file );
- // store the chunk version number
- ::fwrite( (const char *)&ver, sizeof(DataChunkVersionType), 1, m_tmp_file );
- // remember this m_tmp_file position so we can write the real data size later
- c->filepos = ::ftell(m_tmp_file);
- #ifdef VERBOSE
- DEBUG_LOG(("Writing chunk %s at %d (%x)\n", name, ::ftell(m_tmp_file), ::ftell(m_tmp_file)));
- #endif
- // store a placeholder for the data size
- Int dummy = 0xffff;
- ::fwrite( (const char *)&dummy, sizeof(Int), 1, m_tmp_file );
- }
- void DataChunkOutput::closeDataChunk( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- return;
- }
- // remember where we are
- Int here = ::ftell(m_tmp_file);
- // rewind to store the data size
- ::fseek(m_tmp_file, m_chunkStack->filepos , SEEK_SET);
- // compute data size (not including the actual data size itself)
- Int size = here - m_chunkStack->filepos - sizeof(Int);
- // store the data size
- ::fwrite( (const char *)&size, sizeof(Int) , 1, m_tmp_file );
- // go back to where we were
- ::fseek(m_tmp_file, here , SEEK_SET);
- // pop the chunk off the stack
- OutputChunk *c = m_chunkStack;
- #ifdef VERBOSE
- DEBUG_LOG(("Closing chunk %s at %d (%x)\n", m_contents.getName(c->id).str(), here, here));
- #endif
- m_chunkStack = m_chunkStack->next;
- c->deleteInstance();
- }
- void DataChunkOutput::writeReal( Real r )
- {
- ::fwrite( (const char *)&r, sizeof(float) , 1, m_tmp_file );
- }
- void DataChunkOutput::writeInt( Int i )
- {
- ::fwrite( (const char *)&i, sizeof(Int) , 1, m_tmp_file );
- }
- void DataChunkOutput::writeByte( Byte b )
- {
- ::fwrite( (const char *)&b, sizeof(Byte) , 1, m_tmp_file );
- }
- void DataChunkOutput::writeArrayOfBytes(char *ptr, Int len)
- {
- ::fwrite( (const char *)ptr, 1, len , m_tmp_file );
- }
- void DataChunkOutput::writeAsciiString( const AsciiString& theString )
- {
- UnsignedShort len = theString.getLength();
- ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
- ::fwrite( theString.str(), len , 1, m_tmp_file );
- }
- void DataChunkOutput::writeUnicodeString( UnicodeString theString )
- {
- UnsignedShort len = theString.getLength();
- ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
- ::fwrite( theString.str(), len*sizeof(WideChar) , 1, m_tmp_file );
- }
- void DataChunkOutput::writeDict( const Dict& d )
- {
- UnsignedShort len = d.getPairCount();
- ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
- for (int i = 0; i < len; i++)
- {
- NameKeyType k = d.getNthKey(i);
- AsciiString kname = TheNameKeyGenerator->keyToName(k);
- Int keyAndType = m_contents.allocateID(kname);
- keyAndType <<= 8;
- Dict::DataType t = d.getNthType(i);
- keyAndType |= (t & 0xff);
- writeInt(keyAndType);
- switch(t)
- {
- case Dict::DICT_BOOL:
- writeByte(d.getNthBool(i)?1:0);
- break;
- case Dict::DICT_INT:
- writeInt(d.getNthInt(i));
- break;
- case Dict::DICT_REAL:
- writeReal(d.getNthReal(i));
- break;
- case Dict::DICT_ASCIISTRING:
- writeAsciiString(d.getNthAsciiString(i));
- break;
- case Dict::DICT_UNICODESTRING:
- writeUnicodeString(d.getNthUnicodeString(i));
- break;
- default:
- DEBUG_CRASH(("impossible"));
- break;
- }
- }
- }
- //----------------------------------------------------------------------
- // DataChunkTableOfContents
- //----------------------------------------------------------------------
- DataChunkTableOfContents::DataChunkTableOfContents( void ) :
- m_list(NULL),
- m_nextID(1),
- m_listLength(0),
- m_headerOpened(false)
- {
- }
- DataChunkTableOfContents::~DataChunkTableOfContents()
- {
- Mapping *m, *next;
- // free all list elements
- for( m=m_list; m; m=next )
- {
- next = m->next;
- m->deleteInstance();
- }
- }
- // return mapping data
- Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
- {
- Mapping *m;
- for( m=m_list; m; m=m->next )
- if (name == m->name )
- return m;
- return NULL;
- }
- // convert name to integer identifier
- UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
- {
- Mapping *m = findMapping( name );
- if (m)
- return m->id;
- DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
- return 0;
- }
- // convert integer identifier to name
- AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
- {
- Mapping *m;
- for( m=m_list; m; m=m->next )
- if (m->id == id)
- return m->name;
- DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
- return AsciiString::TheEmptyString;
- }
- // create new ID for given name or return existing mapping
- UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
- {
- Mapping *m = findMapping( name );
- if (m)
- return m->id;
- else
- {
- // allocate new id mapping
- m = newInstance(Mapping);
- m->id = m_nextID++;
- m->name = name ;
- // prepend to list
- m->next = m_list;
- m_list = m;
- m_listLength++;
- return m->id;
- }
- }
- // output the table of m_contents to a binary m_tmp_file stream
- void DataChunkTableOfContents::write( OutputStream &s )
- {
- Mapping *m;
- unsigned char len;
- Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
- s.write(tag,sizeof(tag));
- // output number of elements in the table
- s.write( (void *)&this->m_listLength, sizeof(Int) );
- // output symbol table
- for( m=this->m_list; m; m=m->next )
- {
- len = m->name.getLength();
- s.write( (char *)&len, sizeof(unsigned char) );
- s.write( (char *)m->name.str(), len);
- s.write( (char *)&m->id, sizeof(UnsignedInt) );
- }
- }
- // read the table of m_contents from a binary m_tmp_file stream
- // TODO: Should this reset the symbol table?
- // Append symbols to table
- void DataChunkTableOfContents::read( ChunkInputStream &s)
- {
- Int count, i;
- UnsignedInt maxID = 0;
- unsigned char len;
- Mapping *m;
- Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
- s.read(tag,sizeof(tag));
- if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
- return; // Don't throw, may happen with legacy files.
- }
- // get number of symbols in table
- s.read( (char *)&count, sizeof(Int) );
- for( i=0; i<count; i++ )
- {
- // allocate new id mapping
- m = newInstance(Mapping);
- // read string length
- s.read( (char *)&len, sizeof(unsigned char) );
- // allocate and read in string
- if (len>0) {
- char *str = m->name.getBufferForRead(len);
- s.read( str, len );
- str[len] = '\000';
- }
- // read id
- s.read( (char *)&m->id, sizeof(UnsignedInt) );
- // prepend to list
- m->next = this->m_list;
- this->m_list = m;
- this->m_listLength++;
- // track max ID used
- if (m->id > maxID)
- maxID = m->id;
- }
- m_headerOpened = count > 0 && !s.eof();
- // adjust next ID so no ID's are reused
- this->m_nextID = max( this->m_nextID, maxID+1 );
- }
- //----------------------------------------------------------------------
- // DataChunkInput
- //----------------------------------------------------------------------
- DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
- m_userData(NULL),
- m_currentObject(NULL),
- m_chunkStack(NULL),
- m_parserList(NULL)
- {
- // read table of m_contents
- m_contents.read(*m_file);
- // store location of first data chunk
- m_fileposOfFirstChunk = m_file->tell();
- }
- DataChunkInput::~DataChunkInput()
- {
- clearChunkStack();
- UserParser *p, *next;
- for (p=m_parserList; p; p=next) {
- next = p->next;
- p->deleteInstance();
- }
- }
- // register a user parsing function for a given DataChunk label
- void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
- DataChunkParserPtr parser, void *userData )
- {
- UserParser *p = newInstance(UserParser);
- p->label.set( label );
- p->parentLabel.set(parentLabel );
- p->parser = parser;
- p->userData = userData;
- // prepend parser to parser list
- p->next = m_parserList;
- m_parserList = p;
- }
- // parse the chunk stream using registered parsers
- // it is assumed that the file position is at the start of a data chunk
- // (it can be inside a parent chunk) when parse is called.
- Bool DataChunkInput::parse( void *userData )
- {
- AsciiString label;
- AsciiString parentLabel;
- DataChunkVersionType ver;
- UserParser *parser;
- Bool scopeOK;
- DataChunkInfo info;
-
- // If the header wasn't a chunk table of contents, we can't parse.
- if (!m_contents.isOpenedForRead()) {
- return false;
- }
- // if we are inside a data chunk right now, get its name
- if (m_chunkStack)
- parentLabel = m_contents.getName( m_chunkStack->id );
- while( atEndOfFile() == false )
- {
- if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
- if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
- DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
- break;
- }
- }
- // open the chunk
- label = openDataChunk( &ver );
- if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
- break;
- }
- // find a registered parser for this chunk
- for( parser=m_parserList; parser; parser=parser->next )
- {
- // chunk labels must match
- if ( parser->label == label )
- {
- // make sure parent name (scope) also matches
- scopeOK = true;
- if (parentLabel != parser->parentLabel)
- scopeOK = false;
- if (scopeOK)
- {
- // m_tmp_file out the chunk info and call the user parser
- info.label = label;
- info.parentLabel = parentLabel;
- info.version = ver;
- info.dataSize = getChunkDataSize();
- if (parser->parser( *this, &info, userData ) == false)
- return false;
- break;
- }
- }
- }
- // close chunk (and skip to end if need be)
- closeDataChunk();
- }
- return true;
- }
- // clear the stack
- void DataChunkInput::clearChunkStack( void )
- {
- InputChunk *c, *next;
- for( c=m_chunkStack; c; c=next )
- {
- next = c->next;
- c->deleteInstance();
- }
- m_chunkStack = NULL;
- }
- // reset the stream to just-opened state - ready to parse the first chunk
- void DataChunkInput::reset( void )
- {
- clearChunkStack();
- m_file->absoluteSeek( m_fileposOfFirstChunk );
- }
- // Checks if the file has our initial tag word.
- Bool DataChunkInput::isValidFileType(void)
- {
- return m_contents.isOpenedForRead();
- }
- AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
- {
- // allocate a new chunk and place it on top of the chunk stack
- InputChunk *c = newInstance(InputChunk);
- c->id = 0;
- c->version = 0;
- c->dataSize = 0;
- //DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
- // read the chunk ID
- m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
- decrementDataLeft( sizeof(UnsignedInt) );
- // read the chunk version number
- m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
- decrementDataLeft( sizeof(DataChunkVersionType) );
- // read the chunk data size
- m_file->read( (char *)&c->dataSize, sizeof(Int) );
- decrementDataLeft( sizeof(Int) );
- // all of the data remains to be read
- c->dataLeft = c->dataSize;
- c->chunkStart = m_file->tell();
- *ver = c->version;
- c->next = m_chunkStack;
- m_chunkStack = c;
- if (this->atEndOfFile()) {
- return (AsciiString(""));
- }
- return m_contents.getName( c->id );
- }
- // close chunk and move to start of next chunk
- void DataChunkInput::closeDataChunk( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- return;
- }
- if (m_chunkStack->dataLeft > 0)
- {
- // skip past the remainder of this chunk
- m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
- decrementDataLeft( m_chunkStack->dataLeft );
- }
- // pop the chunk off the stack
- InputChunk *c = m_chunkStack;
- m_chunkStack = m_chunkStack->next;
- c->deleteInstance();
- }
- // return label of current data chunk
- AsciiString DataChunkInput::getChunkLabel( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- DEBUG_CRASH(("Bad."));
- return AsciiString("");
- }
- return m_contents.getName( m_chunkStack->id );
- }
- // return version of current data chunk
- DataChunkVersionType DataChunkInput::getChunkVersion( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- DEBUG_CRASH(("Bad."));
- return NULL;
- }
- return m_chunkStack->version;
- }
- // return size of data stored in this chunk
- UnsignedInt DataChunkInput::getChunkDataSize( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- DEBUG_CRASH(("Bad."));
- return NULL;
- }
- return m_chunkStack->dataSize;
- }
- // return size of data left to read in this chunk
- UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
- {
- if (m_chunkStack == NULL)
- {
- // TODO: Throw exception
- DEBUG_CRASH(("Bad."));
- return NULL;
- }
- return m_chunkStack->dataLeft;
- }
- Bool DataChunkInput::atEndOfChunk( void )
- {
- if (m_chunkStack)
- {
- if (m_chunkStack->dataLeft <= 0)
- return true;
- return false;
- }
- return true;
- }
- // update data left in chunk(s)
- // since data read from a chunk is also read from all parent chunks,
- // traverse the chunk stack and decrement the data left for each
- void DataChunkInput::decrementDataLeft( Int size )
- {
- InputChunk *c;
- c = m_chunkStack;
- while (c) {
- c->dataLeft -= size;
- c = c->next;
- }
- // The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
- }
- Real DataChunkInput::readReal(void)
- {
- Real r;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
- m_file->read( (char *)&r, sizeof(Real) );
- decrementDataLeft( sizeof(Real) );
- return r;
- }
- Int DataChunkInput::readInt(void)
- {
- Int i;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
- m_file->read( (char *)&i, sizeof(Int) );
- decrementDataLeft( sizeof(Int) );
- return i;
- }
- Byte DataChunkInput::readByte(void)
- {
- Byte b;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
- m_file->read( (char *)&b, sizeof(Byte) );
- decrementDataLeft( sizeof(Byte) );
- return b;
- }
- void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
- {
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
- m_file->read( ptr, len );
- decrementDataLeft( len );
- }
- Dict DataChunkInput::readDict()
- {
- UnsignedShort len;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
- m_file->read( &len, sizeof(UnsignedShort) );
- decrementDataLeft( sizeof(UnsignedShort) );
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
- Dict d(len);
- for (int i = 0; i < len; i++)
- {
- Int keyAndType = readInt();
- Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
- keyAndType >>= 8;
- AsciiString kname = m_contents.getName(keyAndType);
- NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
- switch(t)
- {
- case Dict::DICT_BOOL:
- d.setBool(k, readByte() ? true : false);
- break;
- case Dict::DICT_INT:
- d.setInt(k, readInt());
- break;
- case Dict::DICT_REAL:
- d.setReal(k, readReal());
- break;
- case Dict::DICT_ASCIISTRING:
- d.setAsciiString(k, readAsciiString());
- break;
- case Dict::DICT_UNICODESTRING:
- d.setUnicodeString(k, readUnicodeString());
- break;
- default:
- throw ERROR_CORRUPT_FILE_FORMAT;
- break;
- }
- }
- return d;
- }
- AsciiString DataChunkInput::readAsciiString(void)
- {
- UnsignedShort len;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
- m_file->read( &len, sizeof(UnsignedShort) );
- decrementDataLeft( sizeof(UnsignedShort) );
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
- AsciiString theString;
- if (len>0) {
- char *str = theString.getBufferForRead(len);
- m_file->read( str, len );
- decrementDataLeft( len );
- // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
- str[len] = '\000';
- }
- return theString;
- }
- UnicodeString DataChunkInput::readUnicodeString(void)
- {
- UnsignedShort len;
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
- m_file->read( &len, sizeof(UnsignedShort) );
- decrementDataLeft( sizeof(UnsignedShort) );
- DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
- UnicodeString theString;
- if (len>0) {
- WideChar *str = theString.getBufferForRead(len);
- m_file->read( (char*)str, len*sizeof(WideChar) );
- decrementDataLeft( len*sizeof(WideChar) );
- // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
- str[len] = '\000';
- }
- return theString;
- }
|