FILELIB.CPP 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. /***************************************************************************
  19. ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S **
  20. ***************************************************************************
  21. * *
  22. * Project Name : FILEIO Library support routines. *
  23. * *
  24. * File Name : FILELIB.C *
  25. * *
  26. * Programmer : Scott K. Bowen *
  27. * *
  28. * Start Date : April 11, 1994 *
  29. * *
  30. * Last Update : April 11, 1994 [SKB] *
  31. * *
  32. * *
  33. * *
  34. *-------------------------------------------------------------------------*
  35. * Notes: This file contains private functions to the fileio system. *
  36. * While these functions may be used by any module in the fileio *
  37. * system, they cannot be used by a user program. For this reason *
  38. * they are put into this module. *
  39. * *
  40. *-------------------------------------------------------------------------*
  41. * Functions: *
  42. * Cache_File -- Attempts to cache file in XMS if flags set. *
  43. * Do_IO_Error -- Performs a non-recoverable error message display. *
  44. * Do_Open_Error -- Does an error message that could return. *
  45. * Is_Handle_Valid -- Determines validity of the specified file handle. *
  46. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  47. #ifndef WWSTD_H
  48. #include "wwstd.h"
  49. #endif
  50. #ifndef _FILE_H
  51. #include "_file.h"
  52. #endif
  53. #ifndef WWMEM_H
  54. #include <wwmem.h>
  55. #endif
  56. #include <fcntl.h>
  57. #include <io.h>
  58. #include <process.h>
  59. #include <sys\stat.h>
  60. /*=========================================================================*/
  61. /* The following PRIVATE functions are in this file: */
  62. /*=========================================================================*/
  63. /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
  64. /***************************************************************************
  65. * DO_ERROR -- Does an error message that could return. *
  66. * *
  67. * This routine displays a file error message and unless the player *
  68. * presses <ESC>, it will return. If the player presses <ESC>, then *
  69. * it will terminate the program. *
  70. * *
  71. * INPUT: error -- Error message number. *
  72. * *
  73. * filename -- File name that the error occured on. *
  74. * *
  75. * OUTPUT: TRUE/FALSE; Should the process be repeated? *
  76. * *
  77. * WARNINGS: none *
  78. * *
  79. * HISTORY: *
  80. * 11/09/1991 JLB : Created. *
  81. *=========================================================================*/
  82. #pragma argsused
  83. WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name)
  84. {
  85. BYTE *ptr=NULL; // Working file name pointer (just name and extension).
  86. /*
  87. ** Since the file name may include a path, we must extract the true
  88. ** file name from the given string.
  89. */
  90. if (file_name) {
  91. #if LIB_EXTERNS_RESOLVED
  92. ptr = strrchr((char *) file_name, (int) '\\');
  93. #else
  94. ptr = NULL;
  95. #endif
  96. if (ptr) {
  97. ptr++;
  98. } else {
  99. ptr = (BYTE *) file_name;
  100. }
  101. }
  102. #if LIB_EXTERNS_RESOLVED
  103. strupr(ptr);
  104. return (IO_Error(errormsgnum, ptr));
  105. #else
  106. return(0);
  107. #endif
  108. }
  109. /***************************************************************************
  110. * DO_IO_ERROR -- Performs a non-recoverable error message display. *
  111. * *
  112. * This routine will perform a non-recoverable file error message *
  113. * display. It is called when an error is detected that has no *
  114. * recovery or retry process defined. *
  115. * *
  116. * INPUT: errornum -- Error number detected. *
  117. * *
  118. * filename -- Name of the file that caused the error. *
  119. * *
  120. * OUTPUT: none *
  121. * *
  122. * WARNINGS: none *
  123. * *
  124. * HISTORY: *
  125. * 11/09/1991 JLB : Created. *
  126. *=========================================================================*/
  127. #pragma argsused
  128. VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename)
  129. {
  130. #if LIB_EXTERNS_RESOLVED
  131. (VOID)IO_Error(errormsgnum, filename);
  132. #endif
  133. #if(TRUE)
  134. Prog_End();
  135. exit((int)errormsgnum);
  136. #else
  137. Program_End();
  138. #endif
  139. }
  140. /***************************************************************************
  141. * Read_File_With_Recovery -- read the same file on another directory if an error *
  142. * occurs. *
  143. * *
  144. * *
  145. * INPUT: *
  146. * *
  147. * OUTPUT: *
  148. * *
  149. * WARNINGS: *
  150. * *
  151. * HISTORY: *
  152. * 02/16/1993 QY : Created. *
  153. *=========================================================================*/
  154. LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes )
  155. {
  156. WORD newhandle;
  157. LONG bytes_read;
  158. do {
  159. Hard_Error_Occured = 0;
  160. // Make sure we are in the right path.
  161. CHANGEDIR( DataPath );
  162. // open the same file
  163. newhandle = Open_File( FileHandleTable[ handle ].Name, FileHandleTable[ handle ].Mode );
  164. Seek_File( newhandle, FileHandleTable[ handle ].Pos, SEEK_SET );
  165. // dos close the old file
  166. FILECLOSE( FileHandleTable[ handle ].Handle );
  167. // copy FileHandleTable[ newhandle ] to FileHandleTable[ handle ]
  168. Mem_Copy( &FileHandleTable[ newhandle ], &FileHandleTable[ handle ],
  169. ( ULONG ) sizeof( FileHandleTable[ newhandle ] ) );
  170. // delete FileHandleTable[newhandle]
  171. FileHandleTable[ newhandle ].Empty = TRUE;
  172. // continue reading file
  173. bytes_read = ( LONG ) FILEREAD( FileHandleTable[ handle ].Handle, buf, bytes );
  174. // if still error, do it again; else return the number of bytes read
  175. if ( !Hard_Error_Occured ) {
  176. return( bytes_read );
  177. }
  178. if (!Do_Open_Error(COULD_NOT_OPEN, FileHandleTable[ handle ].Name)) {
  179. return(FALSE);
  180. }
  181. } while (CHANGEDIR( DataPath ));
  182. return (NULL);
  183. }
  184. /***************************************************************************
  185. * Open_File_With_Recovery -- open the same file on another directory *
  186. * *
  187. * *
  188. * *
  189. * INPUT: *
  190. * *
  191. * OUTPUT: *
  192. * *
  193. * WARNINGS: *
  194. * *
  195. * HISTORY: *
  196. * 02/16/1993 QY : Created. *
  197. *=========================================================================*/
  198. WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode )
  199. {
  200. WORD handle;
  201. Hard_Error_Occured = FALSE;
  202. handle = FILEOPEN(file_name, mode);
  203. // Do not return if there was a HardError and Using a CD and we are looking at
  204. // the CD.
  205. if (!Hard_Error_Occured || !UseCD || (ibm_getdisk() != (*DataPath - 'A'))) {
  206. return (handle);
  207. }
  208. #if DEBUGPRINT
  209. Mono_Print(file_name); Mono_Print(":OPENERROR ");
  210. #endif
  211. Hard_Error_Occured = 0;
  212. // It is possible that the CD has been poped out and put back in, let us
  213. // change there and then try again.
  214. ibm_setdisk(*DataPath - 'A');
  215. CHANGEDIR( DataPath );
  216. // open the same file
  217. handle = FILEOPEN( file_name, mode );
  218. // if still error, do it again; else return the dos handle
  219. if ( !Hard_Error_Occured ) {
  220. return( handle );
  221. }
  222. Hard_Error_Occured = 0;
  223. return (FILEOPENERROR);
  224. }
  225. /***************************************************************************
  226. * CACHE_FILE -- Attempts to cache file in XMS if flags set. *
  227. * *
  228. * *
  229. * INPUT: WORD index - the index of the file in the FileData table. *
  230. * WORD file_handle - WWS file handle of file. *
  231. * *
  232. * OUTPUT: BOOL : was it cached? *
  233. * *
  234. * WARNINGS: *
  235. * *
  236. * HISTORY: *
  237. * 06/21/1993 SKB : Created. *
  238. *=========================================================================*/
  239. BOOL cdecl Cache_File(WORD index, WORD file_handle)
  240. {
  241. LONG filesize; // Size of the memory block needed.
  242. LONG freecache; // Amount of free XMS.
  243. FileDataType *filedata = NULL;
  244. FileDataType hold;
  245. FileHandleType *filehandletable;
  246. WORD flag; // Type of system memory to cache file.
  247. WORD file;
  248. // Only files in the file table can be cached.
  249. if (index == ERROR) {
  250. return FALSE;
  251. }
  252. // Setup our pointer to the file we may want to cache.
  253. filedata = &FileDataPtr[index];
  254. // Should this be cached, and is it not yet cached?
  255. if ((filedata->Flag & (FILEF_RESIDENT|FILEF_PRELOAD)) && !filedata->Ptr) {
  256. filesize = filedata->Size;
  257. /*
  258. ** If there is o room to cache the file, then turn off its cache file.
  259. */
  260. if (filesize > Mem_Pool_Size(FileCacheHeap)) {
  261. // Remove resident flags so that it will not keep trying to cache itself
  262. // since there will never be enough room for it.
  263. filedata->Flag &= ~(FILEF_PRELOAD|FILEF_KEEP|FILEF_RESIDENT|FILEF_FLUSH);
  264. return FALSE;
  265. }
  266. // Go through freeing files until there is enouph space in the
  267. // memory pool.
  268. while (filesize > Mem_Avail(FileCacheHeap)) {
  269. VOID *node;
  270. // Get the oldest non used file pointer.
  271. node = Mem_Find_Oldest(FileCacheHeap);
  272. // If non was found, sorry no room for the new file.
  273. if (!node) {
  274. return (FALSE);
  275. }
  276. // Get a pointer to the structure for convenience.
  277. filedata = &FileDataPtr[Mem_Get_ID(node)];
  278. // Free it from the heap and update the file system so it knows that
  279. // the file is no longer in memory.
  280. Mem_Free(FileCacheHeap, filedata->Ptr);
  281. filedata->Ptr = NULL;
  282. }
  283. // If there is not a big enough space we will have to take garbage
  284. // collection hit. (OUCH!!!!!!)
  285. if (filesize > Mem_Largest_Avail(FileCacheHeap)) {
  286. Unfragment_File_Cache();
  287. }
  288. // Make sure we have a big enough space and if so, put the file into memory.
  289. if (filesize < Mem_Largest_Avail(FileCacheHeap)) {
  290. // Get some pointers to save code space and time.
  291. filehandletable = &FileHandleTable[file_handle];
  292. filedata = &FileDataPtr[index];
  293. // Alloc the buffer in our file cache, then read the file in.
  294. filedata->Ptr = Mem_Alloc(FileCacheHeap, filesize, index);
  295. // Extra check - it should not fail.
  296. if (!filedata->Ptr) return(FALSE);
  297. // Mark it so that it never comes back as Oldest used.
  298. Mem_In_Use(filedata->Ptr);
  299. // Get the file into memory.
  300. Read_File(file_handle, filedata->Ptr, filesize);
  301. // reset the read index from the above read.
  302. filehandletable->Pos = 0L;
  303. // This makes caching inner pak file possible. No longer is the
  304. // PAK'd file based off the parent file.
  305. filehandletable->Start = 0;
  306. // Close the parent file. Remove it's open count.
  307. if (filedata->Flag & FILEF_PACKED) {
  308. FileDataType p_hold;
  309. FileDataType *parent;
  310. parent = &FileDataPtr[filedata->Disk];
  311. parent->OpenCount--;
  312. }
  313. FILECLOSE(filehandletable->Handle);
  314. filehandletable->Handle = 0;
  315. return (TRUE);
  316. }
  317. }
  318. // The file was not cached, let the caller know.
  319. return (FALSE);
  320. }