CCFILE.CPP 32 KB

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