CCFILE.CPP 28 KB

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