310 lines
8.8 KiB
C++
310 lines
8.8 KiB
C++
/*
|
|
** 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 <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 S T U D I O S ***
|
|
***********************************************************************************************
|
|
* *
|
|
* Project Name : Command & Conquer *
|
|
* *
|
|
* $Archive:: /Commando/Code/wwlib/ffactory.cpp $*
|
|
* *
|
|
* $Author:: Jani_p $*
|
|
* *
|
|
* $Modtime:: 8/24/01 11:50a $*
|
|
* *
|
|
* $Revision:: 17 $*
|
|
* *
|
|
*---------------------------------------------------------------------------------------------*
|
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
#include "ffactory.h"
|
|
#include "rawfile.h"
|
|
#include "bufffile.h"
|
|
#include "realcrc.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
** Statics
|
|
** NOTE: If _TheFileFactory is ever changed to point to an object of a different class which does
|
|
** not derive from SimpleFileFactoryClass, _TheSimpleFileFactory should be set to NULL.
|
|
*/
|
|
SimpleFileFactoryClass _DefaultFileFactory;
|
|
FileFactoryClass * _TheFileFactory = &_DefaultFileFactory;
|
|
SimpleFileFactoryClass * _TheSimpleFileFactory = &_DefaultFileFactory;
|
|
|
|
SimpleFileFactoryClass _DefaultWritingFileFactory;
|
|
FileFactoryClass * _TheWritingFileFactory = &_DefaultWritingFileFactory;
|
|
|
|
/*
|
|
**
|
|
*/
|
|
file_auto_ptr::file_auto_ptr(FileFactoryClass *fac, const char *filename) :
|
|
_Ptr(NULL), _Fac(fac)
|
|
{
|
|
assert(_Fac);
|
|
_Ptr=_Fac->Get_File(filename);
|
|
if ( _Ptr == NULL ) {
|
|
_Ptr = new BufferedFileClass();
|
|
}
|
|
}
|
|
|
|
file_auto_ptr::~file_auto_ptr()
|
|
{
|
|
_Fac->Return_File(_Ptr);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
** RawFileFactoryClass implementation
|
|
*/
|
|
FileClass * RawFileFactoryClass::Get_File( char const *filename )
|
|
{
|
|
return new RawFileClass( filename );
|
|
}
|
|
|
|
void RawFileFactoryClass::Return_File( FileClass *file )
|
|
{
|
|
delete file;
|
|
}
|
|
|
|
/*
|
|
** SimpleFileFactoryClass implementation
|
|
*/
|
|
|
|
SimpleFileFactoryClass::SimpleFileFactoryClass( void ) :
|
|
IsStripPath( false ),
|
|
Mutex( )
|
|
{
|
|
}
|
|
|
|
|
|
void SimpleFileFactoryClass::Get_Sub_Directory( StringClass& new_dir ) const
|
|
{
|
|
// BEGIN SERIALIZATION
|
|
|
|
// We cannot return a const char * here because the StringClass
|
|
// may reallocate its buffer during a call to Set_Sub_Directory.
|
|
// I opted to return a StringClass instead of a reference to
|
|
// StringClass because it seems like that would behave more
|
|
// reasonably. (no sudden changes from or to empty string in
|
|
// the middle of a calling function.) (DRM, 04/19/01)
|
|
|
|
// Jani: Returning a StringClass object causes a memory allocation
|
|
// and release so it is better to take a reference to the
|
|
// destination StringClass object and modify that.
|
|
|
|
CriticalSectionClass::LockClass lock(Mutex);
|
|
new_dir=SubDirectory;
|
|
// END SERIALIZATION
|
|
}
|
|
|
|
|
|
void SimpleFileFactoryClass::Set_Sub_Directory( const char * sub_directory )
|
|
{
|
|
// BEGIN SERIALIZATION
|
|
|
|
// StringClass makes no guarantees on the atomicity of assignment.
|
|
// Just to be safe, we lock before executing the assignment code.
|
|
// (DRM, 04/19/01)
|
|
|
|
CriticalSectionClass::LockClass lock(Mutex);
|
|
SubDirectory = sub_directory;
|
|
// END SERIALIZATION
|
|
}
|
|
|
|
|
|
void SimpleFileFactoryClass::Prepend_Sub_Directory( const char * sub_directory )
|
|
{
|
|
int sub_len = strlen(sub_directory);
|
|
// Overflow prevention
|
|
if (sub_len > 1021) {
|
|
WWASSERT(0);
|
|
return;
|
|
} else if (sub_len < 1) {
|
|
return;
|
|
}
|
|
|
|
// Ensure sub_directory ends with a slash, and append a semicolon
|
|
char temp_sub_dir[1024];
|
|
strcpy(temp_sub_dir, sub_directory);
|
|
if (temp_sub_dir[sub_len - 1] != '\\') {
|
|
temp_sub_dir[sub_len] = '\\';
|
|
temp_sub_dir[sub_len + 1] = 0;
|
|
sub_len++;
|
|
}
|
|
temp_sub_dir[sub_len] = ';';
|
|
temp_sub_dir[sub_len + 1] = 0;
|
|
|
|
// BEGIN SERIALIZATION
|
|
|
|
// StringClass makes no guarantees on the atomicity of concatenation.
|
|
// Just to be safe, we lock before executing the concatenation code.
|
|
// (NH, 04/23/01)
|
|
|
|
CriticalSectionClass::LockClass lock(Mutex);
|
|
SubDirectory = temp_sub_dir + SubDirectory;
|
|
|
|
// END SERIALIZATION
|
|
}
|
|
|
|
|
|
void SimpleFileFactoryClass::Append_Sub_Directory( const char * sub_directory )
|
|
{
|
|
int sub_len = strlen(sub_directory);
|
|
// Overflow prevention
|
|
if (sub_len > 1022) {
|
|
WWASSERT(0);
|
|
return;
|
|
} else if (sub_len < 1) {
|
|
return;
|
|
}
|
|
|
|
// Ensure sub_directory ends with a slash
|
|
char temp_sub_dir[1024];
|
|
strcpy(temp_sub_dir, sub_directory);
|
|
if (temp_sub_dir[sub_len - 1] != '\\') {
|
|
temp_sub_dir[sub_len] = '\\';
|
|
temp_sub_dir[sub_len + 1] = 0;
|
|
sub_len++;
|
|
}
|
|
|
|
// BEGIN SERIALIZATION
|
|
|
|
// We are doing various dependent operations on SubDirectory.
|
|
// Just to be safe, we lock before this section.
|
|
// (NH, 04/23/01)
|
|
|
|
CriticalSectionClass::LockClass lock(Mutex);
|
|
|
|
// Ensure a trailing semicolon is present, unless the directory list is empty
|
|
int len = SubDirectory.Get_Length();
|
|
if (len && SubDirectory[len - 1] != ';') {
|
|
SubDirectory += ';';
|
|
}
|
|
|
|
SubDirectory += temp_sub_dir;
|
|
// END SERIALIZATION
|
|
}
|
|
|
|
|
|
/*
|
|
** Is_Full_Path
|
|
*/
|
|
static bool
|
|
Is_Full_Path (const char *path)
|
|
{
|
|
bool retval = false;
|
|
|
|
if (path != NULL && path[0] != 0) {
|
|
|
|
// Check for drive designation
|
|
retval = bool(path[1] == ':');
|
|
|
|
// Check for network path
|
|
retval |= bool((path[0] == '\\') && (path[1] == '\\'));
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/*
|
|
**
|
|
*/
|
|
FileClass * SimpleFileFactoryClass::Get_File( char const *filename )
|
|
{
|
|
// strip off the path (if needed). Note that if path stripping is off, and the requested file
|
|
// has a path in its name, and the current subdirectory is not empty, the paths will just be
|
|
// concatenated which may not produce reasonable results.
|
|
StringClass stripped_name(true);
|
|
if (IsStripPath) {
|
|
const char * ptr = ::strrchr( filename, '\\' );
|
|
|
|
if (ptr != 0) {
|
|
ptr++;
|
|
stripped_name = ptr;
|
|
} else {
|
|
stripped_name = filename;
|
|
}
|
|
} else {
|
|
stripped_name = filename;
|
|
}
|
|
|
|
RawFileClass *file = new BufferedFileClass();// new RawWritingFileClass();
|
|
assert( file );
|
|
|
|
//
|
|
// Do we need to find the path for this file request?
|
|
//
|
|
StringClass new_name(stripped_name,true);
|
|
if (Is_Full_Path ( new_name ) == false) {
|
|
|
|
// BEGIN SERIALIZATION
|
|
|
|
// We need to lock here because we are using the contents of SubDirectory
|
|
// in two places. I'd rather be overly cautious about the implementation
|
|
// of StringClass and wrap all calls to it. We can optimize later if this
|
|
// proves too slow. (DRM, 04/19/01)
|
|
|
|
CriticalSectionClass::LockClass lock(Mutex);
|
|
|
|
if (!SubDirectory.Is_Empty()) {
|
|
|
|
//
|
|
// SubDirectory may contain a semicolon seperated search path...
|
|
// If the file doesn't exist, we'll set the path to the last dir in
|
|
// the search path. Therefore newly created files will always go in the
|
|
// last dir in the search path.
|
|
//
|
|
StringClass subdir(SubDirectory,true);
|
|
|
|
if (strchr(subdir,';'))
|
|
{
|
|
char *tokstart=subdir.Peek_Buffer();
|
|
const char *tok;
|
|
while((tok=strtok(tokstart, ";")) != NULL) {
|
|
tokstart=NULL;
|
|
new_name.Format("%s%s",tok,stripped_name.Peek_Buffer());
|
|
file->Set_Name( new_name ); // Call Set_Name to force an allocated name
|
|
if (file->Open()) {
|
|
file->Close();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
new_name.Format("%s%s",SubDirectory,stripped_name);
|
|
}
|
|
}
|
|
|
|
// END SERIALIZATION
|
|
}
|
|
|
|
file->Set_Name( new_name ); // Call Set_Name to force an allocated name
|
|
return file;
|
|
}
|
|
|
|
void SimpleFileFactoryClass::Return_File( FileClass *file )
|
|
{
|
|
delete file;
|
|
}
|
|
|
|
|