This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/wwlib/ffactory.cpp

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;
}