MIXFILE.CPP 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. //
  2. // Copyright 2020 Electronic Arts Inc.
  3. //
  4. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
  5. // software: you can redistribute it and/or modify it under the terms of
  6. // the GNU General Public License as published by the Free Software Foundation,
  7. // either version 3 of the License, or (at your option) any later version.
  8. // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
  9. // in the hope that it will be useful, but with permitted additional restrictions
  10. // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
  11. // distributed with this program. You should have received a copy of the
  12. // GNU General Public License along with permitted additional restrictions
  13. // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
  14. /* $Header: /CounterStrike/MIXFILE.CPP 2 3/13/97 2:06p Steve_tall $ */
  15. /***********************************************************************************************
  16. *** 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 ***
  17. ***********************************************************************************************
  18. * *
  19. * Project Name : Command & Conquer *
  20. * *
  21. * File Name : MIXFILE.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : August 8, 1994 *
  26. * *
  27. * Last Update : July 12, 1996 [JLB] *
  28. * *
  29. * *
  30. * Modified by Vic Grippi for WwXlat Tool 10/14/96 *
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * MixFileClass::Cache -- Caches the named mixfile into RAM. *
  35. * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
  36. * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
  37. * MixFileClass::Free -- Uncaches a cached mixfile. *
  38. * MixFileClass::MixFileClass -- Constructor for mixfile object. *
  39. * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.*
  40. * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
  41. * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
  42. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43. #include "buff.h"
  44. #include "function.h"
  45. #include <direct.h>
  46. #include <fcntl.h>
  47. #include <io.h>
  48. #include <dos.h>
  49. #include <errno.h>
  50. #include <share.h>
  51. #include "mixfile.h"
  52. #include "cdfile.h"
  53. extern MFCD temp;
  54. //template<class T> int Compare(T const *obj1, T const *obj2) {
  55. // if (*obj1 < *obj2) return(-1);
  56. // if (*obj1 > *obj2) return(1);
  57. // return(0);
  58. //};
  59. /*
  60. ** This is the pointer to the first mixfile in the list of mixfiles registered
  61. ** with the mixfile system.
  62. */
  63. template<class T>
  64. List<MixFileClass<T> > MixFileClass<T>::List;
  65. template class MixFileClass<CCFileClass>;
  66. /***********************************************************************************************
  67. * MixFileClass::Free -- Uncaches a cached mixfile. *
  68. * *
  69. * Use this routine to uncache a mixfile that has been cached. *
  70. * *
  71. * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. *
  72. * *
  73. * OUTPUT: bool; Was the mixfile found and freed? *
  74. * *
  75. * WARNINGS: none *
  76. * *
  77. * HISTORY: *
  78. * 01/23/1995 JLB : Created. *
  79. *=============================================================================================*/
  80. template<class T>
  81. bool MixFileClass<T>::Free(char const * filename)
  82. {
  83. MixFileClass * ptr = Finder(filename);
  84. if (ptr) {
  85. ptr->Free();
  86. return(true);
  87. }
  88. return(false);
  89. }
  90. /***********************************************************************************************
  91. * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
  92. * *
  93. * This destructor will free all memory allocated by this mixfile and will remove it from *
  94. * the system. A mixfile removed in this fashion must be created anew in order to be *
  95. * subsequent used. *
  96. * *
  97. * INPUT: none *
  98. * *
  99. * OUTPUT: none *
  100. * *
  101. * WARNINGS: none *
  102. * *
  103. * HISTORY: *
  104. * 08/08/1994 JLB : Created. *
  105. * 01/06/1995 JLB : Puts mixfile header table into EMS. *
  106. *=============================================================================================*/
  107. template<class T>
  108. MixFileClass<T>::~MixFileClass(void)
  109. {
  110. /*
  111. ** Deallocate any allocated memory.
  112. */
  113. if (Filename) {
  114. free((char *)Filename);
  115. }
  116. if (Data != NULL && IsAllocated) {
  117. delete [] Data;
  118. IsAllocated = false;
  119. }
  120. Data = NULL;
  121. if (HeaderBuffer != NULL) {
  122. delete [] HeaderBuffer;
  123. HeaderBuffer = NULL;
  124. }
  125. /*
  126. ** Unlink this mixfile object from the chain.
  127. */
  128. Unlink();
  129. }
  130. /***********************************************************************************************
  131. * MixFileClass::MixFileClass -- Constructor for mixfile object. *
  132. * *
  133. * This is the constructor for the mixfile object. It takes a filename and a memory *
  134. * handler object and registers the mixfile object with the system. The index block is *
  135. * allocated and loaded from disk by this routine. *
  136. * *
  137. * INPUT: filename -- Pointer to the filename of the mixfile object. *
  138. * *
  139. * OUTPUT: none *
  140. * *
  141. * WARNINGS: none *
  142. * *
  143. * HISTORY: *
  144. * 08/08/1994 JLB : Created. *
  145. * 07/12/1996 JLB : Handles compressed file header. *
  146. *=============================================================================================*/
  147. template<class T>
  148. MixFileClass<T>::MixFileClass(char const * filename, PKey const * key) :
  149. IsDigest(false),
  150. IsEncrypted(false),
  151. IsAllocated(false),
  152. Filename(0),
  153. Count(0),
  154. DataSize(0),
  155. DataStart(0),
  156. HeaderBuffer(0),
  157. Data(0)
  158. {
  159. if (filename == NULL) return; // ST - 5/9/2019
  160. /*
  161. ** Check to see if the file is available. If it isn't, then
  162. ** no further processing is needed or possible.
  163. */
  164. if (!Force_CD_Available(RequiredCD)) {
  165. Prog_End("MixFileClass Force_CD_Available failed", true);
  166. if (!RunningAsDLL) { //PG
  167. Emergency_Exit(EXIT_FAILURE);
  168. }
  169. }
  170. T file(filename); // Working file object.
  171. Filename = strdup(file.File_Name());
  172. FileStraw fstraw(file);
  173. PKStraw pstraw(PKStraw::DECRYPT, CryptRandom);
  174. Straw * straw = &fstraw;
  175. if (!file.Is_Available()) return;
  176. /*
  177. ** Stuctures used to hold the various file headers.
  178. */
  179. FileHeader fileheader;
  180. struct {
  181. short First; // Always zero for extended mixfile format.
  182. short Second; // Bitfield of extensions to this mixfile.
  183. } alternate;
  184. /*
  185. ** Fetch the first bit of the file. From this bit, it is possible to detect
  186. ** whether this is an extended mixfile format or the plain format. An
  187. ** extended format may have extra options or data layout.
  188. */
  189. int got = straw->Get(&alternate, sizeof(alternate));
  190. /*
  191. ** Detect if this is an extended mixfile. If so, then see if it is encrypted
  192. ** and/or has a message digest attached. Otherwise, just retrieve the
  193. ** plain mixfile header.
  194. */
  195. if (alternate.First == 0) {
  196. IsDigest = ((alternate.Second & 0x01) != 0);
  197. IsEncrypted = ((alternate.Second & 0x02) != 0);
  198. if (IsEncrypted) {
  199. pstraw.Key(key);
  200. pstraw.Get_From(&fstraw);
  201. straw = &pstraw;
  202. }
  203. straw->Get(&fileheader, sizeof(fileheader));
  204. } else {
  205. memmove(&fileheader, &alternate, sizeof(alternate));
  206. straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate));
  207. }
  208. Count = fileheader.count;
  209. DataSize = fileheader.size;
  210. //BGMono_Printf("Mixfileclass %s DataSize: %08x \n",filename,DataSize);Get_Key();
  211. /*
  212. ** Load up the offset control array. If RAM is exhausted, then the mixfile is invalid.
  213. */
  214. HeaderBuffer = new SubBlock [Count];
  215. if (HeaderBuffer == NULL) return;
  216. straw->Get(HeaderBuffer, Count * sizeof(SubBlock));
  217. /*
  218. ** The start of the embedded mixfile data will be at the current file offset.
  219. ** This should be true even if the file header has been encrypted because the file
  220. ** header was cleverly written with just the sufficient number of padding bytes so
  221. ** that this condition would be true.
  222. */
  223. DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart;
  224. // DataStart = file.Seek(0, SEEK_CUR);
  225. /*
  226. ** Attach to list of mixfiles.
  227. */
  228. List.Add_Tail(this);
  229. }
  230. /***********************************************************************************************
  231. * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
  232. * *
  233. * This routine will return with a pointer to the specified data file if the file resides *
  234. * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident *
  235. * file directly rather than going through the process of pseudo disk access. This will *
  236. * save both time and RAM. *
  237. * *
  238. * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. *
  239. * *
  240. * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then *
  241. * NULL is returned. *
  242. * *
  243. * WARNINGS: none *
  244. * *
  245. * HISTORY: *
  246. * 08/23/1994 JLB : Created. *
  247. *=============================================================================================*/
  248. template<class T>
  249. void const * MixFileClass<T>::Retrieve(char const * filename)
  250. {
  251. void * ptr = 0;
  252. Offset(filename, &ptr);
  253. return(ptr);
  254. };
  255. /***********************************************************************************************
  256. * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
  257. * *
  258. * This routine will scan through all registered mixfiles and return with a pointer to *
  259. * the matching mixfile. If no mixfile could be found that matches the name specified, *
  260. * then NULL is returned. *
  261. * *
  262. * INPUT: filename -- Pointer to the filename to search for. *
  263. * *
  264. * OUTPUT: Returns with a pointer to the matching mixfile -- if found. *
  265. * *
  266. * WARNINGS: none *
  267. * *
  268. * HISTORY: *
  269. * 08/08/1994 JLB : Created. *
  270. * 06/08/1996 JLB : Only compares filename and extension. *
  271. *=============================================================================================*/
  272. template<class T>
  273. MixFileClass<T> * MixFileClass<T>::Finder(char const * filename)
  274. {
  275. MixFileClass<T> * ptr = List.First();
  276. while (ptr->Is_Valid()) {
  277. char path[_MAX_PATH];
  278. char name[_MAX_FNAME];
  279. char ext[_MAX_EXT];
  280. /*
  281. ** Strip the drive and path (if present) off of the filename
  282. ** in the mixfile list. This enables a simple comparison to the
  283. ** filename specified. The filename specified won't have a path attached and
  284. ** the full pathname in the mixfile list WILL have a path attached. Hence, this
  285. ** stripping of the path is necessary.
  286. */
  287. _splitpath(ptr->Filename, NULL, NULL, name, ext);
  288. _makepath(path, NULL, NULL, name, ext);
  289. if (stricmp(path, filename) == 0) {
  290. return(ptr);
  291. }
  292. ptr = ptr->Next();
  293. }
  294. return(0);
  295. }
  296. /***********************************************************************************************
  297. * MixFileClass::Cache -- Caches the named mixfile into RAM. *
  298. * *
  299. * This routine will cache the mixfile, specified by name, into RAM. *
  300. * *
  301. * INPUT: filename -- The name of the mixfile that should be cached. *
  302. * *
  303. * OUTPUT: bool; Was the cache successful? *
  304. * *
  305. * WARNINGS: This routine could go to disk for a very long time. *
  306. * *
  307. * HISTORY: *
  308. * 08/08/1994 JLB : Created. *
  309. *=============================================================================================*/
  310. template<class T>
  311. bool MixFileClass<T>::Cache(char const * filename, Buffer const * buffer)
  312. {
  313. MixFileClass<T> * mixer = Finder(filename);
  314. if (mixer != NULL) {
  315. return(mixer->Cache(buffer));
  316. }
  317. return(false);
  318. }
  319. /***********************************************************************************************
  320. * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
  321. * *
  322. * This load the mixfile data into ram for this mixfile object. This is the counterpart *
  323. * to the Free() function. *
  324. * *
  325. * INPUT: none *
  326. * *
  327. * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room *
  328. * to allocate the raw data block. *
  329. * *
  330. * WARNINGS: This routine goes to disk for a potentially very long time. *
  331. * *
  332. * HISTORY: *
  333. * 08/08/1994 JLB : Created. *
  334. * 07/12/1996 JLB : Handles attached message digest. *
  335. *=============================================================================================*/
  336. template<class T>
  337. bool MixFileClass<T>::Cache(Buffer const * buffer)
  338. {
  339. /*
  340. ** If the mixfile is already cached, then no action needs to be performed.
  341. */
  342. if (Data != NULL) return(true);
  343. /*
  344. ** If a buffer was supplied (and it is big enough), then use it as the data block
  345. ** pointer. Otherwise, the data block must be allocated.
  346. */
  347. if (buffer != NULL) {
  348. if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) {
  349. Data = buffer->Get_Buffer();
  350. }
  351. } else {
  352. Data = new char [DataSize];
  353. IsAllocated = true;
  354. }
  355. /*
  356. ** If there is a data buffer to fill, then fill it now.
  357. */
  358. if (Data != NULL) {
  359. T file(Filename);
  360. FileStraw fstraw(file);
  361. Straw * straw = &fstraw;
  362. /*
  363. ** If a message digest is attached, then link a SHA straw segment to the data
  364. ** stream so that the actual SHA can be compared with the attached one.
  365. */
  366. SHAStraw sha;
  367. if (IsDigest) {
  368. sha.Get_From(fstraw);
  369. straw = &sha;
  370. }
  371. /*
  372. ** Bias the file to the actual start of the data. This is necessary because the
  373. ** real data starts some distance (not so easily determined) from the beginning of
  374. ** the real file.
  375. */
  376. file.Open(READ);
  377. file.Bias(0);
  378. file.Bias(DataStart);
  379. /*
  380. ** Fetch the whole mixfile data in one step. If the number of bytes retrieved
  381. ** does not equal that requested, then this indicates a serious error.
  382. */
  383. long actual = straw->Get(Data, DataSize);
  384. if (actual != DataSize) {
  385. delete [] Data;
  386. Data = NULL;
  387. file.Error(EIO);
  388. return(false);
  389. }
  390. /*
  391. ** If there is a digest attached to this mixfile, then read it in and
  392. ** compare it to the generated digest. If they don't match, then
  393. ** return with the "failure to cache" error code.
  394. */
  395. if (IsDigest) {
  396. char digest1[20];
  397. char digest2[20];
  398. sha.Result(digest2);
  399. fstraw.Get(digest1, sizeof(digest1));
  400. if (memcmp(digest1, digest2, sizeof(digest1)) != 0) {
  401. delete [] Data;
  402. Data = NULL;
  403. return(false);
  404. }
  405. }
  406. return(true);
  407. }
  408. IsAllocated = false;
  409. return(false);
  410. }
  411. /***********************************************************************************************
  412. * MixFileClass::Free -- Frees the allocated raw data block (not the index block). *
  413. * *
  414. * This routine will free the (presumably large) raw data block, but leave the index *
  415. * block intact. By using this in conjunction with the Cache() function, one can maintain *
  416. * tight control of memory usage. If the index block is desired to be freed, then the *
  417. * mixfile object must be deleted. *
  418. * *
  419. * INPUT: none *
  420. * *
  421. * OUTPUT: none *
  422. * *
  423. * WARNINGS: none *
  424. * *
  425. * HISTORY: *
  426. * 08/08/1994 JLB : Created. *
  427. *=============================================================================================*/
  428. template<class T>
  429. void MixFileClass<T>::Free(void)
  430. {
  431. if (Data != NULL && IsAllocated) {
  432. delete [] Data;
  433. }
  434. Data = NULL;
  435. IsAllocated = false;
  436. }
  437. int compfunc(void const * ptr1, void const * ptr2)
  438. {
  439. if (*(long const *)ptr1 < *(long const *)ptr2) return(-1);
  440. if (*(long const *)ptr1 > *(long const *)ptr2) return(1);
  441. return(0);
  442. }
  443. /***********************************************************************************************
  444. * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
  445. * *
  446. * This routine will take the filename specified and search through the mixfile system *
  447. * looking for it. If the file was found, then the mixfile it was found in, the offset *
  448. * from the start of the mixfile, and the size of the embedded file will be returned. *
  449. * Using this method it is possible for the CCFileClass system to process it as a normal *
  450. * file. *
  451. * *
  452. * INPUT: filename -- The filename to search for. *
  453. * *
  454. * realptr -- Stores a pointer to the start of the file in memory here. If the *
  455. * file is not in memory, then NULL is stored here. *
  456. * *
  457. * mixfile -- The pointer to the corresponding mixfile is placed here. If no *
  458. * mixfile was found that contains the file, then NULL is stored here. *
  459. * *
  460. * offset -- The starting offset from the beginning of the parent mixfile is *
  461. * stored here. *
  462. * *
  463. * size -- The size of the embedded file is stored here. *
  464. * *
  465. * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist *
  466. * and can be opened. *
  467. * *
  468. * WARNINGS: none *
  469. * *
  470. * HISTORY: *
  471. * 10/17/1994 JLB : Created. *
  472. *=============================================================================================*/
  473. template<class T>
  474. bool MixFileClass<T>::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size)
  475. {
  476. MixFileClass<T> * ptr;
  477. if (filename == NULL) {
  478. assert(filename != NULL);//BG
  479. return(false);
  480. }
  481. /*
  482. ** Create the key block that will be used to binary search for the file.
  483. */
  484. // Can't call strupr on a const string. ST - 5/20/2019
  485. //long crc = Calculate_CRC(strupr((char *)filename), strlen(filename));
  486. char filename_upper[_MAX_PATH];
  487. strcpy(filename_upper, filename);
  488. strupr(filename_upper);
  489. long crc = Calculate_CRC(strupr(filename_upper), strlen(filename_upper));
  490. SubBlock key;
  491. key.CRC = crc;
  492. /*
  493. ** Sweep through all registered mixfiles, trying to find the file in question.
  494. */
  495. ptr = List.First();
  496. while (ptr->Is_Valid()) {
  497. SubBlock * block;
  498. /*
  499. ** Binary search for the file in this mixfile. If it is found, then extract the
  500. ** appropriate information and store it in the locations provided and then return.
  501. */
  502. block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc);
  503. if (block != NULL) {
  504. if (mixfile != NULL) *mixfile = ptr;
  505. if (size != NULL) *size = block->Size;
  506. if (realptr != NULL) *realptr = NULL;
  507. if (offset != NULL) *offset = block->Offset;
  508. if (realptr != NULL && ptr->Data != NULL) {
  509. *realptr = (char *)ptr->Data + block->Offset;
  510. }
  511. if (ptr->Data == NULL && offset != NULL) {
  512. *offset += ptr->DataStart;
  513. }
  514. return(true);
  515. }
  516. /*
  517. ** Advance to next mixfile.
  518. */
  519. ptr = ptr->Next();
  520. }
  521. /*
  522. ** All the mixfiles have been examined but no match was found. Return with the non success flag.
  523. */
  524. assert(1);//BG
  525. return(false);
  526. }
  527. // ST - 12/18/2019 11:36AM
  528. template<class T>
  529. void MixFileClass<T>::Free_All(void)
  530. {
  531. MixFileClass<T> * ptr = List.First();
  532. while (ptr->Is_Valid()) {
  533. delete ptr;
  534. ptr = List.First();
  535. }
  536. }