FileSystem.cpp (97294B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 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. 23 24 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. 25 26 =========================================================================== 27 */ 28 29 #include "../idlib/precompiled.h" 30 #pragma hdrstop 31 32 #include "Unzip.h" 33 #include "Zip.h" 34 35 #ifdef WIN32 36 #include <io.h> // for _read 37 #else 38 #if !__MACH__ && __MWERKS__ 39 #include <types.h> 40 #include <stat.h> 41 #else 42 #include <sys/types.h> 43 #include <sys/stat.h> 44 #endif 45 #include <unistd.h> 46 #endif 47 48 49 /* 50 ============================================================================= 51 52 DOOM FILESYSTEM 53 54 All of Doom's data access is through a hierarchical file system, but the contents of 55 the file system can be transparently merged from several sources. 56 57 A "relativePath" is a reference to game file data, which must include a terminating zero. 58 "..", "\\", and ":" are explicitly illegal in qpaths to prevent any references 59 outside the Doom directory system. 60 61 The "base path" is the path to the directory holding all the game directories and 62 usually the executable. It defaults to the current directory, but can be overridden 63 with "+set fs_basepath c:\doom" on the command line. The base path cannot be modified 64 at all after startup. 65 66 The "save path" is the path to the directory where game files will be saved. It defaults 67 to the base path, but can be overridden with a "+set fs_savepath c:\doom" on the 68 command line. Any files that are created during the game (demos, screenshots, etc.) will 69 be created reletive to the save path. 70 71 If a user runs the game directly from a CD, the base path would be on the CD. This 72 should still function correctly, but all file writes will fail (harmlessly). 73 74 The "base game" is the directory under the paths where data comes from by default, and 75 can be either "base" or "demo". 76 77 The "current game" may be the same as the base game, or it may be the name of another 78 directory under the paths that should be searched for files before looking in the base 79 game. The game directory is set with "+set fs_game myaddon" on the command line. This is 80 the basis for addons. 81 82 No other directories outside of the base game and current game will ever be referenced by 83 filesystem functions. 84 85 Because we will have updated executables freely available online, there is no point to 86 trying to restrict demo / oem versions of the game with code changes. Demo / oem versions 87 should be exactly the same executables as release versions, but with different data that 88 automatically restricts where game media can come from to prevent add-ons from working. 89 90 If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the base 91 path, it will be copied over to the save path. This is a development aid to help build 92 test releases and to copy working sets of files. 93 94 The relative path "sound/newstuff/test.wav" would be searched for in the following places: 95 96 for save path, base path: 97 for current game, base game: 98 search directory 99 search zip files 100 101 downloaded files, to be written to save path + current game's directory 102 103 The filesystem can be safely shutdown and reinitialized with different 104 basedir / cddir / game combinations, but all other subsystems that rely on it 105 (sound, video) must also be forced to restart. 106 107 "additional mod path search": 108 fs_game_base can be used to set an additional search path 109 in search order, fs_game, fs_game_base, BASEGAME 110 for instance to base a mod of D3 + D3XP assets, fs_game mymod, fs_game_base d3xp 111 112 ============================================================================= 113 */ 114 115 #define MAX_ZIPPED_FILE_NAME 2048 116 #define FILE_HASH_SIZE 1024 117 118 struct searchpath_t { 119 idStr path; // c:\doom 120 idStr gamedir; // base 121 }; 122 123 // search flags when opening a file 124 #define FSFLAG_SEARCH_DIRS ( 1 << 0 ) 125 #define FSFLAG_RETURN_FILE_MEM ( 1 << 1 ) 126 127 class idFileSystemLocal : public idFileSystem { 128 public: 129 idFileSystemLocal(); 130 131 virtual void Init(); 132 virtual void Restart(); 133 virtual void Shutdown( bool reloading ); 134 virtual bool IsInitialized() const; 135 virtual idFileList * ListFiles( const char *relativePath, const char *extension, bool sort = false, bool fullRelativePath = false, const char* gamedir = NULL ); 136 virtual idFileList * ListFilesTree( const char *relativePath, const char *extension, bool sort = false, const char* gamedir = NULL ); 137 virtual void FreeFileList( idFileList *fileList ); 138 virtual const char * OSPathToRelativePath( const char *OSPath ); 139 virtual const char * RelativePathToOSPath( const char *relativePath, const char *basePath ); 140 virtual const char * BuildOSPath( const char *base, const char *game, const char *relativePath ); 141 virtual const char * BuildOSPath( const char *base, const char *relativePath ); 142 virtual void CreateOSPath( const char *OSPath ); 143 virtual int ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp ); 144 virtual void FreeFile( void *buffer ); 145 virtual int WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath = "fs_savepath" ); 146 virtual void RemoveFile( const char *relativePath ); 147 virtual bool RemoveDir( const char * relativePath ); 148 virtual bool RenameFile( const char * relativePath, const char * newName, const char * basePath = "fs_savepath" ); 149 virtual idFile * OpenFileReadFlags( const char *relativePath, int searchFlags, bool allowCopyFiles = true, const char* gamedir = NULL ); 150 virtual idFile * OpenFileRead( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL ); 151 virtual idFile * OpenFileReadMemory( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL ); 152 virtual idFile * OpenFileWrite( const char *relativePath, const char *basePath = "fs_savepath" ); 153 virtual idFile * OpenFileAppend( const char *relativePath, bool sync = false, const char *basePath = "fs_basepath" ); 154 virtual idFile * OpenFileByMode( const char *relativePath, fsMode_t mode ); 155 virtual idFile * OpenExplicitFileRead( const char *OSPath ); 156 virtual idFile * OpenExplicitFileWrite( const char *OSPath ); 157 virtual idFile_Cached * OpenExplicitPakFile( const char *OSPath ); 158 virtual void CloseFile( idFile *f ); 159 virtual void FindDLL( const char *basename, char dllPath[ MAX_OSPATH ] ); 160 virtual void CopyFile( const char *fromOSPath, const char *toOSPath ); 161 virtual findFile_t FindFile( const char *path ); 162 virtual bool FilenameCompare( const char *s1, const char *s2 ) const; 163 virtual int GetFileLength( const char * relativePath ); 164 virtual sysFolder_t IsFolder( const char * relativePath, const char *basePath = "fs_basepath" ); 165 // resource tracking 166 virtual void EnableBackgroundCache( bool enable ); 167 virtual void BeginLevelLoad( const char *name, char *_blockBuffer, int _blockBufferSize ); 168 virtual void EndLevelLoad(); 169 virtual bool InProductionMode() { return ( resourceFiles.Num() > 0 ) | ( com_productionMode.GetInteger() != 0 ); } 170 virtual bool UsingResourceFiles() { return resourceFiles.Num() > 0; } 171 virtual void UnloadMapResources( const char *name ); 172 virtual void UnloadResourceContainer( const char *name ); 173 idFile * GetResourceContainer( int idx ) { 174 if ( idx >= 0 && idx < resourceFiles.Num() ) { 175 return resourceFiles[ idx ]->resourceFile; 176 } 177 return NULL; 178 } 179 180 virtual void StartPreload( const idStrList &_preload ); 181 virtual void StopPreload(); 182 idFile * GetResourceFile( const char *fileName, bool memFile ); 183 bool GetResourceCacheEntry( const char *fileName, idResourceCacheEntry &rc ); 184 virtual int ReadFromBGL( idFile *_resourceFile, void * _buffer, int _offset, int _len ); 185 virtual bool IsBinaryModel( const idStr & resName ) const; 186 virtual bool IsSoundSample( const idStr & resName ) const; 187 virtual void FreeResourceBuffer() { resourceBufferAvailable = resourceBufferSize; } 188 virtual void AddImagePreload( const char *resName, int _filter, int _repeat, int _usage, int _cube ) { 189 preloadList.AddImage( resName, _filter, _repeat, _usage, _cube ); 190 } 191 virtual void AddSamplePreload( const char *resName ) { 192 preloadList.AddSample( resName ); 193 } 194 virtual void AddModelPreload( const char *resName ) { 195 preloadList.AddModel( resName ); 196 } 197 virtual void AddAnimPreload( const char *resName ) { 198 preloadList.AddAnim( resName ); 199 } 200 virtual void AddCollisionPreload( const char *resName ) { 201 preloadList.AddCollisionModel( resName ); 202 } 203 virtual void AddParticlePreload( const char *resName ) { 204 preloadList.AddParticle( resName ); 205 } 206 207 static void Dir_f( const idCmdArgs &args ); 208 static void DirTree_f( const idCmdArgs &args ); 209 static void Path_f( const idCmdArgs &args ); 210 static void TouchFile_f( const idCmdArgs &args ); 211 static void TouchFileList_f( const idCmdArgs &args ); 212 static void BuildGame_f( const idCmdArgs &args ); 213 //static void FileStats_f( const idCmdArgs &args ); 214 static void WriteResourceFile_f ( const idCmdArgs &args ); 215 static void ExtractResourceFile_f( const idCmdArgs &args ); 216 static void UpdateResourceFile_f( const idCmdArgs &args ); 217 static void GenerateResourceCRCs_f( const idCmdArgs &args ); 218 static void CreateCRCsForResourceFileList( const idFileList & list ); 219 220 void BuildOrderedStartupContainer(); 221 private: 222 idList<searchpath_t> searchPaths; 223 int loadCount; // total files read 224 int loadStack; // total files in memory 225 idStr gameFolder; // this will be a single name without separators 226 227 static idCVar fs_debug; 228 static idCVar fs_debugResources; 229 static idCVar fs_copyfiles; 230 static idCVar fs_buildResources; 231 static idCVar fs_game; 232 static idCVar fs_game_base; 233 static idCVar fs_enableBGL; 234 static idCVar fs_debugBGL; 235 236 idStr manifestName; 237 idStrList fileManifest; 238 idPreloadManifest preloadList; 239 240 idList< idResourceContainer * > resourceFiles; 241 byte * resourceBufferPtr; 242 int resourceBufferSize; 243 int resourceBufferAvailable; 244 int numFilesOpenedAsCached; 245 246 private: 247 248 // .resource file creation 249 void ClearResourcePacks(); 250 void WriteResourcePacks(); 251 void AddRenderProgs( idStrList &files ); 252 void AddFonts( idStrList &files ); 253 254 void ReplaceSeparators( idStr &path, char sep = PATHSEPARATOR_CHAR ); 255 int ListOSFiles( const char *directory, const char *extension, idStrList &list ); 256 idFileHandle OpenOSFile( const char *name, fsMode_t mode ); 257 void CloseOSFile( idFileHandle o ); 258 int DirectFileLength( idFileHandle o ); 259 void CopyFile( idFile *src, const char *toOSPath ); 260 int AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const; 261 void GetExtensionList( const char *extension, idStrList &extensionList ) const; 262 int GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir = NULL ); 263 264 int GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir = NULL ); 265 void AddGameDirectory( const char *path, const char *dir ); 266 267 int AddResourceFile( const char * resourceFileName ); 268 void RemoveMapResourceFile( const char * resourceFileName ); 269 void RemoveResourceFileByIndex( const int & idx ); 270 void RemoveResourceFile( const char * resourceFileName ); 271 int FindResourceFile( const char * resourceFileName ); 272 273 void SetupGameDirectories( const char *gameName ); 274 void Startup(); 275 void InitPrecache(); 276 void ReOpenCacheFiles(); 277 }; 278 279 idCVar idFileSystemLocal::fs_debug( "fs_debug", "0", CVAR_SYSTEM | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> ); 280 idCVar idFileSystemLocal::fs_debugResources( "fs_debugResources", "0", CVAR_SYSTEM | CVAR_BOOL, "" ); 281 idCVar idFileSystemLocal::fs_enableBGL( "fs_enableBGL", "0", CVAR_SYSTEM | CVAR_BOOL, "" ); 282 idCVar idFileSystemLocal::fs_debugBGL( "fs_debugBGL", "0", CVAR_SYSTEM | CVAR_BOOL, "" ); 283 idCVar idFileSystemLocal::fs_copyfiles( "fs_copyfiles", "0", CVAR_SYSTEM | CVAR_INIT | CVAR_BOOL, "Copy every file touched to fs_savepath" ); 284 idCVar idFileSystemLocal::fs_buildResources( "fs_buildresources", "0", CVAR_SYSTEM | CVAR_BOOL | CVAR_INIT, "Copy every file touched to a resource file" ); 285 idCVar idFileSystemLocal::fs_game( "fs_game", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "mod path" ); 286 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" ); 287 288 idCVar fs_basepath( "fs_basepath", "", CVAR_SYSTEM | CVAR_INIT, "" ); 289 idCVar fs_savepath( "fs_savepath", "", CVAR_SYSTEM | CVAR_INIT, "" ); 290 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" ); 291 idCVar fs_enableBackgroundCaching( "fs_enableBackgroundCaching", "1", CVAR_SYSTEM , "if 1 allow the 360 to precache game files in the background" ); 292 293 idFileSystemLocal fileSystemLocal; 294 idFileSystem * fileSystem = &fileSystemLocal; 295 296 /* 297 ================ 298 idFileSystemLocal::ReadFromBGL 299 ================ 300 */ 301 int idFileSystemLocal::ReadFromBGL( idFile *_resourceFile, void * _buffer, int _offset, int _len ) { 302 if ( _resourceFile->Tell() != _offset ) { 303 _resourceFile->Seek( _offset, FS_SEEK_SET ); 304 } 305 return _resourceFile->Read( _buffer, _len ); 306 } 307 308 /* 309 ================ 310 idFileSystemLocal::StartPreload 311 ================ 312 */ 313 void idFileSystemLocal::StartPreload( const idStrList & _preload ) { 314 } 315 316 /* 317 ================ 318 idFileSystemLocal::StopPreload 319 ================ 320 */ 321 void idFileSystemLocal::StopPreload() { 322 } 323 324 /* 325 ================ 326 idFileSystemLocal::idFileSystemLocal 327 ================ 328 */ 329 idFileSystemLocal::idFileSystemLocal() { 330 loadCount = 0; 331 loadStack = 0; 332 resourceBufferPtr = NULL; 333 resourceBufferSize = 0; 334 resourceBufferAvailable = 0; 335 numFilesOpenedAsCached = 0; 336 } 337 338 /* 339 =========== 340 idFileSystemLocal::FilenameCompare 341 342 Ignore case and separator char distinctions 343 =========== 344 */ 345 bool idFileSystemLocal::FilenameCompare( const char *s1, const char *s2 ) const { 346 int c1, c2; 347 348 do { 349 c1 = *s1++; 350 c2 = *s2++; 351 352 if ( c1 >= 'a' && c1 <= 'z' ) { 353 c1 -= ('a' - 'A'); 354 } 355 if ( c2 >= 'a' && c2 <= 'z' ) { 356 c2 -= ('a' - 'A'); 357 } 358 359 if ( c1 == '\\' || c1 == ':' ) { 360 c1 = '/'; 361 } 362 if ( c2 == '\\' || c2 == ':' ) { 363 c2 = '/'; 364 } 365 366 if ( c1 != c2 ) { 367 return true; // strings not equal 368 } 369 } while( c1 ); 370 371 return false; // strings are equal 372 } 373 374 /* 375 ======================== 376 idFileSystemLocal::GetFileLength 377 ======================== 378 */ 379 int idFileSystemLocal::GetFileLength( const char * relativePath ) { 380 idFile * f; 381 int len; 382 383 if ( !IsInitialized() ) { 384 idLib::FatalError( "Filesystem call made without initialization" ); 385 } 386 387 if ( !relativePath || !relativePath[0] ) { 388 idLib::Warning( "idFileSystemLocal::GetFileLength with empty name" ); 389 return -1; 390 } 391 392 if ( resourceFiles.Num() > 0 ) { 393 idResourceCacheEntry rc; 394 if ( GetResourceCacheEntry( relativePath, rc ) ) { 395 return rc.length; 396 } 397 } 398 399 // look for it in the filesystem or pack files 400 f = OpenFileRead( relativePath, false ); 401 if ( f == NULL ) { 402 return -1; 403 } 404 405 len = (int)f->Length(); 406 407 delete f; 408 return len; 409 } 410 411 /* 412 ================ 413 idFileSystemLocal::OpenOSFile 414 ================ 415 */ 416 idFileHandle idFileSystemLocal::OpenOSFile( const char *fileName, fsMode_t mode ) { 417 idFileHandle fp; 418 419 420 DWORD dwAccess = 0; 421 DWORD dwShare = 0; 422 DWORD dwCreate = 0; 423 DWORD dwFlags = 0; 424 425 if ( mode == FS_WRITE ) { 426 dwAccess = GENERIC_READ | GENERIC_WRITE; 427 dwShare = FILE_SHARE_READ; 428 dwCreate = CREATE_ALWAYS; 429 dwFlags = FILE_ATTRIBUTE_NORMAL; 430 } else if ( mode == FS_READ ) { 431 dwAccess = GENERIC_READ; 432 dwShare = FILE_SHARE_READ; 433 dwCreate = OPEN_EXISTING; 434 dwFlags = FILE_ATTRIBUTE_NORMAL; 435 } else if ( mode == FS_APPEND ) { 436 dwAccess = GENERIC_READ | GENERIC_WRITE; 437 dwShare = FILE_SHARE_READ; 438 dwCreate = OPEN_ALWAYS; 439 dwFlags = FILE_ATTRIBUTE_NORMAL; 440 } 441 442 fp = CreateFile( fileName, dwAccess, dwShare, NULL, dwCreate, dwFlags, NULL ); 443 if ( fp == INVALID_HANDLE_VALUE ) { 444 return NULL; 445 } 446 return fp; 447 } 448 449 /* 450 ================ 451 idFileSystemLocal::CloseOSFile 452 ================ 453 */ 454 void idFileSystemLocal::CloseOSFile( idFileHandle o ) { 455 ::CloseHandle( o ); 456 } 457 458 /* 459 ================ 460 idFileSystemLocal::DirectFileLength 461 ================ 462 */ 463 int idFileSystemLocal::DirectFileLength( idFileHandle o ) { 464 return GetFileSize( o, NULL ); 465 } 466 467 /* 468 ============ 469 idFileSystemLocal::CreateOSPath 470 471 Creates any directories needed to store the given filename 472 ============ 473 */ 474 void idFileSystemLocal::CreateOSPath( const char *OSPath ) { 475 char *ofs; 476 477 // make absolutely sure that it can't back up the path 478 // FIXME: what about c: ? 479 if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) { 480 #ifdef _DEBUG 481 common->DPrintf( "refusing to create relative path \"%s\"\n", OSPath ); 482 #endif 483 return; 484 } 485 486 idStrStatic< MAX_OSPATH > path( OSPath ); 487 path.SlashesToBackSlashes(); 488 for( ofs = &path[ 1 ]; *ofs ; ofs++ ) { 489 if ( *ofs == PATHSEPARATOR_CHAR ) { 490 // create the directory 491 *ofs = 0; 492 Sys_Mkdir( path ); 493 *ofs = PATHSEPARATOR_CHAR; 494 } 495 } 496 } 497 498 /* 499 ================= 500 idFileSystemLocal::EnableBackgroundCache 501 ================= 502 */ 503 void idFileSystemLocal::EnableBackgroundCache( bool enable ) { 504 if ( !fs_enableBackgroundCaching.GetBool() ) { 505 return; 506 } 507 } 508 509 /* 510 ================= 511 idFileSystemLocal::BeginLevelLoad 512 ================= 513 */ 514 void idFileSystemLocal::BeginLevelLoad( const char *name, char *_blockBuffer, int _blockBufferSize ) { 515 516 if ( name == NULL || *name == '\0' ) { 517 return; 518 } 519 520 resourceBufferPtr = ( byte* )_blockBuffer; 521 resourceBufferAvailable = _blockBufferSize; 522 resourceBufferSize = _blockBufferSize; 523 524 manifestName = name; 525 526 fileManifest.Clear(); 527 preloadList.Clear(); 528 529 EnableBackgroundCache( false ); 530 531 ReOpenCacheFiles(); 532 manifestName.StripPath(); 533 534 if ( resourceFiles.Num() > 0 ) { 535 AddResourceFile( va( "%s.resources", manifestName.c_str() ) ); 536 } 537 538 } 539 540 /* 541 ================= 542 idFileSystemLocal::UnloadResourceContainer 543 544 ================= 545 */ 546 void idFileSystemLocal::UnloadResourceContainer( const char *name ) { 547 if ( name == NULL || *name == '\0' ) { 548 return; 549 } 550 RemoveResourceFile( va( "%s.resources", name ) ); 551 } 552 553 /* 554 ================= 555 idFileSystemLocal::UnloadMapResources 556 ================= 557 */ 558 void idFileSystemLocal::UnloadMapResources( const char *name ) { 559 if ( name == NULL || *name == '\0' || idStr::Icmp( "_startup", name ) == 0 ) { 560 return; 561 } 562 563 if ( resourceFiles.Num() > 0 ) { 564 RemoveMapResourceFile( va( "%s.resources", name ) ); 565 } 566 } 567 568 /* 569 ================= 570 idFileSystemLocal::EndLevelLoad 571 572 ================= 573 */ 574 void idFileSystemLocal::EndLevelLoad() { 575 if ( fs_buildResources.GetBool() ) { 576 int saveCopyFiles = fs_copyfiles.GetInteger(); 577 fs_copyfiles.SetInteger( 0 ); 578 579 idStr manifestFileName = manifestName; 580 manifestFileName.StripPath(); 581 manifestFileName.SetFileExtension( "manifest" ); 582 manifestFileName.Insert( "maps/", 0 ); 583 idFile *outFile = fileSystem->OpenFileWrite( manifestFileName ); 584 if ( outFile != NULL ) { 585 int num = fileManifest.Num(); 586 outFile->WriteBig( num ); 587 for ( int i = 0; i < num; i++ ) { 588 outFile->WriteString( fileManifest[ i ] ); 589 } 590 delete outFile; 591 } 592 593 idStrStatic< MAX_OSPATH > preloadName = manifestName; 594 preloadName.Insert( "maps/", 0 ); 595 preloadName += ".preload"; 596 idFile *fileOut = fileSystem->OpenFileWrite( preloadName, "fs_savepath" ); 597 preloadList.WriteManifestToFile( fileOut ); 598 delete fileOut; 599 600 fs_copyfiles.SetInteger( saveCopyFiles ); 601 } 602 603 EnableBackgroundCache( true ); 604 605 resourceBufferPtr = NULL; 606 resourceBufferAvailable = 0; 607 resourceBufferSize = 0; 608 609 } 610 611 bool FileExistsInAllManifests( const char *filename, idList< idFileManifest > &manifests ) { 612 for ( int i = 0; i < manifests.Num(); i++ ) { 613 if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) { 614 continue; 615 } 616 if ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) { 617 continue; 618 } 619 if ( manifests[ i ].FindFile( filename ) == -1 ) { 620 return false; 621 } 622 } 623 return true; 624 } 625 626 bool FileExistsInAllPreloadManifests( const char *filename, idList< idPreloadManifest > &manifests ) { 627 for ( int i = 0; i < manifests.Num(); i++ ) { 628 if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) { 629 continue; 630 } 631 if ( manifests[ i ].FindResource( filename ) == -1 ) { 632 return false; 633 } 634 } 635 return true; 636 } 637 638 void RemoveFileFromAllManifests( const char *filename, idList< idFileManifest > &manifests ) { 639 for ( int i = 0; i < manifests.Num(); i++ ) { 640 if ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) { 641 continue; 642 } 643 if ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) { 644 continue; 645 } 646 manifests[ i ].RemoveAll( filename ); 647 } 648 } 649 650 651 /* 652 ================ 653 idFileSystemLocal::AddPerPlatformResources 654 ================ 655 */ 656 void idFileSystemLocal::AddRenderProgs( idStrList &files ) { 657 idStrList work; 658 659 // grab all the renderprogs 660 idStr path = RelativePathToOSPath( "renderprogs/cgb", "fs_basepath" ); 661 ListOSFiles( path, "*.cgb", work ); 662 for ( int i = 0; i < work.Num(); i++ ) { 663 files.Append( idStr( "renderprogs/cgb/" ) + work[i] ); 664 } 665 666 path = RelativePathToOSPath( "renderprogs/hlsl", "fs_basepath" ); 667 ListOSFiles( path, "*.v360", work ); 668 for ( int i = 0; i < work.Num(); i++ ) { 669 files.Append( idStr( "renderprogs/hlsl/" ) + work[i] ); 670 } 671 ListOSFiles( path, "*.p360", work ); 672 for ( int i = 0; i < work.Num(); i++ ) { 673 files.Append( idStr( "renderprogs/hlsl/" ) + work[i] ); 674 } 675 676 path = RelativePathToOSPath( "renderprogs/gl", "fs_basepath" ); 677 ListOSFiles( path, "*.*", work ); 678 for ( int i = 0; i < work.Num(); i++ ) { 679 files.Append( idStr( "renderprogs/gl/" ) + work[i] ); 680 } 681 682 } 683 684 /* 685 ================ 686 idFileSystemLocal::AddSoundResources 687 ================ 688 */ 689 void idFileSystemLocal::AddFonts( idStrList &files ) { 690 // temp fix for getting idaudio files in 691 idFileList *fl = ListFilesTree( "generated/images/newfonts", "*.bimage", false ); 692 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 693 files.AddUnique( fl->GetList()[i] ); 694 } 695 FreeFileList( fl ); 696 697 fl = ListFilesTree( "newfonts", "*.dat", false ); 698 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 699 files.AddUnique( fl->GetList()[i] ); 700 } 701 FreeFileList( fl ); 702 } 703 704 705 const char * excludeExtensions[] = { 706 ".idxma", ".idmsf", ".idwav", ".xma", ".msf", ".wav", ".resource" 707 }; 708 const int numExcludeExtensions = sizeof( excludeExtensions ) / sizeof( excludeExtensions[ 0 ] ); 709 710 bool IsExcludedFile( const idStr & resName ) { 711 for ( int k = 0; k < numExcludeExtensions; k++ ) { 712 if ( resName.Find( excludeExtensions[ k ], false ) >= 0 ) { 713 return true; 714 } 715 } 716 return false; 717 } 718 719 /* 720 ================ 721 idFileSystemLocal::IsBinaryModel 722 ================ 723 */ 724 bool idFileSystemLocal::IsBinaryModel( const idStr & resName ) const { 725 idStrStatic< 32 > ext; 726 resName.ExtractFileExtension( ext ); 727 if ( ( ext.Icmp( "base" ) == 0 ) || ( ext.Icmp( "blwo" ) == 0 ) || ( ext.Icmp( "bflt" ) == 0 ) || ( ext.Icmp( "bma" ) == 0 ) ) { 728 return true; 729 } 730 return false; 731 } 732 733 /* 734 ================ 735 idFileSystemLocal::IsSoundSample 736 ================ 737 */ 738 bool idFileSystemLocal::IsSoundSample( const idStr & resName ) const { 739 idStrStatic< 32 > ext; 740 resName.ExtractFileExtension( ext ); 741 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 ) ) { 742 return true; 743 } 744 return false; 745 } 746 747 748 void idFileSystemLocal::BuildOrderedStartupContainer() { 749 idStrList orderedFiles( 1024 ); 750 751 idFileList * fl = ListFilesTree( "materials", "*.mtr", true ); 752 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 753 orderedFiles.AddUnique( fl->GetList()[i] ); 754 } 755 FreeFileList( fl ); 756 757 fl = ListFilesTree( "renderprogs", "*.v360", true ); 758 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 759 orderedFiles.AddUnique( fl->GetList()[i] ); 760 } 761 FreeFileList( fl ); 762 763 fl = ListFilesTree( "renderprogs", "*.p360", true ); 764 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 765 orderedFiles.AddUnique( fl->GetList()[i] ); 766 } 767 FreeFileList( fl ); 768 769 fl = ListFilesTree( "renderprogs", "*.cgb", true ); 770 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 771 orderedFiles.AddUnique( fl->GetList()[i] ); 772 } 773 FreeFileList( fl ); 774 775 fl = ListFilesTree( "renderprogs/gl", "*.*", true ); 776 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 777 orderedFiles.AddUnique( fl->GetList()[i] ); 778 } 779 FreeFileList( fl ); 780 781 fl = ListFilesTree( "skins", "*.skin", true ); 782 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 783 orderedFiles.AddUnique( fl->GetList()[i] ); 784 } 785 FreeFileList( fl ); 786 787 fl = ListFilesTree( "sound", "*.sndshd", false ); 788 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 789 orderedFiles.AddUnique( fl->GetList()[i] ); 790 } 791 FreeFileList( fl ); 792 793 fl = ListFilesTree( "def", "*.def", false ); 794 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 795 orderedFiles.AddUnique( fl->GetList()[i] ); 796 } 797 FreeFileList( fl ); 798 799 fl = ListFilesTree( "fx", "*.fx", false ); 800 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 801 orderedFiles.AddUnique( fl->GetList()[i] ); 802 } 803 FreeFileList( fl ); 804 805 fl = ListFilesTree( "particles", "*.prt", false ); 806 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 807 orderedFiles.AddUnique( fl->GetList()[i] ); 808 } 809 FreeFileList( fl ); 810 811 fl = ListFilesTree( "af", "*.af", false ); 812 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 813 orderedFiles.AddUnique( fl->GetList()[i] ); 814 } 815 FreeFileList( fl ); 816 fl = ListFilesTree( "newpdas", "*.pda", false ); 817 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 818 orderedFiles.AddUnique( fl->GetList()[i] ); 819 } 820 FreeFileList( fl ); 821 822 orderedFiles.Append( "script/doom_main.script" ); 823 orderedFiles.Append( "script/doom_defs.script" ); 824 orderedFiles.Append( "script/doom_defs.script" ); 825 orderedFiles.Append( "script/doom_events.script" ); 826 orderedFiles.Append( "script/doom_util.script" ); 827 orderedFiles.Append( "script/weapon_base.script" ); 828 orderedFiles.Append( "script/ai_base.script" ); 829 orderedFiles.Append( "script/weapon_fists.script" ); 830 orderedFiles.Append( "script/weapon_pistol.script" ); 831 orderedFiles.Append( "script/weapon_shotgun.script" ); 832 orderedFiles.Append( "script/weapon_machinegun.script" ); 833 orderedFiles.Append( "script/weapon_chaingun.script" ); 834 orderedFiles.Append( "script/weapon_handgrenade.script" ); 835 orderedFiles.Append( "script/weapon_plasmagun.script" ); 836 orderedFiles.Append( "script/weapon_rocketlauncher.script" ); 837 orderedFiles.Append( "script/weapon_bfg.script" ); 838 orderedFiles.Append( "script/weapon_soulcube.script" ); 839 orderedFiles.Append( "script/weapon_chainsaw.script" ); 840 orderedFiles.Append( "script/weapon_flashlight.script" ); 841 orderedFiles.Append( "script/weapon_pda.script" ); 842 orderedFiles.Append( "script/ai_monster_base.script" ); 843 orderedFiles.Append( "script/ai_monster_zombie_base.script" ); 844 orderedFiles.Append( "script/ai_monster_demon_archvile.script" ); 845 orderedFiles.Append( "script/ai_monster_demon_cherub.script" ); 846 orderedFiles.Append( "script/ai_monster_demon_hellknight.script" ); 847 orderedFiles.Append( "script/ai_monster_demon_imp.script" ); 848 orderedFiles.Append( "script/ai_monster_demon_maggot.script" ); 849 orderedFiles.Append( "script/ai_monster_demon_mancubus.script" ); 850 orderedFiles.Append( "script/ai_monster_demon_pinky.script" ); 851 orderedFiles.Append( "script/ai_monster_demon_revenant.script" ); 852 orderedFiles.Append( "script/ai_monster_demon_trite.script" ); 853 orderedFiles.Append( "script/ai_monster_demon_wraith.script" ); 854 orderedFiles.Append( "script/ai_monster_flying_lostsoul.script" ); 855 orderedFiles.Append( "script/ai_monster_flying_cacodemon.script" ); 856 orderedFiles.Append( "script/ai_monster_zombie.script" ); 857 orderedFiles.Append( "script/ai_monster_zombie_morgue.script" ); 858 orderedFiles.Append( "script/ai_monster_zombie_sawyer.script" ); 859 orderedFiles.Append( "script/ai_monster_zombie_bernie.script" ); 860 orderedFiles.Append( "script/ai_monster_zombie_commando_cgun.script" ); 861 orderedFiles.Append( "script/ai_monster_zombie_commando_tentacle.script" ); 862 orderedFiles.Append( "script/ai_monster_zombie_security_pistol.script" ); 863 orderedFiles.Append( "script/ai_monster_turret.script" ); 864 orderedFiles.Append( "script/ai_monster_boss_vagary.script" ); 865 orderedFiles.Append( "script/ai_monster_boss_cyberdemon.script" ); 866 orderedFiles.Append( "script/ai_monster_boss_guardian.script" ); 867 orderedFiles.Append( "script/ai_monster_boss_guardian_seeker.script" ); 868 orderedFiles.Append( "script/ai_monster_boss_sabaoth.script" ); 869 orderedFiles.Append( "script/ai_character.script" ); 870 orderedFiles.Append( "script/ai_character_prone.script" ); 871 orderedFiles.Append( "script/ai_character_sentry.script" ); 872 orderedFiles.Append( "script/ai_player.script" ); 873 orderedFiles.Append( "script/ai_alphalabs2_scientist1.script" ); 874 orderedFiles.Append( "script/ai_cinematic_le.script" ); 875 orderedFiles.Append( "script/map_admin1.script" ); 876 orderedFiles.Append( "script/map_alphalabs1.script" ); 877 orderedFiles.Append( "script/map_alphalabs2.script" ); 878 orderedFiles.Append( "script/map_alphalabs3.script" ); 879 orderedFiles.Append( "script/map_alphalabs3_crane.script" ); 880 orderedFiles.Append( "script/map_alphalabs4.script" ); 881 orderedFiles.Append( "script/map_caves.script" ); 882 orderedFiles.Append( "script/map_caves2.script" ); 883 orderedFiles.Append( "script/map_comm1.script" ); 884 orderedFiles.Append( "script/map_commoutside_lift.script" ); 885 orderedFiles.Append( "script/map_commoutside.script" ); 886 orderedFiles.Append( "script/map_cpu.script" ); 887 orderedFiles.Append( "script/map_cpuboss.script" ); 888 orderedFiles.Append( "script/map_delta1.script" ); 889 orderedFiles.Append( "script/map_delta2a.script" ); 890 orderedFiles.Append( "script/map_delta2b.script" ); 891 orderedFiles.Append( "script/map_delta3.script" ); 892 orderedFiles.Append( "script/map_delta5.script" ); 893 orderedFiles.Append( "script/map_enpro.script" ); 894 orderedFiles.Append( "script/map_hell1.script" ); 895 orderedFiles.Append( "script/map_hellhole.script" ); 896 orderedFiles.Append( "script/map_recycling1.script" ); 897 orderedFiles.Append( "script/map_recycling2.script" ); 898 orderedFiles.Append( "script/map_site3.script" ); 899 orderedFiles.Append( "script/map_marscity1.script" ); 900 orderedFiles.Append( "script/map_marscity2.script" ); 901 orderedFiles.Append( "script/map_mc_underground.script" ); 902 orderedFiles.Append( "script/map_monorail.script" ); 903 orderedFiles.Append( "script/d3xp_events.script" ); 904 orderedFiles.Append( "script/weapon_bloodstone_passive.script" ); 905 orderedFiles.Append( "script/weapon_bloodstone_active1.script" ); 906 orderedFiles.Append( "script/weapon_bloodstone_active2.script" ); 907 orderedFiles.Append( "script/weapon_bloodstone_active3.script" ); 908 orderedFiles.Append( "script/weapon_shotgun_double.script" ); 909 orderedFiles.Append( "script/weapon_grabber.script" ); 910 orderedFiles.Append( "script/ai_monster_hunter_helltime.script" ); 911 orderedFiles.Append( "script/ai_monster_hunter_berserk.script" ); 912 orderedFiles.Append( "script/ai_monster_hunter_invul.script" ); 913 orderedFiles.Append( "script/ai_monster_boss_maledict.script" ); 914 orderedFiles.Append( "script/ai_monster_demon_vulgar.script" ); 915 orderedFiles.Append( "script/ai_monster_demon_d3xp_bruiser.script" ); 916 orderedFiles.Append( "script/ai_monster_dummy_target.script" ); 917 orderedFiles.Append( "script/ai_monster_dummy.script" ); 918 orderedFiles.Append( "script/ai_monster_demon_sentry.script" ); 919 orderedFiles.Append( "script/ai_monster_demon_trite_jump.script" ); 920 orderedFiles.Append( "script/ai_monster_turret_ancient.script" ); 921 orderedFiles.Append( "script/ai_monster_flying_forgotten.script" ); 922 orderedFiles.Append( "script/ai_character_erebus3.script" ); 923 orderedFiles.Append( "script/d3xp_airlock.script" ); 924 orderedFiles.Append( "script/d3xp_bloodstone.script" ); 925 orderedFiles.Append( "script/map_erebus1.script" ); 926 orderedFiles.Append( "script/map_erebus2_helltime.script" ); 927 orderedFiles.Append( "script/map_erebus2.script" ); 928 orderedFiles.Append( "script/map_erebus3.script" ); 929 orderedFiles.Append( "script/map_erebus4.script" ); 930 orderedFiles.Append( "script/map_erebus5.script" ); 931 orderedFiles.Append( "script/map_erebus5_cloud.script" ); 932 orderedFiles.Append( "script/map_erebus6.script" ); 933 orderedFiles.Append( "script/map_erebus6_berzerk.script" ); 934 orderedFiles.Append( "script/map_phobos1.script" ); 935 orderedFiles.Append( "script/map_phobos2.script" ); 936 orderedFiles.Append( "script/map_phobos2_invul.script" ); 937 orderedFiles.Append( "script/map_phobos3.script" ); 938 orderedFiles.Append( "script/map_phobos4.script" ); 939 orderedFiles.Append( "script/map_deltax.script" ); 940 orderedFiles.Append( "script/map_hell.script" ); 941 orderedFiles.Append( "script/map_maledict.script" ); 942 orderedFiles.Append( "script/d3le-ai_monster_boss_guardian2.script" ); 943 orderedFiles.Append( "script/ai_follower.script" ); 944 orderedFiles.Append( "generated/swf/shell.bswf" ); 945 fl = ListFilesTree( "newfonts", "*.dat", false ); 946 for ( int i = 0; i < fl->GetList().Num(); i++ ) { 947 orderedFiles.AddUnique( fl->GetList()[i] ); 948 } 949 FreeFileList( fl ); 950 951 idResourceContainer::WriteResourceFile( "_ordered.resources", orderedFiles, false ); 952 } 953 954 /* 955 ================ 956 idFileSystemLocal::WriteResourcePacks 957 ================ 958 */ 959 void idFileSystemLocal::WriteResourcePacks() { 960 961 idStrList filesNotCommonToAllMaps( 16384 ); // files that are not shared by all maps, used to trim the common list 962 idStrList filesCommonToAllMaps( 16384 ); // files that are shared by all maps, will include startup files, renderprogs etc.. 963 idPreloadManifest commonPreloads; // preload entries that exist in all map preload files 964 965 idStr path = RelativePathToOSPath( "maps/", "fs_savepath" ); 966 967 idStrList manifestFiles; 968 ListOSFiles( path, ".manifest", manifestFiles ); 969 idStrList preloadFiles; 970 ListOSFiles( path, ".preload", preloadFiles ); 971 972 idList< idFileManifest > manifests; // list of all manifest files 973 // load all file manifests 974 for ( int i = 0; i < manifestFiles.Num(); i++ ) { 975 idStr path = "maps/"; 976 path += manifestFiles[ i ]; 977 idFileManifest manifest; 978 if ( manifest.LoadManifest( path ) ) { 979 //manifest.Print(); 980 manifest.RemoveAll( va( "strings/%s", ID_LANG_ENGLISH ) ); // remove all .lang files 981 manifest.RemoveAll( va( "strings/%s", ID_LANG_FRENCH ) ); 982 manifest.RemoveAll( va( "strings/%s", ID_LANG_ITALIAN ) ); 983 manifest.RemoveAll( va( "strings/%s", ID_LANG_GERMAN ) ); 984 manifest.RemoveAll( va( "strings/%s", ID_LANG_SPANISH ) ); 985 manifest.RemoveAll( va( "strings/%s", ID_LANG_JAPANESE ) ); 986 manifests.Append( manifest ); 987 } 988 } 989 990 idList< idPreloadManifest > preloadManifests; // list of all preload manifest files 991 // load all preload manifests 992 for ( int i = 0; i < preloadFiles.Num(); i++ ) { 993 idStr path = "maps/"; 994 path += preloadFiles[ i ]; 995 if ( path.Find( "_startup", false ) >= 0 ) { 996 continue; 997 } 998 idPreloadManifest preload; 999 if ( preload.LoadManifest( path ) ) { 1000 preloadManifests.Append( preload ); 1001 //preload.Print(); 1002 } 1003 } 1004 1005 // build common list of files 1006 for ( int i = 0; i < manifests.Num(); i++ ) { 1007 idFileManifest &manifest = manifests[ i ]; 1008 for ( int j = 0; j < manifest.NumFiles(); j++ ) { 1009 idStr name = manifest.GetFileNameByIndex( j ); 1010 if ( name.CheckExtension( ".cfg" ) || (name.Find( ".lang", false ) >= 0) ) { 1011 continue; 1012 } 1013 if ( FileExistsInAllManifests( name, manifests ) ) { 1014 filesCommonToAllMaps.AddUnique( name ); 1015 } else { 1016 filesNotCommonToAllMaps.AddUnique( name ); 1017 } 1018 } 1019 } 1020 // common list of preload reosurces, image, sample or models 1021 for ( int i = 0; i < preloadManifests.Num(); i++ ) { 1022 idPreloadManifest &preload = preloadManifests[ i ]; 1023 for ( int j = 0; j < preload.NumResources(); j++ ) { 1024 idStr name = preload.GetResourceNameByIndex( j ); 1025 if ( FileExistsInAllPreloadManifests( name, preloadManifests ) ) { 1026 commonPreloads.Add( preload.GetPreloadByIndex( j ) ); 1027 idLib::Printf( "Common preload added %s\n", name.c_str() ); 1028 } else { 1029 idLib::Printf( "preload missed %s\n", name.c_str() ); 1030 } 1031 } 1032 } 1033 1034 AddRenderProgs( filesCommonToAllMaps ); 1035 AddFonts( filesCommonToAllMaps ); 1036 1037 idStrList work; 1038 1039 // remove all common files from each map manifest 1040 for ( int i = 0; i < manifests.Num(); i++ ) { 1041 if ( ( strstr( manifests[ i ].GetManifestName(), "_startup" ) != NULL ) || ( strstr( manifests[ i ].GetManifestName(), "_pc" ) != NULL ) ) { 1042 continue; 1043 } 1044 //idLib::Printf( "%04d referenced files for %s\n", manifests[ i ].GetReferencedFileCount(), manifests[ i ].GetManifestName() ); 1045 1046 for ( int j = 0; j < filesCommonToAllMaps.Num(); j++ ) { 1047 manifests[ i ].RemoveAll( filesCommonToAllMaps[ j ] ); 1048 } 1049 //idLib::Printf( "%04d referenced files for %s\n", manifests[ i ].GetReferencedFileCount(), manifests[ i ].GetManifestName() ); 1050 } 1051 1052 idStrList commonImages( 2048 ); 1053 idStrList commonModels( 2048 ); 1054 idStrList commonAnims( 2048 ); 1055 idStrList commonCollision( 2048 ); 1056 idStrList soundFiles( 2048 ); // don't write these per map so we fit on disc 1057 1058 for ( int i = 0; i < manifests.Num(); i++ ) { 1059 idStr resourceFileName = manifests[ i ].GetManifestName(); 1060 if ( resourceFileName.Find( "_startup.manifest", false ) >= 0 ) { 1061 // add all the startup manifest files to the common list 1062 for ( int j = 0; j < manifests[ i ].NumFiles(); j++ ) { 1063 idStr check = manifests[i].GetFileNameByIndex( j ); 1064 if ( check.CheckExtension( ".cfg" ) == false ) { 1065 filesCommonToAllMaps.AddUnique( check.c_str() ); 1066 } 1067 } 1068 continue; 1069 } 1070 1071 idStaticList< idStr, 16384 > mapFiles; // map files from the manifest, these are static for easy debugging 1072 idStaticList< idStr, 16384 > mapFilesTwo; // accumulates non bimage, bmodel and sample files 1073 commonImages.Clear(); // collect images and models separately so they can be added in linear preload order 1074 commonModels.Clear(); 1075 commonAnims.Clear(); 1076 commonCollision.Clear(); 1077 1078 manifests[ i ].PopulateList( mapFiles ); 1079 1080 for ( int j = 0; j < mapFiles.Num(); j++ ) { 1081 idStr & resName = mapFiles[ j ]; 1082 if ( resName.Find( ".bimage", false ) >= 0 ) { 1083 commonImages.AddUnique( resName ); 1084 continue; 1085 } 1086 if ( IsBinaryModel( resName ) ) { 1087 commonModels.AddUnique( resName ); 1088 continue; 1089 } 1090 if ( IsSoundSample( resName ) ) { 1091 soundFiles.AddUnique( resName ); 1092 continue; 1093 } 1094 if ( resName.Find( ".bik", false ) >= 0 ) { 1095 // don't add bik files 1096 continue; 1097 } 1098 if ( resName.Find ( ".bmd5anim", false ) >= 0 ) { 1099 commonAnims.AddUnique( resName ); 1100 continue; 1101 } 1102 if ( resName.Find ( ".bcmodel", false ) >= 0 ) { 1103 commonCollision.AddUnique( resName ); 1104 continue; 1105 } 1106 if ( resName.Find( ".lang", false ) >= 0 ) { 1107 continue; 1108 } 1109 mapFilesTwo.AddUnique( resName ); 1110 } 1111 1112 for ( int j = 0; j < commonImages.Num(); j++ ) { 1113 mapFilesTwo.AddUnique( commonImages[ j ] ); 1114 } 1115 for ( int j = 0; j < commonModels.Num(); j++ ) { 1116 mapFilesTwo.AddUnique( commonModels[ j ] ); 1117 } 1118 for ( int j = 0; j < commonAnims.Num(); j++ ) { 1119 mapFilesTwo.AddUnique( commonAnims[ j ] ); 1120 } 1121 for ( int j = 0; j < commonCollision.Num(); j++ ) { 1122 mapFilesTwo.AddUnique( commonCollision[ j ] ); 1123 } 1124 // write map resources 1125 idStrList mapFilesToWrite; 1126 for ( int j = 0; j < mapFilesTwo.Num(); j++ ) { 1127 mapFilesToWrite.Append( mapFilesTwo[ j ] ); 1128 } 1129 idResourceContainer::WriteResourceFile( resourceFileName, mapFilesToWrite, false ); 1130 } 1131 1132 // add the new manifests just written 1133 path = RelativePathToOSPath( "maps", "fs_savepath" ); 1134 ListOSFiles( path, "*.preload", work ); 1135 for ( int i = 0; i < work.Num(); i++ ) { 1136 filesCommonToAllMaps.Append( idStr( "maps/" ) + work[ i ] ); 1137 } 1138 1139 filesCommonToAllMaps.Append( "_common.preload" ); 1140 1141 // write out common models, images and sounds to separate containers 1142 //idStrList commonSounds( 2048 ); 1143 commonImages.Clear(); 1144 commonModels.Clear(); 1145 1146 idStrList commonFiles; 1147 for ( int i = 0; i < filesCommonToAllMaps.Num(); i++ ) { 1148 idStr & resName = filesCommonToAllMaps[ i ]; 1149 if ( resName.Find( ".bimage", false ) >= 0 ) { 1150 commonImages.AddUnique( resName ); 1151 continue; 1152 } 1153 if ( IsBinaryModel( resName ) ) { 1154 commonModels.AddUnique( resName ); 1155 continue; 1156 } 1157 if ( IsSoundSample( resName ) ) { 1158 soundFiles.AddUnique( resName ); 1159 continue; 1160 } 1161 if ( resName.Find( ".bik", false ) >= 0 ) { 1162 // no bik files in the .resource 1163 continue; 1164 } 1165 if ( resName.Find( ".lang", false ) >= 0 ) { 1166 // no bik files in the .resource 1167 continue; 1168 } 1169 commonFiles.AddUnique( resName ); 1170 } 1171 1172 for ( int j = 0; j < commonImages.Num(); j++ ) { 1173 commonFiles.AddUnique( commonImages[ j ] ); 1174 } 1175 for ( int j = 0; j < commonModels.Num(); j++ ) { 1176 commonFiles.AddUnique( commonModels[ j ] ); 1177 } 1178 1179 //idResourceContainer::WriteResourceFile( "_common_images", commonImages ); 1180 //idResourceContainer::WriteResourceFile( "_common_models", commonModels ); 1181 1182 commonPreloads.WriteManifest( "_common.preload" ); 1183 idResourceContainer::WriteResourceFile( "_common", commonFiles, false ); 1184 1185 1186 idList< idStrList > soundOutputFiles; 1187 soundOutputFiles.SetNum( 16 ); 1188 1189 struct soundVOInfo_t { 1190 const char *filename; 1191 const char *voqualifier; 1192 idStrList * samples; 1193 }; 1194 const soundVOInfo_t soundFileInfo[] = { 1195 { "fr", "sound/vo/french/", &soundOutputFiles[ 0 ] }, 1196 { "it", "sound/vo/italian/", &soundOutputFiles[ 1 ] }, 1197 { "gr", "sound/vo/german/", &soundOutputFiles[ 2 ] }, 1198 { "sp", "sound/vo/spanish/", &soundOutputFiles[ 3 ] }, 1199 { "jp", "sound/vo/japanese/", &soundOutputFiles[ 4 ] }, 1200 { "en", "sound/vo/", &soundOutputFiles[ 5 ] } // english last so the other langs are culled first 1201 }; 1202 const int numSoundFiles = sizeof( soundFileInfo ) / sizeof ( soundVOInfo_t ); 1203 1204 for ( int k = soundFiles.Num() - 1; k > 0; k-- ) { 1205 for ( int l = 0; l < numSoundFiles; l++ ) { 1206 if ( soundFiles[ k ].Find( soundFileInfo[ l ].voqualifier, false ) >= 0 ) { 1207 soundFileInfo[ l ].samples->AddUnique( soundFiles[ k ] ); 1208 soundFiles.RemoveIndex( k ); 1209 } 1210 } 1211 } 1212 1213 for ( int k = 0; k < numSoundFiles; k++ ) { 1214 idStrList & sampleList = *soundFileInfo[ k ].samples; 1215 1216 // write pc 1217 idResourceContainer::WriteResourceFile( va( "_sound_pc_%s", soundFileInfo[ k ].filename ), sampleList, false ); 1218 for ( int l = 0; l < sampleList.Num(); l++ ) { 1219 sampleList[ l ].Replace( ".idwav", ".idxma" ); 1220 } 1221 } 1222 1223 idResourceContainer::WriteResourceFile( "_sound_pc", soundFiles, false ); 1224 for ( int k = 0; k < soundFiles.Num(); k++ ) { 1225 soundFiles[ k ].Replace( ".idwav", ".idxma" ); 1226 } 1227 1228 for ( int k = 0; k < soundFiles.Num(); k++ ) { 1229 soundFiles[ k ].Replace( ".idxma", ".idmsf" ); 1230 } 1231 1232 BuildOrderedStartupContainer(); 1233 1234 ClearResourcePacks(); 1235 } 1236 1237 1238 /* 1239 ================= 1240 idFileSystemLocal::CopyFile 1241 1242 Copy a fully specified file from one place to another` 1243 ================= 1244 */ 1245 void idFileSystemLocal::CopyFile( const char *fromOSPath, const char *toOSPath ) { 1246 1247 idFile * src = OpenExplicitFileRead( fromOSPath ); 1248 if ( src == NULL ) { 1249 idLib::Warning( "Could not open %s for read", fromOSPath ); 1250 return; 1251 } 1252 1253 if ( idStr::Icmp( fromOSPath, toOSPath ) == 0 ) { 1254 // same file can happen during build games 1255 return; 1256 } 1257 1258 CopyFile( src, toOSPath ); 1259 delete src; 1260 1261 if ( strstr( fromOSPath, ".wav" ) != NULL ) { 1262 idStrStatic< MAX_OSPATH > newFromPath = fromOSPath; 1263 idStrStatic< MAX_OSPATH > newToPath = toOSPath; 1264 1265 idLib::Printf( "Copying console samples for %s\n", newFromPath.c_str() ); 1266 newFromPath.SetFileExtension( "xma" ); 1267 newToPath.SetFileExtension( "xma" ); 1268 src = OpenExplicitFileRead( newFromPath ); 1269 if ( src == NULL ) { 1270 idLib::Warning( "Could not open %s for read", newFromPath.c_str() ); 1271 } else { 1272 CopyFile( src, newToPath ); 1273 delete src; 1274 src = NULL; 1275 } 1276 1277 newFromPath.SetFileExtension( "msf" ); 1278 newToPath.SetFileExtension( "msf" ); 1279 src = OpenExplicitFileRead( newFromPath ); 1280 if ( src == NULL ) { 1281 idLib::Warning( "Could not open %s for read", newFromPath.c_str() ); 1282 } else { 1283 CopyFile( src, newToPath ); 1284 delete src; 1285 } 1286 1287 newFromPath.BackSlashesToSlashes(); 1288 newFromPath.ToLower(); 1289 if ( newFromPath.Find( "/vo/", false ) >= 0 ) { 1290 for ( int i = 0; i < Sys_NumLangs(); i++ ) { 1291 const char *lang = Sys_Lang( i ); 1292 if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) { 1293 continue; 1294 } 1295 newFromPath = fromOSPath; 1296 newToPath = toOSPath; 1297 newFromPath.BackSlashesToSlashes(); 1298 newFromPath.ToLower(); 1299 newToPath.BackSlashesToSlashes(); 1300 newToPath.ToLower(); 1301 newFromPath.Replace( "/vo/", va( "/vo/%s/", lang ) ); 1302 newToPath.Replace( "/vo/", va( "/vo/%s/", lang ) ); 1303 1304 src = OpenExplicitFileRead( newFromPath ); 1305 if ( src == NULL ) { 1306 idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() ); 1307 } else { 1308 CopyFile( src, newToPath ); 1309 delete src; 1310 src = NULL; 1311 } 1312 1313 newFromPath.SetFileExtension( "xma" ); 1314 newToPath.SetFileExtension( "xma" ); 1315 src = OpenExplicitFileRead( newFromPath ); 1316 if ( src == NULL ) { 1317 idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() ); 1318 } else { 1319 CopyFile( src, newToPath ); 1320 delete src; 1321 src = NULL; 1322 } 1323 1324 newFromPath.SetFileExtension( "msf" ); 1325 newToPath.SetFileExtension( "msf" ); 1326 src = OpenExplicitFileRead( newFromPath ); 1327 if ( src == NULL ) { 1328 idLib::Warning( "LOCALIZATION PROBLEM: Could not open %s for read", newFromPath.c_str() ); 1329 } else { 1330 CopyFile( src, newToPath ); 1331 delete src; 1332 } 1333 1334 } 1335 } 1336 } 1337 } 1338 1339 /* 1340 ================= 1341 idFileSystemLocal::CopyFile 1342 ================= 1343 */ 1344 void idFileSystemLocal::CopyFile( idFile *src, const char *toOSPath ) { 1345 idFile * dst = OpenExplicitFileWrite( toOSPath ); 1346 if ( dst == NULL ) { 1347 idLib::Warning( "Could not open %s for write", toOSPath ); 1348 return; 1349 } 1350 1351 common->Printf( "copy %s to %s\n", src->GetName(), toOSPath ); 1352 1353 int len = src->Length(); 1354 int copied = 0; 1355 while ( copied < len ) { 1356 byte buffer[4096]; 1357 int read = src->Read( buffer, Min( 4096, len - copied ) ); 1358 if ( read <= 0 ) { 1359 idLib::Warning( "Copy failed during read" ); 1360 break; 1361 } 1362 int written = dst->Write( buffer, read ); 1363 if ( written < read ) { 1364 idLib::Warning( "Copy failed during write" ); 1365 break; 1366 } 1367 copied += written; 1368 } 1369 1370 delete dst; 1371 } 1372 1373 /* 1374 ==================== 1375 idFileSystemLocal::ReplaceSeparators 1376 1377 Fix things up differently for win/unix/mac 1378 ==================== 1379 */ 1380 void idFileSystemLocal::ReplaceSeparators( idStr &path, char sep ) { 1381 char *s; 1382 1383 for( s = &path[ 0 ]; *s ; s++ ) { 1384 if ( *s == '/' || *s == '\\' ) { 1385 *s = sep; 1386 } 1387 } 1388 } 1389 1390 /* 1391 ======================== 1392 IsOSPath 1393 ======================== 1394 */ 1395 static bool IsOSPath( const char * path ) { 1396 assert( path ); 1397 1398 if ( idStr::Icmpn( path, "mtp:", 4 ) == 0 ) { 1399 return true; 1400 } 1401 1402 1403 if ( idStr::Length( path ) >= 2 ) { 1404 if ( path[ 1 ] == ':' ) { 1405 if ( ( path[ 0 ] > 64 && path[ 0 ] < 91 ) || ( path[ 0 ] > 96 && path[ 0 ] < 123 ) ) { 1406 // already an OS path starting with a drive. 1407 return true; 1408 } 1409 } 1410 if ( path[ 0 ] == '\\' || path[ 0 ] == '/' ) { 1411 // a root path 1412 return true; 1413 } 1414 } 1415 return false; 1416 } 1417 1418 /* 1419 ======================== 1420 idFileSystemLocal::BuildOSPath 1421 ======================== 1422 */ 1423 const char * idFileSystemLocal::BuildOSPath( const char * base, const char * relativePath ) { 1424 // handle case of this already being an OS path 1425 if ( IsOSPath( relativePath ) ) { 1426 return relativePath; 1427 } 1428 1429 return BuildOSPath( base, gameFolder, relativePath ); 1430 } 1431 1432 /* 1433 =================== 1434 idFileSystemLocal::BuildOSPath 1435 =================== 1436 */ 1437 const char *idFileSystemLocal::BuildOSPath( const char *base, const char *game, const char *relativePath ) { 1438 static char OSPath[MAX_STRING_CHARS]; 1439 idStr newPath; 1440 1441 // handle case of this already being an OS path 1442 if ( IsOSPath( relativePath ) ) { 1443 return relativePath; 1444 } 1445 1446 idStr strBase = base; 1447 strBase.StripTrailing( '/' ); 1448 strBase.StripTrailing( '\\' ); 1449 sprintf( newPath, "%s/%s/%s", strBase.c_str(), game, relativePath ); 1450 ReplaceSeparators( newPath ); 1451 idStr::Copynz( OSPath, newPath, sizeof( OSPath ) ); 1452 return OSPath; 1453 } 1454 1455 /* 1456 ================ 1457 idFileSystemLocal::OSPathToRelativePath 1458 1459 takes a full OS path, as might be found in data from a media creation 1460 program, and converts it to a relativePath by stripping off directories 1461 1462 Returns false if the osPath tree doesn't match any of the existing 1463 search paths. 1464 1465 ================ 1466 */ 1467 const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) { 1468 if ( ( OSPath[0] != '/' ) && ( OSPath[0] != '\\' ) && ( idStr::FindChar( OSPath, ':' ) < 0 ) ) { 1469 // No colon and it doesn't start with a slash... it must already be a relative path 1470 return OSPath; 1471 } 1472 idStaticList< idStrStatic< 32 >, 5 > basePaths; 1473 basePaths.Append( "base" ); 1474 basePaths.Append( "d3xp" ); 1475 basePaths.Append( "d3le" ); 1476 if ( fs_game.GetString()[0] != 0 ) { 1477 basePaths.Append( fs_game.GetString() ); 1478 } 1479 if ( fs_game_base.GetString()[0] != 0 ) { 1480 basePaths.Append( fs_game_base.GetString() ); 1481 } 1482 idStaticList<int, MAX_OSPATH> slashes; 1483 for ( const char * s = OSPath; *s != 0; s++ ) { 1484 if ( *s == '/' || *s == '\\' ) { 1485 slashes.Append( s - OSPath ); 1486 } 1487 } 1488 for ( int n = 0; n < slashes.Num() - 1; n++ ) { 1489 const char * start = OSPath + slashes[n] + 1; 1490 const char * end = OSPath + slashes[n+1]; 1491 int componentLength = end - start; 1492 if ( componentLength == 0 ) { 1493 continue; 1494 } 1495 for ( int i = 0; i < basePaths.Num(); i++ ) { 1496 if ( componentLength != basePaths[i].Length() ) { 1497 continue; 1498 } 1499 if ( basePaths[i].Icmpn( start, componentLength ) == 0 ) { 1500 // There are some files like: 1501 // W:\d3xp\base\... 1502 // But we can't search backwards because there are others like: 1503 // W:\doom3\base\models\mapobjects\base\... 1504 // So instead we check for 2 base paths next to each other and take the 2nd in that case 1505 if ( n < slashes.Num() - 2 ) { 1506 const char * start2 = OSPath + slashes[n+1] + 1; 1507 const char * end2 = OSPath + slashes[n+2]; 1508 int componentLength2 = end2 - start2; 1509 if ( componentLength2 > 0 ) { 1510 for ( int j = 0; j < basePaths.Num(); j++ ) { 1511 if ( componentLength2 != basePaths[j].Length() ) { 1512 continue; 1513 } 1514 if ( basePaths[j].Icmpn( start2, basePaths[j].Length() ) == 0 ) { 1515 return end2 + 1; 1516 } 1517 } 1518 } 1519 } 1520 return end + 1; 1521 } 1522 } 1523 } 1524 idLib::Warning( "OSPathToRelativePath failed on %s", OSPath ); 1525 return OSPath; 1526 } 1527 1528 /* 1529 ===================== 1530 idFileSystemLocal::RelativePathToOSPath 1531 1532 Returns a fully qualified path that can be used with stdio libraries 1533 ===================== 1534 */ 1535 const char *idFileSystemLocal::RelativePathToOSPath( const char *relativePath, const char *basePath ) { 1536 const char *path = cvarSystem->GetCVarString( basePath ); 1537 if ( !path[0] ) { 1538 path = fs_savepath.GetString(); 1539 } 1540 return BuildOSPath( path, gameFolder, relativePath ); 1541 } 1542 1543 /* 1544 ================= 1545 idFileSystemLocal::RemoveFile 1546 ================= 1547 */ 1548 void idFileSystemLocal::RemoveFile( const char *relativePath ) { 1549 idStr OSPath; 1550 1551 if ( fs_basepath.GetString()[0] ) { 1552 OSPath = BuildOSPath( fs_basepath.GetString(), gameFolder, relativePath ); 1553 ::DeleteFile( OSPath ); 1554 } 1555 1556 OSPath = BuildOSPath( fs_savepath.GetString(), gameFolder, relativePath ); 1557 ::DeleteFile( OSPath ); 1558 } 1559 1560 /* 1561 ======================== 1562 idFileSystemLocal::RemoveDir 1563 ======================== 1564 */ 1565 bool idFileSystemLocal::RemoveDir( const char * relativePath ) { 1566 bool success = true; 1567 if ( fs_savepath.GetString()[0] ) { 1568 success &= Sys_Rmdir( BuildOSPath( fs_savepath.GetString(), relativePath ) ); 1569 } 1570 success &= Sys_Rmdir( BuildOSPath( fs_basepath.GetString(), relativePath ) ); 1571 return success; 1572 } 1573 1574 /* 1575 ============ 1576 idFileSystemLocal::ReadFile 1577 1578 Filename are relative to the search path 1579 a null buffer will just return the file length and time without loading 1580 timestamp can be NULL if not required 1581 ============ 1582 */ 1583 int idFileSystemLocal::ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp ) { 1584 1585 idFile * f; 1586 byte * buf; 1587 int len; 1588 bool isConfig; 1589 1590 if ( !IsInitialized() ) { 1591 common->FatalError( "Filesystem call made without initialization\n" ); 1592 return 0; 1593 } 1594 1595 if ( relativePath == NULL || !relativePath[0] ) { 1596 common->FatalError( "idFileSystemLocal::ReadFile with empty name\n" ); 1597 return 0; 1598 } 1599 1600 if ( timestamp ) { 1601 *timestamp = FILE_NOT_FOUND_TIMESTAMP; 1602 } 1603 1604 if ( buffer ) { 1605 *buffer = NULL; 1606 } 1607 1608 if ( buffer == NULL && timestamp != NULL && resourceFiles.Num() > 0 ) { 1609 static idResourceCacheEntry rc; 1610 int size = 0; 1611 if ( GetResourceCacheEntry( relativePath, rc ) ) { 1612 *timestamp = 0; 1613 size = rc.length; 1614 } 1615 return size; 1616 } 1617 1618 buf = NULL; // quiet compiler warning 1619 1620 // if this is a .cfg file and we are playing back a journal, read 1621 // it from the journal file 1622 if ( strstr( relativePath, ".cfg" ) == relativePath + strlen( relativePath ) - 4 ) { 1623 isConfig = true; 1624 if ( eventLoop && eventLoop->JournalLevel() == 2 ) { 1625 int r; 1626 1627 loadCount++; 1628 loadStack++; 1629 1630 common->DPrintf( "Loading %s from journal file.\n", relativePath ); 1631 len = 0; 1632 r = eventLoop->com_journalDataFile->Read( &len, sizeof( len ) ); 1633 if ( r != sizeof( len ) ) { 1634 *buffer = NULL; 1635 return -1; 1636 } 1637 buf = (byte *)Mem_ClearedAlloc(len+1, TAG_IDFILE); 1638 *buffer = buf; 1639 r = eventLoop->com_journalDataFile->Read( buf, len ); 1640 if ( r != len ) { 1641 common->FatalError( "Read from journalDataFile failed" ); 1642 } 1643 1644 // guarantee that it will have a trailing 0 for string operations 1645 buf[len] = 0; 1646 1647 return len; 1648 } 1649 } else { 1650 isConfig = false; 1651 } 1652 1653 // look for it in the filesystem or pack files 1654 f = OpenFileRead( relativePath, ( buffer != NULL ) ); 1655 if ( f == NULL ) { 1656 if ( buffer ) { 1657 *buffer = NULL; 1658 } 1659 return -1; 1660 } 1661 len = f->Length(); 1662 1663 if ( timestamp ) { 1664 *timestamp = f->Timestamp(); 1665 } 1666 1667 if ( !buffer ) { 1668 CloseFile( f ); 1669 return len; 1670 } 1671 1672 loadCount++; 1673 loadStack++; 1674 1675 buf = (byte *)Mem_ClearedAlloc(len+1, TAG_IDFILE); 1676 *buffer = buf; 1677 1678 f->Read( buf, len ); 1679 1680 // guarantee that it will have a trailing 0 for string operations 1681 buf[len] = 0; 1682 CloseFile( f ); 1683 1684 // if we are journalling and it is a config file, write it to the journal file 1685 if ( isConfig && eventLoop && eventLoop->JournalLevel() == 1 ) { 1686 common->DPrintf( "Writing %s to journal file.\n", relativePath ); 1687 eventLoop->com_journalDataFile->Write( &len, sizeof( len ) ); 1688 eventLoop->com_journalDataFile->Write( buf, len ); 1689 eventLoop->com_journalDataFile->Flush(); 1690 } 1691 1692 return len; 1693 } 1694 1695 /* 1696 ============= 1697 idFileSystemLocal::FreeFile 1698 ============= 1699 */ 1700 void idFileSystemLocal::FreeFile( void *buffer ) { 1701 if ( !IsInitialized() ) { 1702 common->FatalError( "Filesystem call made without initialization\n" ); 1703 } 1704 if ( !buffer ) { 1705 common->FatalError( "idFileSystemLocal::FreeFile( NULL )" ); 1706 } 1707 loadStack--; 1708 1709 Mem_Free( buffer ); 1710 } 1711 1712 /* 1713 ============ 1714 idFileSystemLocal::WriteFile 1715 1716 Filenames are relative to the search path 1717 ============ 1718 */ 1719 int idFileSystemLocal::WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath ) { 1720 idFile *f; 1721 1722 if ( !IsInitialized() ) { 1723 common->FatalError( "Filesystem call made without initialization\n" ); 1724 } 1725 1726 if ( !relativePath || !buffer ) { 1727 common->FatalError( "idFileSystemLocal::WriteFile: NULL parameter" ); 1728 } 1729 1730 f = idFileSystemLocal::OpenFileWrite( relativePath, basePath ); 1731 if ( !f ) { 1732 common->Printf( "Failed to open %s\n", relativePath ); 1733 return -1; 1734 } 1735 1736 size = f->Write( buffer, size ); 1737 1738 CloseFile( f ); 1739 1740 return size; 1741 } 1742 1743 /* 1744 ======================== 1745 idFileSystemLocal::RenameFile 1746 ======================== 1747 */ 1748 bool idFileSystemLocal::RenameFile( const char * relativePath, const char * newName, const char * basePath ) { 1749 const char * path = cvarSystem->GetCVarString( basePath ); 1750 if ( !path[0] ) { 1751 path = fs_savepath.GetString(); 1752 } 1753 1754 idStr oldOSPath = BuildOSPath( path, gameFolder, relativePath ); 1755 idStr newOSPath = BuildOSPath( path, gameFolder, newName ); 1756 1757 // this gives atomic-delete-on-rename, like POSIX rename() 1758 // There is a MoveFileTransacted() on vista and above, not sure if that means there 1759 // is a race condition inside MoveFileEx... 1760 const bool success = ( MoveFileEx( oldOSPath.c_str(), newOSPath.c_str(), MOVEFILE_REPLACE_EXISTING ) != 0 ); 1761 1762 if ( !success ) { 1763 const int err = GetLastError(); 1764 idLib::Warning( "RenameFile( %s, %s ) error %i", newOSPath.c_str(), oldOSPath.c_str(), err ); 1765 } 1766 return success; 1767 } 1768 1769 /* 1770 =============== 1771 idFileSystemLocal::AddUnique 1772 =============== 1773 */ 1774 int idFileSystemLocal::AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const { 1775 int i, hashKey; 1776 1777 hashKey = hashIndex.GenerateKey( name ); 1778 for ( i = hashIndex.First( hashKey ); i >= 0; i = hashIndex.Next( i ) ) { 1779 if ( list[i].Icmp( name ) == 0 ) { 1780 return i; 1781 } 1782 } 1783 i = list.Append( name ); 1784 hashIndex.Add( hashKey, i ); 1785 return i; 1786 } 1787 1788 /* 1789 =============== 1790 idFileSystemLocal::GetExtensionList 1791 =============== 1792 */ 1793 void idFileSystemLocal::GetExtensionList( const char *extension, idStrList &extensionList ) const { 1794 int s, e, l; 1795 1796 l = idStr::Length( extension ); 1797 s = 0; 1798 while( 1 ) { 1799 e = idStr::FindChar( extension, '|', s, l ); 1800 if ( e != -1 ) { 1801 extensionList.Append( idStr( extension, s, e ) ); 1802 s = e + 1; 1803 } else { 1804 extensionList.Append( idStr( extension, s, l ) ); 1805 break; 1806 } 1807 } 1808 } 1809 1810 /* 1811 =============== 1812 idFileSystemLocal::GetFileList 1813 1814 Does not clear the list first so this can be used to progressively build a file list. 1815 When 'sort' is true only the new files added to the list are sorted. 1816 =============== 1817 */ 1818 int idFileSystemLocal::GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char * gamedir ) { 1819 if ( !IsInitialized() ) { 1820 common->FatalError( "Filesystem call made without initialization\n" ); 1821 } 1822 1823 if ( !extensions.Num() ) { 1824 return 0; 1825 } 1826 1827 if ( !relativePath ) { 1828 return 0; 1829 } 1830 1831 int pathLength = strlen( relativePath ); 1832 if ( pathLength ) { 1833 pathLength++; // for the trailing '/' 1834 } 1835 1836 idStrStatic< MAX_OSPATH > strippedName; 1837 if ( resourceFiles.Num() > 0 ) { 1838 int idx = resourceFiles.Num() - 1; 1839 while ( idx >= 0 ) { 1840 for ( int i = 0; i < resourceFiles[ idx ]->cacheTable.Num(); i++ ) { 1841 idResourceCacheEntry & rt = resourceFiles[ idx ]->cacheTable[ i ]; 1842 // if the name is not long anough to at least contain the path 1843 1844 if ( rt.filename.Length() <= pathLength ) { 1845 continue; 1846 } 1847 1848 // check for a path match without the trailing '/' 1849 if ( pathLength && idStr::Icmpn( rt.filename, relativePath, pathLength - 1 ) != 0 ) { 1850 continue; 1851 } 1852 1853 // ensure we have a path, and not just a filename containing the path 1854 if ( rt.filename[ pathLength ] == '\0' || rt.filename[pathLength - 1] != '/' ) { 1855 continue; 1856 } 1857 1858 // make sure the file is not in a subdirectory 1859 int j = pathLength; 1860 for ( ; rt.filename[j+1] != '\0'; j++ ) { 1861 if ( rt.filename[ j ] == '/' ) { 1862 break; 1863 } 1864 } 1865 if ( rt.filename[ j + 1 ] ) { 1866 continue; 1867 } 1868 1869 // check for extension match 1870 for ( j = 0; j < extensions.Num(); j++ ) { 1871 if ( rt.filename.Length() >= extensions[j].Length() && extensions[j].Icmp( rt.filename.c_str() + rt.filename.Length() - extensions[j].Length() ) == 0 ) { 1872 break; 1873 } 1874 } 1875 if ( j >= extensions.Num() ) { 1876 continue; 1877 } 1878 1879 // unique the match 1880 if ( fullRelativePath ) { 1881 idStr work = relativePath; 1882 work += "/"; 1883 work += rt.filename.c_str() + pathLength; 1884 work.StripTrailing( '/' ); 1885 AddUnique( work, list, hashIndex ); 1886 } else { 1887 idStr work = rt.filename.c_str() + pathLength; 1888 work.StripTrailing( '/' ); 1889 AddUnique( work, list, hashIndex ); 1890 } 1891 } 1892 idx--; 1893 } 1894 } 1895 1896 // search through the path, one element at a time, adding to list 1897 for ( int sp = searchPaths.Num() - 1; sp >= 0; sp-- ) { 1898 if ( gamedir != NULL && gamedir[0] != 0 ) { 1899 if ( searchPaths[sp].gamedir != gamedir) { 1900 continue; 1901 } 1902 } 1903 1904 idStr netpath = BuildOSPath( searchPaths[sp].path, searchPaths[sp].gamedir, relativePath ); 1905 1906 for ( int i = 0; i < extensions.Num(); i++ ) { 1907 1908 // scan for files in the filesystem 1909 idStrList sysFiles; 1910 ListOSFiles( netpath, extensions[i], sysFiles ); 1911 1912 // if we are searching for directories, remove . and .. 1913 if ( extensions[i][0] == '/' && extensions[i][1] == 0 ) { 1914 sysFiles.Remove( "." ); 1915 sysFiles.Remove( ".." ); 1916 } 1917 1918 for ( int j = 0; j < sysFiles.Num(); j++ ) { 1919 // unique the match 1920 if ( fullRelativePath ) { 1921 idStr work = relativePath; 1922 work += "/"; 1923 work += sysFiles[j]; 1924 AddUnique( work, list, hashIndex ); 1925 } else { 1926 AddUnique( sysFiles[j], list, hashIndex ); 1927 } 1928 } 1929 } 1930 } 1931 1932 return list.Num(); 1933 } 1934 1935 /* 1936 =============== 1937 idFileSystemLocal::ListFiles 1938 =============== 1939 */ 1940 idFileList *idFileSystemLocal::ListFiles( const char *relativePath, const char *extension, bool sort, bool fullRelativePath, const char* gamedir ) { 1941 idHashIndex hashIndex( 4096, 4096 ); 1942 idStrList extensionList; 1943 1944 idFileList *fileList = new (TAG_IDFILE) idFileList; 1945 fileList->basePath = relativePath; 1946 1947 GetExtensionList( extension, extensionList ); 1948 1949 GetFileList( relativePath, extensionList, fileList->list, hashIndex, fullRelativePath, gamedir ); 1950 1951 if ( sort ) { 1952 fileList->list.SortWithTemplate( idSort_PathStr() ); 1953 } 1954 1955 return fileList; 1956 } 1957 1958 /* 1959 =============== 1960 idFileSystemLocal::GetFileListTree 1961 =============== 1962 */ 1963 int idFileSystemLocal::GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir ) { 1964 int i; 1965 idStrList slash, folders( 128 ); 1966 idHashIndex folderHashIndex( 1024, 128 ); 1967 1968 // recurse through the subdirectories 1969 slash.Append( "/" ); 1970 GetFileList( relativePath, slash, folders, folderHashIndex, true, gamedir ); 1971 for ( i = 0; i < folders.Num(); i++ ) { 1972 if ( folders[i][0] == '.' ) { 1973 continue; 1974 } 1975 if ( folders[i].Icmp( relativePath ) == 0 ){ 1976 continue; 1977 } 1978 GetFileListTree( folders[i], extensions, list, hashIndex, gamedir ); 1979 } 1980 1981 // list files in the current directory 1982 GetFileList( relativePath, extensions, list, hashIndex, true, gamedir ); 1983 1984 return list.Num(); 1985 } 1986 1987 /* 1988 =============== 1989 idFileSystemLocal::ListFilesTree 1990 =============== 1991 */ 1992 idFileList *idFileSystemLocal::ListFilesTree( const char *relativePath, const char *extension, bool sort, const char* gamedir ) { 1993 idHashIndex hashIndex( 4096, 4096 ); 1994 idStrList extensionList; 1995 1996 idFileList *fileList = new (TAG_IDFILE) idFileList(); 1997 fileList->basePath = relativePath; 1998 fileList->list.SetGranularity( 4096 ); 1999 2000 GetExtensionList( extension, extensionList ); 2001 2002 GetFileListTree( relativePath, extensionList, fileList->list, hashIndex, gamedir ); 2003 2004 if ( sort ) { 2005 fileList->list.SortWithTemplate( idSort_PathStr() ); 2006 } 2007 2008 return fileList; 2009 } 2010 2011 /* 2012 =============== 2013 idFileSystemLocal::FreeFileList 2014 =============== 2015 */ 2016 void idFileSystemLocal::FreeFileList( idFileList *fileList ) { 2017 delete fileList; 2018 } 2019 2020 /* 2021 =============== 2022 idFileSystemLocal::ListOSFiles 2023 2024 call to the OS for a listing of files in an OS directory 2025 =============== 2026 */ 2027 int idFileSystemLocal::ListOSFiles( const char *directory, const char *extension, idStrList &list ) { 2028 if ( !extension ) { 2029 extension = ""; 2030 } 2031 2032 return Sys_ListFiles( directory, extension, list ); 2033 } 2034 2035 /* 2036 ================ 2037 idFileSystemLocal::Dir_f 2038 ================ 2039 */ 2040 void idFileSystemLocal::Dir_f( const idCmdArgs &args ) { 2041 idStr relativePath; 2042 idStr extension; 2043 idFileList *fileList; 2044 int i; 2045 2046 if ( args.Argc() < 2 || args.Argc() > 3 ) { 2047 common->Printf( "usage: dir <directory> [extension]\n" ); 2048 return; 2049 } 2050 2051 if ( args.Argc() == 2 ) { 2052 relativePath = args.Argv( 1 ); 2053 extension = ""; 2054 } 2055 else { 2056 relativePath = args.Argv( 1 ); 2057 extension = args.Argv( 2 ); 2058 if ( extension[0] != '.' ) { 2059 common->Warning( "extension should have a leading dot" ); 2060 } 2061 } 2062 relativePath.BackSlashesToSlashes(); 2063 relativePath.StripTrailing( '/' ); 2064 2065 common->Printf( "Listing of %s/*%s\n", relativePath.c_str(), extension.c_str() ); 2066 common->Printf( "---------------\n" ); 2067 2068 fileList = fileSystemLocal.ListFiles( relativePath, extension ); 2069 2070 for ( i = 0; i < fileList->GetNumFiles(); i++ ) { 2071 common->Printf( "%s\n", fileList->GetFile( i ) ); 2072 } 2073 common->Printf( "%d files\n", fileList->list.Num() ); 2074 2075 fileSystemLocal.FreeFileList( fileList ); 2076 } 2077 2078 /* 2079 ================ 2080 idFileSystemLocal::DirTree_f 2081 ================ 2082 */ 2083 void idFileSystemLocal::DirTree_f( const idCmdArgs &args ) { 2084 idStr relativePath; 2085 idStr extension; 2086 idFileList *fileList; 2087 int i; 2088 2089 if ( args.Argc() < 2 || args.Argc() > 3 ) { 2090 common->Printf( "usage: dirtree <directory> [extension]\n" ); 2091 return; 2092 } 2093 2094 if ( args.Argc() == 2 ) { 2095 relativePath = args.Argv( 1 ); 2096 extension = ""; 2097 } 2098 else { 2099 relativePath = args.Argv( 1 ); 2100 extension = args.Argv( 2 ); 2101 if ( extension[0] != '.' ) { 2102 common->Warning( "extension should have a leading dot" ); 2103 } 2104 } 2105 relativePath.BackSlashesToSlashes(); 2106 relativePath.StripTrailing( '/' ); 2107 2108 common->Printf( "Listing of %s/*%s /s\n", relativePath.c_str(), extension.c_str() ); 2109 common->Printf( "---------------\n" ); 2110 2111 fileList = fileSystemLocal.ListFilesTree( relativePath, extension ); 2112 2113 for ( i = 0; i < fileList->GetNumFiles(); i++ ) { 2114 common->Printf( "%s\n", fileList->GetFile( i ) ); 2115 } 2116 common->Printf( "%d files\n", fileList->list.Num() ); 2117 2118 fileSystemLocal.FreeFileList( fileList ); 2119 } 2120 2121 /* 2122 ================ 2123 idFileSystemLocal::ClearResourcePacks 2124 ================ 2125 */ 2126 void idFileSystemLocal::ClearResourcePacks() { 2127 } 2128 2129 /* 2130 ================ 2131 idFileSystemLocal::BuildGame_f 2132 ================ 2133 */ 2134 void idFileSystemLocal::BuildGame_f( const idCmdArgs &args ) { 2135 fileSystemLocal.WriteResourcePacks(); 2136 } 2137 2138 /* 2139 ================ 2140 idFileSystemLocal::WriteResourceFile_f 2141 ================ 2142 */ 2143 void idFileSystemLocal::WriteResourceFile_f( const idCmdArgs &args ) { 2144 if ( args.Argc() != 2 ) { 2145 common->Printf( "Usage: writeResourceFile <manifest file>\n" ); 2146 return; 2147 } 2148 2149 idStrList manifest; 2150 idResourceContainer::ReadManifestFile( args.Argv( 1 ), manifest ); 2151 idResourceContainer::WriteResourceFile( args.Argv( 1 ), manifest, false ); 2152 } 2153 2154 2155 /* 2156 ================ 2157 idFileSystemLocal::UpdateResourceFile_f 2158 ================ 2159 */ 2160 void idFileSystemLocal::UpdateResourceFile_f( const idCmdArgs &args ) { 2161 if ( args.Argc() < 3 ) { 2162 common->Printf( "Usage: updateResourceFile <resource file> <files>\n" ); 2163 return; 2164 } 2165 2166 idStr filename = args.Argv( 1 ); 2167 idStrList filesToAdd; 2168 for ( int i = 2; i < args.Argc(); i++ ) { 2169 filesToAdd.Append( args.Argv( i ) ); 2170 } 2171 idResourceContainer::UpdateResourceFile( filename, filesToAdd ); 2172 } 2173 2174 /* 2175 ================ 2176 idFileSystemLocal::ExtractResourceFile_f 2177 ================ 2178 */ 2179 void idFileSystemLocal::ExtractResourceFile_f( const idCmdArgs &args ) { 2180 if ( args.Argc() < 3 ) { 2181 common->Printf( "Usage: extractResourceFile <resource file> <outpath> <copysound>\n" ); 2182 return; 2183 } 2184 2185 idStr filename = args.Argv( 1 ); 2186 idStr outPath = args.Argv( 2 ); 2187 bool copyWaves = ( args.Argc() > 3 ); 2188 idResourceContainer::ExtractResourceFile( filename, outPath, copyWaves ); 2189 } 2190 2191 /* 2192 ============ 2193 idFileSystemLocal::Path_f 2194 ============ 2195 */ 2196 void idFileSystemLocal::Path_f( const idCmdArgs &args ) { 2197 common->Printf( "Current search path:\n" ); 2198 for ( int i = 0; i < fileSystemLocal.searchPaths.Num(); i++ ) { 2199 common->Printf( "%s/%s\n", fileSystemLocal.searchPaths[i].path.c_str(), fileSystemLocal.searchPaths[i].gamedir.c_str() ); 2200 } 2201 } 2202 2203 /* 2204 ============ 2205 idFileSystemLocal::TouchFile_f 2206 2207 The only purpose of this function is to allow game script files to copy 2208 arbitrary files furing an "fs_copyfiles 1" run. 2209 ============ 2210 */ 2211 void idFileSystemLocal::TouchFile_f( const idCmdArgs &args ) { 2212 idFile *f; 2213 2214 if ( args.Argc() != 2 ) { 2215 common->Printf( "Usage: touchFile <file>\n" ); 2216 return; 2217 } 2218 2219 f = fileSystemLocal.OpenFileRead( args.Argv( 1 ) ); 2220 if ( f ) { 2221 fileSystemLocal.CloseFile( f ); 2222 } 2223 } 2224 2225 /* 2226 ============ 2227 idFileSystemLocal::TouchFileList_f 2228 2229 Takes a text file and touches every file in it, use one file per line. 2230 ============ 2231 */ 2232 void idFileSystemLocal::TouchFileList_f( const idCmdArgs &args ) { 2233 2234 if ( args.Argc() != 2 ) { 2235 common->Printf( "Usage: touchFileList <filename>\n" ); 2236 return; 2237 } 2238 2239 const char *buffer = NULL; 2240 idParser src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT ); 2241 if ( fileSystem->ReadFile( args.Argv( 1 ), ( void** )&buffer, NULL ) && buffer ) { 2242 src.LoadMemory( buffer, strlen( buffer ), args.Argv( 1 ) ); 2243 if ( src.IsLoaded() ) { 2244 idToken token; 2245 while( src.ReadToken( &token ) ) { 2246 common->Printf( "%s\n", token.c_str() ); 2247 const bool captureToImage = false; 2248 common->UpdateScreen( captureToImage ); 2249 idFile *f = fileSystemLocal.OpenFileRead( token ); 2250 if ( f ) { 2251 fileSystemLocal.CloseFile( f ); 2252 } 2253 } 2254 } 2255 } 2256 2257 } 2258 2259 /* 2260 ============ 2261 idFileSystemLocal::GenerateResourceCRCs_f 2262 2263 Generates a CRC checksum file for each .resources file. 2264 ============ 2265 */ 2266 void idFileSystemLocal::GenerateResourceCRCs_f( const idCmdArgs &args ) { 2267 idLib::Printf( "Generating CRCs for resource files...\n" ); 2268 2269 std::auto_ptr<idFileList> baseResourceFileList( fileSystem->ListFiles( ".", ".resources" ) ); 2270 if ( baseResourceFileList.get() != NULL ) { 2271 CreateCRCsForResourceFileList ( *baseResourceFileList ); 2272 } 2273 2274 std::auto_ptr<idFileList> mapResourceFileList( fileSystem->ListFilesTree( "maps", ".resources" ) ); 2275 if ( mapResourceFileList.get() != NULL ) { 2276 CreateCRCsForResourceFileList ( *mapResourceFileList ); 2277 } 2278 2279 idLib::Printf( "Done generating CRCs for resource files.\n" ); 2280 } 2281 2282 /* 2283 ================ 2284 idFileSystemLocal::CreateCRCsForResourceFileList 2285 ================ 2286 */ 2287 void idFileSystemLocal::CreateCRCsForResourceFileList( const idFileList & list ) { 2288 for ( int fileIndex = 0; fileIndex < list.GetNumFiles(); ++fileIndex ) { 2289 idLib::Printf( " Processing %s.\n", list.GetFile( fileIndex ) ); 2290 2291 std::auto_ptr<idFile_Memory> currentFile( static_cast<idFile_Memory *>( fileSystem->OpenFileReadMemory( list.GetFile( fileIndex ) ) ) ); 2292 2293 if ( currentFile.get() == NULL ) { 2294 idLib::Printf( " Error reading %s.\n", list.GetFile( fileIndex ) ); 2295 continue; 2296 } 2297 2298 uint32 resourceMagic; 2299 currentFile->ReadBig( resourceMagic ); 2300 2301 if ( resourceMagic != RESOURCE_FILE_MAGIC ) { 2302 idLib::Printf( "Resource file magic number doesn't match, skipping %s.\n", list.GetFile( fileIndex ) ); 2303 continue; 2304 } 2305 2306 int tableOffset; 2307 currentFile->ReadBig( tableOffset ); 2308 2309 int tableLength; 2310 currentFile->ReadBig( tableLength ); 2311 2312 // Read in the table 2313 currentFile->Seek( tableOffset, FS_SEEK_SET ); 2314 2315 int numFileResources; 2316 currentFile->ReadBig( numFileResources ); 2317 2318 idList< idResourceCacheEntry > cacheEntries; 2319 cacheEntries.SetNum( numFileResources ); 2320 2321 for ( int innerFileIndex = 0; innerFileIndex < numFileResources; ++innerFileIndex ) { 2322 cacheEntries[innerFileIndex].Read( currentFile.get() ); 2323 } 2324 2325 // All tables read, now seek to each one and calculate the CRC. 2326 idTempArray< unsigned long > innerFileCRCs( numFileResources ); 2327 for ( int innerFileIndex = 0; innerFileIndex < numFileResources; ++innerFileIndex ) { 2328 const char * innerFileDataBegin = currentFile->GetDataPtr() + cacheEntries[innerFileIndex].offset; 2329 2330 innerFileCRCs[innerFileIndex] = CRC32_BlockChecksum( innerFileDataBegin, cacheEntries[innerFileIndex].length ); 2331 } 2332 2333 // Get the CRC for all the CRCs. 2334 const unsigned long totalCRC = CRC32_BlockChecksum( innerFileCRCs.Ptr(), innerFileCRCs.Size() ); 2335 2336 // Write the .crc file corresponding to the .resources file. 2337 idStr crcFilename = list.GetFile( fileIndex ); 2338 crcFilename.SetFileExtension( ".crc" ); 2339 std::auto_ptr<idFile> crcOutputFile( fileSystem->OpenFileWrite( crcFilename, "fs_basepath" ) ); 2340 if ( crcOutputFile.get() == NULL ) { 2341 idLib::Printf( "Error writing CRC file %s.\n", crcFilename ); 2342 continue; 2343 } 2344 2345 const uint32 CRC_FILE_MAGIC = 0xCC00CC00; // I just made this up, it has no meaning. 2346 const uint32 CRC_FILE_VERSION = 1; 2347 crcOutputFile->WriteBig( CRC_FILE_MAGIC ); 2348 crcOutputFile->WriteBig( CRC_FILE_VERSION ); 2349 crcOutputFile->WriteBig( totalCRC ); 2350 crcOutputFile->WriteBig( numFileResources ); 2351 crcOutputFile->WriteBigArray( innerFileCRCs.Ptr(), numFileResources ); 2352 } 2353 } 2354 2355 /* 2356 ================ 2357 idFileSystemLocal::AddResourceFile 2358 ================ 2359 */ 2360 int idFileSystemLocal::AddResourceFile( const char * resourceFileName ) { 2361 idStrStatic< MAX_OSPATH > resourceFile = va( "maps/%s", resourceFileName ); 2362 idResourceContainer *rc = new idResourceContainer(); 2363 if ( rc->Init( resourceFile, resourceFiles.Num() ) ) { 2364 resourceFiles.Append( rc ); 2365 common->Printf( "Loaded resource file %s\n", resourceFile.c_str() ); 2366 return resourceFiles.Num() - 1; 2367 } 2368 return -1; 2369 } 2370 2371 /* 2372 ================ 2373 idFileSystemLocal::FindResourceFile 2374 ================ 2375 */ 2376 int idFileSystemLocal::FindResourceFile( const char * resourceFileName ) { 2377 for ( int i = 0; i < resourceFiles.Num(); i++ ) { 2378 if ( idStr::Icmp( resourceFileName, resourceFiles[ i ]->GetFileName() ) == 0 ) { 2379 return i; 2380 } 2381 } 2382 return -1; 2383 } 2384 /* 2385 ================ 2386 idFileSystemLocal::RemoveResourceFileByIndex 2387 ================ 2388 */ 2389 void idFileSystemLocal::RemoveResourceFileByIndex( const int &idx ) { 2390 if ( idx >= 0 && idx < resourceFiles.Num() ) { 2391 if ( idx >= 0 && idx < resourceFiles.Num() ) { 2392 delete resourceFiles[ idx ]; 2393 resourceFiles.RemoveIndex( idx ); 2394 for ( int i = 0; i < resourceFiles.Num(); i++ ) { 2395 // fixup any container indexes 2396 resourceFiles[ i ]->SetContainerIndex( i ); 2397 } 2398 } 2399 } 2400 } 2401 2402 /* 2403 ================ 2404 idFileSystemLocal::RemoveMapResourceFile 2405 ================ 2406 */ 2407 void idFileSystemLocal::RemoveMapResourceFile( const char * resourceFileName ) { 2408 int idx = FindResourceFile( va( "maps/%s", resourceFileName ) ); 2409 if ( idx >= 0 ) { 2410 RemoveResourceFileByIndex( idx ); 2411 } 2412 } 2413 2414 /* 2415 ================ 2416 idFileSystemLocal::RemoveResourceFile 2417 ================ 2418 */ 2419 void idFileSystemLocal::RemoveResourceFile( const char * resourceFileName ) { 2420 int idx = FindResourceFile( resourceFileName ); 2421 if ( idx >= 0 ) { 2422 RemoveResourceFileByIndex( idx ); 2423 } 2424 } 2425 2426 /* 2427 ================ 2428 idFileSystemLocal::AddGameDirectory 2429 2430 Sets gameFolder, adds the directory to the head of the search paths 2431 ================ 2432 */ 2433 void idFileSystemLocal::AddGameDirectory( const char *path, const char *dir ) { 2434 // check if the search path already exists 2435 for ( int i = 0; i < searchPaths.Num(); i++ ) { 2436 if ( searchPaths[i].path.Cmp( path ) == 0 && searchPaths[i].gamedir.Cmp( dir ) == 0 ) { 2437 return; 2438 } 2439 } 2440 2441 gameFolder = dir; 2442 2443 // 2444 // add the directory to the search path 2445 // 2446 searchpath_t & search = searchPaths.Alloc(); 2447 search.path = path; 2448 search.gamedir = dir; 2449 2450 idStr pakfile = BuildOSPath( path, dir, "" ); 2451 pakfile[ pakfile.Length() - 1 ] = 0; // strip the trailing slash 2452 2453 idStrList pakfiles; 2454 ListOSFiles( pakfile, ".resources", pakfiles ); 2455 pakfiles.SortWithTemplate( idSort_PathStr() ); 2456 if ( pakfiles.Num() > 0 ) { 2457 // resource files present, ignore pak files 2458 for ( int i = 0; i < pakfiles.Num(); i++ ) { 2459 pakfile = pakfiles[i]; //BuildOSPath( path, dir, pakfiles[i] ); 2460 idResourceContainer *rc = new idResourceContainer(); 2461 if ( rc->Init( pakfile, resourceFiles.Num() ) ) { 2462 resourceFiles.Append( rc ); 2463 common->Printf( "Loaded resource file %s\n", pakfile.c_str() ); 2464 //com_productionMode.SetInteger( 2 ); 2465 } 2466 } 2467 } 2468 } 2469 2470 /* 2471 ================ 2472 idFileSystemLocal::SetupGameDirectories 2473 2474 Takes care of the correct search order. 2475 ================ 2476 */ 2477 void idFileSystemLocal::SetupGameDirectories( const char *gameName ) { 2478 // setup basepath 2479 if ( fs_basepath.GetString()[0] ) { 2480 AddGameDirectory( fs_basepath.GetString(), gameName ); 2481 } 2482 // setup savepath 2483 if ( fs_savepath.GetString()[0] ) { 2484 AddGameDirectory( fs_savepath.GetString(), gameName ); 2485 } 2486 } 2487 2488 2489 const char *cachedStartupFiles[] = { 2490 "game:\\base\\video\\loadvideo.bik" 2491 }; 2492 const int numStartupFiles = sizeof( cachedStartupFiles ) / sizeof ( cachedStartupFiles[ 0 ] ); 2493 2494 const char *cachedNormalFiles[] = { 2495 "game:\\base\\_sound_xenon_en.resources", // these will fail silently on the files that are not on disc 2496 "game:\\base\\_sound_xenon_fr.resources", 2497 "game:\\base\\_sound_xenon_jp.resources", 2498 "game:\\base\\_sound_xenon_sp.resources", 2499 "game:\\base\\_sound_xenon_it.resources", 2500 "game:\\base\\_sound_xenon_gr.resources", 2501 "game:\\base\\_sound_xenon.resources", 2502 "game:\\base\\_common.resources", 2503 "game:\\base\\_ordered.resources", 2504 "game:\\base\\video\\mars_rotation.bik" // cache this to save the consumer from hearing SEEK.. SEEK... SEEK.. SEEK SEEEK while at the main menu 2505 }; 2506 const int numNormalFiles = sizeof( cachedNormalFiles ) / sizeof ( cachedNormalFiles[ 0 ] ); 2507 2508 const char *dontCacheFiles[] = { 2509 "game:\\base\\maps\\*.*", // these will fail silently on the files that are not on disc 2510 "game:\\base\\video\\*.*", 2511 "game:\\base\\sound\\*.*", 2512 }; 2513 const int numDontCacheFiles = sizeof( dontCacheFiles ) / sizeof ( dontCacheFiles[ 0 ] ); 2514 2515 /* 2516 ================ 2517 idFileSystemLocal::InitPrecache 2518 ================ 2519 */ 2520 void idFileSystemLocal::InitPrecache() { 2521 if ( !fs_enableBackgroundCaching.GetBool() ) { 2522 return; 2523 } 2524 numFilesOpenedAsCached = 0; 2525 } 2526 2527 /* 2528 ================ 2529 idFileSystemLocal::ReOpenCacheFiles 2530 ================ 2531 */ 2532 void idFileSystemLocal::ReOpenCacheFiles() { 2533 2534 if ( !fs_enableBackgroundCaching.GetBool() ) { 2535 return; 2536 } 2537 } 2538 2539 2540 /* 2541 ================ 2542 idFileSystemLocal::Startup 2543 ================ 2544 */ 2545 void idFileSystemLocal::Startup() { 2546 common->Printf( "------ Initializing File System ------\n" ); 2547 2548 InitPrecache(); 2549 2550 SetupGameDirectories( BASE_GAMEDIR ); 2551 2552 // fs_game_base override 2553 if ( fs_game_base.GetString()[0] && 2554 idStr::Icmp( fs_game_base.GetString(), BASE_GAMEDIR ) ) { 2555 SetupGameDirectories( fs_game_base.GetString() ); 2556 } 2557 2558 // fs_game override 2559 if ( fs_game.GetString()[0] && 2560 idStr::Icmp( fs_game.GetString(), BASE_GAMEDIR ) && 2561 idStr::Icmp( fs_game.GetString(), fs_game_base.GetString() ) ) { 2562 SetupGameDirectories( fs_game.GetString() ); 2563 } 2564 2565 // add our commands 2566 cmdSystem->AddCommand( "dir", Dir_f, CMD_FL_SYSTEM, "lists a folder", idCmdSystem::ArgCompletion_FileName ); 2567 cmdSystem->AddCommand( "dirtree", DirTree_f, CMD_FL_SYSTEM, "lists a folder with subfolders" ); 2568 cmdSystem->AddCommand( "path", Path_f, CMD_FL_SYSTEM, "lists search paths" ); 2569 cmdSystem->AddCommand( "touchFile", TouchFile_f, CMD_FL_SYSTEM, "touches a file" ); 2570 cmdSystem->AddCommand( "touchFileList", TouchFileList_f, CMD_FL_SYSTEM, "touches a list of files" ); 2571 2572 cmdSystem->AddCommand( "buildGame", BuildGame_f, CMD_FL_SYSTEM, "builds game pak files" ); 2573 cmdSystem->AddCommand( "writeResourceFile", WriteResourceFile_f, CMD_FL_SYSTEM, "writes a .resources file from a supplied manifest" ); 2574 cmdSystem->AddCommand( "extractResourceFile", ExtractResourceFile_f, CMD_FL_SYSTEM, "extracts to the supplied resource file to the supplied path" ); 2575 cmdSystem->AddCommand( "updateResourceFile", UpdateResourceFile_f, CMD_FL_SYSTEM, "updates or appends the supplied files in the supplied resource file" ); 2576 2577 cmdSystem->AddCommand( "generateResourceCRCs", GenerateResourceCRCs_f, CMD_FL_SYSTEM, "Generates CRC checksums for all the resource files." ); 2578 2579 // print the current search paths 2580 Path_f( idCmdArgs() ); 2581 2582 common->Printf( "file system initialized.\n" ); 2583 common->Printf( "--------------------------------------\n" ); 2584 } 2585 2586 /* 2587 ================ 2588 idFileSystemLocal::Init 2589 2590 Called only at inital startup, not when the filesystem 2591 is resetting due to a game change 2592 ================ 2593 */ 2594 void idFileSystemLocal::Init() { 2595 // allow command line parms to override our defaults 2596 // we have to specially handle this, because normal command 2597 // line variable sets don't happen until after the filesystem 2598 // has already been initialized 2599 common->StartupVariable( "fs_basepath" ); 2600 common->StartupVariable( "fs_savepath" ); 2601 common->StartupVariable( "fs_game" ); 2602 common->StartupVariable( "fs_game_base" ); 2603 common->StartupVariable( "fs_copyfiles" ); 2604 2605 if ( fs_basepath.GetString()[0] == '\0' ) { 2606 fs_basepath.SetString( Sys_DefaultBasePath() ); 2607 } 2608 if ( fs_savepath.GetString()[0] == '\0' ) { 2609 fs_savepath.SetString( Sys_DefaultSavePath() ); 2610 } 2611 2612 // try to start up normally 2613 Startup(); 2614 2615 // if we can't find default.cfg, assume that the paths are 2616 // busted and error out now, rather than getting an unreadable 2617 // graphics screen when the font fails to load 2618 // Dedicated servers can run with no outside files at all 2619 if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) { 2620 common->FatalError( "Couldn't load default.cfg" ); 2621 } 2622 } 2623 2624 /* 2625 ================ 2626 idFileSystemLocal::Restart 2627 ================ 2628 */ 2629 void idFileSystemLocal::Restart() { 2630 // free anything we currently have loaded 2631 Shutdown( true ); 2632 2633 Startup(); 2634 2635 // if we can't find default.cfg, assume that the paths are 2636 // busted and error out now, rather than getting an unreadable 2637 // graphics screen when the font fails to load 2638 if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) { 2639 common->FatalError( "Couldn't load default.cfg" ); 2640 } 2641 } 2642 2643 /* 2644 ================ 2645 idFileSystemLocal::Shutdown 2646 2647 Frees all resources and closes all files 2648 ================ 2649 */ 2650 void idFileSystemLocal::Shutdown( bool reloading ) { 2651 gameFolder.Clear(); 2652 searchPaths.Clear(); 2653 2654 resourceFiles.DeleteContents(); 2655 2656 2657 cmdSystem->RemoveCommand( "path" ); 2658 cmdSystem->RemoveCommand( "dir" ); 2659 cmdSystem->RemoveCommand( "dirtree" ); 2660 cmdSystem->RemoveCommand( "touchFile" ); 2661 } 2662 2663 /* 2664 ================ 2665 idFileSystemLocal::IsInitialized 2666 ================ 2667 */ 2668 bool idFileSystemLocal::IsInitialized() const { 2669 return ( searchPaths.Num() != 0 ); 2670 } 2671 2672 2673 /* 2674 ================================================================================= 2675 2676 Opening files 2677 2678 ================================================================================= 2679 */ 2680 2681 /* 2682 ======================== 2683 idFileSystemLocal::GetResourceCacheEntry 2684 2685 Returns false if the entry isn't found 2686 ======================== 2687 */ 2688 bool idFileSystemLocal::GetResourceCacheEntry( const char *fileName, idResourceCacheEntry &rc ) { 2689 idStrStatic< MAX_OSPATH > canonical; 2690 if ( strstr( fileName, ":") != NULL ) { 2691 // os path, convert to relative? scripts can pass in an OS path 2692 //idLib::Printf( "RESOURCE: os path passed %s\n", fileName ); 2693 return NULL; 2694 } else { 2695 canonical = fileName; 2696 } 2697 2698 canonical.BackSlashesToSlashes(); 2699 canonical.ToLower(); 2700 int idx = resourceFiles.Num() - 1; 2701 while ( idx >= 0 ) { 2702 const int key = resourceFiles[ idx ]->cacheHash.GenerateKey( canonical, false ); 2703 for ( int index = resourceFiles[ idx ]->cacheHash.GetFirst( key ); index != idHashIndex::NULL_INDEX; index = resourceFiles[ idx ]->cacheHash.GetNext( index ) ) { 2704 idResourceCacheEntry & rt = resourceFiles[ idx ]->cacheTable[ index ]; 2705 if ( idStr::Icmp( rt.filename, canonical ) == 0 ) { 2706 rc.filename = rt.filename; 2707 rc.length = rt.length; 2708 rc.containerIndex = idx; 2709 rc.offset = rt.offset; 2710 return true; 2711 } 2712 } 2713 idx--; 2714 } 2715 return false; 2716 } 2717 2718 /* 2719 ======================== 2720 idFileSystemLocal::GetResourceFile 2721 2722 Returns NULL 2723 ======================== 2724 */ 2725 2726 idFile * idFileSystemLocal::GetResourceFile( const char *fileName, bool memFile ) { 2727 2728 if ( resourceFiles.Num() == 0 ) { 2729 return NULL; 2730 } 2731 2732 static idResourceCacheEntry rc; 2733 if ( GetResourceCacheEntry( fileName, rc ) ) { 2734 if ( fs_debugResources.GetBool() ) { 2735 idLib::Printf( "RES: loading file %s\n", rc.filename.c_str() ); 2736 } 2737 idFile_InnerResource *file = new idFile_InnerResource( rc.filename, resourceFiles[ rc.containerIndex ]->resourceFile, rc.offset, rc.length ); 2738 if ( file != NULL && ( memFile || rc.length <= resourceBufferAvailable ) || rc.length < 8 * 1024 * 1024 ) { 2739 byte *buf = NULL; 2740 if ( rc.length < resourceBufferAvailable ) { 2741 buf = resourceBufferPtr; 2742 resourceBufferAvailable = 0; 2743 } else { 2744 if ( fs_debugResources.GetBool() ) { 2745 idLib::Printf( "MEM: Allocating %05d bytes for a resource load\n", rc.length ); 2746 } 2747 buf = ( byte * )Mem_Alloc( rc.length, TAG_TEMP ); 2748 } 2749 file->Read( (void*)buf, rc.length ); 2750 2751 if ( buf == resourceBufferPtr ) { 2752 file->SetResourceBuffer( buf ); 2753 return file; 2754 } else { 2755 idFile_Memory *mfile = new idFile_Memory( rc.filename, ( const char * )buf, rc.length ); 2756 if ( mfile != NULL ) { 2757 mfile->TakeDataOwnership(); 2758 delete file; 2759 return mfile; 2760 } 2761 } 2762 } 2763 return file; 2764 } 2765 2766 return NULL; 2767 } 2768 2769 2770 /* 2771 =========== 2772 idFileSystemLocal::OpenFileReadFlags 2773 2774 Finds the file in the search path, following search flag recommendations 2775 Returns filesize and an open FILE pointer. 2776 Used for streaming data out of either a 2777 separate file or a ZIP file. 2778 =========== 2779 */ 2780 idFile *idFileSystemLocal::OpenFileReadFlags( const char *relativePath, int searchFlags, bool allowCopyFiles, const char* gamedir ) { 2781 2782 if ( !IsInitialized() ) { 2783 common->FatalError( "Filesystem call made without initialization\n" ); 2784 return NULL; 2785 } 2786 2787 if ( relativePath == NULL ) { 2788 common->FatalError( "idFileSystemLocal::OpenFileRead: NULL 'relativePath' parameter passed\n" ); 2789 return NULL; 2790 } 2791 2792 // qpaths are not supposed to have a leading slash 2793 if ( relativePath[0] == '/' || relativePath[0] == '\\' ) { 2794 relativePath++; 2795 } 2796 2797 // make absolutely sure that it can't back up the path. 2798 // The searchpaths do guarantee that something will always 2799 // be prepended, so we don't need to worry about "c:" or "//limbo" 2800 if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) { 2801 return NULL; 2802 } 2803 2804 // edge case 2805 if ( relativePath[0] == '\0' ) { 2806 return NULL; 2807 } 2808 2809 if ( fs_debug.GetBool() ) { 2810 idLib::Printf( "FILE DEBUG: opening %s\n", relativePath ); 2811 } 2812 2813 if ( resourceFiles.Num() > 0 && fs_resourceLoadPriority.GetInteger() == 1 ) { 2814 idFile * rf = GetResourceFile( relativePath, ( searchFlags & FSFLAG_RETURN_FILE_MEM ) != 0 ); 2815 if ( rf != NULL ) { 2816 return rf; 2817 } 2818 } 2819 2820 // 2821 // search through the path, one element at a time 2822 // 2823 if ( searchFlags & FSFLAG_SEARCH_DIRS ) { 2824 for ( int sp = searchPaths.Num() - 1; sp >= 0; sp-- ) { 2825 if ( gamedir != NULL && gamedir[0] != 0 ) { 2826 if ( searchPaths[sp].gamedir != gamedir ) { 2827 continue; 2828 } 2829 } 2830 2831 idStr netpath = BuildOSPath( searchPaths[sp].path, searchPaths[sp].gamedir, relativePath ); 2832 idFileHandle fp = OpenOSFile( netpath, FS_READ ); 2833 if ( !fp ) { 2834 continue; 2835 } 2836 2837 idFile_Permanent * file = new (TAG_IDFILE) idFile_Permanent(); 2838 file->o = fp; 2839 file->name = relativePath; 2840 file->fullPath = netpath; 2841 file->mode = ( 1 << FS_READ ); 2842 file->fileSize = DirectFileLength( file->o ); 2843 if ( fs_debug.GetInteger() ) { 2844 common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s/%s')\n", relativePath, searchPaths[sp].path.c_str(), searchPaths[sp].gamedir.c_str() ); 2845 } 2846 2847 // if fs_copyfiles is set 2848 if ( allowCopyFiles ) { 2849 2850 idStr copypath; 2851 idStr name; 2852 copypath = BuildOSPath( fs_savepath.GetString(), searchPaths[sp].gamedir, relativePath ); 2853 netpath.ExtractFileName( name ); 2854 copypath.StripFilename(); 2855 copypath += PATHSEPARATOR_STR; 2856 copypath += name; 2857 2858 if ( fs_buildResources.GetBool() ) { 2859 idStrStatic< MAX_OSPATH > relativePath = OSPathToRelativePath( copypath ); 2860 relativePath.BackSlashesToSlashes(); 2861 relativePath.ToLower(); 2862 2863 if ( IsSoundSample( relativePath ) ) { 2864 idStrStatic< MAX_OSPATH > samplePath = relativePath; 2865 samplePath.SetFileExtension( "idwav" ); 2866 if ( samplePath.Find( "generated/" ) == -1 ) { 2867 samplePath.Insert( "generated/", 0 ); 2868 } 2869 fileManifest.AddUnique( samplePath ); 2870 if ( relativePath.Find( "/vo/", false ) >= 0 ) { 2871 // this is vo so add the language variants 2872 for ( int i = 0; i < Sys_NumLangs(); i++ ) { 2873 const char *lang = Sys_Lang( i ); 2874 if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) { 2875 continue; 2876 } 2877 samplePath = relativePath; 2878 samplePath.Replace( "/vo/", va( "/vo/%s/", lang ) ); 2879 samplePath.SetFileExtension( "idwav" ); 2880 if ( samplePath.Find( "generated/" ) == -1 ) { 2881 samplePath.Insert( "generated/", 0 ); 2882 } 2883 fileManifest.AddUnique( samplePath ); 2884 2885 } 2886 } 2887 } else if ( relativePath.Icmpn( "guis/", 5 ) == 0 ) { 2888 // this is a gui so add the language variants 2889 for ( int i = 0; i < Sys_NumLangs(); i++ ) { 2890 const char *lang = Sys_Lang( i ); 2891 if ( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 ) { 2892 fileManifest.Append( relativePath ); 2893 continue; 2894 } 2895 idStrStatic< MAX_OSPATH > guiPath = relativePath; 2896 guiPath.Replace( "guis/", va( "guis/%s/", lang ) ); 2897 fileManifest.Append( guiPath ); 2898 } 2899 } else { 2900 // never add .amp files 2901 if ( strstr( relativePath, ".amp" ) == NULL ) { 2902 fileManifest.Append( relativePath ); 2903 } 2904 } 2905 2906 } 2907 2908 if ( fs_copyfiles.GetBool() ) { 2909 CopyFile( netpath, copypath ); 2910 } 2911 } 2912 2913 if ( searchFlags & FSFLAG_RETURN_FILE_MEM ) { 2914 idFile_Memory * memFile = new (TAG_IDFILE) idFile_Memory( file->name ); 2915 memFile->SetLength( file->fileSize ); 2916 file->Read( (void *)memFile->GetDataPtr(), file->fileSize ); 2917 delete file; 2918 memFile->TakeDataOwnership(); 2919 return memFile; 2920 } 2921 2922 return file; 2923 } 2924 } 2925 2926 if ( resourceFiles.Num() > 0 && fs_resourceLoadPriority.GetInteger() == 0 ) { 2927 idFile * rf = GetResourceFile( relativePath, ( searchFlags & FSFLAG_RETURN_FILE_MEM ) != 0 ); 2928 if ( rf != NULL ) { 2929 return rf; 2930 } 2931 } 2932 2933 if ( fs_debug.GetInteger( ) ) { 2934 common->Printf( "Can't find %s\n", relativePath ); 2935 } 2936 2937 return NULL; 2938 } 2939 2940 /* 2941 =========== 2942 idFileSystemLocal::OpenFileRead 2943 =========== 2944 */ 2945 idFile *idFileSystemLocal::OpenFileRead( const char *relativePath, bool allowCopyFiles, const char* gamedir ) { 2946 return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS, allowCopyFiles, gamedir ); 2947 } 2948 2949 /* 2950 =========== 2951 idFileSystemLocal::OpenFileReadMemory 2952 =========== 2953 */ 2954 idFile *idFileSystemLocal::OpenFileReadMemory( const char *relativePath, bool allowCopyFiles, const char* gamedir ) { 2955 return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS | FSFLAG_RETURN_FILE_MEM, allowCopyFiles, gamedir ); 2956 } 2957 2958 /* 2959 =========== 2960 idFileSystemLocal::OpenFileWrite 2961 =========== 2962 */ 2963 idFile *idFileSystemLocal::OpenFileWrite( const char *relativePath, const char *basePath ) { 2964 2965 const char *path; 2966 idStr OSpath; 2967 idFile_Permanent *f; 2968 2969 if ( !IsInitialized() ) { 2970 common->FatalError( "Filesystem call made without initialization\n" ); 2971 } 2972 2973 path = cvarSystem->GetCVarString( basePath ); 2974 if ( !path[0] ) { 2975 path = fs_savepath.GetString(); 2976 } 2977 2978 OSpath = BuildOSPath( path, gameFolder, relativePath ); 2979 2980 if ( fs_debug.GetInteger() ) { 2981 common->Printf( "idFileSystem::OpenFileWrite: %s\n", OSpath.c_str() ); 2982 } 2983 2984 common->DPrintf( "writing to: %s\n", OSpath.c_str() ); 2985 CreateOSPath( OSpath ); 2986 2987 f = new (TAG_IDFILE) idFile_Permanent(); 2988 f->o = OpenOSFile( OSpath, FS_WRITE ); 2989 if ( !f->o ) { 2990 delete f; 2991 return NULL; 2992 } 2993 f->name = relativePath; 2994 f->fullPath = OSpath; 2995 f->mode = ( 1 << FS_WRITE ); 2996 f->handleSync = false; 2997 f->fileSize = 0; 2998 2999 return f; 3000 } 3001 3002 /* 3003 =========== 3004 idFileSystemLocal::OpenExplicitFileRead 3005 =========== 3006 */ 3007 idFile *idFileSystemLocal::OpenExplicitFileRead( const char *OSPath ) { 3008 idFile_Permanent *f; 3009 3010 if ( !IsInitialized() ) { 3011 common->FatalError( "Filesystem call made without initialization\n" ); 3012 } 3013 3014 if ( fs_debug.GetInteger() ) { 3015 common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath ); 3016 } 3017 3018 //common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath ); 3019 3020 f = new (TAG_IDFILE) idFile_Permanent(); 3021 f->o = OpenOSFile( OSPath, FS_READ ); 3022 if ( !f->o ) { 3023 delete f; 3024 return NULL; 3025 } 3026 f->name = OSPath; 3027 f->fullPath = OSPath; 3028 f->mode = ( 1 << FS_READ ); 3029 f->handleSync = false; 3030 f->fileSize = DirectFileLength( f->o ); 3031 3032 return f; 3033 } 3034 3035 /* 3036 =========== 3037 idFileSystemLocal::OpenExplicitPakFile 3038 =========== 3039 */ 3040 idFile_Cached *idFileSystemLocal::OpenExplicitPakFile( const char *OSPath ) { 3041 idFile_Cached *f; 3042 3043 if ( !IsInitialized() ) { 3044 common->FatalError( "Filesystem call made without initialization\n" ); 3045 } 3046 3047 //if ( fs_debug.GetInteger() ) { 3048 // common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath ); 3049 //} 3050 3051 //common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath ); 3052 3053 f = new (TAG_IDFILE) idFile_Cached(); 3054 f->o = OpenOSFile( OSPath, FS_READ ); 3055 if ( !f->o ) { 3056 delete f; 3057 return NULL; 3058 } 3059 f->name = OSPath; 3060 f->fullPath = OSPath; 3061 f->mode = ( 1 << FS_READ ); 3062 f->handleSync = false; 3063 f->fileSize = DirectFileLength( f->o ); 3064 3065 return f; 3066 } 3067 3068 /* 3069 =========== 3070 idFileSystemLocal::OpenExplicitFileWrite 3071 =========== 3072 */ 3073 idFile *idFileSystemLocal::OpenExplicitFileWrite( const char *OSPath ) { 3074 idFile_Permanent *f; 3075 3076 if ( !IsInitialized() ) { 3077 common->FatalError( "Filesystem call made without initialization\n" ); 3078 } 3079 3080 if ( fs_debug.GetInteger() ) { 3081 common->Printf( "idFileSystem::OpenExplicitFileWrite: %s\n", OSPath ); 3082 } 3083 3084 common->DPrintf( "writing to: %s\n", OSPath ); 3085 CreateOSPath( OSPath ); 3086 3087 f = new (TAG_IDFILE) idFile_Permanent(); 3088 f->o = OpenOSFile( OSPath, FS_WRITE ); 3089 if ( !f->o ) { 3090 delete f; 3091 return NULL; 3092 } 3093 f->name = OSPath; 3094 f->fullPath = OSPath; 3095 f->mode = ( 1 << FS_WRITE ); 3096 f->handleSync = false; 3097 f->fileSize = 0; 3098 3099 return f; 3100 } 3101 3102 /* 3103 =========== 3104 idFileSystemLocal::OpenFileAppend 3105 =========== 3106 */ 3107 idFile *idFileSystemLocal::OpenFileAppend( const char *relativePath, bool sync, const char *basePath ) { 3108 3109 const char *path; 3110 idStr OSpath; 3111 idFile_Permanent *f; 3112 3113 if ( !IsInitialized() ) { 3114 common->FatalError( "Filesystem call made without initialization\n" ); 3115 } 3116 3117 path = cvarSystem->GetCVarString( basePath ); 3118 if ( !path[0] ) { 3119 path = fs_savepath.GetString(); 3120 } 3121 3122 OSpath = BuildOSPath( path, gameFolder, relativePath ); 3123 CreateOSPath( OSpath ); 3124 3125 if ( fs_debug.GetInteger() ) { 3126 common->Printf( "idFileSystem::OpenFileAppend: %s\n", OSpath.c_str() ); 3127 } 3128 3129 f = new (TAG_IDFILE) idFile_Permanent(); 3130 f->o = OpenOSFile( OSpath, FS_APPEND ); 3131 if ( !f->o ) { 3132 delete f; 3133 return NULL; 3134 } 3135 f->name = relativePath; 3136 f->fullPath = OSpath; 3137 f->mode = ( 1 << FS_WRITE ) + ( 1 << FS_APPEND ); 3138 f->handleSync = sync; 3139 f->fileSize = DirectFileLength( f->o ); 3140 3141 return f; 3142 } 3143 3144 /* 3145 ================ 3146 idFileSystemLocal::OpenFileByMode 3147 ================ 3148 */ 3149 idFile *idFileSystemLocal::OpenFileByMode( const char *relativePath, fsMode_t mode ) { 3150 if ( mode == FS_READ ) { 3151 return OpenFileRead( relativePath ); 3152 } 3153 if ( mode == FS_WRITE ) { 3154 return OpenFileWrite( relativePath ); 3155 } 3156 if ( mode == FS_APPEND ) { 3157 return OpenFileAppend( relativePath, true ); 3158 } 3159 common->FatalError( "idFileSystemLocal::OpenFileByMode: bad mode" ); 3160 return NULL; 3161 } 3162 3163 /* 3164 ============== 3165 idFileSystemLocal::CloseFile 3166 ============== 3167 */ 3168 void idFileSystemLocal::CloseFile( idFile *f ) { 3169 if ( !IsInitialized() ) { 3170 common->FatalError( "Filesystem call made without initialization\n" ); 3171 } 3172 delete f; 3173 } 3174 3175 /* 3176 ================= 3177 idFileSystemLocal::FindDLL 3178 ================= 3179 */ 3180 void idFileSystemLocal::FindDLL( const char *name, char _dllPath[ MAX_OSPATH ] ) { 3181 char dllName[MAX_OSPATH]; 3182 sys->DLL_GetFileName( name, dllName, MAX_OSPATH ); 3183 3184 // from executable directory first - this is handy for developement 3185 idStr dllPath = Sys_EXEPath( ); 3186 dllPath.StripFilename( ); 3187 dllPath.AppendPath( dllName ); 3188 idFile * dllFile = OpenExplicitFileRead( dllPath ); 3189 3190 if ( dllFile ) { 3191 dllPath = dllFile->GetFullPath(); 3192 CloseFile( dllFile ); 3193 dllFile = NULL; 3194 } else { 3195 dllPath = ""; 3196 } 3197 idStr::snPrintf( _dllPath, MAX_OSPATH, dllPath.c_str() ); 3198 } 3199 3200 /* 3201 =============== 3202 idFileSystemLocal::FindFile 3203 =============== 3204 */ 3205 findFile_t idFileSystemLocal::FindFile( const char *path ) { 3206 idFile *f = OpenFileReadFlags( path, FSFLAG_SEARCH_DIRS ); 3207 if ( f == NULL ) { 3208 return FIND_NO; 3209 } 3210 delete f; 3211 return FIND_YES; 3212 } 3213 3214 /* 3215 =============== 3216 idFileSystemLocal::IsFolder 3217 =============== 3218 */ 3219 sysFolder_t idFileSystemLocal::IsFolder( const char * relativePath, const char *basePath ) { 3220 return Sys_IsFolder( RelativePathToOSPath( relativePath, basePath ) ); 3221 }