tagblock.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /*
  2. ** Command & Conquer Renegade(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. /* $Header: /G/wwlib/tagblock.cpp 5 11/30/99 3:46p Scott_b $ */
  19. /***********************************************************************************************
  20. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : WWLib *
  24. * *
  25. * $Archive:: /G/wwlib/tagblock.cpp $*
  26. * *
  27. * $Author:: Scott_b $*
  28. * *
  29. * $Modtime:: 11/29/99 6:42p $*
  30. * *
  31. * $Revision:: 5 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * TagBlockFile::TagBlockFile -- Create/open tag file *
  36. * TagBlockFile::~TagBlockFile -- Close down the tag file. *
  37. * TagBlockFile::Create_Index -- Create a index into the IndexList sorted by CRC. *
  38. * TagBlockFile::Find_Block -- Find block assocated with name. *
  39. * TagBlockFile::Open_Tag -- Open an existing tag block. *
  40. * TagBlockFile::Create_Tag -- Create a new tag at the end of the block. *
  41. * TagBlockFile::Close_Tag -- Close the handle that Create or Open made. *
  42. * TagBlockFile::Destroy_Handle -- Shut down a handle. *
  43. * TagBlockFile::End_Write_Access -- Stop write access for handle - flushes data bug keeps ha*
  44. * TagBlockFile::Reset_File -- Clear file so no blocks exist. *
  45. * TagBlockFile::Empty_Index_List -- Clear out tag block list in memory *
  46. *---------------------------------------------------------------------------------------------*
  47. * TagBlockHandle::Write -- Write data to the block. *
  48. * TagBlockHandle::Read -- Read from a tag block. *
  49. * TagBlockHandle::Seek -- Seek within the file. *
  50. * TagBlockHandle::~TagBlockHandle -- Destroy handle. *
  51. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  52. #include "tagblock.h"
  53. #include "realcrc.h"
  54. #include <assert.h>
  55. int TagBlockHandle::_InDestructor = 0;
  56. ////////////////////////////////////////////////////////////////////////////////////////////////////
  57. ///////////////////////////////////// Start of TagBlockIndex ///////////////////////////////////////
  58. class TagBlockIndex
  59. {
  60. public:
  61. TagBlockIndex(const char *tagname, int blockoffset):
  62. CRC(CRC_Stringi(tagname)),
  63. BlockOffset(blockoffset),
  64. DataOffset(TagBlockFile::Calc_Data_Offset(blockoffset, tagname))
  65. {}
  66. unsigned Get_CRC() {
  67. return(CRC);
  68. }
  69. int Get_BlockOffset() {
  70. return(BlockOffset);
  71. }
  72. int Get_TagOffset() {
  73. return(TagBlockFile::Calc_Tag_Offset(BlockOffset));
  74. }
  75. int Get_TagSize() {
  76. return(DataOffset - Get_TagOffset());
  77. }
  78. int Get_DataOffset() {
  79. return(DataOffset);
  80. }
  81. private:
  82. // The index file is sorted by the CRC of the file name for
  83. // quicker retrieval. The filename is saved in the texture file.
  84. unsigned long CRC;
  85. // Start of the block - this is the start of TagBlockFile::BlockHeader.
  86. int BlockOffset;
  87. // Offset of block inside of TagFile.
  88. // This is first byte after header and TagName.
  89. // It is actual data used by external methods.
  90. int DataOffset;
  91. };
  92. ///////////////////////////////////// End of TagBlockIndex /////////////////////////////////////////
  93. ////////////////////////////////////////////////////////////////////////////////////////////////////
  94. /////////////////////////////////// Start of TagBlockHandle/////////////////////////////////////////
  95. /***********************************************************************************************
  96. * RawFileClass -- Open up the tag file (it may not exist). *
  97. * *
  98. * INPUT: *
  99. * const char *fname - name of file that is or wants to be a TagBlockFile. *
  100. * *
  101. * OUTPUT: *
  102. * Will assume a file that has invalid data to be corrupt and will write over it. *
  103. * So don't pass in a file that is not a tag file. *
  104. * *
  105. * WARNINGS: *
  106. * *
  107. * HISTORY: *
  108. * 05/11/1999 SKB : Created. *
  109. *=============================================================================================*/
  110. TagBlockFile::TagBlockFile(const char *fname):
  111. RawFileClass(),
  112. Header(),
  113. CreateHandle(NULL),
  114. NumOpenHandles(0),
  115. IndexList()
  116. {
  117. // Open file up, create it if it does not exist.
  118. // Pass in name to Open function so that the file name will be strdup'd.
  119. Open(fname, READ|WRITE);
  120. FileTime = RawFileClass::Get_Date_Time();
  121. // Read in header so we can tell if it is proper file.
  122. Read(&Header, sizeof(Header));
  123. // See if there is any data in file (or was it just created?)
  124. if (Header.Version == FILE_VERSION) {
  125. TagBlockIndex *lasttag = NULL;
  126. int curpos = sizeof(Header);
  127. // Loop through each block in file and create an in memory index for it.
  128. int block;
  129. for (block = 0; block < Header.NumBlocks; block++) {
  130. BlockHeader blockheader;
  131. // Read in next header.
  132. Seek(curpos, SEEK_SET);
  133. Read(&blockheader, sizeof(blockheader));
  134. // Make sure things are in order.
  135. if (blockheader.Index == block) {
  136. char tagname[MAX_TAG_NAME_SIZE];
  137. // Read in tag name, this includes the NULL at end of string.
  138. Read(tagname, blockheader.TagSize);
  139. // Create new tag index for fast retrievel.
  140. lasttag = Create_Index(tagname, curpos);
  141. // Now get past the data.
  142. curpos = Calc_Tag_Offset(curpos) + blockheader.TagSize + blockheader.DataSize;
  143. } else {
  144. break;
  145. }
  146. }
  147. // See if there is a difference between the header and actual data.
  148. if ((curpos != Header.FileSize) || (block != Header.NumBlocks)) {
  149. Header.NumBlocks = block;
  150. Header.FileSize = curpos;
  151. // Start at begining of file and write out our new header.
  152. Seek(0, SEEK_SET);
  153. Write(&Header, sizeof(Header));
  154. }
  155. } else {
  156. Reset_File();
  157. }
  158. }
  159. /***********************************************************************************************
  160. * TagBlockFile::~TagBlockFile -- Close down the tag file. *
  161. * *
  162. * INPUT: *
  163. * *
  164. * OUTPUT: *
  165. * *
  166. * WARNINGS: *
  167. * Any TagBlockHandles that have not been deleted are now invalide but cannot be deleted.*
  168. * You must delete any handles associated with this before closing the TagFile. *
  169. * *
  170. * HISTORY: *
  171. * 05/11/1999 SKB : Created. *
  172. *=============================================================================================*/
  173. TagBlockFile::~TagBlockFile()
  174. {
  175. Empty_Index_List();
  176. }
  177. /***********************************************************************************************
  178. * TagBlockFile::Reset_File -- Clear file so no blocks exist. *
  179. * *
  180. * INPUT: *
  181. * *
  182. * OUTPUT: *
  183. * *
  184. * WARNINGS: *
  185. * *
  186. * HISTORY: *
  187. * 11/29/1999 SKB : Created. *
  188. *=============================================================================================*/
  189. void TagBlockFile::Reset_File()
  190. {
  191. Empty_Index_List();
  192. // Save a clean header out.
  193. Header.Version = FILE_VERSION;
  194. Header.NumBlocks = 0;
  195. Header.FileSize = sizeof(Header);
  196. Save_Header();
  197. // Close, then open file so we get a new time stamp on it.
  198. Close();
  199. Open(READ|WRITE);
  200. // Reget file creation time.
  201. FileTime = RawFileClass::Get_Date_Time();
  202. }
  203. /***********************************************************************************************
  204. * *TagBlockFile::Open_Tag -- Open an existing tag block. *
  205. * *
  206. * INPUT: *
  207. * *
  208. * OUTPUT: *
  209. * *
  210. * WARNINGS: *
  211. * *
  212. * HISTORY: *
  213. * 05/11/1999 SKB : Created. *
  214. *=============================================================================================*/
  215. TagBlockHandle *TagBlockFile::Open_Tag(const char *tagname)
  216. {
  217. // Find tag to open up.
  218. TagBlockIndex *index = Find_Block(tagname);
  219. if (!index) {
  220. return(NULL);
  221. }
  222. // Load up the block header information.
  223. BlockHeader *blockheader = new BlockHeader();
  224. Seek(index->Get_BlockOffset(), SEEK_SET);
  225. Read(blockheader, sizeof(*blockheader));
  226. // Now that we have all that we need, create the
  227. TagBlockHandle *handle = new TagBlockHandle(this, index, blockheader);
  228. // Keep track of how many handles there are so we can assert if they are not all shut down.
  229. NumOpenHandles++;
  230. // Return with our new handle.
  231. return(handle);
  232. }
  233. /***********************************************************************************************
  234. * *TagBlockFile::Create_Tag -- Create a new tag at the end of the block. *
  235. * *
  236. * INPUT: *
  237. * *
  238. * OUTPUT: *
  239. * *
  240. * WARNINGS: *
  241. * *
  242. * HISTORY: *
  243. * 05/11/1999 SKB : Created. *
  244. *=============================================================================================*/
  245. TagBlockHandle *TagBlockFile::Create_Tag(const char *tagname)
  246. {
  247. // Only allow one handle to be creating open at a time.
  248. if (CreateHandle) {
  249. return(NULL);
  250. }
  251. // Create a new index that we can write too.
  252. TagBlockIndex *index = Create_Index(tagname, Header.FileSize);
  253. // An index may not be created if a tag of the same name already exists.
  254. if (!index) {
  255. return(NULL);
  256. }
  257. // Create a header.
  258. // Use -1 for index to indecate that block is not yet written out competely.
  259. BlockHeader *blockheader = new BlockHeader(-1, index->Get_TagSize(), 0);
  260. // Write out the block header and the tag.
  261. Seek(index->Get_BlockOffset(), SEEK_SET);
  262. Write(blockheader, sizeof(*blockheader));
  263. Write(tagname, strlen(tagname) + 1);
  264. // Now that we have all that we need, create the
  265. CreateHandle = new TagBlockHandle(this, index, blockheader);
  266. // Keep track of how many handles there are so we can assert if they are not all shut down.
  267. NumOpenHandles++;
  268. return(CreateHandle);
  269. }
  270. /***********************************************************************************************
  271. * TagBlockFile::Close_Tag -- Close the handle that Create or Open made. *
  272. * *
  273. * INPUT: *
  274. * *
  275. * OUTPUT: *
  276. * *
  277. * WARNINGS: *
  278. * *
  279. * HISTORY: *
  280. * 05/12/1999 SKB : Created. *
  281. *=============================================================================================*/
  282. void TagBlockFile::Close_Tag(TagBlockHandle *handle)
  283. {
  284. delete handle;
  285. }
  286. /***********************************************************************************************
  287. * TagBlockFile::Destroy_Handle -- Shut down a handle. *
  288. * *
  289. * INPUT: *
  290. * *
  291. * OUTPUT: *
  292. * *
  293. * WARNINGS: *
  294. * *
  295. * HISTORY: *
  296. * 05/12/1999 SKB : Created. *
  297. *=============================================================================================*/
  298. void TagBlockFile::Destroy_Handle(TagBlockHandle *handle)
  299. {
  300. // Make sure those sneaky programmers aren't trying to fool me.
  301. assert(handle->Called_By_Destructor());
  302. // If we had write access to handle, flush it out.
  303. End_Write_Access(handle);
  304. // This was allocated by TagBlockFile::Create_Tag() or Open_Tag().
  305. delete handle->BlockHeader;
  306. // Keep track of how many handles there are so we can assert if they are not all shut down.
  307. NumOpenHandles--;
  308. }
  309. /***********************************************************************************************
  310. * TagBlockFile::End_Write_Access -- Stop write access for handle - flushes data bug keeps han *
  311. * *
  312. * INPUT: *
  313. * *
  314. * OUTPUT: *
  315. * *
  316. * WARNINGS: *
  317. * *
  318. * HISTORY: *
  319. * 06/02/1999 SKB : Created. *
  320. *=============================================================================================*/
  321. int TagBlockFile::End_Write_Access(TagBlockHandle *handle)
  322. {
  323. // Make sure this handle is the proper one.
  324. if (CreateHandle == handle) {
  325. // Update file header and block header.
  326. handle->BlockHeader->Index = Header.NumBlocks;
  327. Header.NumBlocks++;
  328. Header.FileSize = handle->Index->Get_DataOffset();
  329. Header.FileSize += handle->BlockHeader->DataSize;
  330. // Write both headers out.
  331. Seek(handle->Index->Get_BlockOffset(), SEEK_SET);
  332. Write(handle->BlockHeader, sizeof(*handle->BlockHeader));
  333. Save_Header();
  334. // Don't allow writing with this handle anymore.
  335. CreateHandle = NULL;
  336. return(true);
  337. }
  338. return(false);
  339. }
  340. /***********************************************************************************************
  341. * *TagBlockFile::Create_Index -- Create a index into the IndexList sorted by CRC. *
  342. * *
  343. * INPUT: *
  344. * *
  345. * OUTPUT: *
  346. * *
  347. * WARNINGS: *
  348. * *
  349. * HISTORY: *
  350. * 05/11/1999 SKB : Created. *
  351. *=============================================================================================*/
  352. TagBlockIndex *TagBlockFile::Create_Index(const char *tagname, int blockoffset)
  353. {
  354. // Don't allow duplicate tags.
  355. if (Find_Block(tagname)) {
  356. return(NULL);
  357. }
  358. TagBlockIndex *index;
  359. index = new TagBlockIndex(tagname, blockoffset);
  360. // Put it into the list.
  361. if (IndexList.Is_Empty()) {
  362. IndexList.Add_Head(index);
  363. } else {
  364. // Put in list sorted by CRC, smallest to largest.
  365. SLNode<TagBlockIndex> *node = IndexList.Head();
  366. while (node) {
  367. TagBlockIndex *cur = node->Data();
  368. if (index->Get_CRC() > cur->Get_CRC()) {
  369. IndexList.Insert_Before(index, cur);
  370. break;
  371. }
  372. node = node->Next();
  373. }
  374. // If we did not find a place, then add at end of list.
  375. if (!node) {
  376. IndexList.Add_Tail(index);
  377. }
  378. }
  379. return (index);
  380. }
  381. /***********************************************************************************************
  382. * *TagBlockFile::Find_Block -- Find block assocated with name. *
  383. * *
  384. * INPUT: *
  385. * *
  386. * OUTPUT: *
  387. * *
  388. * WARNINGS: *
  389. * *
  390. * HISTORY: *
  391. * 05/11/1999 SKB : Created. *
  392. *=============================================================================================*/
  393. TagBlockIndex *TagBlockFile::Find_Block(const char *tagname)
  394. {
  395. if (IndexList.Is_Empty()) {
  396. return(NULL);
  397. }
  398. unsigned long crc = CRC_Stringi(tagname);
  399. SLNode<TagBlockIndex> *node = IndexList.Head();
  400. while (node) {
  401. TagBlockIndex *cur = node->Data();
  402. // Did we find it?
  403. if (cur->Get_CRC() == crc) {
  404. // Now read from file to verify that it is the right name.
  405. char name[MAX_TAG_NAME_SIZE];
  406. Seek(cur->Get_TagOffset(), SEEK_SET);
  407. // Read in the name.
  408. Read(name, cur->Get_TagSize());
  409. // Is it a match?
  410. assert(name != NULL);
  411. assert(tagname != NULL);
  412. if (!strcmpi(name, tagname)) {
  413. return(cur);
  414. }
  415. }
  416. // Are we out of range?
  417. if (cur->Get_CRC() < crc) {
  418. break;
  419. }
  420. // Next in line please.
  421. node = node->Next();
  422. }
  423. return(NULL);
  424. }
  425. /***********************************************************************************************
  426. * TagBlockFile::Empty_Index_List -- Clear out tag block list in memory *
  427. * *
  428. * INPUT: *
  429. * *
  430. * OUTPUT: *
  431. * *
  432. * WARNINGS: *
  433. * *
  434. * HISTORY: *
  435. * 11/29/1999 SKB : Created. *
  436. *=============================================================================================*/
  437. void TagBlockFile::Empty_Index_List()
  438. {
  439. assert(!NumOpenHandles);
  440. // Get rid of index list in memory.
  441. while (!IndexList.Is_Empty()) {
  442. TagBlockIndex *index = IndexList.Remove_Head();
  443. delete index;
  444. }
  445. }
  446. ///////////////////////////////////////// End of TagBlockFile /////////////////////////////////////
  447. ///////////////////////////////////////////////////////////////////////////////////////////////////
  448. ////////////////////////////////////// Start of TagBlockHandle/////////////////////////////////////
  449. /***********************************************************************************************
  450. * Position -- Create a handle for user to access the TagBlock. *
  451. * *
  452. * INPUT: *
  453. * *
  454. * OUTPUT: *
  455. * *
  456. * WARNINGS: *
  457. * *
  458. * HISTORY: *
  459. * 05/12/1999 SKB : Created. *
  460. *=============================================================================================*/
  461. TagBlockHandle::TagBlockHandle(TagBlockFile *tagfile, TagBlockIndex *tagindex, TagBlockFile::BlockHeader *blockheader):
  462. File(tagfile),
  463. Index(tagindex),
  464. BlockHeader(blockheader),
  465. Position(0)
  466. {
  467. }
  468. /***********************************************************************************************
  469. * TagBlockHandle::~TagBlockHandle -- Destroy handle. *
  470. * *
  471. * INPUT: *
  472. * *
  473. * OUTPUT: *
  474. * *
  475. * WARNINGS: *
  476. * *
  477. * HISTORY: *
  478. * 05/12/1999 SKB : Created. *
  479. *=============================================================================================*/
  480. TagBlockHandle::~TagBlockHandle()
  481. {
  482. _InDestructor++;
  483. File->Destroy_Handle(this);
  484. _InDestructor--;
  485. }
  486. /***********************************************************************************************
  487. * TagBlockHandle::Write -- Write data to the block. *
  488. * *
  489. * INPUT: *
  490. * *
  491. * OUTPUT: *
  492. * *
  493. * WARNINGS: *
  494. * *
  495. * HISTORY: *
  496. * 05/12/1999 SKB : Created. *
  497. *=============================================================================================*/
  498. int TagBlockHandle::Write(const void *buf, int nbytes)
  499. {
  500. // Make sure this handle is the proper one.
  501. if (!File->Handle_Can_Write(this)) {
  502. return(-1);
  503. }
  504. // Get to correct position to write out and write the buffer.
  505. File->Seek(Index->Get_DataOffset() + Position, SEEK_SET);
  506. nbytes = File->Write(buf, nbytes);
  507. // Advance the EOF marker for the block.
  508. Position += nbytes;
  509. if (Position > BlockHeader->DataSize) {
  510. BlockHeader->DataSize = Position;
  511. }
  512. // Return about written out.
  513. return(nbytes);
  514. }
  515. /***********************************************************************************************
  516. * TagBlockHandle::Read -- Read from a tag block. *
  517. * *
  518. * INPUT: *
  519. * *
  520. * OUTPUT: *
  521. * *
  522. * WARNINGS: *
  523. * *
  524. * HISTORY: *
  525. * 05/12/1999 SKB : Created. *
  526. *=============================================================================================*/
  527. int TagBlockHandle::Read(void *buf, int nbytes)
  528. {
  529. // Make sure user does not read past end of buffer.
  530. if ((Position + nbytes) > BlockHeader->DataSize) {
  531. nbytes = BlockHeader->DataSize - Position;
  532. }
  533. // Get to correct position to write out and read in the buffer.
  534. File->Seek(Index->Get_DataOffset() + Position, SEEK_SET);
  535. nbytes = File->Read(buf, nbytes);
  536. // Adjust the read head.
  537. Position += nbytes;
  538. // Tell user how much was read from the file.
  539. return(nbytes);
  540. }
  541. /***********************************************************************************************
  542. * TagBlockHandle::Seek -- Seek within the file. *
  543. * *
  544. * INPUT: *
  545. * *
  546. * OUTPUT: *
  547. * *
  548. * WARNINGS: *
  549. * *
  550. * HISTORY: *
  551. * 05/12/1999 SKB : Created. *
  552. *=============================================================================================*/
  553. int TagBlockHandle::Seek(int pos, int dir)
  554. {
  555. switch (dir) {
  556. case SEEK_CUR:
  557. Position += pos;
  558. break;
  559. case SEEK_SET:
  560. Position = pos;
  561. break;
  562. case SEEK_END:
  563. Position = BlockHeader->DataSize + pos;
  564. break;
  565. }
  566. return(Position);
  567. }
  568. // EOF