DataChunk.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  1. /*
  2. ** Command & Conquer Generals(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::writeDict( const Dict& d )
  316. {
  317. UnsignedShort len = d.getPairCount();
  318. ::fwrite( (const char *)&len, sizeof(UnsignedShort) , 1, m_tmp_file );
  319. for (int i = 0; i < len; i++)
  320. {
  321. NameKeyType k = d.getNthKey(i);
  322. AsciiString kname = TheNameKeyGenerator->keyToName(k);
  323. Int keyAndType = m_contents.allocateID(kname);
  324. keyAndType <<= 8;
  325. Dict::DataType t = d.getNthType(i);
  326. keyAndType |= (t & 0xff);
  327. writeInt(keyAndType);
  328. switch(t)
  329. {
  330. case Dict::DICT_BOOL:
  331. writeByte(d.getNthBool(i)?1:0);
  332. break;
  333. case Dict::DICT_INT:
  334. writeInt(d.getNthInt(i));
  335. break;
  336. case Dict::DICT_REAL:
  337. writeReal(d.getNthReal(i));
  338. break;
  339. case Dict::DICT_ASCIISTRING:
  340. writeAsciiString(d.getNthAsciiString(i));
  341. break;
  342. case Dict::DICT_UNICODESTRING:
  343. writeUnicodeString(d.getNthUnicodeString(i));
  344. break;
  345. default:
  346. DEBUG_CRASH(("impossible"));
  347. break;
  348. }
  349. }
  350. }
  351. //----------------------------------------------------------------------
  352. // DataChunkTableOfContents
  353. //----------------------------------------------------------------------
  354. DataChunkTableOfContents::DataChunkTableOfContents( void ) :
  355. m_list(NULL),
  356. m_nextID(1),
  357. m_listLength(0),
  358. m_headerOpened(false)
  359. {
  360. }
  361. DataChunkTableOfContents::~DataChunkTableOfContents()
  362. {
  363. Mapping *m, *next;
  364. // free all list elements
  365. for( m=m_list; m; m=next )
  366. {
  367. next = m->next;
  368. m->deleteInstance();
  369. }
  370. }
  371. // return mapping data
  372. Mapping *DataChunkTableOfContents::findMapping( const AsciiString& name )
  373. {
  374. Mapping *m;
  375. for( m=m_list; m; m=m->next )
  376. if (name == m->name )
  377. return m;
  378. return NULL;
  379. }
  380. // convert name to integer identifier
  381. UnsignedInt DataChunkTableOfContents::getID( const AsciiString& name )
  382. {
  383. Mapping *m = findMapping( name );
  384. if (m)
  385. return m->id;
  386. DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for name %s\n",name.str()));
  387. return 0;
  388. }
  389. // convert integer identifier to name
  390. AsciiString DataChunkTableOfContents::getName( UnsignedInt id )
  391. {
  392. Mapping *m;
  393. for( m=m_list; m; m=m->next )
  394. if (m->id == id)
  395. return m->name;
  396. DEBUG_CRASH(("name not found in DataChunkTableOfContents::getName for id %d\n",id));
  397. return AsciiString::TheEmptyString;
  398. }
  399. // create new ID for given name or return existing mapping
  400. UnsignedInt DataChunkTableOfContents::allocateID(const AsciiString& name )
  401. {
  402. Mapping *m = findMapping( name );
  403. if (m)
  404. return m->id;
  405. else
  406. {
  407. // allocate new id mapping
  408. m = newInstance(Mapping);
  409. m->id = m_nextID++;
  410. m->name = name ;
  411. // prepend to list
  412. m->next = m_list;
  413. m_list = m;
  414. m_listLength++;
  415. return m->id;
  416. }
  417. }
  418. // output the table of m_contents to a binary m_tmp_file stream
  419. void DataChunkTableOfContents::write( OutputStream &s )
  420. {
  421. Mapping *m;
  422. unsigned char len;
  423. Byte tag[4]={'C','k', 'M', 'p'}; // Chunky height map. jba.
  424. s.write(tag,sizeof(tag));
  425. // output number of elements in the table
  426. s.write( (void *)&this->m_listLength, sizeof(Int) );
  427. // output symbol table
  428. for( m=this->m_list; m; m=m->next )
  429. {
  430. len = m->name.getLength();
  431. s.write( (char *)&len, sizeof(unsigned char) );
  432. s.write( (char *)m->name.str(), len);
  433. s.write( (char *)&m->id, sizeof(UnsignedInt) );
  434. }
  435. }
  436. // read the table of m_contents from a binary m_tmp_file stream
  437. // TODO: Should this reset the symbol table?
  438. // Append symbols to table
  439. void DataChunkTableOfContents::read( ChunkInputStream &s)
  440. {
  441. Int count, i;
  442. UnsignedInt maxID = 0;
  443. unsigned char len;
  444. Mapping *m;
  445. Byte tag[4]={'x','x', 'x', 'x'}; // Chunky height map. jba.
  446. s.read(tag,sizeof(tag));
  447. if (tag[0] != 'C' || tag[1] != 'k' || tag[2] != 'M' || tag[3] != 'p') {
  448. return; // Don't throw, may happen with legacy files.
  449. }
  450. // get number of symbols in table
  451. s.read( (char *)&count, sizeof(Int) );
  452. for( i=0; i<count; i++ )
  453. {
  454. // allocate new id mapping
  455. m = newInstance(Mapping);
  456. // read string length
  457. s.read( (char *)&len, sizeof(unsigned char) );
  458. // allocate and read in string
  459. if (len>0) {
  460. char *str = m->name.getBufferForRead(len);
  461. s.read( str, len );
  462. str[len] = '\000';
  463. }
  464. // read id
  465. s.read( (char *)&m->id, sizeof(UnsignedInt) );
  466. // prepend to list
  467. m->next = this->m_list;
  468. this->m_list = m;
  469. this->m_listLength++;
  470. // track max ID used
  471. if (m->id > maxID)
  472. maxID = m->id;
  473. }
  474. m_headerOpened = count > 0 && !s.eof();
  475. // adjust next ID so no ID's are reused
  476. this->m_nextID = max( this->m_nextID, maxID+1 );
  477. }
  478. //----------------------------------------------------------------------
  479. // DataChunkInput
  480. //----------------------------------------------------------------------
  481. DataChunkInput::DataChunkInput( ChunkInputStream *pStream ) : m_file( pStream ),
  482. m_userData(NULL),
  483. m_currentObject(NULL),
  484. m_chunkStack(NULL),
  485. m_parserList(NULL)
  486. {
  487. // read table of m_contents
  488. m_contents.read(*m_file);
  489. // store location of first data chunk
  490. m_fileposOfFirstChunk = m_file->tell();
  491. }
  492. DataChunkInput::~DataChunkInput()
  493. {
  494. clearChunkStack();
  495. UserParser *p, *next;
  496. for (p=m_parserList; p; p=next) {
  497. next = p->next;
  498. p->deleteInstance();
  499. }
  500. }
  501. // register a user parsing function for a given DataChunk label
  502. void DataChunkInput::registerParser( const AsciiString& label, const AsciiString& parentLabel,
  503. DataChunkParserPtr parser, void *userData )
  504. {
  505. UserParser *p = newInstance(UserParser);
  506. p->label.set( label );
  507. p->parentLabel.set(parentLabel );
  508. p->parser = parser;
  509. p->userData = userData;
  510. // prepend parser to parser list
  511. p->next = m_parserList;
  512. m_parserList = p;
  513. }
  514. // parse the chunk stream using registered parsers
  515. // it is assumed that the file position is at the start of a data chunk
  516. // (it can be inside a parent chunk) when parse is called.
  517. Bool DataChunkInput::parse( void *userData )
  518. {
  519. AsciiString label;
  520. AsciiString parentLabel;
  521. DataChunkVersionType ver;
  522. UserParser *parser;
  523. Bool scopeOK;
  524. DataChunkInfo info;
  525. // If the header wasn't a chunk table of contents, we can't parse.
  526. if (!m_contents.isOpenedForRead()) {
  527. return false;
  528. }
  529. // if we are inside a data chunk right now, get its name
  530. if (m_chunkStack)
  531. parentLabel = m_contents.getName( m_chunkStack->id );
  532. while( atEndOfFile() == false )
  533. {
  534. if (m_chunkStack) { // If we are parsing chunks in a chunk, check current length.
  535. if (m_chunkStack->dataLeft < CHUNK_HEADER_BYTES) {
  536. DEBUG_ASSERTCRASH( m_chunkStack->dataLeft==0, ("Unexpected extra data in chunk."));
  537. break;
  538. }
  539. }
  540. // open the chunk
  541. label = openDataChunk( &ver );
  542. if (atEndOfFile()) { // FILE * returns eof after you read past end of file, so check.
  543. break;
  544. }
  545. // find a registered parser for this chunk
  546. for( parser=m_parserList; parser; parser=parser->next )
  547. {
  548. // chunk labels must match
  549. if ( parser->label == label )
  550. {
  551. // make sure parent name (scope) also matches
  552. scopeOK = true;
  553. if (parentLabel != parser->parentLabel)
  554. scopeOK = false;
  555. if (scopeOK)
  556. {
  557. // m_tmp_file out the chunk info and call the user parser
  558. info.label = label;
  559. info.parentLabel = parentLabel;
  560. info.version = ver;
  561. info.dataSize = getChunkDataSize();
  562. if (parser->parser( *this, &info, userData ) == false)
  563. return false;
  564. break;
  565. }
  566. }
  567. }
  568. // close chunk (and skip to end if need be)
  569. closeDataChunk();
  570. }
  571. return true;
  572. }
  573. // clear the stack
  574. void DataChunkInput::clearChunkStack( void )
  575. {
  576. InputChunk *c, *next;
  577. for( c=m_chunkStack; c; c=next )
  578. {
  579. next = c->next;
  580. c->deleteInstance();
  581. }
  582. m_chunkStack = NULL;
  583. }
  584. // reset the stream to just-opened state - ready to parse the first chunk
  585. void DataChunkInput::reset( void )
  586. {
  587. clearChunkStack();
  588. m_file->absoluteSeek( m_fileposOfFirstChunk );
  589. }
  590. // Checks if the file has our initial tag word.
  591. Bool DataChunkInput::isValidFileType(void)
  592. {
  593. return m_contents.isOpenedForRead();
  594. }
  595. AsciiString DataChunkInput::openDataChunk(DataChunkVersionType *ver )
  596. {
  597. // allocate a new chunk and place it on top of the chunk stack
  598. InputChunk *c = newInstance(InputChunk);
  599. c->id = 0;
  600. c->version = 0;
  601. c->dataSize = 0;
  602. //DEBUG_LOG(("Opening data chunk at offset %d (%x)\n", m_file->tell(), m_file->tell()));
  603. // read the chunk ID
  604. m_file->read( (char *)&c->id, sizeof(UnsignedInt) );
  605. decrementDataLeft( sizeof(UnsignedInt) );
  606. // read the chunk version number
  607. m_file->read( (char *)&c->version, sizeof(DataChunkVersionType) );
  608. decrementDataLeft( sizeof(DataChunkVersionType) );
  609. // read the chunk data size
  610. m_file->read( (char *)&c->dataSize, sizeof(Int) );
  611. decrementDataLeft( sizeof(Int) );
  612. // all of the data remains to be read
  613. c->dataLeft = c->dataSize;
  614. c->chunkStart = m_file->tell();
  615. *ver = c->version;
  616. c->next = m_chunkStack;
  617. m_chunkStack = c;
  618. if (this->atEndOfFile()) {
  619. return (AsciiString(""));
  620. }
  621. return m_contents.getName( c->id );
  622. }
  623. // close chunk and move to start of next chunk
  624. void DataChunkInput::closeDataChunk( void )
  625. {
  626. if (m_chunkStack == NULL)
  627. {
  628. // TODO: Throw exception
  629. return;
  630. }
  631. if (m_chunkStack->dataLeft > 0)
  632. {
  633. // skip past the remainder of this chunk
  634. m_file->absoluteSeek( m_file->tell()+m_chunkStack->dataLeft );
  635. decrementDataLeft( m_chunkStack->dataLeft );
  636. }
  637. // pop the chunk off the stack
  638. InputChunk *c = m_chunkStack;
  639. m_chunkStack = m_chunkStack->next;
  640. c->deleteInstance();
  641. }
  642. // return label of current data chunk
  643. AsciiString DataChunkInput::getChunkLabel( void )
  644. {
  645. if (m_chunkStack == NULL)
  646. {
  647. // TODO: Throw exception
  648. DEBUG_CRASH(("Bad."));
  649. return AsciiString("");
  650. }
  651. return m_contents.getName( m_chunkStack->id );
  652. }
  653. // return version of current data chunk
  654. DataChunkVersionType DataChunkInput::getChunkVersion( void )
  655. {
  656. if (m_chunkStack == NULL)
  657. {
  658. // TODO: Throw exception
  659. DEBUG_CRASH(("Bad."));
  660. return NULL;
  661. }
  662. return m_chunkStack->version;
  663. }
  664. // return size of data stored in this chunk
  665. UnsignedInt DataChunkInput::getChunkDataSize( void )
  666. {
  667. if (m_chunkStack == NULL)
  668. {
  669. // TODO: Throw exception
  670. DEBUG_CRASH(("Bad."));
  671. return NULL;
  672. }
  673. return m_chunkStack->dataSize;
  674. }
  675. // return size of data left to read in this chunk
  676. UnsignedInt DataChunkInput::getChunkDataSizeLeft( void )
  677. {
  678. if (m_chunkStack == NULL)
  679. {
  680. // TODO: Throw exception
  681. DEBUG_CRASH(("Bad."));
  682. return NULL;
  683. }
  684. return m_chunkStack->dataLeft;
  685. }
  686. Bool DataChunkInput::atEndOfChunk( void )
  687. {
  688. if (m_chunkStack)
  689. {
  690. if (m_chunkStack->dataLeft <= 0)
  691. return true;
  692. return false;
  693. }
  694. return true;
  695. }
  696. // update data left in chunk(s)
  697. // since data read from a chunk is also read from all parent chunks,
  698. // traverse the chunk stack and decrement the data left for each
  699. void DataChunkInput::decrementDataLeft( Int size )
  700. {
  701. InputChunk *c;
  702. c = m_chunkStack;
  703. while (c) {
  704. c->dataLeft -= size;
  705. c = c->next;
  706. }
  707. // The sizes of the parent chunks on the stack are adjusted in closeDataChunk.
  708. }
  709. Real DataChunkInput::readReal(void)
  710. {
  711. Real r;
  712. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Real), ("Read past end of chunk."));
  713. m_file->read( (char *)&r, sizeof(Real) );
  714. decrementDataLeft( sizeof(Real) );
  715. return r;
  716. }
  717. Int DataChunkInput::readInt(void)
  718. {
  719. Int i;
  720. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Int), ("Read past end of chunk."));
  721. m_file->read( (char *)&i, sizeof(Int) );
  722. decrementDataLeft( sizeof(Int) );
  723. return i;
  724. }
  725. Byte DataChunkInput::readByte(void)
  726. {
  727. Byte b;
  728. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(Byte), ("Read past end of chunk."));
  729. m_file->read( (char *)&b, sizeof(Byte) );
  730. decrementDataLeft( sizeof(Byte) );
  731. return b;
  732. }
  733. void DataChunkInput::readArrayOfBytes(char *ptr, Int len)
  734. {
  735. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  736. m_file->read( ptr, len );
  737. decrementDataLeft( len );
  738. }
  739. Dict DataChunkInput::readDict()
  740. {
  741. UnsignedShort len;
  742. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  743. m_file->read( &len, sizeof(UnsignedShort) );
  744. decrementDataLeft( sizeof(UnsignedShort) );
  745. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  746. Dict d(len);
  747. for (int i = 0; i < len; i++)
  748. {
  749. Int keyAndType = readInt();
  750. Dict::DataType t = (Dict::DataType)(keyAndType & 0xff);
  751. keyAndType >>= 8;
  752. AsciiString kname = m_contents.getName(keyAndType);
  753. NameKeyType k = TheNameKeyGenerator->nameToKey(kname);
  754. switch(t)
  755. {
  756. case Dict::DICT_BOOL:
  757. d.setBool(k, readByte() ? true : false);
  758. break;
  759. case Dict::DICT_INT:
  760. d.setInt(k, readInt());
  761. break;
  762. case Dict::DICT_REAL:
  763. d.setReal(k, readReal());
  764. break;
  765. case Dict::DICT_ASCIISTRING:
  766. d.setAsciiString(k, readAsciiString());
  767. break;
  768. case Dict::DICT_UNICODESTRING:
  769. d.setUnicodeString(k, readUnicodeString());
  770. break;
  771. default:
  772. throw ERROR_CORRUPT_FILE_FORMAT;
  773. break;
  774. }
  775. }
  776. return d;
  777. }
  778. AsciiString DataChunkInput::readAsciiString(void)
  779. {
  780. UnsignedShort len;
  781. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  782. m_file->read( &len, sizeof(UnsignedShort) );
  783. decrementDataLeft( sizeof(UnsignedShort) );
  784. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  785. AsciiString theString;
  786. if (len>0) {
  787. char *str = theString.getBufferForRead(len);
  788. m_file->read( str, len );
  789. decrementDataLeft( len );
  790. // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
  791. str[len] = '\000';
  792. }
  793. return theString;
  794. }
  795. UnicodeString DataChunkInput::readUnicodeString(void)
  796. {
  797. UnsignedShort len;
  798. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=sizeof(UnsignedShort), ("Read past end of chunk."));
  799. m_file->read( &len, sizeof(UnsignedShort) );
  800. decrementDataLeft( sizeof(UnsignedShort) );
  801. DEBUG_ASSERTCRASH(m_chunkStack->dataLeft>=len, ("Read past end of chunk."));
  802. UnicodeString theString;
  803. if (len>0) {
  804. WideChar *str = theString.getBufferForRead(len);
  805. m_file->read( (char*)str, len*sizeof(WideChar) );
  806. decrementDataLeft( len*sizeof(WideChar) );
  807. // add null delimiter to string. Note that getBufferForRead allocates space for terminating null.
  808. str[len] = '\000';
  809. }
  810. return theString;
  811. }