/* ** Command & Conquer Red Alert(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*************************************************************************** ** 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 ** *************************************************************************** * * * Project Name : Library - Fileio init routines. * * * * File Name : FILEINIT.C * * * * Programmer : Scott K. Bowen * * * * Start Date : September 13, 1993 * * * * Last Update : April 19, 1994 [SKB] * * * *-------------------------------------------------------------------------* * Functions: * * WWDOS_Init -- Initialize the fileio WWS fileio system. * * WWDOS_Shutdown -- Clean up any things that needs to be to exit game. * * Init_FileData_Table -- Initializes or reads in FileData Table. * * Sort_FileData_Table -- Sorts the FileData table that is in memory. * * Preload_Files -- Loads files marked with FILEF_PRELOAD into cache. * * Init_File_Cache -- Initializes and allocs the file cache heap. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #ifndef WWSTD_H #include "wwstd.h" #endif #ifndef _FILE_H #include "_file.h" #endif #ifndef WWMEM_H #include #endif #ifndef MISC_H #include #endif #include #include #include /*=========================================================================*/ /* The following PRIVATE functions are in this file: */ /*=========================================================================*/ PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize); PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename); PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ); PRIVATE FileInitErrorType cdecl Preload_Files(VOID); PRIVATE int QSort_Comp_Func(const void *p1, const void *p2); PRIVATE VOID Sort_FileData_Table(VOID); /*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ /*************************************************************************** * WWDOS_INIT -- Initialize the fileio WWS fileio system. * * * * * * INPUT: ULONG cachesize - size wanted for the cache. * * BYTE *filedat - NULL or name of filedata table file. * * BYTE *cdpath - NULL or secondary search path on a CD. * * * * OUTPUT: Returns all errors encountered or'd together. * * * * WARNINGS: User should call the WWDOS_Init function for all file * * initialization. * * * * HISTORY: * * 04/19/1994 SKB : Created. * *=========================================================================*/ FileInitErrorType cdecl WWDOS_Init(ULONG cachesize, BYTE *filedata, BYTE *cdpath) { // FileInitErrorType errors; unsigned errors ; // This has not been completed yet, when it is, uncomment it and add errors. Install_Hard_Error_Handler () ; Get_Devices(); if (cachesize) { errors = Init_File_Cache(cachesize); } else { errors = FI_SUCCESS; } errors = errors | Init_FileData_Table(filedata); errors = errors | Set_Search_Drives(cdpath); errors = errors | Preload_Files(); return ( FileInitErrorType ) errors ; } /*************************************************************************** * WWDOS_SHUTDOWN -- Clean up any things that needs to be in file syste to * * exit game. * * One could shut down the file system and open it back * * up with a different size cache or filetable. * * * * INPUT: NONE. * * * * OUTPUT: NONE. * * * * WARNINGS: * * * * HISTORY: * * 04/19/1994 SKB : Created. * *=========================================================================*/ VOID cdecl WWDOS_Shutdown(VOID) { FileDataType *filedata; // Pointer to the current FileData. WORD file_handle; FileHandleType *filehandletable; // Pointer to the current file handle. // Close all open files. filehandletable = FileHandleTable; for (file_handle = 0; file_handle < TABLE_MAX; file_handle++, filehandletable++) { if (!filehandletable->Empty) { Close_File(file_handle); } } // Free the file cache heap. if (FileCacheHeap) { // Get a pointer to the current filedata. if (FileDataPtr) { filedata = FileDataPtr; } else { filedata = FileData; } while(filedata->Name && filedata->Name[0]) { filedata->Ptr = NULL; filedata++; } Free(FileCacheHeap); FileCacheHeap = NULL; } // Free up the file data. if (FileDataPtr != FileData) { Free(FileDataPtr); } FileDataPtr = NULL; chdir(StartPath); ibm_setdisk(*StartPath - 'A'); // This has not been completed yet, when it is, uncomment it and add errors. Remove_Hard_Error_Handler(); } /*************************************************************************** * INIT_FILE_CACHE -- Initializes and allocs the file cache heap. * * * * INPUT: ULONG cachesize - size of heap cache.. * * * * OUTPUT: FileInitErrorType error code. * * * * WARNINGS: * * * * HISTORY: * * 04/19/1994 SKB : Created. * *=========================================================================*/ PRIVATE FileInitErrorType cdecl Init_File_Cache(ULONG cachesize) { // Allocate and initialize the file cache heap. if (FileCacheHeap) { return (FI_CACHE_ALREADY_INIT); } if ((Ram_Free(MEM_NORMAL) >= cachesize)) { FileCacheHeap = Alloc(cachesize, MEM_NORMAL); Mem_Init(FileCacheHeap, cachesize); } if (!FileCacheHeap) { return (FI_CACHE_TOO_BIG); } return (FI_SUCCESS); } /*************************************************************************** * INIT_FILEDATA_TABLE -- Initializes or reads in FileData Table. * * * * INPUT: * * * * OUTPUT: FileInitErrorType error code. * * * * WARNINGS: * * * * HISTORY: * * 09/13/1993 SKB : Created. * *=========================================================================*/ PRIVATE FileInitErrorType cdecl Init_FileData_Table(BYTE const *filename) { WORD fd; ULONG fsize; FileDataType *ptr; WORD index; BYTE fname[13]; /* ** Inialize the file handle table to reflect no files open. */ for (index = 0; index < TABLE_MAX; index++) { FileHandleTable[index].Empty = TRUE; } // Set up our FileData ptr to be the initial FileData table. FileDataPtr = FileData; // Sort the filedata table. // This needs to be done even if we load it off disk since the initial file data // table might contain a filename. Sort_FileData_Table(); // If there is a file name, then the filedata table will be loaded from disk. if (filename) { if (!Find_File(filename)) { return (FI_FILEDATA_FILE_NOT_FOUND); } fd = Open_File(filename, READ); fsize = File_Size(fd); if ((Ram_Free(MEM_NORMAL) < fsize)) { Close_File(fd); return (FI_FILEDATA_TOO_BIG); } // Allocate some system memory. // Setup the new FileDataPtr and this time. FileDataPtr = ptr = (FileDataType *) Alloc(fsize, MEM_NORMAL); // Load the file up into memory. Read_File(fd, FileDataPtr, fsize); Close_File(fd); // Process the filetable. The filenames need their pointers adjusted. // At this time we will also count the number of files and number of PAK files. NumPAKFiles = NumFiles = 0; // Make sure that the file name will have a NUL at the end. fname[12] = 0; while(TRUE) { // Have we reached the end of the list? if (!ptr->Name) break; // Adjust the name pointer to point the the correct area. ptr->Name = (BYTE *)FileDataPtr + (LONG) ptr->Name; // Count up weather it is a PAK file or a normal file. if (!NumFiles && strstr((char *) ptr->Name, (char *) ".PAK")) { NumPAKFiles++; // Mark that it has been processed so that Open_File() will not do it. ptr->Flag |= FILEF_PROCESSED; } else { NumFiles++; } // Next record. ptr++; } } return (FI_SUCCESS); } /*************************************************************************** * Set_Search_Drives -- Sets up the CDRom and HardDrive paths. * * * * INPUT: BYTE *cdpath - path of data files on a CD. * * Should pass in NULL for non CD products. * * * * OUTPUT: FileInitErrorType error code. * * Varibable defined: * * ExecPath = Full path of EXE file. * * StartPath = Directory user started in. * * DataPath = secondary search path (typically CD-ROM). * * Note: format of paths is "C:\PATH" * * * * WARNINGS: The cdpath may be overiden by a "-CD" command line * * arguement that specifies another drive (HARD or CD) and path * * where the data resides. Whenever a file is opened, it checks * * the startup drive first, then the CD search path if the first * * search was unsuccessful. * * * * HISTORY: * * 01/14/1993 SB : Created. * * 04/19/1994 SKB : Mods for 32 bit library. * *=========================================================================*/ PRIVATE FileInitErrorType cdecl Set_Search_Drives( BYTE *cdpath ) { BYTE *ptr; #if LIB_EXTERNS_RESOLVED // NOTE: THIS IS WRONG, THIS IS NOT THE WAY TO GET THE EXE's PATH. // Locate the executable. strcpy(ExecPath, _argv[0]); // Find the very last '\' on the path. ptr = strrchr((char *) ExecPath, (int) '\\'); #else ptr = NULL; #endif // Remove the exe name to just have the path. if (ptr == NULL) { *ExecPath = 0; } else { *ptr = 0; } // Did the user specify a second path? ptr = Find_Argv("-CD"); // If so, set the data path to that. if (ptr) { strcpy(DataPath, ptr + 3); } // Otherwise check to see if there is a CD-Rom drive. else { if (cdpath && *cdpath) { #if LIB_EXTERNS_RESOLVED UseCD = GetCDDrive(); #else UseCD = FALSE; #endif } else { UseCD = FALSE; } // If so, set the Drive to it and find out if any directories. if ( UseCD ) { strcpy( DataPath, "A:" ); strcat( DataPath, cdpath); *DataPath = 'A'+UseCD; } // If not, set the Data path to the execacutable path. else { strcpy(DataPath, ExecPath); } } // Finnally, set the starting path. getcwd(StartPath, XMAXPATH); // Make sure they are all uppercase. strupr(StartPath); strupr(DataPath); strupr(ExecPath); // Change directories to the secondary search path (DataPath). if (*DataPath && chdir(DataPath)) { return (FI_SEARCH_PATH_NOT_FOUND); } // Lastley, Make sure we are in the startup directory. This will overide // the secondary data path if they are on the same drive. if (chdir(StartPath)) { return (FI_STARTUP_PATH_NOT_FOUND); } return (FI_SUCCESS); } /*************************************************************************** * PRELOAD_FILES -- Loads files marked with FILEF_PRELOAD into cache. * * * * * * INPUT: none. * * * * OUTPUT: FileInitErrorType error code. * * * * WARNINGS: The FileData must be initialized and the file heap initialized* * in order for this to work. * * * * HISTORY: * * 04/19/1994 SKB : Created. * *=========================================================================*/ PRIVATE FileInitErrorType cdecl Preload_Files(VOID) { FileDataType *filedata; // Working file data table pointer. BOOL oldflag; // Previous file flag. if (!FileDataPtr) { return (FI_FILETABLE_NOT_INIT); } if (!FileCacheHeap) { return (FI_NO_CACHE_FOR_PRELOAD); } /* ** Make all files flagged to be made resident at startup, resident. */ filedata = FileDataPtr; while (filedata->Name && strlen(filedata->Name)) { if (filedata->Flag & FILEF_PRELOAD) { oldflag = filedata->Flag; filedata->Flag |= FILEF_RESIDENT; // Make it resident. filedata->Flag &= ~FILEF_FLUSH; // Don't purge on Close_File. Close_File(Open_File(filedata->Name, READ)); filedata->Flag &= ~(FILEF_RESIDENT|FILEF_FLUSH); // Clear bits. filedata->Flag |= oldflag & (FILEF_RESIDENT|FILEF_FLUSH); // Restore bits. } filedata++; } return (FI_SUCCESS); } /*************************************************************************** * SORT_FILEDATA_TABLE -- Sorts the FileData table that is in memory. * * * * INPUT: NONE * * * * OUTPUT: NONE. * * * * WARNINGS: * * * * HISTORY: * * 09/13/1993 SKB : Created. * *=========================================================================*/ PRIVATE int QSort_Comp_Func(const void *p1, const void *p2) { return(strcmp(((FileDataType*)p1)->Name, ((FileDataType*)p2)->Name)); } PRIVATE VOID Sort_FileData_Table(VOID) { /* ** Sort the filetable it but keep the pack file indexes correct. */ /* ** The number of pak files in the file table. */ NumPAKFiles = 0; strupr(FileData[NumPAKFiles].Name); while (strstr((char *) FileData[NumPAKFiles].Name, (char *) ".PAK")) { strupr(FileData[NumPAKFiles].Name); NumPAKFiles++; } /* ** Count the remaining files within the file table. */ NumFiles = 0; while(FileData[NumFiles+NumPAKFiles].Name && FileData[NumFiles+NumPAKFiles].Name[0]) { strupr(FileData[NumFiles+NumPAKFiles].Name); NumFiles++; } /* ** Sort the file entries (past the pak files). */ if (NumFiles) { qsort(&FileData[NumPAKFiles], NumFiles, sizeof(FileDataType), QSort_Comp_Func); } }