/* ** Command & Conquer Renegade(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 S T U D I O S *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * $Archive:: /Commando/Code/wwlib/rawfile.cpp $* * * * $Author:: Jani_p $* * * * $Modtime:: 11/25/01 1:26p $* * * * $Revision:: 13 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * RawFileClass::Bias -- Bias a file with a specific starting position and length. * * RawFileClass::Close -- Perform a closure of the file. * * RawFileClass::Create -- Creates an empty file. * * RawFileClass::Delete -- Deletes the file object from the disk. * * RawFileClass::Error -- Handles displaying a file error message. * * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * * RawFileClass::Open -- Assigns name and opens file in one operation. * * RawFileClass::Open -- Opens the file object with the rights specified. * * RawFileClass::RawFileClass -- Simple constructor for a file object. * * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * * RawFileClass::Seek -- Reposition the file pointer as indicated. * * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * * RawFileClass::Set_Name -- Manually sets the name for a file object. * * RawFileClass::Size -- Determines size of file (in bytes). * * RawFileClass::Write -- Writes the specified data to the buffer specified. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "always.h" #include "rawfile.h" #include //#include #include #include #include #include #include "win.h" #include #include #ifdef _UNIX #include #include #endif #if 0 //#ifdef NEVER (gth) the MAX sdk must #define NEVER! yikes :-) /* ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles ** these errors. If the error routine is overridden and additional errors are defined, then ** use numbers starting with 100. Note that these errors here are listed in numerical order. ** These errors are defined in the standard header file "ERRNO.H". */ EZERO, // Non-error. EINVFNC, // Invalid function number. ENOFILE, // File not found. ENOENT=ENOFILE, // No such file or directory. ENOPATH, // Path not found. EMFILE, // Too many open files. EACCES, // Permission denied. EBADF, // Bad file number. ECONTR, // Memory blocks destroyed. ENOMEM, // Not enough core memory. EINVMEM, // Invalid memory block address. EINVENV, // Invalid environment. EINVFMT, // Invalid format. EINVACC, // Invalid access code. EINVDAT, // Invalid data. EFAULT, // Unknown error. EINVDRV, // Invalid drive specified. ENODEV=EINVDRV, // No such device. ECURDIR, // Attempt to remove CurDir. ENOTSAM, // Not same device. ENMFILE, // No more files. EINVAL, // Invalid argument. E2BIG, // Argument list too long. ENOEXEC, // exec format error. EXDEV, // Cross-device link. ENFILE, // Too many open files. ECHILD, // No child process. ENOTTY, // not used ETXTBSY, // not used EFBIG, // not used ENOSPC, // No space left on device. ESPIPE, // Illegal seek. EROFS, // Read-only file system. EMLINK, // not used EPIPE, // Broken pipe. EDOM, // Math argument. ERANGE, // Result too large. EEXIST, // File already exists. EDEADLOCK, // Locking violation. EPERM, // Operation not permitted. ESRCH, // not used EINTR, // Interrupted function call. EIO, // Input/output error. ENXIO, // No such device or address. EAGAIN, // Resource temporarily unavailable. ENOTBLK, // not used EBUSY, // Resource busy. ENOTDIR, // not used EISDIR, // not used EUCLEAN, // not used #endif /*********************************************************************************************** * RawFileClass::RawFileClass -- Default constructor for a file object. * * * * This constructs a null file object. A null file object has no file handle or filename * * associated with it. In order to use a file object created in this fashion it must be * * assigned a name and then opened. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ RawFileClass::RawFileClass(void) : Rights(READ), BiasStart(0), BiasLength(-1), Handle(NULL_HANDLE), Filename(""), Date(0), Time(0) { } /*********************************************************************************************** * RawFileClass::Is_Open -- Checks to see if the file is open or not. * * * * Use this routine to determine if the file is open. It returns true if it is. * * * * INPUT: none * * * * OUTPUT: bool; Is the file open? * * * * * * WARNINGS: none * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ bool RawFileClass::Is_Open(void) const { return(Handle != NULL_HANDLE); } /*********************************************************************************************** * RawFileClass::Error -- Handles displaying a file error message. * * * * Display an error message as indicated. If it is allowed to retry, then pressing a key * * will return from this function. Otherwise, it will exit the program with "exit()". * * * * INPUT: error -- The error number (same as the DOSERR.H error numbers). * * * * canretry -- Can this routine exit normally so that retrying can occur? If this is * * false, then the program WILL exit in this routine. * * * * filename -- Optional filename to report with this error. If no filename is * * supplied, then no filename is listed in the error message. * * * * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * * false or the player pressed ESC. * * * * WARNINGS: This routine may not return at all. It handles being in text mode as well as * * if in a graphic mode. * * * * HISTORY: * * 10/17/1994 JLB : Created. * *=============================================================================================*/ void RawFileClass::Error(int, int, char const * ) { } /*********************************************************************************************** * RawFileClass::Transfer_Block_Size * * * * This function returns the largest size a low level DOS read or write may * * perform. Larger file transfers are performed in chunks of this size or less. * * * * INPUT: none * * * * OUTPUT: * * * * * * WARNINGS: none * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Transfer_Block_Size(void) { return (int)((unsigned)UINT_MAX)-16L; } /*********************************************************************************************** * RawFileClass::RawFileClass -- Simple 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: * * 10/17/1994 JLB : Created. * *=============================================================================================*/ RawFileClass::RawFileClass(char const * filename) : Rights(0), BiasStart(0), BiasLength(-1), Handle(NULL_HANDLE), Filename(filename), Date(0), Time(0) { } /*********************************************************************************************** * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * * * * This constructs a null file object. A null file object has no file handle or filename * * associated with it. In order to use a file object created in this fashion it must be * * assigned a name and then opened. * * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ RawFileClass::~RawFileClass(void) { Reset (); } /*********************************************************************************************** * RawFileClass::Reset -- Closes the file handle and resets the object's state. * * * INPUT: none * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 06/10/1999 PDS : Created. * *=============================================================================================*/ void RawFileClass::Reset(void) { Close(); Filename = ""; } /*********************************************************************************************** * RawFileClass::Set_Name -- Manually sets the name for a file object. * * * * This routine will set the name for the file object to the name specified. Setting the * * name in this fashion doesn't affect the closed or opened state * * of the file. * * * * INPUT: filename -- The filename to assign to this file object. * * * * OUTPUT: pointer to filename. * * * * * * HISTORY: * * 10/17/1994 JLB : Created. * * 11/25/2001 Jani : Changed the name storage from strdup to StringClass to benefit from our * * Fast memory allocation system. *=============================================================================================*/ char const * RawFileClass::Set_Name(char const * filename) { Bias(0); Filename=filename; /* ** If this is a UNIX build, fix the filename from the DOS-like name passed in */ #ifdef _UNIX for (int i=0; i 0) { bytesread = 0; int readok=TRUE; #ifdef _UNIX readok=TRUE; bytesread=fread(buffer,1,size,Handle); if ((bytesread == 0)&&( ! feof(Handle))) readok=ferror(Handle); #else readok=ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL); #endif if (! readok) { size -= bytesread; total += bytesread; Error(GetLastError(), true, Filename); continue; } size -= bytesread; total += bytesread; if (bytesread == 0) break; } bytesread = total; /* ** Close the file if it was opened by this routine and return ** the actual number of bytes read into the buffer. */ if (opened) Close(); return(bytesread); } /*********************************************************************************************** * RawFileClass::Write -- Writes the specified data to the buffer specified. * * * * This routine will write the data specified to the file. * * * * INPUT: buffer -- The buffer that holds the data to write. * * * * size -- The number of bytes to write to the file. * * * * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * * case of a disk full condition, so this routine will always return with the number * * matching the size request. * * * * WARNINGS: A fatal file condition could cause this routine to never return. * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Write(void const * buffer, int size) { long byteswritten = 0; int opened = false; // Was the file manually opened? /* ** Check to open status of the file. If the file is open, then merely write to ** it. Otherwise, open the file for writing and then close the file when the ** output is finished. */ if (!Is_Open()) { if (!Open(WRITE)) { return(0); } opened = true; } int writeok=TRUE; #ifdef _UNIX byteswritten = fwrite(buffer, 1, size, Handle); if (byteswritten != size) writeok = FALSE; #else writeok=WriteFile(Handle, buffer, size, &(unsigned long&)byteswritten, NULL); #endif if (! writeok) { Error(GetLastError(), false, Filename); } /* ** Fixup the bias length if necessary. */ if (BiasLength != -1) { if (Raw_Seek(0) > BiasStart+BiasLength) { BiasLength = Raw_Seek(0) - BiasStart; } } /* ** If this routine had to open the file, then close it before returning. */ if (opened) { Close(); } /* ** Return with the number of bytes written. This will always be the number of bytes ** requested, since the case of the disk being full is caught by this routine. */ return(byteswritten); } /*********************************************************************************************** * RawFileClass::Seek -- Reposition the file pointer as indicated. * * * * Use this routine to move the filepointer to the position indicated. It can move either * * relative to current position or absolute from the beginning or ending of the file. This * * routine will only return if it successfully performed the seek. * * * * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * * indicated by the "dir" parameter. * * * * dir -- The relative position to relate the seek to. This can be either SEEK_SET * * for the beginning of the file, SEEK_CUR for the current position, or * * SEEK_END for the end of the file. * * * * OUTPUT: This routine returns the position that the seek ended up at. * * * * WARNINGS: If there was a file error, then this routine might never return. * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Seek(int pos, int dir) { /* ** A file that is biased will have a seek operation modified so that the file appears to ** exist only within the bias range. All bytes outside of this range appear to be ** non-existant. */ if (BiasLength != -1) { switch (dir) { case SEEK_SET: if (pos > BiasLength) { pos = BiasLength; } pos += BiasStart; break; case SEEK_CUR: break; case SEEK_END: dir = SEEK_SET; pos += BiasStart + BiasLength; // pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; // pos = (pos >= BiasStart) ? pos : BiasStart; break; } /* ** Perform the modified raw seek into the file. */ long newpos = Raw_Seek(pos, dir) - BiasStart; /* ** Perform a final double check to make sure the file position fits with the bias range. */ if (newpos < 0) { newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; } if (newpos > BiasLength) { newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; } return(newpos); } /* ** If the file is not biased in any fashion, then the normal seek logic will ** work just fine. */ return(Raw_Seek(pos, dir)); } /*********************************************************************************************** * RawFileClass::Size -- Determines size of file (in bytes). * * * * Use this routine to determine the size of the file. The file must exist or this is an * * error condition. * * * * INPUT: none * * * * OUTPUT: Returns with the number of bytes in the file. * * * * WARNINGS: This routine handles error conditions and will not return unless the file * * exists and can successfully be queried for file length. * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Size(void) { int size = 0; /* ** A biased file already has its length determined. */ if (BiasLength != -1) { return(BiasLength); } /* ** If the file is open, then proceed normally. */ if (Is_Open()) { #ifdef _UNIX fpos_t curpos,startpos,endpos; fgetpos(Handle,&curpos); fseek(Handle,0,SEEK_SET); fgetpos(Handle,&startpos); fseek(Handle,0,SEEK_END); fgetpos(Handle,&endpos); size=endpos-startpos; fsetpos(Handle,&curpos); #else size = GetFileSize(Handle, NULL); #endif /* ** If there was in internal error, then call the error function. */ if (size == 0xFFFFFFFF) { Error(GetLastError(), false, Filename); } } else { /* ** If the file wasn't open, then open the file and call this routine again. Count on ** the fact that the open function must succeed. */ if (Open()) { size = Size(); /* ** Since we needed to open the file we must remember to close the file when the ** size has been determined. */ Close(); } } BiasLength = size-BiasStart; return(BiasLength); } /*********************************************************************************************** * RawFileClass::Create -- Creates an empty file. * * * * This routine will create an empty file from the file object. The file object's filename * * must already have been assigned before this routine will function. * * * * INPUT: none * * * * OUTPUT: bool; Was the file successfully created? This routine will always return true. * * * * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * * is full or a read-only media was selected. * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Create(void) { Close(); if (Open(WRITE)) { /* ** A biased file must be at least as long as the bias offset. Seeking to the ** appropriate start offset has the effect of lengthening the file to the ** correct length. */ if (BiasLength != -1) { Seek(0, SEEK_SET); } Close(); return(true); } return(false); } /*********************************************************************************************** * RawFileClass::Delete -- Deletes the file object from the disk. * * * * This routine will delete the file object from the disk. If the file object doesn't * * exist, then this routine will return as if it had succeeded (since the effect is the * * same). * * * * INPUT: none * * * * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * * be false. * * * * WARNINGS: none * * * * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ int RawFileClass::Delete(void) { /* ** If the file was open, then it must be closed first. */ Close(); /* ** If there is no filename associated with this object, then this indicates a fatal error ** condition. Report this and abort. */ if (!Filename) { Error(ENOENT, false); } /* ** Repetitively try to delete the file if possible. Either return with success, or ** abort the program with an error. */ for (;;) { /* ** If the file is already missing, then return with this fact. No action is necessary. ** This can occur as this section loops if the file exists on a floppy and the floppy ** was removed, the file deleted on another machine, and then the floppy was ** reinserted. Admittedly, this is a rare case, but is handled here. */ if (!Is_Available()) { return(false); } int deleteok; #ifdef _UNIX deleteok=(unlink(Filename)==0)?TRUE:FALSE; #else deleteok=DeleteFile(Filename); #endif if (! deleteok) { Error(GetLastError(), false, Filename); return(false); } break; } /* ** DOS reports that the file was successfully deleted. Return with this fact. */ return(true); } /*********************************************************************************************** * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * * * * Use this routine to get the date and time of the file. * * * * INPUT: none * * * * OUTPUT: Returns with the file date and time as a long. * * Use the YEAR(long), MONTH(),.... * * * * WARNINGS: none * * * * HISTORY: * * 11/14/1995 DRD : Created. * * 07/13/1996 JLB : Handles win32 method. * *=============================================================================================*/ unsigned long RawFileClass::Get_Date_Time(void) { #ifdef _UNIX struct stat statbuf; lstat(Filename, &statbuf); return(statbuf.st_mtime); #else BY_HANDLE_FILE_INFORMATION info; if (GetFileInformationByHandle(Handle, &info)) { WORD dosdate; WORD dostime; FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); return((dosdate << 16) | dostime); } return(0); #endif } /*********************************************************************************************** * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * * * * Use this routine to set the date and time of the file. * * * * INPUT: the file date and time as a long * * * * OUTPUT: successful or not if the file date and time was changed. * * * * WARNINGS: none * * * * HISTORY: * * 11/14/1995 DRD : Created. * * 07/13/1996 JLB : Handles win 32 method * *=============================================================================================*/ bool RawFileClass::Set_Date_Time(unsigned long datetime) { #ifdef _UNIX assert(0); return(false); #else if (RawFileClass::Is_Open()) { BY_HANDLE_FILE_INFORMATION info; if (GetFileInformationByHandle(Handle, &info)) { FILETIME filetime; if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime) != 0); } } } return(false); #endif } /*********************************************************************************************** * RawFileClass::Bias -- Bias a file with a specific starting position and length. * * * * This will bias a file by giving it an artificial starting position and length. By * * using this routine, it is possible to 'fool' the file into ignoring a header and * * trailing extra data. An example of this would be a file inside of a mixfile. * * * * INPUT: start -- The starting offset that will now be considered the start of the * * file. * * * * length -- The forced length of the file. For files that are opened for write, * * this serves as the artificial constraint on the file's length. For * * files opened for read, this limits the usable file size. * * * * OUTPUT: none * * * * WARNINGS: none * * * * HISTORY: * * 06/02/1996 JLB : Created. * *=============================================================================================*/ void RawFileClass::Bias(int start, int length) { if (start == 0) { BiasStart = 0; BiasLength = -1; return; } BiasLength = RawFileClass::Size(); BiasStart += start; if (length != -1) { BiasLength = BiasLength < length ? BiasLength : length; } BiasLength = BiasLength > 0 ? BiasLength : 0; /* ** Move the current file offset to a legal position if necessary and the ** file was open. */ if (Is_Open()) { RawFileClass::Seek(0, SEEK_SET); } } /*********************************************************************************************** * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * * * * This will perform a seek on the file as if it were unbiased. This is in spite of any * * bias setting the file may have. The ability to perform a raw seek in this fasion is * * necessary to maintain the bias ability. * * * * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * * * * dir -- The origin of the seek operation. * * * * OUTPUT: Returns with the new position of the seek operation. * * * * WARNINGS: none * * * * HISTORY: * * 08/04/1996 JLB : Created. * *=============================================================================================*/ int RawFileClass::Raw_Seek(int pos, int dir) { /* ** If the file isn't opened, then this is a fatal error condition. */ if (!Is_Open()) { Error(EBADF, false, Filename); } #ifdef _UNIX pos=fseek(Handle, pos, dir); #else switch (dir) { case SEEK_SET: dir = FILE_BEGIN; break; case SEEK_CUR: dir = FILE_CURRENT; break; case SEEK_END: dir = FILE_END; break; } pos = SetFilePointer(Handle, pos, NULL, dir); #endif /* ** If there was an error in the seek, then bail with an error condition. */ if (pos == 0xFFFFFFFF) { Error(GetLastError(), false, Filename); } /* ** Return with the new position of the file. This will range between zero and the number of ** bytes the file contains. */ return(pos); } /*********************************************************************************************** * RawFileClass::Attach -- Provides a file handle for the class to use. * * * INPUT: handle -- the Win32 file handle. * * * OUTPUT: * * * WARNINGS: none * * * * HISTORY: * * 06/10/1999 PDS : Created. * *=============================================================================================*/ void RawFileClass::Attach (void *handle, int rights) { Reset (); Rights = rights; BiasStart = 0; BiasLength = -1; Date = 0; Time = 0; #ifdef _UNIX Handle = (FILE *)handle; #else Handle = handle; #endif } /*********************************************************************************************** * RawFileClass::Detach -- Removes the file handle from the object without closing the handle. * * * INPUT * * * OUTPUT: * * * WARNINGS: none * * * * HISTORY: * * 06/10/1999 PDS : Created. * *=============================================================================================*/ void RawFileClass::Detach (void) { Rights = 0; BiasStart = 0; BiasLength = -1; Date = 0; Time = 0; Handle = NULL_HANDLE; }