CnC_Remastered_Collection/REDALERT/BFIOFILE.CPP

985 lines
40 KiB
C++
Raw Normal View History

//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code 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.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
/* $Header: /CounterStrike/BFIOFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */
/***********************************************************************************************
*** 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 ***
***********************************************************************************************
* *
* Project Name : Westwood Library *
* *
* File Name : RAMFILE.CPP *
* *
* Programmer : David R. Dettmer *
* *
* Start Date : November 10, 1995 *
* *
* Last Update : November 10, 1995 [DRD] *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. *
* BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. *
* BufferIOFileClass::Cache -- Load part or all of a file data into RAM. *
* BufferIOFileClass::Close -- Perform a closure of the file. *
* BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. *
* BufferIOFileClass::Free -- Frees the allocated buffer. *
* BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. *
* BufferIOFileClass::Is_Open -- Determines if the file is open. *
* BufferIOFileClass::Open -- Assigns name and opens file in one operation. *
* BufferIOFileClass::Open -- Opens the file object with the rights specified. *
* BufferIOFileClass::Read -- Reads data from the file cache. *
* BufferIOFileClass::Seek -- Moves the current file pointer in the file. *
* BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. *
* BufferIOFileClass::Size -- Determines size of file (in bytes). *
* BufferIOFileClass::Write -- Writes data to the file cache. *
* BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "bfiofile.h"
#include <string.h>
/***********************************************************************************************
* BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. *
* *
* This constructor is called when a file object is created with a supplied filename, but *
* not opened at the same time. In this case, an assumption is made that the supplied *
* filename is a constant string. A duplicate of the filename string is not created since *
* it would be wasteful in that case. *
* *
* INPUT: filename -- The filename to assign to this file object. *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/10/1995 DRD : Created. *
*=============================================================================================*/
BufferIOFileClass::BufferIOFileClass(char const * filename) :
IsAllocated(false),
IsOpen(false),
IsDiskOpen(false),
IsCached(false),
IsChanged(false),
UseBuffer(false),
BufferRights(0),
Buffer(0),
BufferSize(0),
BufferPos(0),
BufferFilePos(0),
BufferChangeBeg(-1),
BufferChangeEnd(-1),
FileSize(0),
FilePos(0),
TrueFileStart(0)
{
BufferIOFileClass::Set_Name(filename);
}
/***********************************************************************************************
* BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. *
* *
* This is the default constructor for a file object. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/10/1995 DRD : Created. *
*=============================================================================================*/
BufferIOFileClass::BufferIOFileClass(void) :
IsAllocated(false),
IsOpen(false),
IsDiskOpen(false),
IsCached(false),
IsChanged(false),
UseBuffer(false),
BufferRights(0),
Buffer(0),
BufferSize(0),
BufferPos(0),
BufferFilePos(0),
BufferChangeBeg(-1),
BufferChangeEnd(-1),
FileSize(0),
FilePos(0),
TrueFileStart(0)
{
}
/***********************************************************************************************
* BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. *
* *
* This destructor will free all memory allocated thru using Cache routines. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/10/1995 DRD : Created. *
*=============================================================================================*/
BufferIOFileClass::~BufferIOFileClass(void)
{
Free();
}
/***********************************************************************************************
* BufferIOFileClass::Cache -- Load part or all of a file data into RAM. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room *
* to allocate the raw data block. *
* *
* WARNINGS: This routine goes to disk for a potentially very long time. *
* *
* HISTORY: *
* 11/10/1995 DRD : Created. *
*=============================================================================================*/
bool BufferIOFileClass::Cache( long size, void * ptr )
{
if (Buffer) {
//
// if trying to cache again with size or ptr fail
//
if (size || ptr) {
return( false );
} else {
return( true );
}
}
if ( Is_Available() ) {
FileSize = Size();
} else {
FileSize = 0;
}
if (size) {
//
// minimum buffer size for performance
//
if (size < MINIMUM_BUFFER_SIZE) {
size = MINIMUM_BUFFER_SIZE;
/*
** Specifying a size smaller than the minimum is an error
** IF a buffer pointer was also specified. In such a case the
** system cannot use the buffer.
*/
if (ptr) {
Error(EINVAL);
}
}
BufferSize = size;
} else {
BufferSize = FileSize;
}
//
// if size == 0 and a ptr to a buffer is specified then that is invalid.
// if the BufferSize is 0 then this must be a new file and no size was
// specified so exit.
//
if ( (size == 0 && ptr) || !BufferSize) {
return( false );
}
if (ptr) {
Buffer = ptr;
} else {
Buffer = new char [BufferSize];
}
if (Buffer) {
IsAllocated = true;
IsDiskOpen = false;
BufferPos = 0;
BufferFilePos = 0;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
FilePos = 0;
TrueFileStart = 0;
//
// the file was checked for availability then set the FileSize
//
if (FileSize) {
long readsize;
int opened = false;
long prevpos = 0;
if (FileSize <= BufferSize) {
readsize = FileSize;
} else {
readsize = BufferSize;
}
if ( Is_Open() ) {
//
// get previous file position
//
prevpos = Seek(0);
//
// get true file position
//
if ( RawFileClass::Is_Open() ) {
TrueFileStart = RawFileClass::Seek(0);
} else {
TrueFileStart = prevpos;
}
if (FileSize <= BufferSize) {
//
// if previous position is non-zero seek to the beginning
//
if (prevpos) {
Seek(0, SEEK_SET);
}
//
// set the buffer position for future reads/writes
//
BufferPos = prevpos;
} else {
BufferFilePos = prevpos;
}
FilePos = prevpos;
} else {
if ( Open() ) {
TrueFileStart = RawFileClass::Seek(0);
opened = true;
}
}
long actual = Read(Buffer, readsize);
if (actual != readsize) {
Error(EIO);
}
if (opened) {
Close();
} else {
//
// seek to the previous position in the file
//
Seek(prevpos, SEEK_SET);
}
IsCached = true;
}
UseBuffer = true;
return(true);
}
Error(ENOMEM);
return(false);
}
/***********************************************************************************************
* BufferIOFileClass::Free -- Frees the allocated buffer. *
* *
* This routine will free the buffer. By using this in conjunction with the *
* Cache() function, one can maintain tight control of memory usage. *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/10/1995 DRD : Created. *
*=============================================================================================*/
void BufferIOFileClass::Free(void)
{
if (Buffer) {
if (IsAllocated) {
delete [] Buffer;
IsAllocated = false;
}
Buffer = 0;
}
BufferSize = 0;
IsOpen = false;
IsCached = false;
IsChanged = false;
UseBuffer = false;
}
/***********************************************************************************************
* BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. *
* *
* *
* INPUT: none *
* *
* OUTPUT: false, did not need to write the buffer. *
* true, wrote the buffer. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/15/1995 DRD : Created. *
*=============================================================================================*/
bool BufferIOFileClass::Commit( void )
{
long size;
if (UseBuffer) {
if (IsChanged) {
size = BufferChangeEnd - BufferChangeBeg;
if (IsDiskOpen) {
RawFileClass::Seek( TrueFileStart + BufferFilePos +
BufferChangeBeg, SEEK_SET );
RawFileClass::Write( Buffer, size );
RawFileClass::Seek( TrueFileStart + FilePos, SEEK_SET );
} else {
RawFileClass::Open();
RawFileClass::Seek( TrueFileStart + BufferFilePos +
BufferChangeBeg, SEEK_SET );
RawFileClass::Write( Buffer, size );
RawFileClass::Close();
}
IsChanged = false;
return( true );
} else {
return( false );
}
} else {
return( false );
}
}
/***********************************************************************************************
* BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. *
* *
* Checks for a previous filename and that it is cached. If so, then check the *
* new filename against the old. If they are the same then return that filename. *
* Otherwise, the file object's name is set with just the raw filename as passed *
* to this routine. *
* *
* INPUT: filename -- Pointer to the filename to set as the name of this file object. *
* *
* OUTPUT: Returns a pointer to the final and complete filename of this file object. This *
* may have a path attached to the file. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/15/1995 DRD : Created. *
*=============================================================================================*/
char const * BufferIOFileClass::Set_Name(char const * filename)
{
if ( File_Name() && UseBuffer) {
if ( strcmp(filename, File_Name() ) == 0) {
return( File_Name() );
} else {
Commit();
IsCached = false;
}
}
RawFileClass::Set_Name(filename);
return( File_Name() );
}
/***********************************************************************************************
* BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. *
* *
* *
* INPUT: none *
* *
* OUTPUT: bool; Is the file available for opening? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/16/1995 DRD : Created. *
*=============================================================================================*/
int BufferIOFileClass::Is_Available(int )
{
if (UseBuffer) {
return(true);
}
return( RawFileClass::Is_Available() );
}
/***********************************************************************************************
* BufferIOFileClass::Is_Open -- Determines if the file is open. *
* *
* If part or all of the file is cached, then return that it is opened. A closed file *
* doesn't have a valid pointer. *
* *
* INPUT: none *
* *
* OUTPUT: bool; Is the file open? *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/14/1995 DRD : Created. *
*=============================================================================================*/
int BufferIOFileClass::Is_Open(void) const
{
if (IsOpen && UseBuffer) {
return( true );
}
return( RawFileClass::Is_Open() );
}
/***********************************************************************************************
* BufferIOFileClass::Open -- Assigns name and opens file in one operation. *
* *
* This routine will assign the specified filename to the file object and open it at the *
* same time. If the file object was already open, then it will be closed first. If the *
* file object was previously assigned a filename, then it will be replaced with the new *
* name. Typically, this routine is used when an anonymous file object has been crated and *
* now it needs to be assigned a name and opened. *
* *
* INPUT: filename -- The filename to assign to this file object. *
* *
* rights -- The open file access rights to use. *
* *
* OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file *
* is designed to never return unless it succeeded. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/14/1995 DRD : Created. *
*=============================================================================================*/
int BufferIOFileClass::Open(char const * filename, int rights)
{
Set_Name(filename);
return( BufferIOFileClass::Open( rights ) );
}
/***********************************************************************************************
* BufferIOFileClass::Open -- Opens the file object with the rights specified. *
* *
* This routine is used to open the specified file object with the access rights indicated. *
* This only works if the file has already been assigned a filename. It is guaranteed, by *
* the error handler, that this routine will always return with success. *
* *
* INPUT: rights -- The file access rights to use when opening this file. This is a *
* combination of READ and/or WRITE bit flags. *
* *
* OUTPUT: bool; Was the file opened successfully? This will always return true by reason of *
* the error handler. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/14/1995 DRD : Created. *
*=============================================================================================*/
int BufferIOFileClass::Open(int rights)
{
BufferIOFileClass::Close();
if (UseBuffer) {
BufferRights = rights; // save rights requested for checks later
if (rights != READ ||
(rights == READ && FileSize > BufferSize) ) {
if (rights == WRITE) {
RawFileClass::Open( rights );
RawFileClass::Close();
rights = READ | WRITE;
TrueFileStart = 0; // now writing to single file
}
if (TrueFileStart) {
UseBuffer = false;
Open( rights );
UseBuffer = true;
} else {
RawFileClass::Open( rights );
}
IsDiskOpen = true;
if (BufferRights == WRITE) {
FileSize = 0;
}
} else {
IsDiskOpen = false;
}
BufferPos = 0;
BufferFilePos = 0;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
FilePos = 0;
IsOpen = true;
} else {
RawFileClass::Open( rights );
}
return( true );
}
/***********************************************************************************************
* BufferIOFileClass::Write -- Writes data to the file cache. *
* *
* *
* INPUT: buffer -- Pointer to the buffer that holds the data to be written. *
* *
* size -- The number of bytes to write. *
* *
* OUTPUT: Returns the number of bytes actually written. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/15/1995 DRD : Created. *
*=============================================================================================*/
long BufferIOFileClass::Write(void const * buffer, long size)
{
int opened = false;
if ( !Is_Open() ) {
if (!Open(WRITE)) {
return(0);
}
TrueFileStart = RawFileClass::Seek(0);
opened = true;
}
if (UseBuffer) {
long sizewritten = 0;
if (BufferRights != READ) {
while (size) {
long sizetowrite;
if (size >= (BufferSize - BufferPos) ) {
sizetowrite = (BufferSize - BufferPos);
} else {
sizetowrite = size;
}
if (sizetowrite != BufferSize) {
if ( !IsCached ) {
long readsize;
if (FileSize < BufferSize) {
readsize = FileSize;
BufferFilePos = 0;
} else {
readsize = BufferSize;
BufferFilePos = FilePos;
}
if (TrueFileStart) {
UseBuffer = false;
Seek( FilePos, SEEK_SET );
Read( Buffer, BufferSize );
Seek( FilePos, SEEK_SET );
UseBuffer = true;
} else {
RawFileClass::Seek( BufferFilePos, SEEK_SET );
RawFileClass::Read( Buffer, readsize );
}
BufferPos = 0;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
IsCached = true;
}
}
memmove((char *)Buffer + BufferPos, (char *)buffer + sizewritten, sizetowrite);
IsChanged = true;
sizewritten += sizetowrite;
size -= sizetowrite;
if (BufferChangeBeg == -1) {
BufferChangeBeg = BufferPos;
BufferChangeEnd = BufferPos;
} else {
if (BufferChangeBeg > BufferPos) {
BufferChangeBeg = BufferPos;
}
}
BufferPos += sizetowrite;
if (BufferChangeEnd < BufferPos) {
BufferChangeEnd = BufferPos;
}
FilePos = BufferFilePos + BufferPos;
if (FileSize < FilePos) {
FileSize = FilePos;
}
//
// end of buffer reached?
//
if (BufferPos == BufferSize) {
Commit();
BufferPos = 0;
BufferFilePos = FilePos;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
if (size && FileSize > FilePos) {
if (TrueFileStart) {
UseBuffer = false;
Seek( FilePos, SEEK_SET );
Read( Buffer, BufferSize );
Seek( FilePos, SEEK_SET );
UseBuffer = true;
} else {
RawFileClass::Seek( FilePos, SEEK_SET );
RawFileClass::Read( Buffer, BufferSize );
}
} else {
IsCached = false;
}
}
}
} else {
Error(EACCES);
}
size = sizewritten;
} else {
size = RawFileClass::Write(buffer, size);
}
if (opened) {
Close();
}
return( size );
}
/***********************************************************************************************
* BufferIOFileClass::Read -- Reads data from the file cache. *
* *
* *
* INPUT: buffer -- Pointer to the buffer to place the read data. *
* *
* size -- The number of bytes to read. *
* *
* OUTPUT: Returns the actual number of bytes read (this could be less than requested). *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/15/1995 DRD : Created. *
*=============================================================================================*/
long BufferIOFileClass::Read(void * buffer, long size)
{
int opened = false;
if ( !Is_Open() ) {
if ( Open() ) {
TrueFileStart = RawFileClass::Seek(0);
opened = true;
}
}
if (UseBuffer) {
long sizeread = 0;
if (BufferRights != WRITE) {
while (size) {
long sizetoread;
if (size >= (BufferSize - BufferPos) ) {
sizetoread = (BufferSize - BufferPos);
} else {
sizetoread = size;
}
if ( !IsCached ) {
long readsize;
if (FileSize < BufferSize) {
readsize = FileSize;
BufferFilePos = 0;
} else {
readsize = BufferSize;
BufferFilePos = FilePos;
}
if (TrueFileStart) {
UseBuffer = false;
Seek( FilePos, SEEK_SET );
Read( Buffer, BufferSize );
Seek( FilePos, SEEK_SET );
UseBuffer = true;
} else {
RawFileClass::Seek( BufferFilePos, SEEK_SET );
RawFileClass::Read( Buffer, readsize );
}
BufferPos = 0;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
IsCached = true;
}
memmove((char *)buffer + sizeread, (char *)Buffer + BufferPos, sizetoread);
sizeread += sizetoread;
size -= sizetoread;
BufferPos += sizetoread;
FilePos = BufferFilePos + BufferPos;
//
// end of buffer reached?
//
if (BufferPos == BufferSize) {
Commit();
BufferPos = 0;
BufferFilePos = FilePos;
BufferChangeBeg = -1;
BufferChangeEnd = -1;
if (size && FileSize > FilePos) {
if (TrueFileStart) {
UseBuffer = false;
Seek( FilePos, SEEK_SET );
Read( Buffer, BufferSize );
Seek( FilePos, SEEK_SET );
UseBuffer = true;
} else {
RawFileClass::Seek( FilePos, SEEK_SET );
RawFileClass::Read( Buffer, BufferSize );
}
} else {
IsCached = false;
}
}
}
} else {
Error(EACCES);
}
size = sizeread;
} else {
size = RawFileClass::Read(buffer, size);
}
if (opened) {
Close();
}
return( size );
}
/***********************************************************************************************
* BufferIOFileClass::Seek -- Moves the current file pointer in the file. *
* *
* This routine will change the current file pointer to the position specified. It follows *
* the same rules the a normal Seek() does, but if the file is part of the mixfile system, *
* then only the position value needs to be updated. *
* *
* INPUT: pos -- The position to move the file to relative to the position indicated *
* by the "dir" parameter. *
* *
* dir -- The direction to affect the position change against. This can be *
* either SEEK_CUR, SEEK_END, or SEEK_SET. *
* *
* OUTPUT: Returns with the position of the new location. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/15/1995 DRD : Created. *
*=============================================================================================*/
long BufferIOFileClass::Seek(long pos, int dir)
{
if (UseBuffer) {
bool adjusted = false;
switch (dir) {
case SEEK_END:
FilePos = FileSize;
break;
case SEEK_SET:
FilePos = 0;
break;
case SEEK_CUR:
default:
break;
}
if (TrueFileStart) {
if (pos >= TrueFileStart) {
pos -= TrueFileStart;
adjusted = true;
}
}
FilePos += pos;
if (FilePos < 0) {
FilePos = 0;
}
if (FilePos > FileSize ) {
FilePos = FileSize;
}
if (FileSize <= BufferSize) {
BufferPos = FilePos;
} else {
if (FilePos >= BufferFilePos &&
FilePos < (BufferFilePos + BufferSize) ) {
BufferPos = FilePos - BufferFilePos;
} else {
Commit();
// check!!
if (TrueFileStart) {
UseBuffer = false;
Seek(FilePos, SEEK_SET);
UseBuffer = true;
} else {
RawFileClass::Seek(FilePos, SEEK_SET);
}
IsCached = false;
}
}
if (TrueFileStart && adjusted) {
return( FilePos + TrueFileStart );
}
return( FilePos );
}
return( RawFileClass::Seek(pos, dir) );
}
/***********************************************************************************************
* BufferIOFileClass::Size -- Determines size of file (in bytes). *
* *
* If part or all of the file is cached, then the size of the file is already *
* determined and available. Otherwise, go to the low level system to find the file *
* size. *
* *
* INPUT: none *
* *
* OUTPUT: Returns with the number of bytes in the file. *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/14/1995 DRD : Created. *
*=============================================================================================*/
long BufferIOFileClass::Size(void)
{
if (IsOpen && UseBuffer) {
return( FileSize );
}
return( RawFileClass::Size() );
}
/***********************************************************************************************
* BufferIOFileClass::Close -- Perform a closure of the file. *
* *
* Call Commit() to write the buffer if the file is cached and the buffer has changed, *
* then call lower level Close(). *
* *
* INPUT: none *
* *
* OUTPUT: none *
* *
* WARNINGS: none *
* *
* HISTORY: *
* 11/14/1995 DRD : Created. *
*=============================================================================================*/
void BufferIOFileClass::Close(void)
{
if (UseBuffer) {
Commit();
if (IsDiskOpen) {
if (TrueFileStart) {
UseBuffer = false;
Close();
UseBuffer = true;
} else {
RawFileClass::Close();
}
IsDiskOpen = false;
}
IsOpen = false;
} else {
RawFileClass::Close();
}
}