doom3-bfg/neo/framework/FileSystem.cpp

3222 lines
95 KiB
C++
Raw Normal View History

2012-11-26 18:58:24 +00:00
/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition 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.
Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../idlib/precompiled.h"
#pragma hdrstop
#include "Unzip.h"
#include "Zip.h"
#ifdef WIN32
#include <io.h> // for _read
#else
#if !__MACH__ && __MWERKS__
#include <types.h>
#include <stat.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <unistd.h>
#endif
/*
=============================================================================
DOOM FILESYSTEM
All of Doom's data access is through a hierarchical file system, but the contents of
the file system can be transparently merged from several sources.
A "relativePath" is a reference to game file data, which must include a terminating zero.
"..", "\\", and ":" are explicitly illegal in qpaths to prevent any references
outside the Doom directory system.
The "base path" is the path to the directory holding all the game directories and
usually the executable. It defaults to the current directory, but can be overridden
with "+set fs_basepath c:\doom" on the command line. The base path cannot be modified
at all after startup.
The "save path" is the path to the directory where game files will be saved. It defaults
to the base path, but can be overridden with a "+set fs_savepath c:\doom" on the
command line. Any files that are created during the game (demos, screenshots, etc.) will
be created reletive to the save path.
If a user runs the game directly from a CD, the base path would be on the CD. This
should still function correctly, but all file writes will fail (harmlessly).
The "base game" is the directory under the paths where data comes from by default, and
can be either "base" or "demo".
The "current game" may be the same as the base game, or it may be the name of another
directory under the paths that should be searched for files before looking in the base
game. The game directory is set with "+set fs_game myaddon" on the command line. This is
the basis for addons.
No other directories outside of the base game and current game will ever be referenced by
filesystem functions.
Because we will have updated executables freely available online, there is no point to
trying to restrict demo / oem versions of the game with code changes. Demo / oem versions
should be exactly the same executables as release versions, but with different data that
automatically restricts where game media can come from to prevent add-ons from working.
If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the base
path, it will be copied over to the save path. This is a development aid to help build
test releases and to copy working sets of files.
The relative path "sound/newstuff/test.wav" would be searched for in the following places:
for save path, base path:
for current game, base game:
search directory
search zip files
downloaded files, to be written to save path + current game's directory
The filesystem can be safely shutdown and reinitialized with different
basedir / cddir / game combinations, but all other subsystems that rely on it
(sound, video) must also be forced to restart.
"additional mod path search":
fs_game_base can be used to set an additional search path
in search order, fs_game, fs_game_base, BASEGAME
for instance to base a mod of D3 + D3XP assets, fs_game mymod, fs_game_base d3xp
=============================================================================
*/
#define MAX_ZIPPED_FILE_NAME 2048
#define FILE_HASH_SIZE 1024
struct searchpath_t {
idStr path; // c:\doom
idStr gamedir; // base
};
// search flags when opening a file
#define FSFLAG_SEARCH_DIRS ( 1 << 0 )
#define FSFLAG_RETURN_FILE_MEM ( 1 << 1 )
class idFileSystemLocal : public idFileSystem {
public:
idFileSystemLocal();
virtual void Init();
virtual void Restart();
virtual void Shutdown( bool reloading );
virtual bool IsInitialized() const;
virtual idFileList * ListFiles( const char *relativePath, const char *extension, bool sort = false, bool fullRelativePath = false, const char* gamedir = NULL );
virtual idFileList * ListFilesTree( const char *relativePath, const char *extension, bool sort = false, const char* gamedir = NULL );
virtual void FreeFileList( idFileList *fileList );
virtual const char * OSPathToRelativePath( const char *OSPath );
virtual const char * RelativePathToOSPath( const char *relativePath, const char *basePath );
virtual const char * BuildOSPath( const char *base, const char *game, const char *relativePath );
virtual const char * BuildOSPath( const char *base, const char *relativePath );
virtual void CreateOSPath( const char *OSPath );
virtual int ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp );
virtual void FreeFile( void *buffer );
virtual int WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath = "fs_savepath" );
virtual void RemoveFile( const char *relativePath );
virtual bool RemoveDir( const char * relativePath );
virtual bool RenameFile( const char * relativePath, const char * newName, const char * basePath = "fs_savepath" );
virtual idFile * OpenFileReadFlags( const char *relativePath, int searchFlags, bool allowCopyFiles = true, const char* gamedir = NULL );
virtual idFile * OpenFileRead( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL );
virtual idFile * OpenFileReadMemory( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL );
virtual idFile * OpenFileWrite( const char *relativePath, const char *basePath = "fs_savepath" );
virtual idFile * OpenFileAppend( const char *relativePath, bool sync = false, const char *basePath = "fs_basepath" );
virtual idFile * OpenFileByMode( const char *relativePath, fsMode_t mode );
virtual idFile * OpenExplicitFileRead( const char *OSPath );
virtual idFile * OpenExplicitFileWrite( const char *OSPath );
virtual idFile_Cached * OpenExplicitPakFile( const char *OSPath );
virtual void CloseFile( idFile *f );
virtual void FindDLL( const char *basename, char dllPath[ MAX_OSPATH ] );
virtual void CopyFile( const char *fromOSPath, const char *toOSPath );
virtual findFile_t FindFile( const char *path );
virtual bool FilenameCompare( const char *s1, const char *s2 ) const;
virtual int GetFileLength( const char * relativePath );
virtual sysFolder_t IsFolder( const char * relativePath, const char *basePath = "fs_basepath" );
// resource tracking
virtual void EnableBackgroundCache( bool enable );
virtual void BeginLevelLoad( const char *name, char *_blockBuffer, int _blockBufferSize );
virtual void EndLevelLoad();
virtual bool InProductionMode() { return ( resourceFiles.Num() > 0 ) | ( com_productionMode.GetInteger() != 0 ); }
virtual bool UsingResourceFiles() { return resourceFiles.Num() > 0; }
virtual void UnloadMapResources( const char *name );
virtual void UnloadResourceContainer( const char *name );
idFile * GetResourceContainer( int idx ) {
if ( idx >= 0 && idx < resourceFiles.Num() ) {
return resourceFiles[ idx ]->resourceFile;
}
return NULL;
}
virtual void StartPreload( const idStrList &_preload );
virtual void StopPreload();
idFile * GetResourceFile( const char *fileName, bool memFile );
bool GetResourceCacheEntry( const char *fileName, idResourceCacheEntry &rc );
virtual int ReadFromBGL( idFile *_resourceFile, void * _buffer, int _offset, int _len );
virtual bool IsBinaryModel( const idStr & resName ) const;
virtual bool IsSoundSample( const idStr & resName ) const;
virtual void FreeResourceBuffer() { resourceBufferAvailable = resourceBufferSize; }
virtual void AddImagePreload( const char *resName, int _filter, int _repeat, int _usage, int _cube ) {
preloadList.AddImage( resName, _filter, _repeat, _usage, _cube );
}
virtual void AddSamplePreload( const char *resName ) {
preloadList.AddSample( resName );
}
virtual void AddModelPreload( const char *resName ) {
preloadList.AddModel( resName );
}
virtual void AddAnimPreload( const char *resName ) {
preloadList.AddAnim( resName );
}
virtual void AddCollisionPreload( const char *resName ) {
preloadList.AddCollisionModel( resName );
}
virtual void AddParticlePreload( const char *resName ) {
preloadList.AddParticle( resName );
}
static void Dir_f( const idCmdArgs &args );
static void DirTree_f( const idCmdArgs &args );
static void Path_f( const idCmdArgs &args );
static void TouchFile_f( const idCmdArgs &args );
static void TouchFileList_f( const idCmdArgs &args );
static void BuildGame_f( const idCmdArgs &args );
//static void FileStats_f( const idCmdArgs &args );
static void WriteResourceFile_f ( const idCmdArgs &args );
static void ExtractResourceFile_f( const idCmdArgs &args );
static void UpdateResourceFile_f( const idCmdArgs &args );
static void GenerateResourceCRCs_f( const idCmdArgs &args );
static void CreateCRCsForResourceFileList( const idFileList & list );
void BuildOrderedStartupContainer();
private:
idList<searchpath_t> searchPaths;
int loadCount; // total files read
int loadStack; // total files in memory
idStr gameFolder; // this will be a single name without separators
static idCVar fs_debug;
static idCVar fs_debugResources;
static idCVar fs_copyfiles;
static idCVar fs_buildResources;
static idCVar fs_game;
static idCVar fs_game_base;
static idCVar fs_enableBGL;
static idCVar fs_debugBGL;
idStr manifestName;
idStrList fileManifest;
idPreloadManifest preloadList;
idList< idResourceContainer * > resourceFiles;
byte * resourceBufferPtr;
int resourceBufferSize;
int resourceBufferAvailable;
int numFilesOpenedAsCached;
private:
// .resource file creation
void ClearResourcePacks();
void WriteResourcePacks();
void AddRenderProgs( idStrList &files );
void AddFonts( idStrList &files );
void ReplaceSeparators( idStr &path, char sep = PATHSEPARATOR_CHAR );
int ListOSFiles( const char *directory, const char *extension, idStrList &list );
idFileHandle OpenOSFile( const char *name, fsMode_t mode );
void CloseOSFile( idFileHandle o );
int DirectFileLength( idFileHandle o );
void CopyFile( idFile *src, const char *toOSPath );
int AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const;
void GetExtensionList( const char *extension, idStrList &extensionList ) const;
int GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir = NULL );
int GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir = NULL );
void AddGameDirectory( const char *path, const char *dir );
int AddResourceFile( const char * resourceFileName );
void RemoveMapResourceFile( const char * resourceFileName );
void RemoveResourceFileByIndex( const int & idx );
void RemoveResourceFile( const char * resourceFileName );
int FindResourceFile( const char * resourceFileName );
void SetupGameDirectories( const char *gameName );
void Startup();
void InitPrecache();
void ReOpenCacheFiles();
};
idCVar idFileSystemLocal::fs_debug( "fs_debug", "0", CVAR_SYSTEM | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar idFileSystemLocal::fs_debugResources( "fs_debugResources", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
idCVar idFileSystemLocal::fs_enableBGL( "fs_enableBGL", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
idCVar idFileSystemLocal::fs_debugBGL( "fs_debugBGL", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
idCVar idFileSystemLocal::fs_copyfiles( "fs_copyfiles", "0", CVAR_SYSTEM | CVAR_INIT | CVAR_BOOL, "Copy every file touched to fs_savepath" );
idCVar idFileSystemLocal::fs_buildResources( "fs_buildresources", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_INIT, "Copy every file touched to a resource file" );
idCVar idFileSystemLocal::fs_game( "fs_game", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "mod path" );
idCVar idFileSystemLocal::fs_game_base( "fs_game_base", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "alternate mod path, searched after the main fs_game path, before the basedir" );
idCVar fs_basepath( "fs_basepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
idCVar fs_savepath( "fs_savepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
idCVar fs_resourceLoadPriority( "fs_resourceLoadPriority", "1", CVAR_SYSTEM , "if 1, open requests will be honored from resource files first; if 0, the resource files are checked after normal search paths" );
idCVar fs_enableBackgroundCaching( "fs_enableBackgroundCaching", "1", CVAR_SYSTEM , "if 1 allow the 360 to precache game files in the background" );
idFileSystemLocal fileSystemLocal;
idFileSystem * fileSystem = &fileSystemLocal;
/*
================
idFileSystemLocal::ReadFromBGL
================
*/
int idFileSystemLocal::ReadFromBGL( idFile *_resourceFile, void * _buffer, int _offset, int _len ) {
if ( _resourceFile->Tell() != _offset ) {
_resourceFile->Seek( _offset, FS_SEEK_SET );
}
return _resourceFile->Read( _buffer, _len );
}
/*
================
idFileSystemLocal::StartPreload
================
*/
void idFileSystemLocal::StartPreload( const idStrList & _preload ) {
}
/*
================
idFileSystemLocal::StopPreload
================
*/
void idFileSystemLocal::StopPreload() {
}
/*
================
idFileSystemLocal::idFileSystemLocal
================
*/
idFileSystemLocal::idFileSystemLocal() {
loadCount = 0;
loadStack = 0;
resourceBufferPtr = NULL;
resourceBufferSize = 0;
resourceBufferAvailable = 0;
numFilesOpenedAsCached = 0;
}
/*
===========
idFileSystemLocal::FilenameCompare
Ignore case and separator char distinctions
===========
*/
bool idFileSystemLocal::FilenameCompare( const char *s1, const char *s2 ) const {
int c1, c2;
do {
c1 = *s1++;
c2 = *s2++;
if ( c1 >= 'a' && c1 <= 'z' ) {
c1 -= ('a' - 'A');
}
if ( c2 >= 'a' && c2 <= 'z' ) {
c2 -= ('a' - 'A');
}
if ( c1 == '\\' || c1 == ':' ) {
c1 = '/';
}
if ( c2 == '\\' || c2 == ':' ) {
c2 = '/';
}
if ( c1 != c2 ) {
return true; // strings not equal
}
} while( c1 );
return false; // strings are equal
}
/*
========================
idFileSystemLocal::GetFileLength
========================
*/
int idFileSystemLocal::GetFileLength( const char * relativePath ) {
idFile * f;
int len;
if ( !IsInitialized() ) {
idLib::FatalError( "Filesystem call made without initialization" );
}
if ( !relativePath || !relativePath[0] ) {
idLib::Warning( "idFileSystemLocal::GetFileLength with empty name" );
return -1;
}
if ( resourceFiles.Num() > 0 ) {
idResourceCacheEntry rc;
if ( GetResourceCacheEntry( relativePath, rc ) ) {
return rc.length;
}
}
// look for it in the filesystem or pack files
f = OpenFileRead( relativePath, false );
if ( f == NULL ) {
return -1;
}
len = (int)f->Length();
delete f;
return len;
}
/*
================
idFileSystemLocal::OpenOSFile
================
*/
idFileHandle idFileSystemLocal::OpenOSFile( const char *fileName, fsMode_t mode ) {
idFileHandle fp;
DWORD dwAccess = 0;
DWORD dwShare = 0;
DWORD dwCreate = 0;
DWORD dwFlags = 0;
if ( mode == FS_WRITE ) {
dwAccess = GENERIC_READ | GENERIC_WRITE;
dwShare = FILE_SHARE_READ;
dwCreate = CREATE_ALWAYS;
dwFlags = FILE_ATTRIBUTE_NORMAL;
} else if ( mode == FS_READ ) {
dwAccess = GENERIC_READ;
dwShare = FILE_SHARE_READ;
dwCreate = OPEN_EXISTING;
dwFlags = FILE_ATTRIBUTE_NORMAL;
} else if ( mode == FS_APPEND ) {
dwAccess = GENERIC_READ | GENERIC_WRITE;
dwShare = FILE_SHARE_READ;
dwCreate = OPEN_ALWAYS;
dwFlags = FILE_ATTRIBUTE_NORMAL;
}
fp = CreateFile( fileName, dwAccess, dwShare, NULL, dwCreate, dwFlags, NULL );
if ( fp == INVALID_HANDLE_VALUE ) {
return NULL;
}
return fp;
}
/*
================
idFileSystemLocal::CloseOSFile
================
*/
void idFileSystemLocal::CloseOSFile( idFileHandle o ) {
::CloseHandle( o );
}
/*
================
idFileSystemLocal::DirectFileLength
================
*/
int idFileSystemLocal::DirectFileLength( idFileHandle o ) {
return GetFileSize( o, NULL );
}
/*
============
idFileSystemLocal::CreateOSPath
Creates any directories needed to store the given filename
============
*/
void idFileSystemLocal::CreateOSPath( const char *OSPath ) {
char *ofs;
// make absolutely sure that it can't back up the path
// FIXME: what about c: ?
if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) {
#ifdef _DEBUG
common->DPrintf( "refusing to create relative path \"%s\"\n", OSPath );
#endif
return;
}
idStrStatic< MAX_OSPATH > path( OSPath );
path.SlashesToBackSlashes();
for( ofs = &path[ 1 ]; *ofs ; ofs++ ) {
if ( *ofs == PATHSEPARATOR_CHAR ) {
// create the directory
*ofs = 0;
Sys_Mkdir( path );
*ofs = PATHSEPARATOR_CHAR;
}
}
}
/*
=================
idFileSystemLocal::EnableBackgroundCache
=================
*/
void idFileSystemLocal::EnableBackgroundCache( bool enable ) {
if ( !fs_enableBackgroundCaching.GetBool() ) {
return;
}
}
/*
=================
idFileSystemLocal::BeginLevelLoad
=================
*/
void idFileSystemLocal::BeginLevelLoad( const char *name, char *_blockBuffer, int _blockBufferSize ) {
if ( name == NULL || *name == '\0' ) {
return;
}
resourceBufferPtr = ( byte* )_blockBuffer;
resourceBufferAvailable = _blockBufferSize;
resourceBufferSize = _blockBufferSize;
manifestName = name;
fileManifest.Clear();
preloadList.Clear();
EnableBackgroundCache( false );
ReOpenCacheFiles();
manifestName.StripPath();
if ( resourceFiles.Num() > 0 ) {
AddResourceFile( va( "%s.resources", manifestName.c_str() ) );
}
}
/*
=================
idFileSystemLocal::UnloadResourceContainer
=================
*/
void idFileSystemLocal::UnloadResourceContainer( const char *name ) {
if ( name == NULL || *name == '\0' ) {
return;
}
RemoveResourceFile( va( "%s.resources", name ) );
}
/*
=================
idFileSystemLocal::UnloadMapResources
=================
*/
void idFileSystemLocal::UnloadMapResources( const char *name ) {
if ( name == NULL || *name == '\0' || idStr::Icmp( "_startup", name ) == 0 ) {
return;
}
if ( resourceFiles.Num() > 0 ) {
RemoveMapResourceFile( va( "%s.resources", name ) );
}
}
/*
=================
idFileSystemLocal::EndLevelLoad
=================
*/
void idFileSystemLocal::EndLevelLoad() {
if ( fs_buildResources.GetBool() ) {
int saveCopyFiles = fs_copyfiles.GetInteger();
fs_copyfiles.SetInteger( 0 );
idStr manifestFileName = manifestName;
manifestFileName.StripPath();
manifestFileName.SetFileExtension( "manifest" );
manifestFileName.Insert( "maps/", 0 );
idFile *outFile = fileSystem->OpenFileWrite( manifestFileName );
if ( outFile != NULL ) {
int num = fileManifest.Num();
outFile->WriteBig( num );
for ( int i = 0; i < num; i++ ) {
outFile->WriteString( fileManifest[ i ] );
}
delete outFile;
}
idStrStatic< MAX_OSPATH > preloadName = manifestName;
preloadName.Insert( "maps/", 0 );
preloadName += ".preload";
idFile *fileOut = fileSystem->OpenFileWrite( preloadName, "fs_savepath" );
preloadList.WriteManifestToFile( fileOut );
delete fileOut;
fs_copyfiles.SetInteger( saveCopyFiles );
}
EnableBackgroundCache( true );
resourceBufferPtr = NULL;
resourceBufferAvailable = 0;
resourceBufferSize = 0;
}
bool FileExistsInAllManifests( const char *filename, idList< idFileManifest > &manifests ) {
for ( int i = 0; i < manifests.Num(); i++ ) {
if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) {
continue;
}
if ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) {
continue;
}
if ( manifests[ i ].FindFile( filename ) == -1 ) {
return false;
}
}
return true;
}
bool FileExistsInAllPreloadManifests( const char *filename, idList< idPreloadManifest > &manifests ) {
for ( int i = 0; i < manifests.Num(); i++ ) {
if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) {
continue;
}
if ( manifests[ i ].FindResource( filename ) == -1 ) {
return false;
}
}
return true;
}
void RemoveFileFromAllManifests( const char *filename, idList< idFileManifest > &manifests ) {
for ( int i = 0; i < manifests.Num(); i++ ) {
if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) {
continue;
}
if ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) {
continue;
}
manifests[ i ].RemoveAll( filename );
}
}
/*
================
idFileSystemLocal::AddPerPlatformResources
================
*/
void idFileSystemLocal::AddRenderProgs( idStrList &files ) {
idStrList work;
// grab all the renderprogs
idStr path = RelativePathToOSPath( "renderprogs/cgb", "fs_basepath" );
ListOSFiles( path, "*.cgb", work );
for ( int i = 0; i < work.Num(); i++ ) {
files.Append( idStr( "renderprogs/cgb/" ) + work[i] );
}
path = RelativePathToOSPath( "renderprogs/hlsl", "fs_basepath" );
ListOSFiles( path, "*.v360", work );
for ( int i = 0; i < work.Num(); i++ ) {
files.Append( idStr( "renderprogs/hlsl/" ) + work[i] );
}
ListOSFiles( path, "*.p360", work );
for ( int i = 0; i < work.Num(); i++ ) {
files.Append( idStr( "renderprogs/hlsl/" ) + work[i] );
}
path = RelativePathToOSPath( "renderprogs/gl", "fs_basepath" );
ListOSFiles( path, "*.*", work );
for ( int i = 0; i < work.Num(); i++ ) {
files.Append( idStr( "renderprogs/gl/" ) + work[i] );
}
}
/*
================
idFileSystemLocal::AddSoundResources
================
*/
void idFileSystemLocal::AddFonts( idStrList &files ) {
// temp fix for getting idaudio files in
idFileList *fl = ListFilesTree( "generated/images/newfonts", "*.bimage", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
files.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "newfonts", "*.dat", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
files.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
}
const char * excludeExtensions[] = {
".idxma", ".idmsf", ".idwav", ".xma", ".msf", ".wav", ".resource"
};
const int numExcludeExtensions = sizeof( excludeExtensions ) / sizeof( excludeExtensions[ 0 ] );
bool IsExcludedFile( const idStr & resName ) {
for ( int k = 0; k < numExcludeExtensions; k++ ) {
if ( resName.Find( excludeExtensions[ k ], false ) >= 0 ) {
return true;
}
}
return false;
}
/*
================
idFileSystemLocal::IsBinaryModel
================
*/
bool idFileSystemLocal::IsBinaryModel( const idStr & resName ) const {
idStrStatic< 32 > ext;
resName.ExtractFileExtension( ext );
if ( ( ext.Icmp( "base" ) == 0 ) || ( ext.Icmp( "blwo" ) == 0 ) || ( ext.Icmp( "bflt" ) == 0 ) || ( ext.Icmp( "bma" ) == 0 ) ) {
return true;
}
return false;
}
/*
================
idFileSystemLocal::IsSoundSample
================
*/
bool idFileSystemLocal::IsSoundSample( const idStr & resName ) const {
idStrStatic< 32 > ext;
resName.ExtractFileExtension( ext );
if ( ( ext.Icmp( "idxma" ) == 0 ) || ( ext.Icmp( "idwav" ) == 0 ) || ( ext.Icmp( "idmsf" ) == 0 ) || ( ext.Icmp( "xma" ) == 0 ) || ( ext.Icmp( "wav" ) == 0 ) || ( ext.Icmp( "msf" ) == 0 ) || ( ext.Icmp( "msadpcm" ) == 0 ) ) {
return true;
}
return false;
}
void idFileSystemLocal::BuildOrderedStartupContainer() {
idStrList orderedFiles( 1024 );
idFileList * fl = ListFilesTree( "materials", "*.mtr", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "renderprogs", "*.v360", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "renderprogs", "*.p360", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "renderprogs", "*.cgb", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "renderprogs/gl", "*.*", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "skins", "*.skin", true );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "sound", "*.sndshd", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "def", "*.def", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "fx", "*.fx", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "particles", "*.prt", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "af", "*.af", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
fl = ListFilesTree( "newpdas", "*.pda", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
orderedFiles.Append( "script/doom_main.script" );
orderedFiles.Append( "script/doom_defs.script" );
orderedFiles.Append( "script/doom_defs.script" );
orderedFiles.Append( "script/doom_events.script" );
orderedFiles.Append( "script/doom_util.script" );
orderedFiles.Append( "script/weapon_base.script" );
orderedFiles.Append( "script/ai_base.script" );
orderedFiles.Append( "script/weapon_fists.script" );
orderedFiles.Append( "script/weapon_pistol.script" );
orderedFiles.Append( "script/weapon_shotgun.script" );
orderedFiles.Append( "script/weapon_machinegun.script" );
orderedFiles.Append( "script/weapon_chaingun.script" );
orderedFiles.Append( "script/weapon_handgrenade.script" );
orderedFiles.Append( "script/weapon_plasmagun.script" );
orderedFiles.Append( "script/weapon_rocketlauncher.script" );
orderedFiles.Append( "script/weapon_bfg.script" );
orderedFiles.Append( "script/weapon_soulcube.script" );
orderedFiles.Append( "script/weapon_chainsaw.script" );
orderedFiles.Append( "script/weapon_flashlight.script" );
orderedFiles.Append( "script/weapon_pda.script" );
orderedFiles.Append( "script/ai_monster_base.script" );
orderedFiles.Append( "script/ai_monster_zombie_base.script" );
orderedFiles.Append( "script/ai_monster_demon_archvile.script" );
orderedFiles.Append( "script/ai_monster_demon_cherub.script" );
orderedFiles.Append( "script/ai_monster_demon_hellknight.script" );
orderedFiles.Append( "script/ai_monster_demon_imp.script" );
orderedFiles.Append( "script/ai_monster_demon_maggot.script" );
orderedFiles.Append( "script/ai_monster_demon_mancubus.script" );
orderedFiles.Append( "script/ai_monster_demon_pinky.script" );
orderedFiles.Append( "script/ai_monster_demon_revenant.script" );
orderedFiles.Append( "script/ai_monster_demon_trite.script" );
orderedFiles.Append( "script/ai_monster_demon_wraith.script" );
orderedFiles.Append( "script/ai_monster_flying_lostsoul.script" );
orderedFiles.Append( "script/ai_monster_flying_cacodemon.script" );
orderedFiles.Append( "script/ai_monster_zombie.script" );
orderedFiles.Append( "script/ai_monster_zombie_morgue.script" );
orderedFiles.Append( "script/ai_monster_zombie_sawyer.script" );
orderedFiles.Append( "script/ai_monster_zombie_bernie.script" );
orderedFiles.Append( "script/ai_monster_zombie_commando_cgun.script" );
orderedFiles.Append( "script/ai_monster_zombie_commando_tentacle.script" );
orderedFiles.Append( "script/ai_monster_zombie_security_pistol.script" );
orderedFiles.Append( "script/ai_monster_turret.script" );
orderedFiles.Append( "script/ai_monster_boss_vagary.script" );
orderedFiles.Append( "script/ai_monster_boss_cyberdemon.script" );
orderedFiles.Append( "script/ai_monster_boss_guardian.script" );
orderedFiles.Append( "script/ai_monster_boss_guardian_seeker.script" );
orderedFiles.Append( "script/ai_monster_boss_sabaoth.script" );
orderedFiles.Append( "script/ai_character.script" );
orderedFiles.Append( "script/ai_character_prone.script" );
orderedFiles.Append( "script/ai_character_sentry.script" );
orderedFiles.Append( "script/ai_player.script" );
orderedFiles.Append( "script/ai_alphalabs2_scientist1.script" );
orderedFiles.Append( "script/ai_cinematic_le.script" );
orderedFiles.Append( "script/map_admin1.script" );
orderedFiles.Append( "script/map_alphalabs1.script" );
orderedFiles.Append( "script/map_alphalabs2.script" );
orderedFiles.Append( "script/map_alphalabs3.script" );
orderedFiles.Append( "script/map_alphalabs3_crane.script" );
orderedFiles.Append( "script/map_alphalabs4.script" );
orderedFiles.Append( "script/map_caves.script" );
orderedFiles.Append( "script/map_caves2.script" );
orderedFiles.Append( "script/map_comm1.script" );
orderedFiles.Append( "script/map_commoutside_lift.script" );
orderedFiles.Append( "script/map_commoutside.script" );
orderedFiles.Append( "script/map_cpu.script" );
orderedFiles.Append( "script/map_cpuboss.script" );
orderedFiles.Append( "script/map_delta1.script" );
orderedFiles.Append( "script/map_delta2a.script" );
orderedFiles.Append( "script/map_delta2b.script" );
orderedFiles.Append( "script/map_delta3.script" );
orderedFiles.Append( "script/map_delta5.script" );
orderedFiles.Append( "script/map_enpro.script" );
orderedFiles.Append( "script/map_hell1.script" );
orderedFiles.Append( "script/map_hellhole.script" );
orderedFiles.Append( "script/map_recycling1.script" );
orderedFiles.Append( "script/map_recycling2.script" );
orderedFiles.Append( "script/map_site3.script" );
orderedFiles.Append( "script/map_marscity1.script" );
orderedFiles.Append( "script/map_marscity2.script" );
orderedFiles.Append( "script/map_mc_underground.script" );
orderedFiles.Append( "script/map_monorail.script" );
orderedFiles.Append( "script/d3xp_events.script" );
orderedFiles.Append( "script/weapon_bloodstone_passive.script" );
orderedFiles.Append( "script/weapon_bloodstone_active1.script" );
orderedFiles.Append( "script/weapon_bloodstone_active2.script" );
orderedFiles.Append( "script/weapon_bloodstone_active3.script" );
orderedFiles.Append( "script/weapon_shotgun_double.script" );
orderedFiles.Append( "script/weapon_grabber.script" );
orderedFiles.Append( "script/ai_monster_hunter_helltime.script" );
orderedFiles.Append( "script/ai_monster_hunter_berserk.script" );
orderedFiles.Append( "script/ai_monster_hunter_invul.script" );
orderedFiles.Append( "script/ai_monster_boss_maledict.script" );
orderedFiles.Append( "script/ai_monster_demon_vulgar.script" );
orderedFiles.Append( "script/ai_monster_demon_d3xp_bruiser.script" );
orderedFiles.Append( "script/ai_monster_dummy_target.script" );
orderedFiles.Append( "script/ai_monster_dummy.script" );
orderedFiles.Append( "script/ai_monster_demon_sentry.script" );
orderedFiles.Append( "script/ai_monster_demon_trite_jump.script" );
orderedFiles.Append( "script/ai_monster_turret_ancient.script" );
orderedFiles.Append( "script/ai_monster_flying_forgotten.script" );
orderedFiles.Append( "script/ai_character_erebus3.script" );
orderedFiles.Append( "script/d3xp_airlock.script" );
orderedFiles.Append( "script/d3xp_bloodstone.script" );
orderedFiles.Append( "script/map_erebus1.script" );
orderedFiles.Append( "script/map_erebus2_helltime.script" );
orderedFiles.Append( "script/map_erebus2.script" );
orderedFiles.Append( "script/map_erebus3.script" );
orderedFiles.Append( "script/map_erebus4.script" );
orderedFiles.Append( "script/map_erebus5.script" );
orderedFiles.Append( "script/map_erebus5_cloud.script" );
orderedFiles.Append( "script/map_erebus6.script" );
orderedFiles.Append( "script/map_erebus6_berzerk.script" );
orderedFiles.Append( "script/map_phobos1.script" );
orderedFiles.Append( "script/map_phobos2.script" );
orderedFiles.Append( "script/map_phobos2_invul.script" );
orderedFiles.Append( "script/map_phobos3.script" );
orderedFiles.Append( "script/map_phobos4.script" );
orderedFiles.Append( "script/map_deltax.script" );
orderedFiles.Append( "script/map_hell.script" );
orderedFiles.Append( "script/map_maledict.script" );
orderedFiles.Append( "script/d3le-ai_monster_boss_guardian2.script" );
orderedFiles.Append( "script/ai_follower.script" );
orderedFiles.Append( "generated/swf/shell.bswf" );
fl = ListFilesTree( "newfonts", "*.dat", false );
for ( int i = 0; i < fl->GetList().Num(); i++ ) {
orderedFiles.AddUnique( fl->GetList()[i] );
}
FreeFileList( fl );
idResourceContainer::WriteResourceFile( "_ordered.resources", orderedFiles, false );
}
/*
================
idFileSystemLocal::WriteResourcePacks
================
*/
void idFileSystemLocal::WriteResourcePacks() {
idStrList filesNotCommonToAllMaps( 16384 ); // files that are not shared by all maps, used to trim the common list
idStrList filesCommonToAllMaps( 16384 ); // files that are shared by all maps, will include startup files, renderprogs etc..
idPreloadManifest commonPreloads; // preload entries that exist in all map preload files
idStr path = RelativePathToOSPath( "maps/", "fs_savepath" );
idStrList manifestFiles;
ListOSFiles( path, ".manifest", manifestFiles );
idStrList preloadFiles;
ListOSFiles( path, ".preload", preloadFiles );
idList< idFileManifest > manifests; // list of all manifest files
// load all file manifests
for ( int i = 0; i < manifestFiles.Num(); i++ ) {
idStr path = "maps/";
path += manifestFiles[ i ];
idFileManifest manifest;
if ( manifest.LoadManifest( path ) ) {
//manifest.Print();
manifest.RemoveAll( va( "strings/%s", ID_LANG_ENGLISH ) ); // remove all .lang files
manifest.RemoveAll( va( "strings/%s", ID_LANG_FRENCH ) );
manifest.RemoveAll( va( "strings/%s", ID_LANG_ITALIAN ) );
manifest.RemoveAll( va( "strings/%s", ID_LANG_GERMAN ) );
manifest.RemoveAll( va( "strings/%s", ID_LANG_SPANISH ) );
manifest.RemoveAll( va( "strings/%s", ID_LANG_JAPANESE ) );
manifests.Append( manifest );
}
}
idList< idPreloadManifest > preloadManifests; // list of all preload manifest files
// load all preload manifests
for ( int i = 0; i < preloadFiles.Num(); i++ ) {
idStr path = "maps/";
path += preloadFiles[ i ];
if ( path.Find( "_startup", false ) >= 0 ) {
continue;
}
idPreloadManifest preload;
if ( preload.LoadManifest( path ) ) {
preloadManifests.Append( preload );
//preload.Print();
}
}
// build common list of files
for ( int i = 0; i < manifests.Num(); i++ ) {
idFileManifest &manifest = manifests[ i ];
for ( int j = 0; j < manifest.NumFiles(); j++ ) {
idStr name = manifest.GetFileNameByIndex( j );
if ( name.CheckExtension( ".cfg" ) || (name.Find( ".lang", false ) >= 0) ) {
continue;
}
if ( FileExistsInAllManifests( name, manifests ) ) {
filesCommonToAllMaps.AddUnique( name );
} else {
filesNotCommonToAllMaps.AddUnique( name );
}
}
}
// common list of preload reosurces, image, sample or models
for ( int i = 0; i < preloadManifests.Num(); i++ ) {
idPreloadManifest &preload = preloadManifests[ i ];
for ( int j = 0; j < preload.NumResources(); j++ ) {
idStr name = preload.GetResourceNameByIndex( j );
if ( FileExistsInAllPreloadManifests( name, preloadManifests ) ) {
commonPreloads.Add( preload.GetPreloadByIndex( j ) );
idLib::Printf( "Common preload added %s\n", name.c_str() );
} else {
idLib::Printf( "preload missed %s\n", name.c_str() );
}
}
}
AddRenderProgs( filesCommonToAllMaps );
AddFonts( filesCommonToAllMaps );
idStrList work;
// remove all common files from each map manifest
for ( int i = 0; i < manifests.Num(); i++ ) {
if ( ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) || ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) ) {
continue;
}
//idLib::Printf( "%04d referenced files for %s\n", manifests[ i ].GetReferencedFileCount(), manifests[ i ].GetManifestName() );
for ( int j = 0; j < filesCommonToAllMaps.Num(); j++ ) {
manifests[ i ].RemoveAll( filesCommonToAllMaps[ j ] );
}
//idLib::Printf( "%04d referenced files for %s\n", manifests[ i ].GetReferencedFileCount(), manifests[ i ].GetManifestName() );
}
idStrList commonImages( 2048 );
idStrList commonModels( 2048 );
idStrList commonAnims( 2048 );
idStrList commonCollision( 2048 );
idStrList soundFiles( 2048 ); // don't write these per map so we fit on disc
for ( int i = 0; i < manifests.Num(); i++ ) {
idStr resourceFileName = manifests[ i ].GetManifestName();
if ( resourceFileName.Find( "_startup.manifest", false ) >= 0 ) {
// add all the startup manifest files to the common list
for ( int j = 0; j < manifests[ i ].NumFiles(); j++ ) {
idStr check = manifests[i].GetFileNameByIndex( j );
if ( check.CheckExtension( ".cfg" ) == false ) {
filesCommonToAllMaps.AddUnique( check.c_str() );
}
}
continue;
}
idStaticList< idStr, 16384 > mapFiles; // map files from the manifest, these are static for easy debugging
idStaticList< idStr, 16384 > mapFilesTwo; // accumulates non bimage, bmodel and sample files
commonImages.Clear(); // collect images and models separately so they can be added in linear preload order
commonModels.Clear();
commonAnims.Clear();
commonCollision.Clear();
manifests[ i ].PopulateList( mapFiles );
for ( int j = 0; j < mapFiles.Num(); j++ ) {
idStr & resName = mapFiles[ j ];
if ( resName.Find( ".bimage", false ) >= 0 ) {
commonImages.AddUnique( resName );
continue;
}
if ( IsBinaryModel( resName ) ) {
commonModels.AddUnique( resName );
continue;
}
if ( IsSoundSample( resName ) ) {
soundFiles.AddUnique( resName );
continue;
}
if ( resName.Find( ".bik", false ) >= 0 ) {
// don't add bik files
continue;
}
if ( resName.Find ( ".bmd5anim", false ) >= 0 ) {
commonAnims.AddUnique( resName );
continue;
}
if ( resName.Find ( ".bcmodel", false ) >= 0 ) {
commonCollision.AddUnique( resName );
continue;
}
if ( resName.Find( ".lang", false ) >= 0 ) {
continue;
}
mapFilesTwo.AddUnique( resName );
}
for ( int j = 0; j < commonImages.Num(); j++ ) {
mapFilesTwo.AddUnique( commonImages[ j ] );
}
for ( int j = 0; j < commonModels.Num(); j++ ) {
mapFilesTwo.AddUnique( commonModels[ j ] );
}
for ( int j = 0; j < commonAnims.Num(); j++ ) {
mapFilesTwo.AddUnique( commonAnims[ j ] );
}
for ( int j = 0; j < commonCollision.Num(); j++ ) {
mapFilesTwo.AddUnique( commonCollision[ j ] );
}
// write map resources
idStrList mapFilesToWrite;
for ( int j = 0; j < mapFilesTwo.Num(); j++ ) {
mapFilesToWrite.Append( mapFilesTwo[ j ] );
}
idResourceContainer::WriteResourceFile( resourceFileName, mapFilesToWrite, false );
}
// add the new manifests just written
path = RelativePathToOSPath( "maps", "fs_savepath" );
ListOSFiles( path, "*.preload", work );
for ( int i = 0; i < work.Num(); i++ ) {
filesCommonToAllMaps.Append( idStr( "maps/" ) + work[ i ] );
}
filesCommonToAllMaps.Append( "_common.preload" );
// write out common models, images and sounds to separate containers
//idStrList commonSounds( 2048 );
commonImages.Clear();
commonModels.Clear();
idStrList commonFiles;
for ( int i = 0; i < filesCommonToAllMaps.Num(); i++ ) {
idStr & resName = filesCommonToAllMaps[ i ];
if ( resName.Find( ".bimage", false ) >= 0 ) {
commonImages.AddUnique( resName );
continue;
}
if ( IsBinaryModel( resName ) ) {
commonModels.AddUnique( resName );
continue;
}
if ( IsSoundSample( resName ) ) {
soundFiles.AddUnique( resName );
continue;
}
if ( resName.Find( ".bik", false ) >= 0 ) {
// no bik files in the .resource
continue;
}
if ( resName.Find( ".lang", false ) >= 0 ) {
// no bik files in the .resource
continue;
}
commonFiles.AddUnique( resName );
}
for ( int j = 0; j < commonImages.Num(); j++ ) {
commonFiles.AddUnique( commonImages[ j ] );
}
for ( int j = 0; j < commonModels.Num(); j++ ) {
commonFiles.AddUnique( commonModels[ j ] );
}
//idResourceContainer::WriteResourceFile( "_common_images", commonImages );
//idResourceContainer::WriteResourceFile( "_common_models", commonModels );
commonPreloads.WriteManifest( "_common.preload" );
idResourceContainer::WriteResourceFile( "_common", commonFiles, false );
idList< idStrList > soundOutputFiles;
soundOutputFiles.SetNum( 16 );
struct soundVOInfo_t {
const char *filename;
const char *voqualifier;
idStrList * samples;
};
const soundVOInfo_t soundFileInfo[] = {
{ "fr", "sound/vo/french/", &soundOutputFiles[ 0 ] },
{ "it", "sound/vo/italian/", &soundOutputFiles[ 1 ] },
{ "gr", "sound/vo/german/", &soundOutputFiles[ 2 ] },
{ "sp", "sound/vo/spanish/", &soundOutputFiles[ 3 ] },
{ "jp", "sound/vo/japanese/", &soundOutputFiles[ 4 ] },
{ "en", "sound/vo/", &soundOutputFiles[ 5 ] } // english last so the other langs are culled first
};
const int numSoundFiles = sizeof( soundFileInfo ) / sizeof ( soundVOInfo_t );
for ( int k = soundFiles.Num() - 1; k > 0; k-- ) {
for ( int l = 0; l < numSoundFiles; l++ ) {
if ( soundFiles[ k ].Find( soundFileInfo[ l ].voqualifier, false ) >= 0 ) {
soundFileInfo[ l ].samples->AddUnique( soundFiles[ k ] );
soundFiles.RemoveIndex( k );
}
}
}
for ( int k = 0; k < numSoundFiles; k++ ) {
idStrList & sampleList = *soundFileInfo[ k ].samples;
// write pc
idResourceContainer::WriteResourceFile( va( "_sound_pc_%s", soundFileInfo[ k ].filename ), sampleList, false );
for ( int l = 0; l < sampleList.Num(); l++ ) {
sampleList[ l ].Replace( ".idwav", ".idxma" );
}
}
idResourceContainer::WriteResourceFile( "_sound_pc", soundFiles, false );
for ( int k = 0; k < soundFiles.Num(); k++ ) {
soundFiles[ k ].Replace( ".idwav", ".idxma" );
}
for ( int k = 0; k < soundFiles.Num(); k++ ) {
soundFiles[ k ].Replace( ".idxma", ".idmsf" );
}
BuildOrderedStartupContainer();
ClearResourcePacks();
}
/*
=================
idFileSystemLocal::CopyFile
Copy a fully specified file from one place to another`
=================
*/
void idFileSystemLocal::CopyFile( const char *fromOSPath, const char *toOSPath ) {
idFile * src = OpenExplicitFileRead( fromOSPath );
if ( src == NULL ) {
idLib::Warning( "Could not open %s for read", fromOSPath );
return;
}
if ( idStr::Icmp( fromOSPath, toOSPath ) == 0 ) {
// same file can happen during build games
return;
}
CopyFile( src, toOSPath );
delete src;
if ( strstr( fromOSPath, ".wav" ) != NULL ) {
idStrStatic< MAX_OSPATH > newFromPath = fromOSPath;
idStrStatic< MAX_OSPATH > newToPath = toOSPath;
idLib::Printf( "Copying console samples for %s\n", newFromPath.c_str() );
newFromPath.SetFileExtension( "xma" );
newToPath.SetFileExtension( "xma" );
src = OpenExplicitFileRead( newFromPath );
if ( src == NULL ) {
idLib::Warning( "Could not open %s for read", newFromPath.c_str() );
} else {
CopyFile( src, newToPath );
delete src;
src = NULL;
}
newFromPath.SetFileExtension( "msf" );
newToPath.SetFileExtension( "msf" );
src = OpenExplicitFileRead( newFromPath );
if ( src == NULL ) {
idLib::Warning( "Could not open %s for read", newFromPath.c_str() );
} else {
CopyFile( src, newToPath );
delete src;
}
newFromPath.BackSlashesToSlashes();
newFromPath.ToLower();
if ( newFromPath.Find( "/vo/", false ) >= 0 ) {
for ( int i = 0; i < Sys_NumLangs(); i++ ) {
const char *lang = Sys_Lang( i );
if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) {
continue;
}
newFromPath = fromOSPath;
newToPath = toOSPath;
newFromPath.BackSlashesToSlashes();
newFromPath.ToLower();
newToPath.BackSlashesToSlashes();
newToPath.ToLower();
newFromPath.Replace( "/vo/", va( "/vo/%s/", lang ) );
newToPath.Replace( "/vo/", va( "/vo/%s/", lang ) );
src = OpenExplicitFileRead( newFromPath );
if ( src == NULL ) {
idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() );
} else {
CopyFile( src, newToPath );
delete src;
src = NULL;
}
newFromPath.SetFileExtension( "xma" );
newToPath.SetFileExtension( "xma" );
src = OpenExplicitFileRead( newFromPath );
if ( src == NULL ) {
idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() );
} else {
CopyFile( src, newToPath );
delete src;
src = NULL;
}
newFromPath.SetFileExtension( "msf" );
newToPath.SetFileExtension( "msf" );
src = OpenExplicitFileRead( newFromPath );
if ( src == NULL ) {
idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() );
} else {
CopyFile( src, newToPath );
delete src;
}
}
}
}
}
/*
=================
idFileSystemLocal::CopyFile
=================
*/
void idFileSystemLocal::CopyFile( idFile *src, const char *toOSPath ) {
idFile * dst = OpenExplicitFileWrite( toOSPath );
if ( dst == NULL ) {
idLib::Warning( "Could not open %s for write", toOSPath );
return;
}
common->Printf( "copy %s to %s\n", src->GetName(), toOSPath );
int len = src->Length();
int copied = 0;
while ( copied < len ) {
byte buffer[4096];
int read = src->Read( buffer, Min( 4096, len - copied ) );
if ( read <= 0 ) {
idLib::Warning( "Copy failed during read" );
break;
}
int written = dst->Write( buffer, read );
if ( written < read ) {
idLib::Warning( "Copy failed during write" );
break;
}
copied += written;
}
delete dst;
}
/*
====================
idFileSystemLocal::ReplaceSeparators
Fix things up differently for win/unix/mac
====================
*/
void idFileSystemLocal::ReplaceSeparators( idStr &path, char sep ) {
char *s;
for( s = &path[ 0 ]; *s ; s++ ) {
if ( *s == '/' || *s == '\\' ) {
*s = sep;
}
}
}
/*
========================
IsOSPath
========================
*/
static bool IsOSPath( const char * path ) {
assert( path );
if ( idStr::Icmpn( path, "mtp:", 4 ) == 0 ) {
return true;
}
if ( idStr::Length( path ) >= 2 ) {
if ( path[ 1 ] == ':' ) {
if ( ( path[ 0 ] > 64 && path[ 0 ] < 91 ) || ( path[ 0 ] > 96 && path[ 0 ] < 123 ) ) {
// already an OS path starting with a drive.
return true;
}
}
if ( path[ 0 ] == '\\' || path[ 0 ] == '/' ) {
// a root path
return true;
}
}
return false;
}
/*
========================
idFileSystemLocal::BuildOSPath
========================
*/
const char * idFileSystemLocal::BuildOSPath( const char * base, const char * relativePath ) {
// handle case of this already being an OS path
if ( IsOSPath( relativePath ) ) {
return relativePath;
}
return BuildOSPath( base, gameFolder, relativePath );
}
/*
===================
idFileSystemLocal::BuildOSPath
===================
*/
const char *idFileSystemLocal::BuildOSPath( const char *base, const char *game, const char *relativePath ) {
static char OSPath[MAX_STRING_CHARS];
idStr newPath;
// handle case of this already being an OS path
if ( IsOSPath( relativePath ) ) {
return relativePath;
}
idStr strBase = base;
strBase.StripTrailing( '/' );
strBase.StripTrailing( '\\' );
sprintf( newPath, "%s/%s/%s", strBase.c_str(), game, relativePath );
ReplaceSeparators( newPath );
idStr::Copynz( OSPath, newPath, sizeof( OSPath ) );
return OSPath;
}
/*
================
idFileSystemLocal::OSPathToRelativePath
takes a full OS path, as might be found in data from a media creation
program, and converts it to a relativePath by stripping off directories
Returns false if the osPath tree doesn't match any of the existing
search paths.
================
*/
const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) {
if ( ( OSPath[0] != '/' ) && ( OSPath[0] != '\\' ) && ( idStr::FindChar( OSPath, ':' ) < 0 ) ) {
// No colon and it doesn't start with a slash... it must already be a relative path
return OSPath;
}
idStaticList< idStrStatic< 32 >, 5 > basePaths;
basePaths.Append( "base" );
basePaths.Append( "d3xp" );
basePaths.Append( "d3le" );
if ( fs_game.GetString()[0] != 0 ) {
basePaths.Append( fs_game.GetString() );
}
if ( fs_game_base.GetString()[0] != 0 ) {
basePaths.Append( fs_game_base.GetString() );
}
idStaticList<int, MAX_OSPATH> slashes;
for ( const char * s = OSPath; *s != 0; s++ ) {
if ( *s == '/' || *s == '\\' ) {
slashes.Append( s - OSPath );
}
}
for ( int n = 0; n < slashes.Num() - 1; n++ ) {
const char * start = OSPath + slashes[n] + 1;
const char * end = OSPath + slashes[n+1];
int componentLength = end - start;
if ( componentLength == 0 ) {
continue;
}
for ( int i = 0; i < basePaths.Num(); i++ ) {
if ( componentLength != basePaths[i].Length() ) {
continue;
}
if ( basePaths[i].Icmpn( start, componentLength ) == 0 ) {
// There are some files like:
// W:\d3xp\base\...
// But we can't search backwards because there are others like:
// W:\doom3\base\models\mapobjects\base\...
// So instead we check for 2 base paths next to each other and take the 2nd in that case
if ( n < slashes.Num() - 2 ) {
const char * start2 = OSPath + slashes[n+1] + 1;
const char * end2 = OSPath + slashes[n+2];
int componentLength2 = end2 - start2;
if ( componentLength2 > 0 ) {
for ( int j = 0; j < basePaths.Num(); j++ ) {
if ( componentLength2 != basePaths[j].Length() ) {
continue;
}
if ( basePaths[j].Icmpn( start2, basePaths[j].Length() ) == 0 ) {
return end2 + 1;
}
}
}
}
return end + 1;
}
}
}
idLib::Warning( "OSPathToRelativePath failed on %s", OSPath );
return OSPath;
}
/*
=====================
idFileSystemLocal::RelativePathToOSPath
Returns a fully qualified path that can be used with stdio libraries
=====================
*/
const char *idFileSystemLocal::RelativePathToOSPath( const char *relativePath, const char *basePath ) {
const char *path = cvarSystem->GetCVarString( basePath );
if ( !path[0] ) {
path = fs_savepath.GetString();
}
return BuildOSPath( path, gameFolder, relativePath );
}
/*
=================
idFileSystemLocal::RemoveFile
=================
*/
void idFileSystemLocal::RemoveFile( const char *relativePath ) {
idStr OSPath;
if ( fs_basepath.GetString()[0] ) {
OSPath = BuildOSPath( fs_basepath.GetString(), gameFolder, relativePath );
::DeleteFile( OSPath );
}
OSPath = BuildOSPath( fs_savepath.GetString(), gameFolder, relativePath );
::DeleteFile( OSPath );
}
/*
========================
idFileSystemLocal::RemoveDir
========================
*/
bool idFileSystemLocal::RemoveDir( const char * relativePath ) {
bool success = true;
if ( fs_savepath.GetString()[0] ) {
success &= Sys_Rmdir( BuildOSPath( fs_savepath.GetString(), relativePath ) );
}
success &= Sys_Rmdir( BuildOSPath( fs_basepath.GetString(), relativePath ) );
return success;
}
/*
============
idFileSystemLocal::ReadFile
Filename are relative to the search path
a null buffer will just return the file length and time without loading
timestamp can be NULL if not required
============
*/
int idFileSystemLocal::ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp ) {
idFile * f;
byte * buf;
int len;
bool isConfig;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
return 0;
}
if ( relativePath == NULL || !relativePath[0] ) {
common->FatalError( "idFileSystemLocal::ReadFile with empty name\n" );
return 0;
}
if ( timestamp ) {
*timestamp = FILE_NOT_FOUND_TIMESTAMP;
}
if ( buffer ) {
*buffer = NULL;
}
if ( buffer == NULL && timestamp != NULL && resourceFiles.Num() > 0 ) {
static idResourceCacheEntry rc;
int size = 0;
if ( GetResourceCacheEntry( relativePath, rc ) ) {
*timestamp = 0;
size = rc.length;
}
return size;
}
buf = NULL; // quiet compiler warning
// if this is a .cfg file and we are playing back a journal, read
// it from the journal file
if ( strstr( relativePath, ".cfg" ) == relativePath + strlen( relativePath ) - 4 ) {
isConfig = true;
if ( eventLoop && eventLoop->JournalLevel() == 2 ) {
int r;
loadCount++;
loadStack++;
common->DPrintf( "Loading %s from journal file.\n", relativePath );
len = 0;
r = eventLoop->com_journalDataFile->Read( &len, sizeof( len ) );
if ( r != sizeof( len ) ) {
*buffer = NULL;
return -1;
}
buf = (byte *)Mem_ClearedAlloc(len+1, TAG_IDFILE);
*buffer = buf;
r = eventLoop->com_journalDataFile->Read( buf, len );
if ( r != len ) {
common->FatalError( "Read from journalDataFile failed" );
}
// guarantee that it will have a trailing 0 for string operations
buf[len] = 0;
return len;
}
} else {
isConfig = false;
}
// look for it in the filesystem or pack files
f = OpenFileRead( relativePath, ( buffer != NULL ) );
if ( f == NULL ) {
if ( buffer ) {
*buffer = NULL;
}
return -1;
}
len = f->Length();
if ( timestamp ) {
*timestamp = f->Timestamp();
}
if ( !buffer ) {
CloseFile( f );
return len;
}
loadCount++;
loadStack++;
buf = (byte *)Mem_ClearedAlloc(len+1, TAG_IDFILE);
*buffer = buf;
f->Read( buf, len );
// guarantee that it will have a trailing 0 for string operations
buf[len] = 0;
CloseFile( f );
// if we are journalling and it is a config file, write it to the journal file
if ( isConfig && eventLoop && eventLoop->JournalLevel() == 1 ) {
common->DPrintf( "Writing %s to journal file.\n", relativePath );
eventLoop->com_journalDataFile->Write( &len, sizeof( len ) );
eventLoop->com_journalDataFile->Write( buf, len );
eventLoop->com_journalDataFile->Flush();
}
return len;
}
/*
=============
idFileSystemLocal::FreeFile
=============
*/
void idFileSystemLocal::FreeFile( void *buffer ) {
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
if ( !buffer ) {
common->FatalError( "idFileSystemLocal::FreeFile( NULL )" );
}
loadStack--;
Mem_Free( buffer );
}
/*
============
idFileSystemLocal::WriteFile
Filenames are relative to the search path
============
*/
int idFileSystemLocal::WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath ) {
idFile *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
if ( !relativePath || !buffer ) {
common->FatalError( "idFileSystemLocal::WriteFile: NULL parameter" );
}
f = idFileSystemLocal::OpenFileWrite( relativePath, basePath );
if ( !f ) {
common->Printf( "Failed to open %s\n", relativePath );
return -1;
}
size = f->Write( buffer, size );
CloseFile( f );
return size;
}
/*
========================
idFileSystemLocal::RenameFile
========================
*/
bool idFileSystemLocal::RenameFile( const char * relativePath, const char * newName, const char * basePath ) {
const char * path = cvarSystem->GetCVarString( basePath );
if ( !path[0] ) {
path = fs_savepath.GetString();
}
idStr oldOSPath = BuildOSPath( path, gameFolder, relativePath );
idStr newOSPath = BuildOSPath( path, gameFolder, newName );
// this gives atomic-delete-on-rename, like POSIX rename()
// There is a MoveFileTransacted() on vista and above, not sure if that means there
// is a race condition inside MoveFileEx...
const bool success = ( MoveFileEx( oldOSPath.c_str(), newOSPath.c_str(), MOVEFILE_REPLACE_EXISTING ) != 0 );
if ( !success ) {
const int err = GetLastError();
idLib::Warning( "RenameFile( %s, %s ) error %i", newOSPath.c_str(), oldOSPath.c_str(), err );
}
return success;
}
/*
===============
idFileSystemLocal::AddUnique
===============
*/
int idFileSystemLocal::AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const {
int i, hashKey;
hashKey = hashIndex.GenerateKey( name );
for ( i = hashIndex.First( hashKey ); i >= 0; i = hashIndex.Next( i ) ) {
if ( list[i].Icmp( name ) == 0 ) {
return i;
}
}
i = list.Append( name );
hashIndex.Add( hashKey, i );
return i;
}
/*
===============
idFileSystemLocal::GetExtensionList
===============
*/
void idFileSystemLocal::GetExtensionList( const char *extension, idStrList &extensionList ) const {
int s, e, l;
l = idStr::Length( extension );
s = 0;
while( 1 ) {
e = idStr::FindChar( extension, '|', s, l );
if ( e != -1 ) {
extensionList.Append( idStr( extension, s, e ) );
s = e + 1;
} else {
extensionList.Append( idStr( extension, s, l ) );
break;
}
}
}
/*
===============
idFileSystemLocal::GetFileList
Does not clear the list first so this can be used to progressively build a file list.
When 'sort' is true only the new files added to the list are sorted.
===============
*/
int idFileSystemLocal::GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char * gamedir ) {
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
if ( !extensions.Num() ) {
return 0;
}
if ( !relativePath ) {
return 0;
}
int pathLength = strlen( relativePath );
if ( pathLength ) {
pathLength++; // for the trailing '/'
}
idStrStatic< MAX_OSPATH > strippedName;
if ( resourceFiles.Num() > 0 ) {
int idx = resourceFiles.Num() - 1;
while ( idx >= 0 ) {
for ( int i = 0; i < resourceFiles[ idx ]->cacheTable.Num(); i++ ) {
idResourceCacheEntry & rt = resourceFiles[ idx ]->cacheTable[ i ];
// if the name is not long anough to at least contain the path
if ( rt.filename.Length() <= pathLength ) {
continue;
}
// check for a path match without the trailing '/'
if ( pathLength && idStr::Icmpn( rt.filename, relativePath, pathLength - 1 ) != 0 ) {
continue;
}
// ensure we have a path, and not just a filename containing the path
if ( rt.filename[ pathLength ] == '\0' || rt.filename[pathLength - 1] != '/' ) {
continue;
}
// make sure the file is not in a subdirectory
int j = pathLength;
for ( ; rt.filename[j+1] != '\0'; j++ ) {
if ( rt.filename[ j ] == '/' ) {
break;
}
}
if ( rt.filename[ j + 1 ] ) {
continue;
}
// check for extension match
for ( j = 0; j < extensions.Num(); j++ ) {
if ( rt.filename.Length() >= extensions[j].Length() && extensions[j].Icmp( rt.filename.c_str() + rt.filename.Length() - extensions[j].Length() ) == 0 ) {
break;
}
}
if ( j >= extensions.Num() ) {
continue;
}
// unique the match
if ( fullRelativePath ) {
idStr work = relativePath;
work += "/";
work += rt.filename.c_str() + pathLength;
work.StripTrailing( '/' );
AddUnique( work, list, hashIndex );
} else {
idStr work = rt.filename.c_str() + pathLength;
work.StripTrailing( '/' );
AddUnique( work, list, hashIndex );
}
}
idx--;
}
}
// search through the path, one element at a time, adding to list
for ( int sp = searchPaths.Num() - 1; sp >= 0; sp-- ) {
if ( gamedir != NULL && gamedir[0] != 0 ) {
if ( searchPaths[sp].gamedir != gamedir) {
continue;
}
}
idStr netpath = BuildOSPath( searchPaths[sp].path, searchPaths[sp].gamedir, relativePath );
for ( int i = 0; i < extensions.Num(); i++ ) {
// scan for files in the filesystem
idStrList sysFiles;
ListOSFiles( netpath, extensions[i], sysFiles );
// if we are searching for directories, remove . and ..
if ( extensions[i][0] == '/' && extensions[i][1] == 0 ) {
sysFiles.Remove( "." );
sysFiles.Remove( ".." );
}
for ( int j = 0; j < sysFiles.Num(); j++ ) {
// unique the match
if ( fullRelativePath ) {
idStr work = relativePath;
work += "/";
work += sysFiles[j];
AddUnique( work, list, hashIndex );
} else {
AddUnique( sysFiles[j], list, hashIndex );
}
}
}
}
return list.Num();
}
/*
===============
idFileSystemLocal::ListFiles
===============
*/
idFileList *idFileSystemLocal::ListFiles( const char *relativePath, const char *extension, bool sort, bool fullRelativePath, const char* gamedir ) {
idHashIndex hashIndex( 4096, 4096 );
idStrList extensionList;
idFileList *fileList = new (TAG_IDFILE) idFileList;
fileList->basePath = relativePath;
GetExtensionList( extension, extensionList );
GetFileList( relativePath, extensionList, fileList->list, hashIndex, fullRelativePath, gamedir );
if ( sort ) {
fileList->list.SortWithTemplate( idSort_PathStr() );
}
return fileList;
}
/*
===============
idFileSystemLocal::GetFileListTree
===============
*/
int idFileSystemLocal::GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir ) {
int i;
idStrList slash, folders( 128 );
idHashIndex folderHashIndex( 1024, 128 );
// recurse through the subdirectories
slash.Append( "/" );
GetFileList( relativePath, slash, folders, folderHashIndex, true, gamedir );
for ( i = 0; i < folders.Num(); i++ ) {
if ( folders[i][0] == '.' ) {
continue;
}
if ( folders[i].Icmp( relativePath ) == 0 ){
continue;
}
GetFileListTree( folders[i], extensions, list, hashIndex, gamedir );
}
// list files in the current directory
GetFileList( relativePath, extensions, list, hashIndex, true, gamedir );
return list.Num();
}
/*
===============
idFileSystemLocal::ListFilesTree
===============
*/
idFileList *idFileSystemLocal::ListFilesTree( const char *relativePath, const char *extension, bool sort, const char* gamedir ) {
idHashIndex hashIndex( 4096, 4096 );
idStrList extensionList;
idFileList *fileList = new (TAG_IDFILE) idFileList();
fileList->basePath = relativePath;
fileList->list.SetGranularity( 4096 );
GetExtensionList( extension, extensionList );
GetFileListTree( relativePath, extensionList, fileList->list, hashIndex, gamedir );
if ( sort ) {
fileList->list.SortWithTemplate( idSort_PathStr() );
}
return fileList;
}
/*
===============
idFileSystemLocal::FreeFileList
===============
*/
void idFileSystemLocal::FreeFileList( idFileList *fileList ) {
delete fileList;
}
/*
===============
idFileSystemLocal::ListOSFiles
call to the OS for a listing of files in an OS directory
===============
*/
int idFileSystemLocal::ListOSFiles( const char *directory, const char *extension, idStrList &list ) {
if ( !extension ) {
extension = "";
}
return Sys_ListFiles( directory, extension, list );
}
/*
================
idFileSystemLocal::Dir_f
================
*/
void idFileSystemLocal::Dir_f( const idCmdArgs &args ) {
idStr relativePath;
idStr extension;
idFileList *fileList;
int i;
if ( args.Argc() < 2 || args.Argc() > 3 ) {
common->Printf( "usage: dir <directory> [extension]\n" );
return;
}
if ( args.Argc() == 2 ) {
relativePath = args.Argv( 1 );
extension = "";
}
else {
relativePath = args.Argv( 1 );
extension = args.Argv( 2 );
if ( extension[0] != '.' ) {
common->Warning( "extension should have a leading dot" );
}
}
relativePath.BackSlashesToSlashes();
relativePath.StripTrailing( '/' );
common->Printf( "Listing of %s/*%s\n", relativePath.c_str(), extension.c_str() );
common->Printf( "---------------\n" );
fileList = fileSystemLocal.ListFiles( relativePath, extension );
for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
common->Printf( "%s\n", fileList->GetFile( i ) );
}
common->Printf( "%d files\n", fileList->list.Num() );
fileSystemLocal.FreeFileList( fileList );
}
/*
================
idFileSystemLocal::DirTree_f
================
*/
void idFileSystemLocal::DirTree_f( const idCmdArgs &args ) {
idStr relativePath;
idStr extension;
idFileList *fileList;
int i;
if ( args.Argc() < 2 || args.Argc() > 3 ) {
common->Printf( "usage: dirtree <directory> [extension]\n" );
return;
}
if ( args.Argc() == 2 ) {
relativePath = args.Argv( 1 );
extension = "";
}
else {
relativePath = args.Argv( 1 );
extension = args.Argv( 2 );
if ( extension[0] != '.' ) {
common->Warning( "extension should have a leading dot" );
}
}
relativePath.BackSlashesToSlashes();
relativePath.StripTrailing( '/' );
common->Printf( "Listing of %s/*%s /s\n", relativePath.c_str(), extension.c_str() );
common->Printf( "---------------\n" );
fileList = fileSystemLocal.ListFilesTree( relativePath, extension );
for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
common->Printf( "%s\n", fileList->GetFile( i ) );
}
common->Printf( "%d files\n", fileList->list.Num() );
fileSystemLocal.FreeFileList( fileList );
}
/*
================
idFileSystemLocal::ClearResourcePacks
================
*/
void idFileSystemLocal::ClearResourcePacks() {
}
/*
================
idFileSystemLocal::BuildGame_f
================
*/
void idFileSystemLocal::BuildGame_f( const idCmdArgs &args ) {
fileSystemLocal.WriteResourcePacks();
}
/*
================
idFileSystemLocal::WriteResourceFile_f
================
*/
void idFileSystemLocal::WriteResourceFile_f( const idCmdArgs &args ) {
if ( args.Argc() != 2 ) {
common->Printf( "Usage: writeResourceFile <manifest file>\n" );
return;
}
idStrList manifest;
idResourceContainer::ReadManifestFile( args.Argv( 1 ), manifest );
idResourceContainer::WriteResourceFile( args.Argv( 1 ), manifest, false );
}
/*
================
idFileSystemLocal::UpdateResourceFile_f
================
*/
void idFileSystemLocal::UpdateResourceFile_f( const idCmdArgs &args ) {
if ( args.Argc() < 3 ) {
common->Printf( "Usage: updateResourceFile <resource file> <files>\n" );
return;
}
idStr filename = args.Argv( 1 );
idStrList filesToAdd;
for ( int i = 2; i < args.Argc(); i++ ) {
filesToAdd.Append( args.Argv( i ) );
}
idResourceContainer::UpdateResourceFile( filename, filesToAdd );
}
/*
================
idFileSystemLocal::ExtractResourceFile_f
================
*/
void idFileSystemLocal::ExtractResourceFile_f( const idCmdArgs &args ) {
if ( args.Argc() < 3 ) {
common->Printf( "Usage: extractResourceFile <resource file> <outpath> <copysound>\n" );
return;
}
idStr filename = args.Argv( 1 );
idStr outPath = args.Argv( 2 );
bool copyWaves = ( args.Argc() > 3 );
idResourceContainer::ExtractResourceFile( filename, outPath, copyWaves );
}
/*
============
idFileSystemLocal::Path_f
============
*/
void idFileSystemLocal::Path_f( const idCmdArgs &args ) {
common->Printf( "Current search path:\n" );
for ( int i = 0; i < fileSystemLocal.searchPaths.Num(); i++ ) {
common->Printf( "%s/%s\n", fileSystemLocal.searchPaths[i].path.c_str(), fileSystemLocal.searchPaths[i].gamedir.c_str() );
}
}
/*
============
idFileSystemLocal::TouchFile_f
The only purpose of this function is to allow game script files to copy
arbitrary files furing an "fs_copyfiles 1" run.
============
*/
void idFileSystemLocal::TouchFile_f( const idCmdArgs &args ) {
idFile *f;
if ( args.Argc() != 2 ) {
common->Printf( "Usage: touchFile <file>\n" );
return;
}
f = fileSystemLocal.OpenFileRead( args.Argv( 1 ) );
if ( f ) {
fileSystemLocal.CloseFile( f );
}
}
/*
============
idFileSystemLocal::TouchFileList_f
Takes a text file and touches every file in it, use one file per line.
============
*/
void idFileSystemLocal::TouchFileList_f( const idCmdArgs &args ) {
if ( args.Argc() != 2 ) {
common->Printf( "Usage: touchFileList <filename>\n" );
return;
}
const char *buffer = NULL;
idParser src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
if ( fileSystem->ReadFile( args.Argv( 1 ), ( void** )&buffer, NULL ) && buffer ) {
src.LoadMemory( buffer, strlen( buffer ), args.Argv( 1 ) );
if ( src.IsLoaded() ) {
idToken token;
while( src.ReadToken( &token ) ) {
common->Printf( "%s\n", token.c_str() );
const bool captureToImage = false;
common->UpdateScreen( captureToImage );
idFile *f = fileSystemLocal.OpenFileRead( token );
if ( f ) {
fileSystemLocal.CloseFile( f );
}
}
}
}
}
/*
============
idFileSystemLocal::GenerateResourceCRCs_f
Generates a CRC checksum file for each .resources file.
============
*/
void idFileSystemLocal::GenerateResourceCRCs_f( const idCmdArgs &args ) {
idLib::Printf( "Generating CRCs for resource files...\n" );
std::auto_ptr<idFileList> baseResourceFileList( fileSystem->ListFiles( ".", ".resources" ) );
if ( baseResourceFileList.get() != NULL ) {
CreateCRCsForResourceFileList ( *baseResourceFileList );
}
std::auto_ptr<idFileList> mapResourceFileList( fileSystem->ListFilesTree( "maps", ".resources" ) );
if ( mapResourceFileList.get() != NULL ) {
CreateCRCsForResourceFileList ( *mapResourceFileList );
}
idLib::Printf( "Done generating CRCs for resource files.\n" );
}
/*
================
idFileSystemLocal::CreateCRCsForResourceFileList
================
*/
void idFileSystemLocal::CreateCRCsForResourceFileList( const idFileList & list ) {
for ( int fileIndex = 0; fileIndex < list.GetNumFiles(); ++fileIndex ) {
idLib::Printf( " Processing %s.\n", list.GetFile( fileIndex ) );
std::auto_ptr<idFile_Memory> currentFile( static_cast<idFile_Memory *>( fileSystem->OpenFileReadMemory( list.GetFile( fileIndex ) ) ) );
if ( currentFile.get() == NULL ) {
idLib::Printf( " Error reading %s.\n", list.GetFile( fileIndex ) );
continue;
}
uint32 resourceMagic;
currentFile->ReadBig( resourceMagic );
if ( resourceMagic != RESOURCE_FILE_MAGIC ) {
idLib::Printf( "Resource file magic number doesn't match, skipping %s.\n", list.GetFile( fileIndex ) );
continue;
}
int tableOffset;
currentFile->ReadBig( tableOffset );
int tableLength;
currentFile->ReadBig( tableLength );
// Read in the table
currentFile->Seek( tableOffset, FS_SEEK_SET );
int numFileResources;
currentFile->ReadBig( numFileResources );
idList< idResourceCacheEntry > cacheEntries;
cacheEntries.SetNum( numFileResources );
for ( int innerFileIndex = 0; innerFileIndex < numFileResources; ++innerFileIndex ) {
cacheEntries[innerFileIndex].Read( currentFile.get() );
}
// All tables read, now seek to each one and calculate the CRC.
idTempArray< unsigned long > innerFileCRCs( numFileResources );
for ( int innerFileIndex = 0; innerFileIndex < numFileResources; ++innerFileIndex ) {
const char * innerFileDataBegin = currentFile->GetDataPtr() + cacheEntries[innerFileIndex].offset;
innerFileCRCs[innerFileIndex] = CRC32_BlockChecksum( innerFileDataBegin, cacheEntries[innerFileIndex].length );
}
// Get the CRC for all the CRCs.
const unsigned long totalCRC = CRC32_BlockChecksum( innerFileCRCs.Ptr(), innerFileCRCs.Size() );
// Write the .crc file corresponding to the .resources file.
idStr crcFilename = list.GetFile( fileIndex );
crcFilename.SetFileExtension( ".crc" );
std::auto_ptr<idFile> crcOutputFile( fileSystem->OpenFileWrite( crcFilename, "fs_basepath" ) );
if ( crcOutputFile.get() == NULL ) {
idLib::Printf( "Error writing CRC file %s.\n", crcFilename );
continue;
}
const uint32 CRC_FILE_MAGIC = 0xCC00CC00; // I just made this up, it has no meaning.
const uint32 CRC_FILE_VERSION = 1;
crcOutputFile->WriteBig( CRC_FILE_MAGIC );
crcOutputFile->WriteBig( CRC_FILE_VERSION );
crcOutputFile->WriteBig( totalCRC );
crcOutputFile->WriteBig( numFileResources );
crcOutputFile->WriteBigArray( innerFileCRCs.Ptr(), numFileResources );
}
}
/*
================
idFileSystemLocal::AddResourceFile
================
*/
int idFileSystemLocal::AddResourceFile( const char * resourceFileName ) {
idStrStatic< MAX_OSPATH > resourceFile = va( "maps/%s", resourceFileName );
idResourceContainer *rc = new idResourceContainer();
if ( rc->Init( resourceFile, resourceFiles.Num() ) ) {
resourceFiles.Append( rc );
common->Printf( "Loaded resource file %s\n", resourceFile.c_str() );
return resourceFiles.Num() - 1;
}
return -1;
}
/*
================
idFileSystemLocal::FindResourceFile
================
*/
int idFileSystemLocal::FindResourceFile( const char * resourceFileName ) {
for ( int i = 0; i < resourceFiles.Num(); i++ ) {
if ( idStr::Icmp( resourceFileName, resourceFiles[ i ]->GetFileName() ) == 0 ) {
return i;
}
}
return -1;
}
/*
================
idFileSystemLocal::RemoveResourceFileByIndex
================
*/
void idFileSystemLocal::RemoveResourceFileByIndex( const int &idx ) {
if ( idx >= 0 && idx < resourceFiles.Num() ) {
if ( idx >= 0 && idx < resourceFiles.Num() ) {
delete resourceFiles[ idx ];
resourceFiles.RemoveIndex( idx );
for ( int i = 0; i < resourceFiles.Num(); i++ ) {
// fixup any container indexes
resourceFiles[ i ]->SetContainerIndex( i );
}
}
}
}
/*
================
idFileSystemLocal::RemoveMapResourceFile
================
*/
void idFileSystemLocal::RemoveMapResourceFile( const char * resourceFileName ) {
int idx = FindResourceFile( va( "maps/%s", resourceFileName ) );
if ( idx >= 0 ) {
RemoveResourceFileByIndex( idx );
}
}
/*
================
idFileSystemLocal::RemoveResourceFile
================
*/
void idFileSystemLocal::RemoveResourceFile( const char * resourceFileName ) {
int idx = FindResourceFile( resourceFileName );
if ( idx >= 0 ) {
RemoveResourceFileByIndex( idx );
}
}
/*
================
idFileSystemLocal::AddGameDirectory
Sets gameFolder, adds the directory to the head of the search paths
================
*/
void idFileSystemLocal::AddGameDirectory( const char *path, const char *dir ) {
// check if the search path already exists
for ( int i = 0; i < searchPaths.Num(); i++ ) {
if ( searchPaths[i].path.Cmp( path ) == 0 && searchPaths[i].gamedir.Cmp( dir ) == 0 ) {
return;
}
}
gameFolder = dir;
//
// add the directory to the search path
//
searchpath_t & search = searchPaths.Alloc();
search.path = path;
search.gamedir = dir;
idStr pakfile = BuildOSPath( path, dir, "" );
pakfile[ pakfile.Length() - 1 ] = 0; // strip the trailing slash
idStrList pakfiles;
ListOSFiles( pakfile, ".resources", pakfiles );
pakfiles.SortWithTemplate( idSort_PathStr() );
if ( pakfiles.Num() > 0 ) {
// resource files present, ignore pak files
for ( int i = 0; i < pakfiles.Num(); i++ ) {
pakfile = pakfiles[i]; //BuildOSPath( path, dir, pakfiles[i] );
idResourceContainer *rc = new idResourceContainer();
if ( rc->Init( pakfile, resourceFiles.Num() ) ) {
resourceFiles.Append( rc );
common->Printf( "Loaded resource file %s\n", pakfile.c_str() );
//com_productionMode.SetInteger( 2 );
}
}
}
}
/*
================
idFileSystemLocal::SetupGameDirectories
Takes care of the correct search order.
================
*/
void idFileSystemLocal::SetupGameDirectories( const char *gameName ) {
// setup basepath
if ( fs_basepath.GetString()[0] ) {
AddGameDirectory( fs_basepath.GetString(), gameName );
}
// setup savepath
if ( fs_savepath.GetString()[0] ) {
AddGameDirectory( fs_savepath.GetString(), gameName );
}
}
const char *cachedStartupFiles[] = {
"game:\\base\\video\\loadvideo.bik"
};
const int numStartupFiles = sizeof( cachedStartupFiles ) / sizeof ( cachedStartupFiles[ 0 ] );
const char *cachedNormalFiles[] = {
"game:\\base\\_sound_xenon_en.resources", // these will fail silently on the files that are not on disc
"game:\\base\\_sound_xenon_fr.resources",
"game:\\base\\_sound_xenon_jp.resources",
"game:\\base\\_sound_xenon_sp.resources",
"game:\\base\\_sound_xenon_it.resources",
"game:\\base\\_sound_xenon_gr.resources",
"game:\\base\\_sound_xenon.resources",
"game:\\base\\_common.resources",
"game:\\base\\_ordered.resources",
"game:\\base\\video\\mars_rotation.bik" // cache this to save the consumer from hearing SEEK.. SEEK... SEEK.. SEEK SEEEK while at the main menu
};
const int numNormalFiles = sizeof( cachedNormalFiles ) / sizeof ( cachedNormalFiles[ 0 ] );
const char *dontCacheFiles[] = {
"game:\\base\\maps\\*.*", // these will fail silently on the files that are not on disc
"game:\\base\\video\\*.*",
"game:\\base\\sound\\*.*",
};
const int numDontCacheFiles = sizeof( dontCacheFiles ) / sizeof ( dontCacheFiles[ 0 ] );
/*
================
idFileSystemLocal::InitPrecache
================
*/
void idFileSystemLocal::InitPrecache() {
if ( !fs_enableBackgroundCaching.GetBool() ) {
return;
}
numFilesOpenedAsCached = 0;
}
/*
================
idFileSystemLocal::ReOpenCacheFiles
================
*/
void idFileSystemLocal::ReOpenCacheFiles() {
if ( !fs_enableBackgroundCaching.GetBool() ) {
return;
}
}
/*
================
idFileSystemLocal::Startup
================
*/
void idFileSystemLocal::Startup() {
common->Printf( "------ Initializing File System ------\n" );
InitPrecache();
SetupGameDirectories( BASE_GAMEDIR );
// fs_game_base override
if ( fs_game_base.GetString()[0] &&
idStr::Icmp( fs_game_base.GetString(), BASE_GAMEDIR ) ) {
SetupGameDirectories( fs_game_base.GetString() );
}
// fs_game override
if ( fs_game.GetString()[0] &&
idStr::Icmp( fs_game.GetString(), BASE_GAMEDIR ) &&
idStr::Icmp( fs_game.GetString(), fs_game_base.GetString() ) ) {
SetupGameDirectories( fs_game.GetString() );
}
// add our commands
cmdSystem->AddCommand( "dir", Dir_f, CMD_FL_SYSTEM, "lists a folder", idCmdSystem::ArgCompletion_FileName );
cmdSystem->AddCommand( "dirtree", DirTree_f, CMD_FL_SYSTEM, "lists a folder with subfolders" );
cmdSystem->AddCommand( "path", Path_f, CMD_FL_SYSTEM, "lists search paths" );
cmdSystem->AddCommand( "touchFile", TouchFile_f, CMD_FL_SYSTEM, "touches a file" );
cmdSystem->AddCommand( "touchFileList", TouchFileList_f, CMD_FL_SYSTEM, "touches a list of files" );
cmdSystem->AddCommand( "buildGame", BuildGame_f, CMD_FL_SYSTEM, "builds game pak files" );
cmdSystem->AddCommand( "writeResourceFile", WriteResourceFile_f, CMD_FL_SYSTEM, "writes a .resources file from a supplied manifest" );
cmdSystem->AddCommand( "extractResourceFile", ExtractResourceFile_f, CMD_FL_SYSTEM, "extracts to the supplied resource file to the supplied path" );
cmdSystem->AddCommand( "updateResourceFile", UpdateResourceFile_f, CMD_FL_SYSTEM, "updates or appends the supplied files in the supplied resource file" );
cmdSystem->AddCommand( "generateResourceCRCs", GenerateResourceCRCs_f, CMD_FL_SYSTEM, "Generates CRC checksums for all the resource files." );
// print the current search paths
Path_f( idCmdArgs() );
common->Printf( "file system initialized.\n" );
common->Printf( "--------------------------------------\n" );
}
/*
================
idFileSystemLocal::Init
Called only at inital startup, not when the filesystem
is resetting due to a game change
================
*/
void idFileSystemLocal::Init() {
// allow command line parms to override our defaults
// we have to specially handle this, because normal command
// line variable sets don't happen until after the filesystem
// has already been initialized
common->StartupVariable( "fs_basepath" );
common->StartupVariable( "fs_savepath" );
common->StartupVariable( "fs_game" );
common->StartupVariable( "fs_game_base" );
common->StartupVariable( "fs_copyfiles" );
if ( fs_basepath.GetString()[0] == '\0' ) {
fs_basepath.SetString( Sys_DefaultBasePath() );
}
if ( fs_savepath.GetString()[0] == '\0' ) {
fs_savepath.SetString( Sys_DefaultSavePath() );
}
// try to start up normally
Startup();
// if we can't find default.cfg, assume that the paths are
// busted and error out now, rather than getting an unreadable
// graphics screen when the font fails to load
// Dedicated servers can run with no outside files at all
if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
common->FatalError( "Couldn't load default.cfg" );
}
}
/*
================
idFileSystemLocal::Restart
================
*/
void idFileSystemLocal::Restart() {
// free anything we currently have loaded
Shutdown( true );
Startup();
// if we can't find default.cfg, assume that the paths are
// busted and error out now, rather than getting an unreadable
// graphics screen when the font fails to load
if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
common->FatalError( "Couldn't load default.cfg" );
}
}
/*
================
idFileSystemLocal::Shutdown
Frees all resources and closes all files
================
*/
void idFileSystemLocal::Shutdown( bool reloading ) {
gameFolder.Clear();
searchPaths.Clear();
resourceFiles.DeleteContents();
cmdSystem->RemoveCommand( "path" );
cmdSystem->RemoveCommand( "dir" );
cmdSystem->RemoveCommand( "dirtree" );
cmdSystem->RemoveCommand( "touchFile" );
}
/*
================
idFileSystemLocal::IsInitialized
================
*/
bool idFileSystemLocal::IsInitialized() const {
return ( searchPaths.Num() != 0 );
}
/*
=================================================================================
Opening files
=================================================================================
*/
/*
========================
idFileSystemLocal::GetResourceCacheEntry
Returns false if the entry isn't found
========================
*/
bool idFileSystemLocal::GetResourceCacheEntry( const char *fileName, idResourceCacheEntry &rc ) {
idStrStatic< MAX_OSPATH > canonical;
if ( strstr( fileName, ":") != NULL ) {
// os path, convert to relative? scripts can pass in an OS path
//idLib::Printf( "RESOURCE: os path passed %s\n", fileName );
return NULL;
} else {
canonical = fileName;
}
canonical.BackSlashesToSlashes();
canonical.ToLower();
int idx = resourceFiles.Num() - 1;
while ( idx >= 0 ) {
const int key = resourceFiles[ idx ]->cacheHash.GenerateKey( canonical, false );
for ( int index = resourceFiles[ idx ]->cacheHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = resourceFiles[ idx ]->cacheHash.GetNext( index ) ) {
idResourceCacheEntry & rt = resourceFiles[ idx ]->cacheTable[ index ];
if ( idStr::Icmp( rt.filename, canonical ) == 0 ) {
rc.filename = rt.filename;
rc.length = rt.length;
rc.containerIndex = idx;
rc.offset = rt.offset;
return true;
}
}
idx--;
}
return false;
}
/*
========================
idFileSystemLocal::GetResourceFile
Returns NULL
========================
*/
idFile * idFileSystemLocal::GetResourceFile( const char *fileName, bool memFile ) {
if ( resourceFiles.Num() == 0 ) {
return NULL;
}
static idResourceCacheEntry rc;
if ( GetResourceCacheEntry( fileName, rc ) ) {
if ( fs_debugResources.GetBool() ) {
idLib::Printf( "RES: loading file %s\n", rc.filename.c_str() );
}
idFile_InnerResource *file = new idFile_InnerResource( rc.filename, resourceFiles[ rc.containerIndex ]->resourceFile, rc.offset, rc.length );
if ( file != NULL && ( memFile || rc.length <= resourceBufferAvailable ) || rc.length < 8 * 1024 * 1024 ) {
byte *buf = NULL;
if ( rc.length < resourceBufferAvailable ) {
buf = resourceBufferPtr;
resourceBufferAvailable = 0;
} else {
if ( fs_debugResources.GetBool() ) {
idLib::Printf( "MEM: Allocating %05d bytes for a resource load\n", rc.length );
}
buf = ( byte * )Mem_Alloc( rc.length, TAG_TEMP );
}
file->Read( (void*)buf, rc.length );
if ( buf == resourceBufferPtr ) {
file->SetResourceBuffer( buf );
return file;
} else {
idFile_Memory *mfile = new idFile_Memory( rc.filename, ( const char * )buf, rc.length );
if ( mfile != NULL ) {
mfile->TakeDataOwnership();
delete file;
return mfile;
}
}
}
return file;
}
return NULL;
}
/*
===========
idFileSystemLocal::OpenFileReadFlags
Finds the file in the search path, following search flag recommendations
Returns filesize and an open FILE pointer.
Used for streaming data out of either a
separate file or a ZIP file.
===========
*/
idFile *idFileSystemLocal::OpenFileReadFlags( const char *relativePath, int searchFlags, bool allowCopyFiles, const char* gamedir ) {
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
return NULL;
}
if ( relativePath == NULL ) {
common->FatalError( "idFileSystemLocal::OpenFileRead: NULL 'relativePath' parameter passed\n" );
return NULL;
}
// qpaths are not supposed to have a leading slash
if ( relativePath[0] == '/' || relativePath[0] == '\\' ) {
relativePath++;
}
// make absolutely sure that it can't back up the path.
// The searchpaths do guarantee that something will always
// be prepended, so we don't need to worry about "c:" or "//limbo"
if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) {
return NULL;
}
// edge case
if ( relativePath[0] == '\0' ) {
return NULL;
}
if ( fs_debug.GetBool() ) {
idLib::Printf( "FILE DEBUG: opening %s\n", relativePath );
}
if ( resourceFiles.Num() > 0 && fs_resourceLoadPriority.GetInteger() == 1 ) {
idFile * rf = GetResourceFile( relativePath, ( searchFlags & FSFLAG_RETURN_FILE_MEM ) != 0 );
if ( rf != NULL ) {
return rf;
}
}
//
// search through the path, one element at a time
//
if ( searchFlags & FSFLAG_SEARCH_DIRS ) {
for ( int sp = searchPaths.Num() - 1; sp >= 0; sp-- ) {
if ( gamedir != NULL && gamedir[0] != 0 ) {
if ( searchPaths[sp].gamedir != gamedir ) {
continue;
}
}
idStr netpath = BuildOSPath( searchPaths[sp].path, searchPaths[sp].gamedir, relativePath );
idFileHandle fp = OpenOSFile( netpath, FS_READ );
if ( !fp ) {
continue;
}
idFile_Permanent * file = new (TAG_IDFILE) idFile_Permanent();
file->o = fp;
file->name = relativePath;
file->fullPath = netpath;
file->mode = ( 1 << FS_READ );
file->fileSize = DirectFileLength( file->o );
if ( fs_debug.GetInteger() ) {
common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s/%s')\n", relativePath, searchPaths[sp].path.c_str(), searchPaths[sp].gamedir.c_str() );
}
// if fs_copyfiles is set
if ( allowCopyFiles ) {
idStr copypath;
idStr name;
copypath = BuildOSPath( fs_savepath.GetString(), searchPaths[sp].gamedir, relativePath );
netpath.ExtractFileName( name );
copypath.StripFilename();
copypath += PATHSEPARATOR_STR;
copypath += name;
if ( fs_buildResources.GetBool() ) {
idStrStatic< MAX_OSPATH > relativePath = OSPathToRelativePath( copypath );
relativePath.BackSlashesToSlashes();
relativePath.ToLower();
if ( IsSoundSample( relativePath ) ) {
idStrStatic< MAX_OSPATH > samplePath = relativePath;
samplePath.SetFileExtension( "idwav" );
if ( samplePath.Find( "generated/" ) == -1 ) {
samplePath.Insert( "generated/", 0 );
}
fileManifest.AddUnique( samplePath );
if ( relativePath.Find( "/vo/", false ) >= 0 ) {
// this is vo so add the language variants
for ( int i = 0; i < Sys_NumLangs(); i++ ) {
const char *lang = Sys_Lang( i );
if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) {
continue;
}
samplePath = relativePath;
samplePath.Replace( "/vo/", va( "/vo/%s/", lang ) );
samplePath.SetFileExtension( "idwav" );
if ( samplePath.Find( "generated/" ) == -1 ) {
samplePath.Insert( "generated/", 0 );
}
fileManifest.AddUnique( samplePath );
}
}
} else if ( relativePath.Icmpn( "guis/", 5 ) == 0 ) {
// this is a gui so add the language variants
for ( int i = 0; i < Sys_NumLangs(); i++ ) {
const char *lang = Sys_Lang( i );
if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) {
fileManifest.Append( relativePath );
continue;
}
idStrStatic< MAX_OSPATH > guiPath = relativePath;
guiPath.Replace( "guis/", va( "guis/%s/", lang ) );
fileManifest.Append( guiPath );
}
} else {
// never add .amp files
if ( strstr( relativePath, ".amp" ) == NULL ) {
fileManifest.Append( relativePath );
}
}
}
if ( fs_copyfiles.GetBool() ) {
CopyFile( netpath, copypath );
}
}
if ( searchFlags & FSFLAG_RETURN_FILE_MEM ) {
idFile_Memory * memFile = new (TAG_IDFILE) idFile_Memory( file->name );
memFile->SetLength( file->fileSize );
file->Read( (void *)memFile->GetDataPtr(), file->fileSize );
delete file;
memFile->TakeDataOwnership();
return memFile;
}
return file;
}
}
if ( resourceFiles.Num() > 0 && fs_resourceLoadPriority.GetInteger() == 0 ) {
idFile * rf = GetResourceFile( relativePath, ( searchFlags & FSFLAG_RETURN_FILE_MEM ) != 0 );
if ( rf != NULL ) {
return rf;
}
}
if ( fs_debug.GetInteger( ) ) {
common->Printf( "Can't find %s\n", relativePath );
}
return NULL;
}
/*
===========
idFileSystemLocal::OpenFileRead
===========
*/
idFile *idFileSystemLocal::OpenFileRead( const char *relativePath, bool allowCopyFiles, const char* gamedir ) {
return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS, allowCopyFiles, gamedir );
}
/*
===========
idFileSystemLocal::OpenFileReadMemory
===========
*/
idFile *idFileSystemLocal::OpenFileReadMemory( const char *relativePath, bool allowCopyFiles, const char* gamedir ) {
return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS | FSFLAG_RETURN_FILE_MEM, allowCopyFiles, gamedir );
}
/*
===========
idFileSystemLocal::OpenFileWrite
===========
*/
idFile *idFileSystemLocal::OpenFileWrite( const char *relativePath, const char *basePath ) {
const char *path;
idStr OSpath;
idFile_Permanent *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
path = cvarSystem->GetCVarString( basePath );
if ( !path[0] ) {
path = fs_savepath.GetString();
}
OSpath = BuildOSPath( path, gameFolder, relativePath );
if ( fs_debug.GetInteger() ) {
common->Printf( "idFileSystem::OpenFileWrite: %s\n", OSpath.c_str() );
}
common->DPrintf( "writing to: %s\n", OSpath.c_str() );
CreateOSPath( OSpath );
f = new (TAG_IDFILE) idFile_Permanent();
f->o = OpenOSFile( OSpath, FS_WRITE );
if ( !f->o ) {
delete f;
return NULL;
}
f->name = relativePath;
f->fullPath = OSpath;
f->mode = ( 1 << FS_WRITE );
f->handleSync = false;
f->fileSize = 0;
return f;
}
/*
===========
idFileSystemLocal::OpenExplicitFileRead
===========
*/
idFile *idFileSystemLocal::OpenExplicitFileRead( const char *OSPath ) {
idFile_Permanent *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
if ( fs_debug.GetInteger() ) {
common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath );
}
//common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath );
f = new (TAG_IDFILE) idFile_Permanent();
f->o = OpenOSFile( OSPath, FS_READ );
if ( !f->o ) {
delete f;
return NULL;
}
f->name = OSPath;
f->fullPath = OSPath;
f->mode = ( 1 << FS_READ );
f->handleSync = false;
f->fileSize = DirectFileLength( f->o );
return f;
}
/*
===========
idFileSystemLocal::OpenExplicitPakFile
===========
*/
idFile_Cached *idFileSystemLocal::OpenExplicitPakFile( const char *OSPath ) {
idFile_Cached *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
//if ( fs_debug.GetInteger() ) {
// common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath );
//}
//common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath );
f = new (TAG_IDFILE) idFile_Cached();
f->o = OpenOSFile( OSPath, FS_READ );
if ( !f->o ) {
delete f;
return NULL;
}
f->name = OSPath;
f->fullPath = OSPath;
f->mode = ( 1 << FS_READ );
f->handleSync = false;
f->fileSize = DirectFileLength( f->o );
return f;
}
/*
===========
idFileSystemLocal::OpenExplicitFileWrite
===========
*/
idFile *idFileSystemLocal::OpenExplicitFileWrite( const char *OSPath ) {
idFile_Permanent *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
if ( fs_debug.GetInteger() ) {
common->Printf( "idFileSystem::OpenExplicitFileWrite: %s\n", OSPath );
}
common->DPrintf( "writing to: %s\n", OSPath );
CreateOSPath( OSPath );
f = new (TAG_IDFILE) idFile_Permanent();
f->o = OpenOSFile( OSPath, FS_WRITE );
if ( !f->o ) {
delete f;
return NULL;
}
f->name = OSPath;
f->fullPath = OSPath;
f->mode = ( 1 << FS_WRITE );
f->handleSync = false;
f->fileSize = 0;
return f;
}
/*
===========
idFileSystemLocal::OpenFileAppend
===========
*/
idFile *idFileSystemLocal::OpenFileAppend( const char *relativePath, bool sync, const char *basePath ) {
const char *path;
idStr OSpath;
idFile_Permanent *f;
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
path = cvarSystem->GetCVarString( basePath );
if ( !path[0] ) {
path = fs_savepath.GetString();
}
OSpath = BuildOSPath( path, gameFolder, relativePath );
CreateOSPath( OSpath );
if ( fs_debug.GetInteger() ) {
common->Printf( "idFileSystem::OpenFileAppend: %s\n", OSpath.c_str() );
}
f = new (TAG_IDFILE) idFile_Permanent();
f->o = OpenOSFile( OSpath, FS_APPEND );
if ( !f->o ) {
delete f;
return NULL;
}
f->name = relativePath;
f->fullPath = OSpath;
f->mode = ( 1 << FS_WRITE ) + ( 1 << FS_APPEND );
f->handleSync = sync;
f->fileSize = DirectFileLength( f->o );
return f;
}
/*
================
idFileSystemLocal::OpenFileByMode
================
*/
idFile *idFileSystemLocal::OpenFileByMode( const char *relativePath, fsMode_t mode ) {
if ( mode == FS_READ ) {
return OpenFileRead( relativePath );
}
if ( mode == FS_WRITE ) {
return OpenFileWrite( relativePath );
}
if ( mode == FS_APPEND ) {
return OpenFileAppend( relativePath, true );
}
common->FatalError( "idFileSystemLocal::OpenFileByMode: bad mode" );
return NULL;
}
/*
==============
idFileSystemLocal::CloseFile
==============
*/
void idFileSystemLocal::CloseFile( idFile *f ) {
if ( !IsInitialized() ) {
common->FatalError( "Filesystem call made without initialization\n" );
}
delete f;
}
/*
=================
idFileSystemLocal::FindDLL
=================
*/
void idFileSystemLocal::FindDLL( const char *name, char _dllPath[ MAX_OSPATH ] ) {
char dllName[MAX_OSPATH];
sys->DLL_GetFileName( name, dllName, MAX_OSPATH );
// from executable directory first - this is handy for developement
idStr dllPath = Sys_EXEPath( );
dllPath.StripFilename( );
dllPath.AppendPath( dllName );
idFile * dllFile = OpenExplicitFileRead( dllPath );
if ( dllFile ) {
dllPath = dllFile->GetFullPath();
CloseFile( dllFile );
dllFile = NULL;
} else {
dllPath = "";
}
idStr::snPrintf( _dllPath, MAX_OSPATH, dllPath.c_str() );
}
/*
===============
idFileSystemLocal::FindFile
===============
*/
findFile_t idFileSystemLocal::FindFile( const char *path ) {
idFile *f = OpenFileReadFlags( path, FSFLAG_SEARCH_DIRS );
if ( f == NULL ) {
return FIND_NO;
}
delete f;
return FIND_YES;
}
/*
===============
idFileSystemLocal::IsFolder
===============
*/
sysFolder_t idFileSystemLocal::IsFolder( const char * relativePath, const char *basePath ) {
return Sys_IsFolder( RelativePathToOSPath( relativePath, basePath ) );
}