389 lines
15 KiB
C++
389 lines
15 KiB
C++
![]() |
/*
|
||
|
** 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 <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***************************************************************************
|
||
|
** 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 : FILEIO Library support routines. *
|
||
|
* *
|
||
|
* File Name : FILELIB.C *
|
||
|
* *
|
||
|
* Programmer : Scott K. Bowen *
|
||
|
* *
|
||
|
* Start Date : April 11, 1994 *
|
||
|
* *
|
||
|
* Last Update : April 11, 1994 [SKB] *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
*-------------------------------------------------------------------------*
|
||
|
* Notes: This file contains private functions to the fileio system. *
|
||
|
* While these functions may be used by any module in the fileio *
|
||
|
* system, they cannot be used by a user program. For this reason *
|
||
|
* they are put into this module. *
|
||
|
* *
|
||
|
*-------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* Cache_File -- Attempts to cache file in XMS if flags set. *
|
||
|
* Do_IO_Error -- Performs a non-recoverable error message display. *
|
||
|
* Do_Open_Error -- Does an error message that could return. *
|
||
|
* Is_Handle_Valid -- Determines validity of the specified file handle. *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
#ifndef WWSTD_H
|
||
|
#include "wwstd.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef _FILE_H
|
||
|
#include "_file.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef WWMEM_H
|
||
|
#include <wwmem.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include <fcntl.h>
|
||
|
#include <io.h>
|
||
|
#include <process.h>
|
||
|
#include <sys\stat.h>
|
||
|
|
||
|
/*=========================================================================*/
|
||
|
/* The following PRIVATE functions are in this file: */
|
||
|
/*=========================================================================*/
|
||
|
|
||
|
|
||
|
/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* DO_ERROR -- Does an error message that could return. *
|
||
|
* *
|
||
|
* This routine displays a file error message and unless the player *
|
||
|
* presses <ESC>, it will return. If the player presses <ESC>, then *
|
||
|
* it will terminate the program. *
|
||
|
* *
|
||
|
* INPUT: error -- Error message number. *
|
||
|
* *
|
||
|
* filename -- File name that the error occured on. *
|
||
|
* *
|
||
|
* OUTPUT: TRUE/FALSE; Should the process be repeated? *
|
||
|
* *
|
||
|
* WARNINGS: none *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 11/09/1991 JLB : Created. *
|
||
|
*=========================================================================*/
|
||
|
#pragma argsused
|
||
|
WORD cdecl Do_Open_Error(FileErrorType errormsgnum, BYTE const *file_name)
|
||
|
{
|
||
|
BYTE *ptr=NULL; // Working file name pointer (just name and extension).
|
||
|
|
||
|
/*
|
||
|
** Since the file name may include a path, we must extract the true
|
||
|
** file name from the given string.
|
||
|
*/
|
||
|
if (file_name) {
|
||
|
#if LIB_EXTERNS_RESOLVED
|
||
|
ptr = strrchr((char *) file_name, (int) '\\');
|
||
|
#else
|
||
|
ptr = NULL;
|
||
|
#endif
|
||
|
if (ptr) {
|
||
|
ptr++;
|
||
|
} else {
|
||
|
ptr = (BYTE *) file_name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if LIB_EXTERNS_RESOLVED
|
||
|
strupr(ptr);
|
||
|
return (IO_Error(errormsgnum, ptr));
|
||
|
#else
|
||
|
return(0);
|
||
|
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* DO_IO_ERROR -- Performs a non-recoverable error message display. *
|
||
|
* *
|
||
|
* This routine will perform a non-recoverable file error message *
|
||
|
* display. It is called when an error is detected that has no *
|
||
|
* recovery or retry process defined. *
|
||
|
* *
|
||
|
* INPUT: errornum -- Error number detected. *
|
||
|
* *
|
||
|
* filename -- Name of the file that caused the error. *
|
||
|
* *
|
||
|
* OUTPUT: none *
|
||
|
* *
|
||
|
* WARNINGS: none *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 11/09/1991 JLB : Created. *
|
||
|
*=========================================================================*/
|
||
|
#pragma argsused
|
||
|
VOID cdecl Do_IO_Error(FileErrorType errormsgnum, BYTE const *filename)
|
||
|
{
|
||
|
#if LIB_EXTERNS_RESOLVED
|
||
|
(VOID)IO_Error(errormsgnum, filename);
|
||
|
#endif
|
||
|
#if(TRUE)
|
||
|
Prog_End();
|
||
|
exit((int)errormsgnum);
|
||
|
#else
|
||
|
Program_End();
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Read_File_With_Recovery -- read the same file on another directory if an error *
|
||
|
* occurs. *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: *
|
||
|
* *
|
||
|
* OUTPUT: *
|
||
|
* *
|
||
|
* WARNINGS: *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 02/16/1993 QY : Created. *
|
||
|
*=========================================================================*/
|
||
|
LONG cdecl Read_File_With_Recovery( WORD handle, VOID *buf, UWORD bytes )
|
||
|
{
|
||
|
WORD newhandle;
|
||
|
LONG bytes_read;
|
||
|
|
||
|
do {
|
||
|
Hard_Error_Occured = 0;
|
||
|
|
||
|
// Make sure we are in the right path.
|
||
|
CHANGEDIR( DataPath );
|
||
|
|
||
|
// open the same file
|
||
|
newhandle = Open_File( FileHandleTable[ handle ].Name, FileHandleTable[ handle ].Mode );
|
||
|
Seek_File( newhandle, FileHandleTable[ handle ].Pos, SEEK_SET );
|
||
|
|
||
|
// dos close the old file
|
||
|
FILECLOSE( FileHandleTable[ handle ].Handle );
|
||
|
|
||
|
// copy FileHandleTable[ newhandle ] to FileHandleTable[ handle ]
|
||
|
Mem_Copy( &FileHandleTable[ newhandle ], &FileHandleTable[ handle ],
|
||
|
( ULONG ) sizeof( FileHandleTable[ newhandle ] ) );
|
||
|
|
||
|
// delete FileHandleTable[newhandle]
|
||
|
|
||
|
FileHandleTable[ newhandle ].Empty = TRUE;
|
||
|
|
||
|
// continue reading file
|
||
|
bytes_read = ( LONG ) FILEREAD( FileHandleTable[ handle ].Handle, buf, bytes );
|
||
|
|
||
|
// if still error, do it again; else return the number of bytes read
|
||
|
if ( !Hard_Error_Occured ) {
|
||
|
return( bytes_read );
|
||
|
}
|
||
|
if (!Do_Open_Error(COULD_NOT_OPEN, FileHandleTable[ handle ].Name)) {
|
||
|
return(FALSE);
|
||
|
}
|
||
|
|
||
|
} while (CHANGEDIR( DataPath ));
|
||
|
|
||
|
return (NULL);
|
||
|
}
|
||
|
|
||
|
/***************************************************************************
|
||
|
* Open_File_With_Recovery -- open the same file on another directory *
|
||
|
* *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: *
|
||
|
* *
|
||
|
* OUTPUT: *
|
||
|
* *
|
||
|
* WARNINGS: *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 02/16/1993 QY : Created. *
|
||
|
*=========================================================================*/
|
||
|
WORD cdecl Open_File_With_Recovery( BYTE const *file_name, UWORD mode )
|
||
|
{
|
||
|
WORD handle;
|
||
|
|
||
|
Hard_Error_Occured = FALSE;
|
||
|
handle = FILEOPEN(file_name, mode);
|
||
|
|
||
|
// Do not return if there was a HardError and Using a CD and we are looking at
|
||
|
// the CD.
|
||
|
if (!Hard_Error_Occured || !UseCD || (ibm_getdisk() != (*DataPath - 'A'))) {
|
||
|
return (handle);
|
||
|
}
|
||
|
|
||
|
#if DEBUGPRINT
|
||
|
Mono_Print(file_name); Mono_Print(":OPENERROR ");
|
||
|
#endif
|
||
|
|
||
|
Hard_Error_Occured = 0;
|
||
|
|
||
|
// It is possible that the CD has been poped out and put back in, let us
|
||
|
// change there and then try again.
|
||
|
ibm_setdisk(*DataPath - 'A');
|
||
|
CHANGEDIR( DataPath );
|
||
|
|
||
|
// open the same file
|
||
|
handle = FILEOPEN( file_name, mode );
|
||
|
|
||
|
// if still error, do it again; else return the dos handle
|
||
|
if ( !Hard_Error_Occured ) {
|
||
|
return( handle );
|
||
|
}
|
||
|
|
||
|
Hard_Error_Occured = 0;
|
||
|
return (FILEOPENERROR);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***************************************************************************
|
||
|
* CACHE_FILE -- Attempts to cache file in XMS if flags set. *
|
||
|
* *
|
||
|
* *
|
||
|
* INPUT: WORD index - the index of the file in the FileData table. *
|
||
|
* WORD file_handle - WWS file handle of file. *
|
||
|
* *
|
||
|
* OUTPUT: BOOL : was it cached? *
|
||
|
* *
|
||
|
* WARNINGS: *
|
||
|
* *
|
||
|
* HISTORY: *
|
||
|
* 06/21/1993 SKB : Created. *
|
||
|
*=========================================================================*/
|
||
|
BOOL cdecl Cache_File(WORD index, WORD file_handle)
|
||
|
{
|
||
|
LONG filesize; // Size of the memory block needed.
|
||
|
LONG freecache; // Amount of free XMS.
|
||
|
FileDataType *filedata = NULL;
|
||
|
FileDataType hold;
|
||
|
FileHandleType *filehandletable;
|
||
|
WORD flag; // Type of system memory to cache file.
|
||
|
WORD file;
|
||
|
|
||
|
// Only files in the file table can be cached.
|
||
|
if (index == ERROR) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Setup our pointer to the file we may want to cache.
|
||
|
filedata = &FileDataPtr[index];
|
||
|
|
||
|
// Should this be cached, and is it not yet cached?
|
||
|
if ((filedata->Flag & (FILEF_RESIDENT|FILEF_PRELOAD)) && !filedata->Ptr) {
|
||
|
|
||
|
filesize = filedata->Size;
|
||
|
|
||
|
/*
|
||
|
** If there is o room to cache the file, then turn off its cache file.
|
||
|
*/
|
||
|
if (filesize > Mem_Pool_Size(FileCacheHeap)) {
|
||
|
|
||
|
// Remove resident flags so that it will not keep trying to cache itself
|
||
|
// since there will never be enough room for it.
|
||
|
filedata->Flag &= ~(FILEF_PRELOAD|FILEF_KEEP|FILEF_RESIDENT|FILEF_FLUSH);
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Go through freeing files until there is enouph space in the
|
||
|
// memory pool.
|
||
|
while (filesize > Mem_Avail(FileCacheHeap)) {
|
||
|
VOID *node;
|
||
|
|
||
|
// Get the oldest non used file pointer.
|
||
|
node = Mem_Find_Oldest(FileCacheHeap);
|
||
|
|
||
|
// If non was found, sorry no room for the new file.
|
||
|
if (!node) {
|
||
|
return (FALSE);
|
||
|
}
|
||
|
|
||
|
// Get a pointer to the structure for convenience.
|
||
|
filedata = &FileDataPtr[Mem_Get_ID(node)];
|
||
|
|
||
|
// Free it from the heap and update the file system so it knows that
|
||
|
// the file is no longer in memory.
|
||
|
Mem_Free(FileCacheHeap, filedata->Ptr);
|
||
|
filedata->Ptr = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If there is not a big enough space we will have to take garbage
|
||
|
// collection hit. (OUCH!!!!!!)
|
||
|
if (filesize > Mem_Largest_Avail(FileCacheHeap)) {
|
||
|
Unfragment_File_Cache();
|
||
|
}
|
||
|
|
||
|
// Make sure we have a big enough space and if so, put the file into memory.
|
||
|
if (filesize < Mem_Largest_Avail(FileCacheHeap)) {
|
||
|
|
||
|
// Get some pointers to save code space and time.
|
||
|
filehandletable = &FileHandleTable[file_handle];
|
||
|
filedata = &FileDataPtr[index];
|
||
|
|
||
|
// Alloc the buffer in our file cache, then read the file in.
|
||
|
filedata->Ptr = Mem_Alloc(FileCacheHeap, filesize, index);
|
||
|
|
||
|
// Extra check - it should not fail.
|
||
|
if (!filedata->Ptr) return(FALSE);
|
||
|
|
||
|
// Mark it so that it never comes back as Oldest used.
|
||
|
Mem_In_Use(filedata->Ptr);
|
||
|
|
||
|
// Get the file into memory.
|
||
|
Read_File(file_handle, filedata->Ptr, filesize);
|
||
|
|
||
|
// reset the read index from the above read.
|
||
|
filehandletable->Pos = 0L;
|
||
|
|
||
|
// This makes caching inner pak file possible. No longer is the
|
||
|
// PAK'd file based off the parent file.
|
||
|
filehandletable->Start = 0;
|
||
|
|
||
|
// Close the parent file. Remove it's open count.
|
||
|
if (filedata->Flag & FILEF_PACKED) {
|
||
|
FileDataType p_hold;
|
||
|
FileDataType *parent;
|
||
|
|
||
|
parent = &FileDataPtr[filedata->Disk];
|
||
|
parent->OpenCount--;
|
||
|
}
|
||
|
FILECLOSE(filehandletable->Handle);
|
||
|
filehandletable->Handle = 0;
|
||
|
|
||
|
return (TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The file was not cached, let the caller know.
|
||
|
return (FALSE);
|
||
|
}
|