CCFILE.CPP 28 KB

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