MIXFILE.CPP 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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: F:\projects\c&c\vcs\code\mixfile.cpv 2.18 16 Oct 1995 16:48:46 JOE_BOSTIC $ */
  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 : January 23, 1995 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * MixFileClass::Cache -- Caches the named mixfile into RAM. *
  32. * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
  33. * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
  34. * MixFileClass::Free -- Frees the allocated raw data block (not the index block). *
  35. * MixFileClass::MixFileClass -- Constructor for mixfile object. *
  36. * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.*
  37. * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.*
  38. * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
  39. * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
  40. * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
  41. * MixFileClass::Free -- Uncaches a cached mixfile. *
  42. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43. #include "function.h"
  44. #include <direct.h>
  45. #include <fcntl.h>
  46. #include <io.h>
  47. #include <dos.h>
  48. #include <errno.h>
  49. #include <share.h>
  50. #include "mixfile.h"
  51. template<class T> int Compare(T const *obj1, T const *obj2) {
  52. if (*obj1 < *obj2) return(-1);
  53. if (*obj1 > *obj2) return(1);
  54. return(0);
  55. };
  56. /*
  57. ** This is the pointer to the first mixfile in the list of mixfiles registered
  58. ** with the mixfile system.
  59. */
  60. MixFileClass * MixFileClass::First = 0;
  61. /***********************************************************************************************
  62. * MixFileClass::Free -- Uncaches a cached mixfile. *
  63. * *
  64. * Use this routine to uncache a mixfile that has been cached. *
  65. * *
  66. * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. *
  67. * *
  68. * OUTPUT: bool; Was the mixfile found and freed? *
  69. * *
  70. * WARNINGS: none *
  71. * *
  72. * HISTORY: *
  73. * 01/23/1995 JLB : Created. *
  74. *=============================================================================================*/
  75. bool MixFileClass::Free(char const *filename)
  76. {
  77. MixFileClass * ptr = Finder(filename);
  78. if (ptr) {
  79. ptr->Free();
  80. return(true);
  81. }
  82. return(false);
  83. }
  84. //#ifndef NOMEMCHECK
  85. void MixFileClass::Free_All(void)
  86. {
  87. while (First) {
  88. delete First;
  89. }
  90. }
  91. //#endif
  92. /***********************************************************************************************
  93. * MixFileClass::~MixFileClass -- Destructor for the mixfile object. *
  94. * *
  95. * This destructor will free all memory allocated by this mixfile and will remove it from *
  96. * the system. A mixfile removed in this fashion must be created anew in order to be *
  97. * subsequent used. *
  98. * *
  99. * INPUT: none *
  100. * *
  101. * OUTPUT: none *
  102. * *
  103. * WARNINGS: none *
  104. * *
  105. * HISTORY: *
  106. * 08/08/1994 JLB : Created. *
  107. * 01/06/1995 JLB : Puts mixfile header table into EMS. *
  108. *=============================================================================================*/
  109. MixFileClass::~MixFileClass(void)
  110. {
  111. /*
  112. ** Deallocate any allocated memory.
  113. */
  114. if (Filename) {
  115. free((char *)Filename);
  116. }
  117. if (Data) {
  118. delete [] Data;
  119. }
  120. if (Buffer) {
  121. delete [] Buffer;
  122. }
  123. /*
  124. ** Unlink this mixfile object from the chain.
  125. */
  126. if (this == First) {
  127. First = (MixFileClass *)Get_Next();
  128. } else {
  129. Remove();
  130. }
  131. // Can't do this here since the link class destructor hasn't been called yet, so clearing out the data will mess up the
  132. // linked list. How did this work before? Did the watcom compiler call the destructors in a different order?
  133. // ST - 1/3/2019 5:34PM
  134. //Zap();
  135. }
  136. /***********************************************************************************************
  137. * MixFileClass::MixFileClass -- Constructor for mixfile object. *
  138. * *
  139. * This is the constructor for the mixfile object. It takes a filename and a memory *
  140. * handler object and registers the mixfile object with the system. The index block is *
  141. * allocated and loaded from disk by this routine. *
  142. * *
  143. * INPUT: filename -- Pointer to the filename of the mixfile object. *
  144. * *
  145. * OUTPUT: none *
  146. * *
  147. * WARNINGS: none *
  148. * *
  149. * HISTORY: *
  150. * 08/08/1994 JLB : Created. *
  151. *=============================================================================================*/
  152. MixFileClass::MixFileClass(char const *filename)
  153. {
  154. CCFileClass file; // Working file object.
  155. /*
  156. ** Load in the control block. It always remains resident.
  157. */
  158. Data = 0;
  159. Count = 0;
  160. Buffer = 0;
  161. file.Set_Name(filename);
  162. Filename = strdup(file.File_Name());
  163. if (!Force_CD_Available(RequiredCD)) {
  164. Prog_End("MixFileClass::MixFileClass CD not found", true);
  165. if (!RunningAsDLL) {
  166. exit(EXIT_FAILURE);
  167. }
  168. return;
  169. }
  170. if (file.Is_Available(true)) {
  171. FileHeader fileheader;
  172. file.Open();
  173. file.Read(&fileheader, sizeof(fileheader));
  174. Count = fileheader.count;
  175. DataSize = fileheader.size;
  176. /*
  177. ** Load up the offset control array. This could be located in
  178. ** EMS if possible.
  179. */
  180. Buffer = new SubBlock [Count];
  181. if (Buffer) {
  182. file.Read(Buffer, Count * sizeof(SubBlock));
  183. }
  184. file.Close();
  185. } else {
  186. // delete this;
  187. return;
  188. }
  189. /*
  190. ** Raw data block starts uncached.
  191. */
  192. Data = 0;
  193. /*
  194. ** Attach to list of mixfiles.
  195. */
  196. Zap();
  197. if (!First) {
  198. First = this;
  199. } else {
  200. Add_Tail(*First);
  201. }
  202. }
  203. /***********************************************************************************************
  204. * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. *
  205. * *
  206. * This routine will return with a pointer to the specified data file if the file resides *
  207. * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident *
  208. * file directly rather than going through the process of pseudo disk access. This will *
  209. * save both time and RAM. *
  210. * *
  211. * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. *
  212. * *
  213. * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then *
  214. * NULL is returned. *
  215. * *
  216. * WARNINGS: none *
  217. * *
  218. * HISTORY: *
  219. * 08/23/1994 JLB : Created. *
  220. *=============================================================================================*/
  221. void const * MixFileClass::Retrieve(char const *filename) {
  222. void *ptr = 0;
  223. Offset(filename, &ptr);
  224. // if (!ptr) {
  225. // errno = ENOENT;
  226. // File_Fatal(filename);
  227. // }
  228. return(ptr);
  229. };
  230. /***********************************************************************************************
  231. * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. *
  232. * *
  233. * This routine will scan through all registered mixfiles and return with a pointer to *
  234. * the matching mixfile. If no mixfile could be found that matches the name specified, *
  235. * then NULL is returned. *
  236. * *
  237. * INPUT: filename -- Pointer to the filename to search for. *
  238. * *
  239. * OUTPUT: Returns with a pointer to the matching mixfile -- if found. *
  240. * *
  241. * WARNINGS: none *
  242. * *
  243. * HISTORY: *
  244. * 08/08/1994 JLB : Created. *
  245. *=============================================================================================*/
  246. MixFileClass * MixFileClass::Finder(char const *filename)
  247. {
  248. MixFileClass * ptr;
  249. ptr = First;
  250. while (ptr) {
  251. if (stricmp(&ptr->Filename[strlen(ptr->Filename)-strlen(filename)], filename) == 0) {
  252. return(ptr);
  253. }
  254. ptr = (MixFileClass *)ptr->Get_Next();
  255. }
  256. return(0);
  257. }
  258. /***********************************************************************************************
  259. * MixFileClass::Cache -- Caches the named mixfile into RAM. *
  260. * *
  261. * This routine will cache the mixfile, specified by name, into RAM. *
  262. * *
  263. * INPUT: filename -- The name of the mixfile that should be cached. *
  264. * *
  265. * OUTPUT: bool; Was the cache successful? *
  266. * *
  267. * WARNINGS: This routine could go to disk for a very long time. *
  268. * *
  269. * HISTORY: *
  270. * 08/08/1994 JLB : Created. *
  271. *=============================================================================================*/
  272. bool MixFileClass::Cache(char const *filename)
  273. {
  274. MixFileClass * mixer = Finder(filename);
  275. if (mixer) {
  276. return(mixer->Cache());
  277. }
  278. return(false);
  279. }
  280. /***********************************************************************************************
  281. * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. *
  282. * *
  283. * This load the mixfile data into ram for this mixfile object. This is the counterpart *
  284. * to the Free() function. *
  285. * *
  286. * INPUT: none *
  287. * *
  288. * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room *
  289. * to allocate the raw data block. *
  290. * *
  291. * WARNINGS: This routine goes to disk for a potentially very long time. *
  292. * *
  293. * HISTORY: *
  294. * 08/08/1994 JLB : Created. *
  295. *=============================================================================================*/
  296. bool MixFileClass::Cache(void)
  297. {
  298. if (Data) return(true);
  299. Data = new char [DataSize];
  300. if (Data) {
  301. CCFileClass file(Filename);
  302. file.Open();
  303. file.Seek(sizeof(SubBlock) * Count + sizeof(FileHeader));
  304. long actual = file.Read(Data, DataSize);
  305. if (actual != DataSize) {
  306. #ifdef GERMAN
  307. Fatal("Korrupte .MIX-Datei \"%s\". Beim Versuch, %ld zu lesen, nur %ld gefunden.", Filename, DataSize, actual);
  308. #else
  309. #ifdef FRENCH
  310. Fatal("Fichier .MIX corrumpu \"%s\". Essai de lecture de %ld, mais %ld obtenu.", Filename, DataSize, actual);
  311. #else
  312. Fatal("Corrupt .MIX file \"%s\". Tried to read %ld, but got %ld.", Filename, DataSize, actual);
  313. #endif
  314. #endif
  315. }
  316. file.Close();
  317. return(true);
  318. }
  319. #ifdef GERMAN
  320. Fatal("Kann Datei \"%s\" nicht laden.", Filename);
  321. #else
  322. #ifdef FRENCH
  323. Fatal("Impossible de charger \"%s\".", Filename);
  324. #else
  325. Fatal("Unable to load \"%s\".", Filename);
  326. #endif
  327. #endif
  328. return(false);
  329. }
  330. /***********************************************************************************************
  331. * MixFileClass::Free -- Frees the allocated raw data block (not the index block). *
  332. * *
  333. * This routine will free the (presumably large) raw data block, but leave the index *
  334. * block intact. By using this in conjunction with the Cache() function, one can maintain *
  335. * tight control of memory usage. If the index block is desired to be freed, then the *
  336. * mixfile object must be deleted. *
  337. * *
  338. * INPUT: none *
  339. * *
  340. * OUTPUT: none *
  341. * *
  342. * WARNINGS: none *
  343. * *
  344. * HISTORY: *
  345. * 08/08/1994 JLB : Created. *
  346. *=============================================================================================*/
  347. void MixFileClass::Free(void)
  348. {
  349. if (Data) {
  350. delete [] Data;
  351. Data = 0;
  352. }
  353. }
  354. /***********************************************************************************************
  355. * MixFileClass::Offset -- Determines if the file is in a mixfile and where its sublocation is.*
  356. * *
  357. * This routine will scan through all registers mixfiles in an attempt to locate the file *
  358. * in question. Whether the file is located in RAM or on disk does not restrict this *
  359. * search operation. *
  360. * *
  361. * INPUT: filename -- The filename of the file to search within the mixfile system. *
  362. * *
  363. * realptr -- Pointer to pointer that will be filled with the RAM pointer to *
  364. * the file if it happens to be in RAM. NULL if otherwise. *
  365. * *
  366. * mixfile -- Pointer to the mixfile object that contains the file in question. *
  367. * *
  368. * offset -- The offset to the start of the data of the file. If the file is *
  369. * on disk then this is the offset from the start of the file, if *
  370. * it is located in RAM, then it is the offset from the start of the *
  371. * raw data block. *
  372. * *
  373. * size -- Pointer to where the size of the file will be stored. *
  374. * *
  375. * OUTPUT: bool; Was the file found in one of the mixfiles? The value stored in "realptr" *
  376. * will be NULL if it is on disk, otherwise it is in RAM. *
  377. * *
  378. * WARNINGS: none *
  379. * *
  380. * HISTORY: *
  381. * 08/08/1994 JLB : Created. *
  382. *=============================================================================================*/
  383. //int _USERENTRY Compare(MixFileClass::SubBlock const *, MixFileClass::SubBlock const *);
  384. // int _USERENTRY compfunc(void const *ptr1, void const *ptr2)
  385. int compfunc(void const *ptr1, void const *ptr2)
  386. {
  387. // long diff = *(long const *)ptr1 - *(long const *)ptr2;
  388. // return FP_SEG(diff);
  389. if (*(long const *)ptr1 < *(long const *)ptr2) return(-1);
  390. if (*(long const *)ptr1 > *(long const *)ptr2) return(1);
  391. return(0);
  392. }
  393. /***********************************************************************************************
  394. * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.*
  395. * *
  396. * This routine will take the falename specified and search through the mixfile system *
  397. * looking for it. If the file was found, then the mixfile it was found int, the offset *
  398. * from the start of the mixfile, and the size of the embedded file will be returned. *
  399. * Using this method it is possible for the CCFileClass system to process it as a normal *
  400. * file. *
  401. * *
  402. * INPUT: filename -- The filename to search for. *
  403. * *
  404. * realptr -- Stores a pointer to the start of the file in memory here. If the *
  405. * file is not in memory, then NULL is stored here. *
  406. * *
  407. * mixfile -- The pointer to the corresponding mixfile is placed here. If no *
  408. * mixfile was found that contains the file, then NULL is stored here. *
  409. * *
  410. * offset -- The starting offset from the beginning of the parent mixfile is *
  411. * stored here. *
  412. * *
  413. * size -- The size of the embedded file is stored here. *
  414. * *
  415. * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist *
  416. * and can be opened. *
  417. * *
  418. * WARNINGS: none *
  419. * *
  420. * HISTORY: *
  421. * 10/17/1994 JLB : Created. *
  422. *=============================================================================================*/
  423. bool MixFileClass::Offset(char const *filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size)
  424. {
  425. MixFileClass * ptr;
  426. if (!filename) return(false);
  427. /*
  428. ** Create the key block that will be used to binary search for the file.
  429. */
  430. char file_name_copy[_MAX_PATH];
  431. strcpy(file_name_copy, filename);
  432. strupr(file_name_copy);
  433. long crc = Calculate_CRC(file_name_copy, strlen(file_name_copy));
  434. SubBlock key;
  435. key.CRC = crc;
  436. /*
  437. ** Sweep through all registered mixfiles, trying to find the file in question.
  438. */
  439. ptr = First;
  440. while (ptr) {
  441. SubBlock * block;
  442. /*
  443. ** Binary search for the file in this mixfile. If it is found, then extract the
  444. ** appropriate information and store it in the locations provided and then return.
  445. */
  446. block = (SubBlock *)bsearch(&key, ptr->Buffer, ptr->Count, sizeof(SubBlock), compfunc);
  447. if (block) {
  448. if (mixfile) *mixfile = ptr;
  449. if (size) *size = block->Size;
  450. if (realptr) *realptr = 0;
  451. if (offset) *offset = block->Offset;
  452. if (realptr && ptr->Data) {
  453. *realptr = Add_Long_To_Pointer(ptr->Data, block->Offset);
  454. }
  455. if (!ptr->Data && offset) {
  456. *offset += sizeof(SubBlock) * ptr->Count + sizeof(FileHeader);
  457. }
  458. return(true);
  459. }
  460. /*
  461. ** Advance to next mixfile.
  462. */
  463. ptr = (MixFileClass *)ptr->Get_Next();
  464. }
  465. /*
  466. ** All the mixfiles have been examined but no match was found. Return with the non success flag.
  467. */
  468. return(false);
  469. }