DataChunk.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // DataChunk.cpp
  24. // Implementation of Data Chunk save/load system
  25. // Author: Michael S. Booth, October 2000
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "stdlib.h"
  28. #include "string.h"
  29. #include "Compression.h"
  30. #include "Common/DataChunk.h"
  31. #include "Common/File.h"
  32. #include "Common/FileSystem.h"
  33. // If verbose, lots of debug logging.
  34. #define not_VERBOSE
  35. CachedFileInputStream::CachedFileInputStream(void):m_buffer(NULL),m_size(0)
  36. {
  37. }
  38. CachedFileInputStream::~CachedFileInputStream(void)
  39. {
  40. if (m_buffer) {
  41. delete[] m_buffer;
  42. m_buffer=NULL;
  43. }
  44. }
  45. Bool CachedFileInputStream::open(AsciiString path)
  46. {
  47. File *file=TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
  48. m_size = 0;
  49. if (file) {
  50. m_size=file->size();
  51. if (m_size) {
  52. m_buffer = file->readEntireAndClose();
  53. file = NULL;
  54. }
  55. m_pos=0;
  56. }
  57. if (CompressionManager::isDataCompressed(m_buffer, m_size) == 0)
  58. {
  59. //DEBUG_LOG(("CachedFileInputStream::open() - file %s is uncompressed at %d bytes!\n", path.str(), m_size));
  60. }
  61. else
  62. {
  63. Int uncompLen = CompressionManager::getUncompressedSize(m_buffer, m_size);
  64. //DEBUG_LOG(("CachedFileInputStream::open() - file %s is compressed! It should go from %d to %d\n", path.str(),
  65. // m_size, uncompLen));
  66. char *uncompBuffer = NEW char[uncompLen];
  67. Int actualLen = CompressionManager::decompressData(m_buffer, m_size, uncompBuffer, uncompLen);
  68. if (actualLen == uncompLen)
  69. {
  70. //DEBUG_LOG(("Using uncompressed data\n"));
  71. delete[] m_buffer;
  72. m_buffer = uncompBuffer;
  73. m_size = uncompLen;
  74. }
  75. else
  76. {
  77. //DEBUG_LOG(("Decompression failed - using compressed data\n"));
  78. // decompression failed. Maybe we invalidly thought it was compressed?
  79. delete[] uncompBuffer;
  80. }
  81. }
  82. //if (m_size >= 4)
  83. //{
  84. // DEBUG_LOG(("File starts as '%c%c%c%c'\n", m_buffer[0], m_buffer[1],
  85. // m_buffer[2], m_buffer[3]));
  86. //}
  87. if (file)
  88. {
  89. file->close();
  90. }
  91. return m_size != 0;
  92. }
  93. void CachedFileInputStream::close(void)
  94. {
  95. if (m_buffer) {
  96. delete[] m_buffer;
  97. m_buffer=NULL;
  98. }
  99. m_pos=0;
  100. m_size=0;
  101. }
  102. Int CachedFileInputStream::read(void *pData, Int numBytes)
  103. {
  104. if (m_buffer) {
  105. if ((numBytes+m_pos)>m_size) {
  106. numBytes=m_size-m_pos;
  107. }
  108. if (numBytes) {
  109. memcpy(pData,m_buffer+m_pos,numBytes);
  110. m_pos+=numBytes;
  111. }
  112. return(numBytes);
  113. }
  114. return 0;
  115. }
  116. UnsignedInt CachedFileInputStream::tell(void)
  117. {
  118. return m_pos;
  119. }
  120. Bool CachedFileInputStream::absoluteSeek(UnsignedInt pos)
  121. {
  122. if (pos<0) return false;
  123. if (pos>m_size) {
  124. pos=m_size;
  125. }
  126. m_pos=pos;
  127. return true;
  128. }
  129. Bool CachedFileInputStream::eof(void)
  130. {
  131. return m_size==m_pos;
  132. }
  133. void CachedFileInputStream::rewind()
  134. {
  135. m_pos=0;
  136. }
  137. // -----------------------------------------------------------
  138. //
  139. // FileInputStream - helper class. Used to read in data using a FILE *
  140. //
  141. /*
  142. FileInputStream::FileInputStream(void):m_file(NULL)
  143. {
  144. }
  145. FileInputStream::~FileInputStream(void)
  146. {
  147. if (m_file != NULL) {
  148. m_file->close();
  149. m_file = NULL;
  150. }
  151. }
  152. Bool FileInputStream::open(AsciiString path)
  153. {
  154. m_file = TheFileSystem->openFile(path.str(), File::READ | File::BINARY);
  155. return m_file==NULL?false:true;
  156. }
  157. void FileInputStream::close(void)
  158. {
  159. if (m_file != NULL) {
  160. m_file->close();
  161. m_file = NULL;
  162. }
  163. }
  164. Int FileInputStream::read(void *pData, Int numBytes)
  165. {
  166. int bytesRead = 0;
  167. if (m_file != NULL) {
  168. bytesRead = m_file->read(pData, numBytes);
  169. }
  170. return(bytesRead);
  171. }
  172. UnsignedInt FileInputStream::tell(void)
  173. {
  174. UnsignedInt pos = 0;
  175. if (m_file != NULL) {
  176. pos = m_file->position();
  177. }
  178. return(pos);
  179. }
  180. Bool FileInputStream::absoluteSeek(UnsignedInt pos)
  181. {
  182. if (m_file != NULL) {
  183. return (m_file->seek(pos, File::START) != -1);
  184. }
  185. return(false);
  186. }
  187. Bool FileInputStream::eof(void)
  188. {
  189. if (m_file != NULL) {
  190. return (m_file->size() == m_file->position());
  191. }
  192. return(true);
  193. }
  194. void FileInputStream::rewind()
  195. {
  196. if (m_file != NULL) {
  197. m_file->seek(0, File::START);
  198. }
  199. }
  200. */
  201. //----------------------------------------------------------------------
  202. // DataChunkOutput
  203. // Data will be stored to a temporary m_tmp_file until the DataChunkOutput
  204. // object is destroyed. At that time, the actual output m_tmp_file will
  205. // be written, including a table of m_contents.
  206. //----------------------------------------------------------------------
  207. #define TEMP_FILENAME "_tmpChunk.dat"
  208. DataChunkOutput::DataChunkOutput( OutputStream *pOut ) :
  209. m_pOut(pOut)
  210. {
  211. AsciiString tmpFileName = TheGlobalData->getPath_UserData();
  212. tmpFileName.concat(TEMP_FILENAME);
  213. m_tmp_file = ::fopen( tmpFileName.str(), "wb" );
  214. // Added Sadullah Nader
  215. // Initializations missing and needed
  216. m_chunkStack = NULL;
  217. // End Add
  218. }
  219. DataChunkOutput::~DataChunkOutput()
  220. {
  221. // store the table of m_contents
  222. m_contents.write(*m_pOut);
  223. // Rewind the temp m_tmp_file
  224. ::fclose(m_tmp_file);
  225. AsciiString tmpFileName = TheGlobalData->getPath_UserData();
  226. tmpFileName.concat(TEMP_FILENAME);
  227. m_tmp_file = ::fopen( tmpFileName.str(), "rb" );
  228. ::fseek(m_tmp_file, 0, SEEK_SET);
  229. // append the temp m_tmp_file m_contents
  230. char buffer[256];
  231. int len = 256;
  232. while( len == 256 )
  233. {
  234. // copy data from the temp m_tmp_file to the output m_tmp_file
  235. len = ::fread( buffer, 1, 256, m_tmp_file );
  236. m_pOut->write( buffer, len );
  237. }
  238. ::fclose(m_tmp_file);
  239. }
  240. void DataChunkOutput::openDataChunk( char *name, DataChunkVersionType ver )
  241. {
  242. // allocate (or get existing) ID from the table of m_contents
  243. UnsignedInt id = m_contents.allocateID( AsciiString(name) );
  244. // allocate a new chunk and place it on top of the chunk stack
  245. OutputChunk *c = newInstance(OutputChunk);
  246. c->next = m_chunkStack;
  247. m_chunkStack = c;
  248. m_chunkStack->id = id;
  249. // store the chunk ID
  250. ::fwrite( (const char *)&id, sizeof(UnsignedInt), 1, m_tmp_file );
  251. // store the chunk version number
  252. ::fwrite( (const char *)&ver, sizeof(DataChunkVersionType), 1, m_tmp_file );
  253. // remember this m_tmp_file position so we can write the real data size later
  254. c->filepos = ::ftell(m_tmp_file);
  255. #ifdef VERBOSE
  256. DEBUG_LOG(("Writing chunk %s at %d (%x)\n", name, ::ftell(m_tmp_file), ::ftell(m_tmp_file)));
  257. #endif
  258. // store a placeholder for the data size
  259. Int dummy = 0xffff;
  260. ::fwrite( (const char *)&dummy, sizeof(Int), 1, m_tmp_file );
  261. }
  262. void DataChunkOutput::closeDataChunk( void )
  263. {
  264. if (m_chunkStack == NULL)
  265. {
  266. // TODO: Throw exception
  267. return;
  268. }
  269. // remember where we are
  270. Int here = ::ftell(m_tmp_file);
  271. // rewind to store the data size
  272. ::fseek(m_tmp_file, m_chunkStack->filepos , SEEK_SET);
  273. // compute data size (not including the actual data size itself)
  274. Int size = here - m_chunkStack->filepos - sizeof(Int);
  275. // store the data size
  276. ::fwrite( (const char *)&size, sizeof(Int) , 1, m_tmp_file );
  277. // go back to where we were
  278. ::fseek(m_tmp_file, here , SEEK_SET);
  279. // pop the chunk off the stack
  280. OutputChunk *c = m_chunkStack;
  281. #ifdef VERBOSE
  282. DEBUG_LOG(("Closing chunk %s at %d (%x)\n", m_contents.getName(c->id).str(), here, here));
  283. #endif
  284. m_chunkStack = m_chunkStack->next;
  285. c->deleteInstance();
  286. }
  287. void DataChunkOutput::writeReal( Real r )
  288. {
  289. ::fwrite( (const char *)&r, sizeof(float) , 1, m_tmp_file );
  290. }
  291. void DataChunkOutput::writeInt( Int i )
  292. {
  293. ::fwrite( (const char *)&i, sizeof(Int) , 1, m_tmp_file );
  294. }
  295. void DataChunkOutput::writeByte( Byte b )
  296. {
  297. ::fwrite( (const char *)&b, sizeof(Byte) , 1, m_tmp_file );
  298. }
  299. void DataChunkOutput::writeArrayOfBytes(char *ptr, Int len)
  300. {
  301. ::fwrite( (const char *)ptr, 1, len , m_tmp_file );
  302. }
  303. void DataChunkOutput::writeAsciiString( const AsciiString& theString )
  304. {
  305. UnsignedShort len = theString.getLength();
  306. ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
  307. ::fwrite( theString.str(), len , 1, m_tmp_file );
  308. }
  309. void DataChunkOutput::writeUnicodeString( UnicodeString theString )
  310. {
  311. UnsignedShort len = theString.getLength();
  312. ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
  313. ::fwrite( theString.str(), len*sizeof(WideChar) , 1, m_tmp_file );
  314. }
  315. void DataChunkOutput::writeNameKey( const NameKeyType key )
  316. {
  317. AsciiString kname = TheNameKeyGenerator->keyToName(key);
  318. Int keyAndType = m_contents.allocateID(kname);
  319. keyAndType <<= 8;
  320. Dict::DataType t = Dict::DICT_ASCIISTRING;
  321. keyAndType |= (t & 0xff);
  322. writeInt(keyAndType);
  323. }
  324. void DataChunkOutput::writeDict( const Dict& d )
  325. {
  326. UnsignedShort len = d.getPairCount();
  327. ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
  328. for (int i = 0; i < len; i++)
  329. {
  330. NameKeyType k = d.getNthKey(i);
  331. AsciiString kname = TheNameKeyGenerator->keyToName(k);
  332. Int keyAndType = m_contents.allocateID(kname);
  333. keyAndType <<= 8;
  334. Dict::DataType t = d.getNthType(i);
  335. keyAndType |= (t & 0xff);
  336. writeInt(keyAndType);
  337. switch(t)
  338. {
  339. case Dict::DICT_BOOL:
  340. writeByte(d.getNthBool(i)?1:0);
  341. break;
  342. case Dict::DICT_INT:
  343. writeInt(d.getNthInt(i));
  344. break;
  345. case Dict::DICT_REAL:
  346. writeReal(d.getNthReal(i));
  347. break;
  348. case Dict::DICT_ASCIISTRING:
  349. writeAsciiString(d.getNthAsciiString(i));
  350. break;
  351. case Dict::DICT_UNICODESTRING:
  352. writeUnicodeString(d.getNthUnicodeString(i));
  353. break;
  354. default:
  355. DEBUG_CRASH(("impossible"));
  356. break;
  357. }
  358. }
  359. }
  360. //----------------------------------------------------------------------
  361. // DataChunkTableOfContents
  362. //----------------------------------------------------------------------
  363. DataChunkTableOfContents::DataChunkTableOfContents( void ) :
  364. m_list(NULL),
  365. m_nextID(1),
  366. m_listLength(0),
  367. m_headerOpened(false)
  368. {
  369. }
  370. DataChunkTableOfContents::~DataChunkTableOfContents()
  371. {
  372. Mapping *m, *next;
  373. // free all list elements
  374. for( m=m_list; m; m=next )
  375. {
  376. next = m->next;
  377. m->deleteInstance();
  378. }
  379. }
  380. // return mapping data
  381. Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
  382. {
  383. Mapping *m;
  384. for( m=m_list; m; m=m->next )
  385. if (name == m->name )
  386. return m;
  387. return NULL;
  388. }
  389. // convert name to integer identifier
  390. UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
  391. {
  392. Mapping *m = findMapping( name );
  393. if (m)
  394. return m->id;
  395. DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
  396. return 0;
  397. }
  398. // convert integer identifier to name
  399. AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
  400. {
  401. Mapping *m;
  402. for( m=m_list; m; m=m->next )
  403. if (m->id == id)
  404. return m->name;
  405. DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
  406. return AsciiString::TheEmptyString;
  407. }
  408. // create new ID for given name or return existing mapping
  409. UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
  410. {
  411. Mapping *m = findMapping( name );
  412. if (m)
  413. return m->id;
  414. else
  415. {
  416. // allocate new id mapping
  417. m = newInstance(Mapping);
  418. m->id = m_nextID++;
  419. m->name = name ;
  420. // prepend to list
  421. m->next = m_list;
  422. m_list = m;
  423. m_listLength++;
  424. return m->id;
  425. }
  426. }
  427. // output the table of m_contents to a binary m_tmp_file stream
  428. void DataChunkTableOfContents::write( OutputStream &s )
  429. {
  430. Mapping *m;
  431. unsigned char len;
  432. Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
  433. s.write(tag,sizeof(tag));
  434. // output number of elements in the table
  435. s.write( (void *)&this->m_listLength, sizeof(Int) );
  436. // output symbol table
  437. for( m=this->m_list; m; m=m->next )
  438. {
  439. len = m->name.getLength();
  440. s.write( (char *)&len, sizeof(unsigned char) );
  441. s.write( (char *)m->name.str(), len);
  442. s.write( (char *)&m->id, sizeof(UnsignedInt) );
  443. }
  444. }
  445. // read the table of m_contents from a binary m_tmp_file stream
  446. // TODO: Should this reset the symbol table?
  447. // Append symbols to table
  448. void DataChunkTableOfContents::read( ChunkInputStream &s)
  449. {
  450. Int count, i;
  451. UnsignedInt maxID = 0;
  452. unsigned char len;
  453. Mapping *m;
  454. Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
  455. s.read(tag,sizeof(tag));
  456. if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
  457. return; // Don't throw, may happen with legacy files.
  458. }
  459. // get number of symbols in table
  460. s.read( (char *)&count, sizeof(Int) );
  461. for( i=0; i<count; i++ )
  462. {
  463. // allocate new id mapping
  464. m = newInstance(Mapping);
  465. // read string length
  466. s.read( (char *)&len, sizeof(unsigned char) );
  467. // allocate and read in string
  468. if (len>0) {
  469. char *str = m->name.getBufferForRead(len);
  470. s.read( str, len );
  471. str[len] = '\000';
  472. }
  473. // read id
  474. s.read( (char *)&m->id, sizeof(UnsignedInt) );
  475. // prepend to list
  476. m->next = this->m_list;
  477. this->m_list = m;
  478. this->m_listLength++;
  479. // track max ID used
  480. if (m->id > maxID)
  481. maxID = m->id;
  482. }
  483. m_headerOpened = count > 0 && !s.eof();
  484. // adjust next ID so no ID's are reused
  485. this->m_nextID = max( this->m_nextID, maxID+1 );
  486. }
  487. //----------------------------------------------------------------------
  488. // DataChunkInput
  489. //----------------------------------------------------------------------
  490. DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
  491. m_userData(NULL),
  492. m_currentObject(NULL),
  493. m_chunkStack(NULL),
  494. m_parserList(NULL)
  495. {
  496. // read table of m_contents
  497. m_contents.read(*m_file);
  498. // store location of first data chunk
  499. m_fileposOfFirstChunk = m_file->tell();
  500. }
  501. DataChunkInput::~DataChunkInput()
  502. {
  503. clearChunkStack();
  504. UserParser *p, *next;
  505. for (p=m_parserList; p; p=next) {
  506. next = p->next;
  507. p->deleteInstance();
  508. }
  509. }
  510. // register a user parsing function for a given DataChunk label
  511. void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
  512. DataChunkParserPtr parser, void *userData )
  513. {
  514. UserParser *p = newInstance(UserParser);
  515. p->label.set( label );
  516. p->parentLabel.set(parentLabel );
  517. p->parser = parser;
  518. p->userData = userData;
  519. // prepend parser to parser list
  520. p->next = m_parserList;
  521. m_parserList = p;
  522. }
  523. // parse the chunk stream using registered parsers
  524. // it is assumed that the file position is at the start of a data chunk
  525. // (it can be inside a parent chunk) when parse is called.
  526. Bool DataChunkInput::parse( void *userData )
  527. {
  528. AsciiString label;
  529. AsciiString parentLabel;
  530. DataChunkVersionType ver;
  531. UserParser *parser;
  532. Bool scopeOK;
  533. DataChunkInfo info;
  534. // If the header wasn't a chunk table of contents, we can't parse.
  535. if (!m_contents.isOpenedForRead()) {
  536. return false;
  537. }
  538. // if we are inside a data chunk right now, get its name
  539. if (m_chunkStack)
  540. parentLabel = m_contents.getName( m_chunkStack->id );
  541. while( atEndOfFile() == false )
  542. {
  543. if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
  544. if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
  545. DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
  546. break;
  547. }
  548. }
  549. // open the chunk
  550. label = openDataChunk( &ver );
  551. if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
  552. break;
  553. }
  554. // find a registered parser for this chunk
  555. for( parser=m_parserList; parser; parser=parser->next )
  556. {
  557. // chunk labels must match
  558. if ( parser->label == label )
  559. {
  560. // make sure parent name (scope) also matches
  561. scopeOK = true;
  562. if (parentLabel != parser->parentLabel)
  563. scopeOK = false;
  564. if (scopeOK)
  565. {
  566. // m_tmp_file out the chunk info and call the user parser
  567. info.label = label;
  568. info.parentLabel = parentLabel;
  569. info.version = ver;
  570. info.dataSize = getChunkDataSize();
  571. if (parser->parser( *this, &info, userData ) == false)
  572. return false;
  573. break;
  574. }
  575. }
  576. }
  577. // close chunk (and skip to end if need be)
  578. closeDataChunk();
  579. }
  580. return true;
  581. }
  582. // clear the stack
  583. void DataChunkInput::clearChunkStack( void )
  584. {
  585. InputChunk *c, *next;
  586. for( c=m_chunkStack; c; c=next )
  587. {
  588. next = c->next;
  589. c->deleteInstance();
  590. }
  591. m_chunkStack = NULL;
  592. }
  593. // reset the stream to just-opened state - ready to parse the first chunk
  594. void DataChunkInput::reset( void )
  595. {
  596. clearChunkStack();
  597. m_file->absoluteSeek( m_fileposOfFirstChunk );
  598. }
  599. // Checks if the file has our initial tag word.
  600. Bool DataChunkInput::isValidFileType(void)
  601. {
  602. return m_contents.isOpenedForRead();
  603. }
  604. AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
  605. {
  606. // allocate a new chunk and place it on top of the chunk stack
  607. InputChunk *c = newInstance(InputChunk);
  608. c->id = 0;
  609. c->version = 0;
  610. c->dataSize = 0;
  611. //DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
  612. // read the chunk ID
  613. m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
  614. decrementDataLeft( sizeof(UnsignedInt) );
  615. // read the chunk version number
  616. m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
  617. decrementDataLeft( sizeof(DataChunkVersionType) );
  618. // read the chunk data size
  619. m_file->read( (char *)&c->dataSize, sizeof(Int) );
  620. decrementDataLeft( sizeof(Int) );
  621. // all of the data remains to be read
  622. c->dataLeft = c->dataSize;
  623. c->chunkStart = m_file->tell();
  624. *ver = c->version;
  625. c->next = m_chunkStack;
  626. m_chunkStack = c;
  627. if (this->atEndOfFile()) {
  628. return (AsciiString(""));
  629. }
  630. return m_contents.getName( c->id );
  631. }
  632. // close chunk and move to start of next chunk
  633. void DataChunkInput::closeDataChunk( void )
  634. {
  635. if (m_chunkStack == NULL)
  636. {
  637. // TODO: Throw exception
  638. return;
  639. }
  640. if (m_chunkStack->dataLeft > 0)
  641. {
  642. // skip past the remainder of this chunk
  643. m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
  644. decrementDataLeft( m_chunkStack->dataLeft );
  645. }
  646. // pop the chunk off the stack
  647. InputChunk *c = m_chunkStack;
  648. m_chunkStack = m_chunkStack->next;
  649. c->deleteInstance();
  650. }
  651. // return label of current data chunk
  652. AsciiString DataChunkInput::getChunkLabel( void )
  653. {
  654. if (m_chunkStack == NULL)
  655. {
  656. // TODO: Throw exception
  657. DEBUG_CRASH(("Bad."));
  658. return AsciiString("");
  659. }
  660. return m_contents.getName( m_chunkStack->id );
  661. }
  662. // return version of current data chunk
  663. DataChunkVersionType DataChunkInput::getChunkVersion( void )
  664. {
  665. if (m_chunkStack == NULL)
  666. {
  667. // TODO: Throw exception
  668. DEBUG_CRASH(("Bad."));
  669. return NULL;
  670. }
  671. return m_chunkStack->version;
  672. }
  673. // return size of data stored in this chunk
  674. UnsignedInt DataChunkInput::getChunkDataSize( void )
  675. {
  676. if (m_chunkStack == NULL)
  677. {
  678. // TODO: Throw exception
  679. DEBUG_CRASH(("Bad."));
  680. return NULL;
  681. }
  682. return m_chunkStack->dataSize;
  683. }
  684. // return size of data left to read in this chunk
  685. UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
  686. {
  687. if (m_chunkStack == NULL)
  688. {
  689. // TODO: Throw exception
  690. DEBUG_CRASH(("Bad."));
  691. return NULL;
  692. }
  693. return m_chunkStack->dataLeft;
  694. }
  695. Bool DataChunkInput::atEndOfChunk( void )
  696. {
  697. if (m_chunkStack)
  698. {
  699. if (m_chunkStack->dataLeft <= 0)
  700. return true;
  701. return false;
  702. }
  703. return true;
  704. }
  705. // update data left in chunk(s)
  706. // since data read from a chunk is also read from all parent chunks,
  707. // traverse the chunk stack and decrement the data left for each
  708. void DataChunkInput::decrementDataLeft( Int size )
  709. {
  710. InputChunk *c;
  711. c = m_chunkStack;
  712. while (c) {
  713. c->dataLeft -= size;
  714. c = c->next;
  715. }
  716. // The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
  717. }
  718. Real DataChunkInput::readReal(void)
  719. {
  720. Real r;
  721. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
  722. m_file->read( (char *)&r, sizeof(Real) );
  723. decrementDataLeft( sizeof(Real) );
  724. return r;
  725. }
  726. Int DataChunkInput::readInt(void)
  727. {
  728. Int i;
  729. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
  730. m_file->read( (char *)&i, sizeof(Int) );
  731. decrementDataLeft( sizeof(Int) );
  732. return i;
  733. }
  734. Byte DataChunkInput::readByte(void)
  735. {
  736. Byte b;
  737. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
  738. m_file->read( (char *)&b, sizeof(Byte) );
  739. decrementDataLeft( sizeof(Byte) );
  740. return b;
  741. }
  742. void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
  743. {
  744. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  745. m_file->read( ptr, len );
  746. decrementDataLeft( len );
  747. }
  748. NameKeyType DataChunkInput::readNameKey(void)
  749. {
  750. Int keyAndType = readInt();
  751. #if (defined(_DEBUG) || defined(_INTERNAL))
  752. Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
  753. DEBUG_ASSERTCRASH(t==Dict::DICT_ASCIISTRING,("Invalid key data."));
  754. #endif
  755. keyAndType >>= 8;
  756. AsciiString kname = m_contents.getName(keyAndType);
  757. NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
  758. return k;
  759. }
  760. Dict DataChunkInput::readDict()
  761. {
  762. UnsignedShort len;
  763. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  764. m_file->read( &len, sizeof(UnsignedShort) );
  765. decrementDataLeft( sizeof(UnsignedShort) );
  766. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  767. Dict d(len);
  768. for (int i = 0; i < len; i++)
  769. {
  770. Int keyAndType = readInt();
  771. Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
  772. keyAndType >>= 8;
  773. AsciiString kname = m_contents.getName(keyAndType);
  774. NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
  775. switch(t)
  776. {
  777. case Dict::DICT_BOOL:
  778. d.setBool(k, readByte() ? true : false);
  779. break;
  780. case Dict::DICT_INT:
  781. d.setInt(k, readInt());
  782. break;
  783. case Dict::DICT_REAL:
  784. d.setReal(k, readReal());
  785. break;
  786. case Dict::DICT_ASCIISTRING:
  787. d.setAsciiString(k, readAsciiString());
  788. break;
  789. case Dict::DICT_UNICODESTRING:
  790. d.setUnicodeString(k, readUnicodeString());
  791. break;
  792. default:
  793. throw ERROR_CORRUPT_FILE_FORMAT;
  794. break;
  795. }
  796. }
  797. return d;
  798. }
  799. AsciiString DataChunkInput::readAsciiString(void)
  800. {
  801. UnsignedShort len;
  802. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  803. m_file->read( &len, sizeof(UnsignedShort) );
  804. decrementDataLeft( sizeof(UnsignedShort) );
  805. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  806. AsciiString theString;
  807. if (len>0) {
  808. char *str = theString.getBufferForRead(len);
  809. m_file->read( str, len );
  810. decrementDataLeft( len );
  811. // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
  812. str[len] = '\000';
  813. }
  814. return theString;
  815. }
  816. UnicodeString DataChunkInput::readUnicodeString(void)
  817. {
  818. UnsignedShort len;
  819. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  820. m_file->read( &len, sizeof(UnsignedShort) );
  821. decrementDataLeft( sizeof(UnsignedShort) );
  822. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  823. UnicodeString theString;
  824. if (len>0) {
  825. WideChar *str = theString.getBufferForRead(len);
  826. m_file->read( (char*)str, len*sizeof(WideChar) );
  827. decrementDataLeft( len*sizeof(WideChar) );
  828. // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
  829. str[len] = '\000';
  830. }
  831. return theString;
  832. }