/* ** 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/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 #include #include #include /* ** 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; }