MIXFILE.CPP 26 KB

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