CCFILE.CPP 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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/CCFILE.CPP 2 3/13/97 2:05p 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 : CCFILE.CPP *
  22. * *
  23. * Programmer : Joe L. Bostic *
  24. * *
  25. * Start Date : August 8, 1994 *
  26. * *
  27. * Last Update : August 5, 1996 [JLB] *
  28. * *
  29. *---------------------------------------------------------------------------------------------*
  30. * Functions: *
  31. * CCFileClass::CCFileClass -- Default constructor for file object. *
  32. * CCFileClass::CCFileClass -- Filename based constructor for C&C file. *
  33. * CCFileClass::Close -- Closes the file. *
  34. * CCFileClass::Error -- Handles displaying a file error message. *
  35. * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. *
  36. * CCFileClass::Is_Open -- Determines if the file is open. *
  37. * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. *
  38. * CCFileClass::Read -- Reads data from the file. *
  39. * CCFileClass::Seek -- Moves the current file pointer in the file. *
  40. * CCFileClass::Size -- Determines the size of the file. *
  41. * CCFileClass::Write -- Writes data to the file (non mixfile files only). *
  42. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  43. #include "function.h"
  44. #include <errno.h>
  45. #include "ccfile.h"
  46. /***********************************************************************************************
  47. * CCFileClass::CCFileClass -- Filename based constructor for C&C file. *
  48. * *
  49. * Use this constructor for a file when the filename is known at construction time. *
  50. * *
  51. * INPUT: filename -- Pointer to the filename to use for this file object. *
  52. * *
  53. * OUTPUT: none *
  54. * *
  55. * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of *
  56. * the file object. If this is not guaranteed, then use the default constructor *
  57. * and then set the name manually. *
  58. * *
  59. * HISTORY: *
  60. * 03/20/1995 JLB : Created. *
  61. *=============================================================================================*/
  62. CCFileClass::CCFileClass(char const * filename) :
  63. Position(0)
  64. {
  65. CCFileClass::Set_Name(filename);
  66. }
  67. /***********************************************************************************************
  68. * CCFileClass::CCFileClass -- Default constructor for file object. *
  69. * *
  70. * This is the default constructor for a C&C file object. *
  71. * *
  72. * INPUT: none *
  73. * *
  74. * OUTPUT: none *
  75. * *
  76. * WARNINGS: none *
  77. * *
  78. * HISTORY: *
  79. * 03/20/1995 JLB : Created. *
  80. *=============================================================================================*/
  81. CCFileClass::CCFileClass(void) :
  82. Position(0)
  83. {
  84. }
  85. /***********************************************************************************************
  86. * CCFileClass::Error -- Handles displaying a file error message. *
  87. * *
  88. * Display an error message as indicated. If it is allowed to retry, then pressing a key *
  89. * will return from this function. Otherwise, it will exit the program with "exit()". *
  90. * *
  91. * INPUT: error -- The error number (same as the DOSERR.H error numbers). *
  92. * *
  93. * canretry -- Can this routine exit normally so that retrying can occur? If this is *
  94. * false, then the program WILL exit in this routine. *
  95. * *
  96. * filename -- Optional filename to report with this error. If no filename is *
  97. * supplied, then no filename is listed in the error message. *
  98. * *
  99. * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is *
  100. * false or the player pressed ESC. *
  101. * *
  102. * WARNINGS: This routine may not return at all. It handles being in text mode as well as *
  103. * if in a graphic mode. *
  104. * *
  105. * HISTORY: *
  106. * 10/17/1994 JLB : Created. *
  107. *=============================================================================================*/
  108. void CCFileClass::Error(int , int , char const * )
  109. {
  110. if (!Force_CD_Available(RequiredCD)) {
  111. Prog_End("CCFileClass::Error CD not found", true);
  112. if (!RunningAsDLL) { //PG
  113. Emergency_Exit(EXIT_FAILURE);
  114. }
  115. }
  116. }
  117. /***********************************************************************************************
  118. * CCFileClass::Write -- Writes data to the file (non mixfile files only). *
  119. * *
  120. * This routine will write data to the file, but NOT to a file that is part of a mixfile. *
  121. * *
  122. * INPUT: buffer -- Pointer to the buffer that holds the data to be written. *
  123. * *
  124. * size -- The number of bytes to write. *
  125. * *
  126. * OUTPUT: Returns the number of bytes actually written. *
  127. * *
  128. * WARNINGS: none *
  129. * *
  130. * HISTORY: *
  131. * 08/08/1994 JLB : Created. *
  132. *=============================================================================================*/
  133. long CCFileClass::Write(void const * buffer, long size)
  134. {
  135. /*
  136. ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal
  137. ** message.
  138. */
  139. if (Is_Resident()) {
  140. Error(EACCES, false, File_Name());
  141. }
  142. return(CDFileClass::Write(buffer, size));
  143. }
  144. /***********************************************************************************************
  145. * CCFileClass::Read -- Reads data from the file. *
  146. * *
  147. * This routine determines if the file is part of the mixfile system. If it is, then *
  148. * the file is copied from RAM if it is located there. Otherwise it is read from disk *
  149. * according to the correct position of the file within the parent mixfile. *
  150. * *
  151. * INPUT: buffer -- Pointer to the buffer to place the read data. *
  152. * *
  153. * size -- The number of bytes to read. *
  154. * *
  155. * OUTPUT: Returns the actual number of bytes read (this could be less than requested). *
  156. * *
  157. * WARNINGS: none *
  158. * *
  159. * HISTORY: *
  160. * 08/08/1994 JLB : Created. *
  161. *=============================================================================================*/
  162. long CCFileClass::Read(void * buffer, long size)
  163. {
  164. bool opened = false;
  165. /*
  166. ** If the file isn't currently open, then open it.
  167. */
  168. if (!Is_Open()) {
  169. if (Open()) {
  170. opened = true;
  171. }
  172. }
  173. /*
  174. ** If the file is part of a loaded mixfile, then a mere copy is
  175. ** all that is required for the read.
  176. */
  177. if (Is_Resident()) {
  178. long maximum = Data.Get_Size() - Position;
  179. size = maximum < size ? maximum : size;
  180. // size = MIN(maximum, size);
  181. if (size) {
  182. memmove(buffer, (char *)Data + Position, size);
  183. // Mem_Copy((char *)Pointer + Position, buffer, size);
  184. Position += size;
  185. }
  186. if (opened) Close();
  187. return(size);
  188. }
  189. long s = CDFileClass::Read(buffer, size);
  190. /*
  191. ** If the file was opened by this routine, then close it at this time.
  192. */
  193. if (opened) Close();
  194. /*
  195. ** Return with the number of bytes read.
  196. */
  197. return(s);
  198. }
  199. /***********************************************************************************************
  200. * CCFileClass::Seek -- Moves the current file pointer in the file. *
  201. * *
  202. * This routine will change the current file pointer to the position specified. It follows *
  203. * the same rules the a normal Seek() does, but if the file is part of the mixfile system, *
  204. * then only the position value needs to be updated. *
  205. * *
  206. * INPUT: pos -- The position to move the file to relative to the position indicated *
  207. * by the "dir" parameter. *
  208. * *
  209. * dir -- The direction to affect the position change against. This can be *
  210. * either SEEK_CUR, SEEK_END, or SEEK_SET. *
  211. * *
  212. * OUTPUT: Returns with the position of the new location. *
  213. * *
  214. * WARNINGS: none *
  215. * *
  216. * HISTORY: *
  217. * 08/08/1994 JLB : Created. *
  218. *=============================================================================================*/
  219. long CCFileClass::Seek(long pos, int dir)
  220. {
  221. /*
  222. ** When the file is resident, a mere adjustment of the virtual file position is
  223. ** all that is required of a seek.
  224. */
  225. if (Is_Resident()) {
  226. switch (dir) {
  227. case SEEK_END:
  228. Position = Data.Get_Size();
  229. break;
  230. case SEEK_SET:
  231. Position = 0;
  232. break;
  233. case SEEK_CUR:
  234. default:
  235. break;
  236. }
  237. Position += pos;
  238. Position = Position < 0 ? 0 : Position;
  239. Position = Position > Data.Get_Size() ? Data.Get_Size() : Position;
  240. // Position = Bound(Position+pos, 0L, Length);
  241. return(Position);
  242. }
  243. return(CDFileClass::Seek(pos, dir));
  244. }
  245. /***********************************************************************************************
  246. * CCFileClass::Size -- Determines the size of the file. *
  247. * *
  248. * If the file is part of the mixfile system, then the size of the file is already *
  249. * determined and available. Otherwise, go to the low level system to find the file *
  250. * size. *
  251. * *
  252. * INPUT: none *
  253. * *
  254. * OUTPUT: Returns with the size of the file in bytes. *
  255. * *
  256. * WARNINGS: none *
  257. * *
  258. * HISTORY: *
  259. * 08/08/1994 JLB : Created. *
  260. * 08/05/1996 JLB : Handles returning size of embedded file. *
  261. *=============================================================================================*/
  262. long CCFileClass::Size(void)
  263. {
  264. /*
  265. ** If the file is resident, the the size is already known. Just return the size in this
  266. ** case.
  267. */
  268. if (Is_Resident()) return(Data.Get_Size());
  269. /*
  270. ** If the file is not available as a stand alone file, then search for it in the
  271. ** mixfiles in order to get its size.
  272. */
  273. if (!CDFileClass::Is_Available()) {
  274. long length = 0;
  275. MFCD::Offset(File_Name(), NULL, NULL, NULL, &length);
  276. return(length);
  277. }
  278. return(CDFileClass::Size());
  279. }
  280. /***********************************************************************************************
  281. * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. *
  282. * *
  283. * This routine will examine the mixfile system looking for the file. If the file could *
  284. * not be found there, then the disk is examined directly. *
  285. * *
  286. * INPUT: none *
  287. * *
  288. * OUTPUT: bool; Is the file available for opening? *
  289. * *
  290. * WARNINGS: none *
  291. * *
  292. * HISTORY: *
  293. * 08/08/1994 JLB : Created. *
  294. *=============================================================================================*/
  295. int CCFileClass::Is_Available(int )
  296. {
  297. /*
  298. ** A file that is open is presumed available.
  299. */
  300. if (Is_Open()) return(true);
  301. /*
  302. ** A file that is part of a mixfile is also presumed available.
  303. */
  304. if (MFCD::Offset(File_Name())) {
  305. return(true);
  306. }
  307. /*
  308. ** Otherwise a manual check of the file system is required to
  309. ** determine if the file is actually available.
  310. */
  311. return(CDFileClass::Is_Available());
  312. }
  313. /***********************************************************************************************
  314. * CCFileClass::Is_Open -- Determines if the file is open. *
  315. * *
  316. * A mixfile is open if there is a pointer to the mixfile data. In absence of this, *
  317. * the the file is open if the file handle is valid. *
  318. * *
  319. * INPUT: none *
  320. * *
  321. * OUTPUT: bool; Is the file open? *
  322. * *
  323. * WARNINGS: none *
  324. * *
  325. * HISTORY: *
  326. * 08/08/1994 JLB : Created. *
  327. *=============================================================================================*/
  328. int CCFileClass::Is_Open(void) const
  329. {
  330. /*
  331. ** If the file is part of a cached file, then return that it is opened. A closed file
  332. ** doesn't have a valid pointer.
  333. */
  334. if (Is_Resident()) return(true);
  335. /*
  336. ** Otherwise, go to a lower level to determine if the file is open.
  337. */
  338. return(CDFileClass::Is_Open());
  339. }
  340. /***********************************************************************************************
  341. * CCFileClass::Close -- Closes the file. *
  342. * *
  343. * If this is a mixfile file, then only the pointers need to be adjusted. *
  344. * *
  345. * INPUT: none *
  346. * *
  347. * OUTPUT: none *
  348. * *
  349. * WARNINGS: none *
  350. * *
  351. * HISTORY: *
  352. * 08/08/1994 JLB : Created. *
  353. *=============================================================================================*/
  354. void CCFileClass::Close(void)
  355. {
  356. new(&Data) ::Buffer;
  357. Position = 0; // Starts at beginning offset.
  358. CDFileClass::Close();
  359. }
  360. /***********************************************************************************************
  361. * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. *
  362. * *
  363. * This routine will open the specified file. It examines the mixfile system to find a *
  364. * match. If one is found then the file is "opened" in a special cached way. Otherwise *
  365. * it is opened as a standard DOS file. *
  366. * *
  367. * INPUT: rights -- The access rights desired. *
  368. * *
  369. * OUTPUT: bool; Was the file opened successfully? *
  370. * *
  371. * WARNINGS: none *
  372. * *
  373. * HISTORY: *
  374. * 08/08/1994 JLB : Created. *
  375. *=============================================================================================*/
  376. int CCFileClass::Open(int rights)
  377. {
  378. /*
  379. ** Always close the file if it was open.
  380. */
  381. Close();
  382. /*
  383. ** Perform a preliminary check to see if the specified file
  384. ** exists on the disk. If it does, then open this file regardless
  385. ** of whether it also exists in RAM. This is slower, but allows
  386. ** upgrade files to work.
  387. */
  388. if ((rights & WRITE) || CDFileClass::Is_Available()) {
  389. return(CDFileClass::Open(rights));
  390. }
  391. /*
  392. ** Check to see if file is part of a mixfile and that mixfile is currently loaded
  393. ** into RAM.
  394. */
  395. MFCD * mixfile = NULL;
  396. void * pointer = NULL;
  397. long length = 0;
  398. long start = 0;
  399. if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) {
  400. assert(mixfile != NULL);
  401. /*
  402. ** If the mixfile is located on disk, then fake out the file system to read from
  403. ** the mixfile, but think it is reading from a solitary file.
  404. */
  405. if (pointer == NULL && mixfile != NULL) {
  406. /*
  407. ** This is a legitimate open to the file. All access to the file through this
  408. ** file object will be appropriately adjusted for mixfile support however. Also
  409. ** note that the filename attached to this object is NOT the same as the file
  410. ** attached to the file handle.
  411. */
  412. char * dupfile = strdup(File_Name());
  413. Open(mixfile->Filename, READ);
  414. Searching(false); // Disable multi-drive search.
  415. Set_Name(dupfile);
  416. Searching(true);
  417. free(dupfile);
  418. Bias(0);
  419. Bias(start, length);
  420. Seek(0, SEEK_SET);
  421. } else {
  422. new (&Data) ::Buffer(pointer, length);
  423. Position = 0;
  424. }
  425. } else {
  426. /*
  427. ** The file cannot be found in any mixfile, so it must reside as
  428. ** an individual file on the disk. Or else it is just plain missing.
  429. */
  430. return(CDFileClass::Open(rights));
  431. }
  432. return(true);
  433. }
  434. /***********************************************************************************************
  435. * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. *
  436. * *
  437. * Use this routine to get the date and time of the file. *
  438. * *
  439. * INPUT: none *
  440. * *
  441. * OUTPUT: Returns with the file date and time as a long. *
  442. * Use the YEAR(long), MONTH(),.... *
  443. * *
  444. * WARNINGS: none *
  445. * *
  446. * HISTORY: *
  447. * 11/14/1995 DRD : Created. *
  448. *=============================================================================================*/
  449. unsigned long CCFileClass::Get_Date_Time(void)
  450. {
  451. unsigned long datetime;
  452. MFCD * mixfile;
  453. datetime = CDFileClass::Get_Date_Time();
  454. if ( !datetime ) {
  455. if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) {
  456. //
  457. // check for nested MIX files
  458. //
  459. return( CCFileClass(mixfile->Filename).Get_Date_Time() );
  460. }
  461. // else return 0 indicating no file
  462. }
  463. return( datetime );
  464. }
  465. /***********************************************************************************************
  466. * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. *
  467. * *
  468. * Use this routine to set the date and time of the file. *
  469. * *
  470. * INPUT: the file date and time as a long *
  471. * *
  472. * OUTPUT: successful or not if the file date and time was changed. *
  473. * *
  474. * WARNINGS: none *
  475. * *
  476. * HISTORY: *
  477. * 11/14/1995 DRD : Created. *
  478. *=============================================================================================*/
  479. bool CCFileClass::Set_Date_Time( unsigned long datetime )
  480. {
  481. bool status;
  482. MFCD * mixfile;
  483. status = CDFileClass::Set_Date_Time( datetime );
  484. if ( !status ) {
  485. if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) {
  486. //
  487. // check for nested MIX files
  488. //
  489. return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) );
  490. }
  491. // else return 0 indicating no file
  492. }
  493. return( status );
  494. }
  495. /***********************************************************************************
  496. ** Backward compatibility section.
  497. */
  498. //extern "C" {
  499. static CCFileClass Handles[10];
  500. int __cdecl Open_File(char const * file_name, int mode)
  501. {
  502. for (int index = 0; index < ARRAY_SIZE(Handles); index++) {
  503. if (!Handles[index].Is_Open()) {
  504. if (Handles[index].Open(file_name, mode)) {
  505. return(index);
  506. }
  507. break;
  508. }
  509. }
  510. return(WWERROR);
  511. }
  512. void __cdecl Close_File(int handle)
  513. {
  514. if (handle != WWERROR && Handles[handle].Is_Open()) {
  515. Handles[handle].Close();
  516. }
  517. }
  518. long __cdecl Read_File(int handle, void * buf, unsigned long bytes)
  519. {
  520. if (handle != WWERROR && Handles[handle].Is_Open()) {
  521. return(Handles[handle].Read(buf, bytes));
  522. }
  523. return(0);
  524. }
  525. long __cdecl Write_File(int handle, void const * buf, unsigned long bytes)
  526. {
  527. if (handle != WWERROR && Handles[handle].Is_Open()) {
  528. return(Handles[handle].Write(buf, bytes));
  529. }
  530. return(0);
  531. }
  532. int __cdecl Find_File(char const * file_name)
  533. {
  534. CCFileClass file(file_name);
  535. return(file.Is_Available());
  536. }
  537. #ifdef NEVER
  538. int __cdecl Delete_File(char const * file_name)
  539. {
  540. return(CCFileClass(file_name).Delete());
  541. }
  542. int __cdecl Create_File(char const * file_name)
  543. {
  544. return(CCFileClass(file_name).Create());
  545. }
  546. unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size)
  547. {
  548. return(CCFileClass(name).Read(ptr, size));
  549. }
  550. #endif
  551. void * __cdecl Load_Alloc_Data(char const * name, int )
  552. {
  553. CCFileClass file(name);
  554. return(Load_Alloc_Data(file));
  555. }
  556. unsigned long __cdecl File_Size(int handle)
  557. {
  558. if (handle != WWERROR && Handles[handle].Is_Open()) {
  559. return(Handles[handle].Size());
  560. }
  561. return(0);
  562. }
  563. #ifdef NEVER
  564. unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size)
  565. {
  566. return(CCFileClass(name).Write(ptr, size));
  567. }
  568. #endif
  569. unsigned long __cdecl Seek_File(int handle, long offset, int starting)
  570. {
  571. if (handle != WWERROR && Handles[handle].Is_Open()) {
  572. return(Handles[handle].Seek(offset, starting));
  573. }
  574. return(0);
  575. }
  576. #ifdef NEVER
  577. bool __cdecl Multi_Drive_Search(bool on)
  578. {
  579. // return(CCFileClass::Multi_Drive_Search(on));
  580. return(on);
  581. }
  582. void __cdecl WWDOS_Init(void)
  583. {
  584. }
  585. void __cdecl WWDOS_Shutdown(void)
  586. {
  587. }
  588. int __cdecl Find_Disk_Number(char const *)
  589. {
  590. return(0);
  591. }
  592. #endif
  593. //unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data)
  594. //{
  595. // return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data));
  596. // return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data));
  597. //}
  598. #ifdef WIN32
  599. extern "C" {
  600. int MaxDevice;
  601. int DefaultDrive;
  602. char CallingDOSInt;
  603. }
  604. #endif
  605. void Unfragment_File_Cache(void)
  606. {
  607. }