doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FileSystem.cpp
Go to the documentation of this file.
1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
8 
9 Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 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 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 
34 #ifdef WIN32
35  #include <io.h> // for _read
36 #else
37  #if !__MACH__ && __MWERKS__
38  #include <types.h>
39  #include <stat.h>
40  #else
41  #include <sys/types.h>
42  #include <sys/stat.h>
43  #endif
44  #include <unistd.h>
45 #endif
46 
47 #if ID_ENABLE_CURL
48  #include "../curl/include/curl/curl.h"
49 #endif
50 
51 /*
52 =============================================================================
53 
54 DOOM FILESYSTEM
55 
56 All of Doom's data access is through a hierarchical file system, but the contents of
57 the file system can be transparently merged from several sources.
58 
59 A "relativePath" is a reference to game file data, which must include a terminating zero.
60 "..", "\\", and ":" are explicitly illegal in qpaths to prevent any references
61 outside the Doom directory system.
62 
63 The "base path" is the path to the directory holding all the game directories and
64 usually the executable. It defaults to the current directory, but can be overridden
65 with "+set fs_basepath c:\doom" on the command line. The base path cannot be modified
66 at all after startup.
67 
68 The "save path" is the path to the directory where game files will be saved. It defaults
69 to the base path, but can be overridden with a "+set fs_savepath c:\doom" on the
70 command line. Any files that are created during the game (demos, screenshots, etc.) will
71 be created reletive to the save path.
72 
73 The "cd path" is the path to an alternate hierarchy that will be searched if a file
74 is not located in the base path. A user can do a partial install that copies some
75 data to a base path created on their hard drive and leave the rest on the cd. It defaults
76 to the current directory, but it can be overridden with "+set fs_cdpath g:\doom" on the
77 command line.
78 
79 The "dev path" is the path to an alternate hierarchy where the editors and tools used
80 during development (Radiant, AF editor, dmap, runAAS) will write files to. It defaults to
81 the cd path, but can be overridden with a "+set fs_devpath c:\doom" on the command line.
82 
83 If a user runs the game directly from a CD, the base path would be on the CD. This
84 should still function correctly, but all file writes will fail (harmlessly).
85 
86 The "base game" is the directory under the paths where data comes from by default, and
87 can be either "base" or "demo".
88 
89 The "current game" may be the same as the base game, or it may be the name of another
90 directory under the paths that should be searched for files before looking in the base
91 game. The game directory is set with "+set fs_game myaddon" on the command line. This is
92 the basis for addons.
93 
94 No other directories outside of the base game and current game will ever be referenced by
95 filesystem functions.
96 
97 To save disk space and speed up file loading, directory trees can be collapsed into zip
98 files. The files use a ".pk4" extension to prevent users from unzipping them accidentally,
99 but otherwise they are simply normal zip files. A game directory can have multiple zip
100 files of the form "pak0.pk4", "pak1.pk4", etc. Zip files are searched in decending order
101 from the highest number to the lowest, and will always take precedence over the filesystem.
102 This allows a pk4 distributed as a patch to override all existing data.
103 
104 Because we will have updated executables freely available online, there is no point to
105 trying to restrict demo / oem versions of the game with code changes. Demo / oem versions
106 should be exactly the same executables as release versions, but with different data that
107 automatically restricts where game media can come from to prevent add-ons from working.
108 
109 After the paths are initialized, Doom will look for the product.txt file. If not found
110 and verified, the game will run in restricted mode. In restricted mode, only files
111 contained in demo/pak0.pk4 will be available for loading, and only if the zip header is
112 verified to not have been modified. A single exception is made for DoomConfig.cfg. Files
113 can still be written out in restricted mode, so screenshots and demos are allowed.
114 Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even
115 if there is a valid product.txt under the basepath or cdpath.
116 
117 If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd
118 path, it will be copied over to the save path. This is a development aid to help build
119 test releases and to copy working sets of files.
120 
121 If the "fs_copyfiles" cvar is set to 2, any file found in fs_cdpath that is newer than
122 it's fs_savepath version will be copied to fs_savepath (in addition to the fs_copyfiles 1
123 behaviour).
124 
125 If the "fs_copyfiles" cvar is set to 3, files from both basepath and cdpath will be copied
126 over to the save path. This is useful when copying working sets of files mainly from base
127 path with an additional cd path (which can be a slower network drive for instance).
128 
129 If the "fs_copyfiles" cvar is set to 4, files that exist in the cd path but NOT the base path
130 will be copied to the save path
131 
132 NOTE: fs_copyfiles and case sensitivity. On fs_caseSensitiveOS 0 filesystems ( win32 ), the
133 copied files may change casing when copied over.
134 
135 The relative path "sound/newstuff/test.wav" would be searched for in the following places:
136 
137 for save path, dev path, base path, cd path:
138  for current game, base game:
139  search directory
140  search zip files
141 
142 downloaded files, to be written to save path + current game's directory
143 
144 The filesystem can be safely shutdown and reinitialized with different
145 basedir / cddir / game combinations, but all other subsystems that rely on it
146 (sound, video) must also be forced to restart.
147 
148 
149 "fs_caseSensitiveOS":
150 This cvar is set on operating systems that use case sensitive filesystems (Linux and OSX)
151 It is a common situation to have the media reference filenames, whereas the file on disc
152 only matches in a case-insensitive way. When "fs_caseSensitiveOS" is set, the filesystem
153 will always do a case insensitive search.
154 IMPORTANT: This only applies to files, and not to directories. There is no case-insensitive
155 matching of directories. All directory names should be lowercase, when "com_developer" is 1,
156 the filesystem will warn when it catches bad directory situations (regardless of the
157 "fs_caseSensitiveOS" setting)
158 When bad casing in directories happen and "fs_caseSensitiveOS" is set, BuildOSPath will
159 attempt to correct the situation by forcing the path to lowercase. This assumes the media
160 is stored all lowercase.
161 
162 "additional mod path search":
163 fs_game_base can be used to set an additional search path
164 in search order, fs_game, fs_game_base, BASEGAME
165 for instance to base a mod of D3 + D3XP assets, fs_game mymod, fs_game_base d3xp
166 
167 =============================================================================
168 */
169 
170 
171 
172 // define to fix special-cases for GetPackStatus so that files that shipped in
173 // the wrong place for Doom 3 don't break pure servers.
174 #define DOOM3_PURE_SPECIAL_CASES
175 
176 typedef bool (*pureExclusionFunc_t)( const struct pureExclusion_s &excl, int l, const idStr &name );
177 
178 typedef struct pureExclusion_s {
179  int nameLen;
180  int extLen;
181  const char * name;
182  const char * ext;
185 
186 bool excludeExtension( const pureExclusion_t &excl, int l, const idStr &name ) {
187  if ( l > excl.extLen && !idStr::Icmp( name.c_str() + l - excl.extLen, excl.ext ) ) {
188  return true;
189  }
190  return false;
191 }
192 
193 bool excludePathPrefixAndExtension( const pureExclusion_t &excl, int l, const idStr &name ) {
194  if ( l > excl.nameLen && !idStr::Icmp( name.c_str() + l - excl.extLen, excl.ext ) && !name.IcmpPrefixPath( excl.name ) ) {
195  return true;
196  }
197  return false;
198 }
199 
200 bool excludeFullName( const pureExclusion_t &excl, int l, const idStr &name ) {
201  if ( l == excl.nameLen && !name.Icmp( excl.name ) ) {
202  return true;
203  }
204  return false;
205 }
206 
207 static pureExclusion_t pureExclusions[] = {
208  { 0, 0, NULL, "/", excludeExtension },
209  { 0, 0, NULL, "\\", excludeExtension },
210  { 0, 0, NULL, ".pda", excludeExtension },
211  { 0, 0, NULL, ".gui", excludeExtension },
212  { 0, 0, NULL, ".pd", excludeExtension },
213  { 0, 0, NULL, ".lang", excludeExtension },
214  { 0, 0, "sound/VO", ".ogg", excludePathPrefixAndExtension },
215  { 0, 0, "sound/VO", ".wav", excludePathPrefixAndExtension },
216 #if defined DOOM3_PURE_SPECIAL_CASES
217  // add any special-case files or paths for pure servers here
218  { 0, 0, "sound/ed/marscity/vo_intro_cutscene.ogg", NULL, excludeFullName },
219  { 0, 0, "sound/weapons/soulcube/energize_01.ogg", NULL, excludeFullName },
220  { 0, 0, "sound/xian/creepy/vocal_fx", ".ogg", excludePathPrefixAndExtension },
221  { 0, 0, "sound/xian/creepy/vocal_fx", ".wav", excludePathPrefixAndExtension },
222  { 0, 0, "sound/feedback", ".ogg", excludePathPrefixAndExtension },
223  { 0, 0, "sound/feedback", ".wav", excludePathPrefixAndExtension },
224  { 0, 0, "guis/assets/mainmenu/chnote.tga", NULL, excludeFullName },
225  { 0, 0, "sound/levels/alphalabs2/uac_better_place.ogg", NULL, excludeFullName },
226  { 0, 0, "textures/bigchars.tga", NULL, excludeFullName },
227  { 0, 0, "dds/textures/bigchars.dds", NULL, excludeFullName },
228  { 0, 0, "fonts", ".tga", excludePathPrefixAndExtension },
229  { 0, 0, "dds/fonts", ".dds", excludePathPrefixAndExtension },
230  { 0, 0, "default.cfg", NULL, excludeFullName },
231  // russian zpak001.pk4
232  { 0, 0, "fonts", ".dat", excludePathPrefixAndExtension },
233  { 0, 0, "guis/temp.guied", NULL, excludeFullName },
234 #endif
235  { 0, 0, NULL, NULL, NULL }
236 };
237 
238 // ensures that lengths for pure exclusions are correct
240 public:
242  for ( int i = 0; pureExclusions[i].func != NULL; i++ ) {
243  if ( pureExclusions[i].name ) {
244  pureExclusions[i].nameLen = idStr::Length( pureExclusions[i].name );
245  }
246  if ( pureExclusions[i].ext ) {
247  pureExclusions[i].extLen = idStr::Length( pureExclusions[i].ext );
248  }
249  }
250  }
251 };
252 
253 static idInitExclusions initExclusions;
254 
255 #define MAX_ZIPPED_FILE_NAME 2048
256 #define FILE_HASH_SIZE 1024
257 
258 typedef struct fileInPack_s {
259  idStr name; // name of the file
260  unsigned long pos; // file info position in zip
261  struct fileInPack_s * next; // next file in the hash
262 } fileInPack_t;
263 
264 typedef enum {
269 
270 typedef enum {
271  PURE_UNKNOWN = 0, // need to run the pak through GetPackStatus
272  PURE_NEUTRAL, // neutral regarding pureness. gets in the pure list if referenced
273  PURE_ALWAYS, // always referenced - for pak* named files, unless NEVER
274  PURE_NEVER // VO paks. may be referenced, won't be in the pure lists
275 } pureStatus_t;
276 
277 typedef struct {
280 } addonInfo_t;
281 
282 typedef struct {
283  idStr pakFilename; // c:\doom\base\pak0.pk4
285  int checksum;
286  int numfiles;
287  int length;
290  bool addon; // this is an addon pack - addon_search tells if it's 'active'
291  bool addon_search; // is in the search list
294  bool isNew; // for downloaded paks
297 } pack_t;
298 
299 typedef struct {
300  idStr path; // c:\doom
301  idStr gamedir; // base
302 } directory_t;
303 
304 typedef struct searchpath_s {
305  pack_t * pack; // only one of pack / dir will be non NULL
308 } searchpath_t;
309 
310 // search flags when opening a file
311 #define FSFLAG_SEARCH_DIRS ( 1 << 0 )
312 #define FSFLAG_SEARCH_PAKS ( 1 << 1 )
313 #define FSFLAG_PURE_NOREF ( 1 << 2 )
314 #define FSFLAG_BINARY_ONLY ( 1 << 3 )
315 #define FSFLAG_SEARCH_ADDONS ( 1 << 4 )
316 
317 // 3 search path (fs_savepath fs_basepath fs_cdpath)
318 // + .jpg and .tga
319 #define MAX_CACHED_DIRS 6
320 
321 // how many OSes to handle game paks for ( we don't have to know them precisely )
322 #define MAX_GAME_OS 6
323 #define BINARY_CONFIG "binary.conf"
324 #define ADDON_CONFIG "addon.conf"
325 
326 class idDEntry : public idStrList {
327 public:
328  idDEntry() {}
329  virtual ~idDEntry() {}
330 
331  bool Matches( const char *directory, const char *extension ) const;
332  void Init( const char *directory, const char *extension, const idStrList &list );
333  void Clear( void );
334 
335 private:
338 };
339 
341 public:
342  idFileSystemLocal( void );
343 
344  virtual void Init( void );
345  virtual void StartBackgroundDownloadThread( void );
346  virtual void Restart( void );
347  virtual void Shutdown( bool reloading );
348  virtual bool IsInitialized( void ) const;
349  virtual bool PerformingCopyFiles( void ) const;
350  virtual idModList * ListMods( void );
351  virtual void FreeModList( idModList *modList );
352  virtual idFileList * ListFiles( const char *relativePath, const char *extension, bool sort = false, bool fullRelativePath = false, const char* gamedir = NULL );
353  virtual idFileList * ListFilesTree( const char *relativePath, const char *extension, bool sort = false, const char* gamedir = NULL );
354  virtual void FreeFileList( idFileList *fileList );
355  virtual const char * OSPathToRelativePath( const char *OSPath );
356  virtual const char * RelativePathToOSPath( const char *relativePath, const char *basePath );
357  virtual const char * BuildOSPath( const char *base, const char *game, const char *relativePath );
358  virtual void CreateOSPath( const char *OSPath );
359  virtual bool FileIsInPAK( const char *relativePath );
360  virtual void UpdatePureServerChecksums( void );
361  virtual bool UpdateGamePakChecksums( void );
362  virtual fsPureReply_t SetPureServerChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int gamePakChecksum, int missingChecksums[ MAX_PURE_PAKS ], int *missingGamePakChecksum );
363  virtual void GetPureServerChecksums( int checksums[ MAX_PURE_PAKS ], int OS, int *gamePakChecksum );
364  virtual void SetRestartChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int gamePakChecksum );
365  virtual void ClearPureChecksums( void );
366  virtual int GetOSMask( void );
367  virtual int ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp );
368  virtual void FreeFile( void *buffer );
369  virtual int WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath = "fs_savepath" );
370  virtual void RemoveFile( const char *relativePath );
371  virtual idFile * OpenFileReadFlags( const char *relativePath, int searchFlags, pack_t **foundInPak = NULL, bool allowCopyFiles = true, const char* gamedir = NULL );
372  virtual idFile * OpenFileRead( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL );
373  virtual idFile * OpenFileWrite( const char *relativePath, const char *basePath = "fs_savepath" );
374  virtual idFile * OpenFileAppend( const char *relativePath, bool sync = false, const char *basePath = "fs_basepath" );
375  virtual idFile * OpenFileByMode( const char *relativePath, fsMode_t mode );
376  virtual idFile * OpenExplicitFileRead( const char *OSPath );
377  virtual idFile * OpenExplicitFileWrite( const char *OSPath );
378  virtual void CloseFile( idFile *f );
379  virtual void BackgroundDownload( backgroundDownload_t *bgl );
380  virtual void ResetReadCount( void ) { readCount = 0; }
381  virtual void AddToReadCount( int c ) { readCount += c; }
382  virtual int GetReadCount( void ) { return readCount; }
383  virtual void FindDLL( const char *basename, char dllPath[ MAX_OSPATH ], bool updateChecksum );
384  virtual void ClearDirCache( void );
385  virtual bool HasD3XP( void );
386  virtual bool RunningD3XP( void );
387  virtual void CopyFile( const char *fromOSPath, const char *toOSPath );
388  virtual int ValidateDownloadPakForChecksum( int checksum, char path[ MAX_STRING_CHARS ], bool isBinary );
389  virtual idFile * MakeTemporaryFile( void );
390  virtual int AddZipFile( const char *path );
391  virtual findFile_t FindFile( const char *path, bool scheduleAddons );
392  virtual int GetNumMaps();
393  virtual const idDict * GetMapDecl( int i );
394  virtual void FindMapScreenshot( const char *path, char *buf, int len );
395  virtual bool FilenameCompare( const char *s1, const char *s2 ) const;
396 
397  static void Dir_f( const idCmdArgs &args );
398  static void DirTree_f( const idCmdArgs &args );
399  static void Path_f( const idCmdArgs &args );
400  static void TouchFile_f( const idCmdArgs &args );
401  static void TouchFileList_f( const idCmdArgs &args );
402 
403 private:
404  friend dword BackgroundDownloadThread( void *parms );
405 
407  int readCount; // total bytes read
408  int loadCount; // total files read
409  int loadStack; // total files in memory
410  idStr gameFolder; // this will be a single name without separators
411 
412  searchpath_t *addonPaks; // not loaded up, but we saw them
413 
414  idDict mapDict; // for GetMapDecl
415 
416  static idCVar fs_debug;
423  static idCVar fs_game;
427 
431 
433  bool loadedFileFromDir; // set to true once a file was loaded from a directory - can't switch to pure anymore
434  idList<int> restartChecksums; // used during a restart to set things in right order
435  idList<int> addonChecksums; // list of checksums that should go to the search list directly ( for restarts )
437  int gameDLLChecksum; // the checksum of the last loaded game DLL
438  int gamePakChecksum; // the checksum of the pak holding the loaded game DLL
439 
441 
445 
446  int d3xp; // 0: didn't check, -1: not installed, 1: installed
447 
448 private:
449  void ReplaceSeparators( idStr &path, char sep = PATHSEPERATOR_CHAR );
450  long HashFileName( const char *fname ) const;
451  int ListOSFiles( const char *directory, const char *extension, idStrList &list );
452  FILE * OpenOSFile( const char *name, const char *mode, idStr *caseSensitiveName = NULL );
453  FILE * OpenOSFileCorrectName( idStr &path, const char *mode );
454  int DirectFileLength( FILE *o );
455  void CopyFile( idFile *src, const char *toOSPath );
456  int AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const;
457  void GetExtensionList( const char *extension, idStrList &extensionList ) const;
458  int GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir = NULL );
459 
460  int GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir = NULL );
461  pack_t * LoadZipFile( const char *zipfile );
462  void AddGameDirectory( const char *path, const char *dir );
463  void SetupGameDirectories( const char *gameName );
464  void Startup( void );
465  void SetRestrictions( void );
466  // some files can be obtained from directories without compromising si_pure
467  bool FileAllowedFromDir( const char *path );
468  // searches all the paks, no pure check
469  pack_t * GetPackForChecksum( int checksum, bool searchAddons = false );
470  // searches all the paks, no pure check
471  pack_t * FindPakForFileChecksum( const char *relativePath, int fileChecksum, bool bReference );
472  idFile_InZip * ReadFileFromZip( pack_t *pak, fileInPack_t *pakFile, const char *relativePath );
473  int GetFileChecksum( idFile *file );
475  addonInfo_t * ParseAddonDef( const char *buf, const int len );
476  void FollowAddonDependencies( pack_t *pak );
477 
478  static size_t CurlWriteFunction( void *ptr, size_t size, size_t nmemb, void *stream );
479  // curl_progress_callback in curl.h
480  static int CurlProgressFunction( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow );
481 };
482 
484 idCVar idFileSystemLocal::fs_debug( "fs_debug", "0", CVAR_SYSTEM | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
485 idCVar idFileSystemLocal::fs_copyfiles( "fs_copyfiles", "0", CVAR_SYSTEM | CVAR_INIT | CVAR_INTEGER, "", 0, 4, idCmdSystem::ArgCompletion_Integer<0,3> );
486 idCVar idFileSystemLocal::fs_basepath( "fs_basepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
487 idCVar idFileSystemLocal::fs_savepath( "fs_savepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
488 idCVar idFileSystemLocal::fs_cdpath( "fs_cdpath", "", CVAR_SYSTEM | CVAR_INIT, "" );
489 idCVar idFileSystemLocal::fs_devpath( "fs_devpath", "", CVAR_SYSTEM | CVAR_INIT, "" );
490 idCVar idFileSystemLocal::fs_game( "fs_game", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "mod path" );
491 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" );
492 #ifdef WIN32
493 idCVar idFileSystemLocal::fs_caseSensitiveOS( "fs_caseSensitiveOS", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
494 #else
495 idCVar idFileSystemLocal::fs_caseSensitiveOS( "fs_caseSensitiveOS", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
496 #endif
497 idCVar idFileSystemLocal::fs_searchAddons( "fs_searchAddons", "0", CVAR_SYSTEM | CVAR_BOOL, "search all addon pk4s ( disables addon functionality )" );
498 
501 
502 /*
503 ================
504 idFileSystemLocal::idFileSystemLocal
505 ================
506 */
508  searchPaths = NULL;
509  readCount = 0;
510  loadCount = 0;
511  loadStack = 0;
512  dir_cache_index = 0;
513  dir_cache_count = 0;
514  d3xp = 0;
515  loadedFileFromDir = false;
517  memset( &backgroundThread, 0, sizeof( backgroundThread ) );
518  addonPaks = NULL;
519 }
520 
521 /*
522 ================
523 idFileSystemLocal::HashFileName
524 
525 return a hash value for the filename
526 ================
527 */
528 long idFileSystemLocal::HashFileName( const char *fname ) const {
529  int i;
530  long hash;
531  char letter;
532 
533  hash = 0;
534  i = 0;
535  while( fname[i] != '\0' ) {
536  letter = idStr::ToLower( fname[i] );
537  if ( letter == '.' ) {
538  break; // don't include extension
539  }
540  if ( letter == '\\' ) {
541  letter = '/'; // damn path names
542  }
543  hash += (long)(letter) * (i+119);
544  i++;
545  }
546  hash &= (FILE_HASH_SIZE-1);
547  return hash;
548 }
549 
550 /*
551 ===========
552 idFileSystemLocal::FilenameCompare
553 
554 Ignore case and separator char distinctions
555 ===========
556 */
557 bool idFileSystemLocal::FilenameCompare( const char *s1, const char *s2 ) const {
558  int c1, c2;
559 
560  do {
561  c1 = *s1++;
562  c2 = *s2++;
563 
564  if ( c1 >= 'a' && c1 <= 'z' ) {
565  c1 -= ('a' - 'A');
566  }
567  if ( c2 >= 'a' && c2 <= 'z' ) {
568  c2 -= ('a' - 'A');
569  }
570 
571  if ( c1 == '\\' || c1 == ':' ) {
572  c1 = '/';
573  }
574  if ( c2 == '\\' || c2 == ':' ) {
575  c2 = '/';
576  }
577 
578  if ( c1 != c2 ) {
579  return true; // strings not equal
580  }
581  } while( c1 );
582 
583  return false; // strings are equal
584 }
585 
586 /*
587 ================
588 idFileSystemLocal::OpenOSFile
589 optional caseSensitiveName is set to case sensitive file name as found on disc (fs_caseSensitiveOS only)
590 ================
591 */
592 FILE *idFileSystemLocal::OpenOSFile( const char *fileName, const char *mode, idStr *caseSensitiveName ) {
593  int i;
594  FILE *fp;
595  idStr fpath, entry;
596  idStrList list;
597 
598 #ifndef __MWERKS__
599 #ifndef WIN32
600  // some systems will let you fopen a directory
601  struct stat buf;
602  if ( stat( fileName, &buf ) != -1 && !S_ISREG(buf.st_mode) ) {
603  return NULL;
604  }
605 #endif
606 #endif
607  fp = fopen( fileName, mode );
608  if ( !fp && fs_caseSensitiveOS.GetBool() ) {
609  fpath = fileName;
610  fpath.StripFilename();
611  fpath.StripTrailing( PATHSEPERATOR_CHAR );
612  if ( ListOSFiles( fpath, NULL, list ) == -1 ) {
613  return NULL;
614  }
615 
616  for ( i = 0; i < list.Num(); i++ ) {
617  entry = fpath + PATHSEPERATOR_CHAR + list[i];
618  if ( !entry.Icmp( fileName ) ) {
619  fp = fopen( entry, mode );
620  if ( fp ) {
621  if ( caseSensitiveName ) {
622  *caseSensitiveName = entry;
623  caseSensitiveName->StripPath();
624  }
625  if ( fs_debug.GetInteger() ) {
626  common->Printf( "idFileSystemLocal::OpenFileRead: changed %s to %s\n", fileName, entry.c_str() );
627  }
628  break;
629  } else {
630  // not supposed to happen if ListOSFiles is doing it's job correctly
631  common->Warning( "idFileSystemLocal::OpenFileRead: fs_caseSensitiveOS 1 could not open %s", entry.c_str() );
632  }
633  }
634  }
635  } else if ( caseSensitiveName ) {
636  *caseSensitiveName = fileName;
637  caseSensitiveName->StripPath();
638  }
639  return fp;
640 }
641 
642 /*
643 ================
644 idFileSystemLocal::OpenOSFileCorrectName
645 ================
646 */
648  idStr caseName;
649  FILE *f = OpenOSFile( path.c_str(), mode, &caseName );
650  if ( f ) {
651  path.StripFilename();
652  path += PATHSEPERATOR_STR;
653  path += caseName;
654  }
655  return f;
656 }
657 
658 /*
659 ================
660 idFileSystemLocal::DirectFileLength
661 ================
662 */
664  int pos;
665  int end;
666 
667  pos = ftell( o );
668  fseek( o, 0, SEEK_END );
669  end = ftell( o );
670  fseek( o, pos, SEEK_SET );
671  return end;
672 }
673 
674 /*
675 ============
676 idFileSystemLocal::CreateOSPath
677 
678 Creates any directories needed to store the given filename
679 ============
680 */
681 void idFileSystemLocal::CreateOSPath( const char *OSPath ) {
682  char *ofs;
683 
684  // make absolutely sure that it can't back up the path
685  // FIXME: what about c: ?
686  if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) {
687 #ifdef _DEBUG
688  common->DPrintf( "refusing to create relative path \"%s\"\n", OSPath );
689 #endif
690  return;
691  }
692 
693  idStr path( OSPath );
694  for( ofs = &path[ 1 ]; *ofs ; ofs++ ) {
695  if ( *ofs == PATHSEPERATOR_CHAR ) {
696  // create the directory
697  *ofs = 0;
698  Sys_Mkdir( path );
699  *ofs = PATHSEPERATOR_CHAR;
700  }
701  }
702 }
703 
704 /*
705 =================
706 idFileSystemLocal::CopyFile
707 
708 Copy a fully specified file from one place to another
709 =================
710 */
711 void idFileSystemLocal::CopyFile( const char *fromOSPath, const char *toOSPath ) {
712  FILE *f;
713  int len;
714  byte *buf;
715 
716  common->Printf( "copy %s to %s\n", fromOSPath, toOSPath );
717  f = OpenOSFile( fromOSPath, "rb" );
718  if ( !f ) {
719  return;
720  }
721  fseek( f, 0, SEEK_END );
722  len = ftell( f );
723  fseek( f, 0, SEEK_SET );
724 
725  buf = (byte *)Mem_Alloc( len );
726  if ( fread( buf, 1, len, f ) != (unsigned int)len ) {
727  common->FatalError( "short read in idFileSystemLocal::CopyFile()\n" );
728  }
729  fclose( f );
730 
731  CreateOSPath( toOSPath );
732  f = OpenOSFile( toOSPath, "wb" );
733  if ( !f ) {
734  common->Printf( "could not create destination file\n" );
735  Mem_Free( buf );
736  return;
737  }
738  if ( fwrite( buf, 1, len, f ) != (unsigned int)len ) {
739  common->FatalError( "short write in idFileSystemLocal::CopyFile()\n" );
740  }
741  fclose( f );
742  Mem_Free( buf );
743 }
744 
745 /*
746 =================
747 idFileSystemLocal::CopyFile
748 =================
749 */
750 void idFileSystemLocal::CopyFile( idFile *src, const char *toOSPath ) {
751  FILE *f;
752  int len;
753  byte *buf;
754 
755  common->Printf( "copy %s to %s\n", src->GetName(), toOSPath );
756  src->Seek( 0, FS_SEEK_END );
757  len = src->Tell();
758  src->Seek( 0, FS_SEEK_SET );
759 
760  buf = (byte *)Mem_Alloc( len );
761  if ( src->Read( buf, len ) != len ) {
762  common->FatalError( "Short read in idFileSystemLocal::CopyFile()\n" );
763  }
764 
765  CreateOSPath( toOSPath );
766  f = OpenOSFile( toOSPath, "wb" );
767  if ( !f ) {
768  common->Printf( "could not create destination file\n" );
769  Mem_Free( buf );
770  return;
771  }
772  if ( fwrite( buf, 1, len, f ) != (unsigned int)len ) {
773  common->FatalError( "Short write in idFileSystemLocal::CopyFile()\n" );
774  }
775  fclose( f );
776  Mem_Free( buf );
777 }
778 
779 /*
780 ====================
781 idFileSystemLocal::ReplaceSeparators
782 
783 Fix things up differently for win/unix/mac
784 ====================
785 */
787  char *s;
788 
789  for( s = &path[ 0 ]; *s ; s++ ) {
790  if ( *s == '/' || *s == '\\' ) {
791  *s = sep;
792  }
793  }
794 }
795 
796 /*
797 ===================
798 idFileSystemLocal::BuildOSPath
799 ===================
800 */
801 const char *idFileSystemLocal::BuildOSPath( const char *base, const char *game, const char *relativePath ) {
802  static char OSPath[MAX_STRING_CHARS];
803  idStr newPath;
804 
806  // extract the path, make sure it's all lowercase
807  idStr testPath, fileName;
808 
809  sprintf( testPath, "%s/%s", game , relativePath );
810  testPath.StripFilename();
811 
812  if ( testPath.HasUpper() ) {
813 
814  common->Warning( "Non-portable: path contains uppercase characters: %s", testPath.c_str() );
815 
816  // attempt a fixup on the fly
817  if ( fs_caseSensitiveOS.GetBool() ) {
818  testPath.ToLower();
819  fileName = relativePath;
820  fileName.StripPath();
821  sprintf( newPath, "%s/%s/%s", base, testPath.c_str(), fileName.c_str() );
822  ReplaceSeparators( newPath );
823  common->DPrintf( "Fixed up to %s\n", newPath.c_str() );
824  idStr::Copynz( OSPath, newPath, sizeof( OSPath ) );
825  return OSPath;
826  }
827  }
828  }
829 
830  idStr strBase = base;
831  strBase.StripTrailing( '/' );
832  strBase.StripTrailing( '\\' );
833  sprintf( newPath, "%s/%s/%s", strBase.c_str(), game, relativePath );
834  ReplaceSeparators( newPath );
835  idStr::Copynz( OSPath, newPath, sizeof( OSPath ) );
836  return OSPath;
837 }
838 
839 /*
840 ================
841 idFileSystemLocal::OSPathToRelativePath
842 
843 takes a full OS path, as might be found in data from a media creation
844 program, and converts it to a relativePath by stripping off directories
845 
846 Returns false if the osPath tree doesn't match any of the existing
847 search paths.
848 
849 ================
850 */
851 const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) {
852  static char relativePath[MAX_STRING_CHARS];
853  char *s, *base;
854 
855  // skip a drive letter?
856 
857  // search for anything with "base" in it
858  // Ase files from max may have the form of:
859  // "//Purgatory/purgatory/doom/base/models/mapobjects/bitch/hologirl.tga"
860  // which won't match any of our drive letter based search paths
861  bool ignoreWarning = false;
862 #ifdef ID_DEMO_BUILD
863  base = strstr( OSPath, BASE_GAMEDIR );
864  idStr tempStr = OSPath;
865  tempStr.ToLower();
866  if ( ( strstr( tempStr, "//" ) || strstr( tempStr, "w:" ) ) && strstr( tempStr, "/doom/base/") ) {
867  // will cause a warning but will load the file. ase models have
868  // hard coded doom/base/ in the material names
869  base = strstr( OSPath, "base" );
870  ignoreWarning = true;
871  }
872 #else
873  // look for the first complete directory name
874  base = (char *)strstr( OSPath, BASE_GAMEDIR );
875  while ( base ) {
876  char c1 = '\0', c2;
877  if ( base > OSPath ) {
878  c1 = *(base - 1);
879  }
880  c2 = *( base + strlen( BASE_GAMEDIR ) );
881  if ( ( c1 == '/' || c1 == '\\' ) && ( c2 == '/' || c2 == '\\' ) ) {
882  break;
883  }
884  base = strstr( base + 1, BASE_GAMEDIR );
885  }
886 #endif
887  // fs_game and fs_game_base support - look for first complete name with a mod path
888  // ( fs_game searched before fs_game_base )
889  const char *fsgame = NULL;
890  int igame = 0;
891  for ( igame = 0; igame < 2; igame++ ) {
892  if ( igame == 0 ) {
893  fsgame = fs_game.GetString();
894  } else if ( igame == 1 ) {
895  fsgame = fs_game_base.GetString();
896  }
897  if ( base == NULL && fsgame && strlen( fsgame ) ) {
898  base = (char *)strstr( OSPath, fsgame );
899  while ( base ) {
900  char c1 = '\0', c2;
901  if ( base > OSPath ) {
902  c1 = *(base - 1);
903  }
904  c2 = *( base + strlen( fsgame ) );
905  if ( ( c1 == '/' || c1 == '\\' ) && ( c2 == '/' || c2 == '\\' ) ) {
906  break;
907  }
908  base = strstr( base + 1, fsgame );
909  }
910  }
911  }
912 
913  if ( base ) {
914  s = strstr( base, "/" );
915  if ( !s ) {
916  s = strstr( base, "\\" );
917  }
918  if ( s ) {
919  strcpy( relativePath, s + 1 );
920  if ( fs_debug.GetInteger() > 1 ) {
921  common->Printf( "idFileSystem::OSPathToRelativePath: %s becomes %s\n", OSPath, relativePath );
922  }
923  return relativePath;
924  }
925  }
926 
927  if ( !ignoreWarning ) {
928  common->Warning( "idFileSystem::OSPathToRelativePath failed on %s", OSPath );
929  }
930  strcpy( relativePath, "" );
931  return relativePath;
932 }
933 
934 /*
935 =====================
936 idFileSystemLocal::RelativePathToOSPath
937 
938 Returns a fully qualified path that can be used with stdio libraries
939 =====================
940 */
941 const char *idFileSystemLocal::RelativePathToOSPath( const char *relativePath, const char *basePath ) {
942  const char *path = cvarSystem->GetCVarString( basePath );
943  if ( !path[0] ) {
944  path = fs_savepath.GetString();
945  }
946  return BuildOSPath( path, gameFolder, relativePath );
947 }
948 
949 /*
950 =================
951 idFileSystemLocal::RemoveFile
952 =================
953 */
954 void idFileSystemLocal::RemoveFile( const char *relativePath ) {
955  idStr OSPath;
956 
957  if ( fs_devpath.GetString()[0] ) {
958  OSPath = BuildOSPath( fs_devpath.GetString(), gameFolder, relativePath );
959  remove( OSPath );
960  }
961 
962  OSPath = BuildOSPath( fs_savepath.GetString(), gameFolder, relativePath );
963  remove( OSPath );
964 
965  ClearDirCache();
966 }
967 
968 /*
969 ================
970 idFileSystemLocal::FileIsInPAK
971 ================
972 */
973 bool idFileSystemLocal::FileIsInPAK( const char *relativePath ) {
974  searchpath_t *search;
975  pack_t *pak;
976  fileInPack_t *pakFile;
977  long hash;
978 
979  if ( !searchPaths ) {
980  common->FatalError( "Filesystem call made without initialization\n" );
981  }
982 
983  if ( !relativePath ) {
984  common->FatalError( "idFileSystemLocal::FileIsInPAK: NULL 'relativePath' parameter passed\n" );
985  }
986 
987  // qpaths are not supposed to have a leading slash
988  if ( relativePath[0] == '/' || relativePath[0] == '\\' ) {
989  relativePath++;
990  }
991 
992  // make absolutely sure that it can't back up the path.
993  // The searchpaths do guarantee that something will always
994  // be prepended, so we don't need to worry about "c:" or "//limbo"
995  if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) {
996  return false;
997  }
998 
999  //
1000  // search through the path, one element at a time
1001  //
1002 
1003  hash = HashFileName( relativePath );
1004 
1005  for ( search = searchPaths; search; search = search->next ) {
1006  // is the element a pak file?
1007  if ( search->pack && search->pack->hashTable[hash] ) {
1008 
1009  // disregard if it doesn't match one of the allowed pure pak files - or is a localization file
1010  if ( serverPaks.Num() ) {
1011  GetPackStatus( search->pack );
1012  if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
1013  continue; // not on the pure server pak list
1014  }
1015  }
1016 
1017  // look through all the pak file elements
1018  pak = search->pack;
1019  pakFile = pak->hashTable[hash];
1020  do {
1021  // case and separator insensitive comparisons
1022  if ( !FilenameCompare( pakFile->name, relativePath ) ) {
1023  return true;
1024  }
1025  pakFile = pakFile->next;
1026  } while( pakFile != NULL );
1027  }
1028  }
1029  return false;
1030 }
1031 
1032 /*
1033 ============
1034 idFileSystemLocal::ReadFile
1035 
1036 Filename are relative to the search path
1037 a null buffer will just return the file length and time without loading
1038 timestamp can be NULL if not required
1039 ============
1040 */
1041 int idFileSystemLocal::ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp ) {
1042  idFile * f;
1043  byte * buf;
1044  int len;
1045  bool isConfig;
1046 
1047  if ( !searchPaths ) {
1048  common->FatalError( "Filesystem call made without initialization\n" );
1049  }
1050 
1051  if ( !relativePath || !relativePath[0] ) {
1052  common->FatalError( "idFileSystemLocal::ReadFile with empty name\n" );
1053  }
1054 
1055  if ( timestamp ) {
1056  *timestamp = FILE_NOT_FOUND_TIMESTAMP;
1057  }
1058 
1059  if ( buffer ) {
1060  *buffer = NULL;
1061  }
1062 
1063  buf = NULL; // quiet compiler warning
1064 
1065  // if this is a .cfg file and we are playing back a journal, read
1066  // it from the journal file
1067  if ( strstr( relativePath, ".cfg" ) == relativePath + strlen( relativePath ) - 4 ) {
1068  isConfig = true;
1069  if ( eventLoop && eventLoop->JournalLevel() == 2 ) {
1070  int r;
1071 
1072  loadCount++;
1073  loadStack++;
1074 
1075  common->DPrintf( "Loading %s from journal file.\n", relativePath );
1076  len = 0;
1077  r = eventLoop->com_journalDataFile->Read( &len, sizeof( len ) );
1078  if ( r != sizeof( len ) ) {
1079  *buffer = NULL;
1080  return -1;
1081  }
1082  buf = (byte *)Mem_ClearedAlloc(len+1);
1083  *buffer = buf;
1084  r = eventLoop->com_journalDataFile->Read( buf, len );
1085  if ( r != len ) {
1086  common->FatalError( "Read from journalDataFile failed" );
1087  }
1088 
1089  // guarantee that it will have a trailing 0 for string operations
1090  buf[len] = 0;
1091 
1092  return len;
1093  }
1094  } else {
1095  isConfig = false;
1096  }
1097 
1098  // look for it in the filesystem or pack files
1099  f = OpenFileRead( relativePath, ( buffer != NULL ) );
1100  if ( f == NULL ) {
1101  if ( buffer ) {
1102  *buffer = NULL;
1103  }
1104  return -1;
1105  }
1106  len = f->Length();
1107 
1108  if ( timestamp ) {
1109  *timestamp = f->Timestamp();
1110  }
1111 
1112  if ( !buffer ) {
1113  CloseFile( f );
1114  return len;
1115  }
1116 
1117  loadCount++;
1118  loadStack++;
1119 
1120  buf = (byte *)Mem_ClearedAlloc(len+1);
1121  *buffer = buf;
1122 
1123  f->Read( buf, len );
1124 
1125  // guarantee that it will have a trailing 0 for string operations
1126  buf[len] = 0;
1127  CloseFile( f );
1128 
1129  // if we are journalling and it is a config file, write it to the journal file
1130  if ( isConfig && eventLoop && eventLoop->JournalLevel() == 1 ) {
1131  common->DPrintf( "Writing %s to journal file.\n", relativePath );
1132  eventLoop->com_journalDataFile->Write( &len, sizeof( len ) );
1133  eventLoop->com_journalDataFile->Write( buf, len );
1135  }
1136 
1137  return len;
1138 }
1139 
1140 /*
1141 =============
1142 idFileSystemLocal::FreeFile
1143 =============
1144 */
1146  if ( !searchPaths ) {
1147  common->FatalError( "Filesystem call made without initialization\n" );
1148  }
1149  if ( !buffer ) {
1150  common->FatalError( "idFileSystemLocal::FreeFile( NULL )" );
1151  }
1152  loadStack--;
1153 
1154  Mem_Free( buffer );
1155 }
1156 
1157 /*
1158 ============
1159 idFileSystemLocal::WriteFile
1160 
1161 Filenames are relative to the search path
1162 ============
1163 */
1164 int idFileSystemLocal::WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath ) {
1165  idFile *f;
1166 
1167  if ( !searchPaths ) {
1168  common->FatalError( "Filesystem call made without initialization\n" );
1169  }
1170 
1171  if ( !relativePath || !buffer ) {
1172  common->FatalError( "idFileSystemLocal::WriteFile: NULL parameter" );
1173  }
1174 
1175  f = idFileSystemLocal::OpenFileWrite( relativePath, basePath );
1176  if ( !f ) {
1177  common->Printf( "Failed to open %s\n", relativePath );
1178  return -1;
1179  }
1180 
1181  size = f->Write( buffer, size );
1182 
1183  CloseFile( f );
1184 
1185  return size;
1186 }
1187 
1188 /*
1189 =================
1190 idFileSystemLocal::ParseAddonDef
1191 =================
1192 */
1193 addonInfo_t *idFileSystemLocal::ParseAddonDef( const char *buf, const int len ) {
1194  idLexer src;
1195  idToken token, token2;
1196  addonInfo_t *info;
1197 
1198  src.LoadMemory( buf, len, "<addon.conf>" );
1199  src.SetFlags( DECL_LEXER_FLAGS );
1200  if ( !src.SkipUntilString( "addonDef" ) ) {
1201  src.Warning( "ParseAddonDef: no addonDef" );
1202  return NULL;
1203  }
1204  if ( !src.ReadToken( &token ) ) {
1205  src.Warning( "Expected {" );
1206  return NULL;
1207  }
1208  info = new addonInfo_t;
1209  // read addonDef
1210  while ( 1 ) {
1211  if ( !src.ReadToken( &token ) ) {
1212  delete info;
1213  return NULL;
1214  }
1215  if ( !token.Icmp( "}" ) ) {
1216  break;
1217  }
1218  if ( token.type != TT_STRING ) {
1219  src.Warning( "Expected quoted string, but found '%s'", token.c_str() );
1220  delete info;
1221  return NULL;
1222  }
1223  int checksum;
1224  if ( sscanf( token.c_str(), "0x%x", &checksum ) != 1 && sscanf( token.c_str(), "%x", &checksum ) != 1 ) {
1225  src.Warning( "Could not parse checksum '%s'", token.c_str() );
1226  delete info;
1227  return NULL;
1228  }
1229  info->depends.Append( checksum );
1230  }
1231  // read any number of mapDef entries
1232  while ( 1 ) {
1233  if ( !src.SkipUntilString( "mapDef" ) ) {
1234  return info;
1235  }
1236  if ( !src.ReadToken( &token ) ) {
1237  src.Warning( "Expected map path" );
1238  info->mapDecls.DeleteContents( true );
1239  delete info;
1240  return NULL;
1241  }
1242  idDict *dict = new idDict;
1243  dict->Set( "path", token.c_str() );
1244  if ( !src.ReadToken( &token ) ) {
1245  src.Warning( "Expected {" );
1246  info->mapDecls.DeleteContents( true );
1247  delete dict;
1248  delete info;
1249  return NULL;
1250  }
1251  while ( 1 ) {
1252  if ( !src.ReadToken( &token ) ) {
1253  break;
1254  }
1255  if ( !token.Icmp( "}" ) ) {
1256  break;
1257  }
1258  if ( token.type != TT_STRING ) {
1259  src.Warning( "Expected quoted string, but found '%s'", token.c_str() );
1260  info->mapDecls.DeleteContents( true );
1261  delete dict;
1262  delete info;
1263  return NULL;
1264  }
1265 
1266  if ( !src.ReadToken( &token2 ) ) {
1267  src.Warning( "Unexpected end of file" );
1268  info->mapDecls.DeleteContents( true );
1269  delete dict;
1270  delete info;
1271  return NULL;
1272  }
1273 
1274  if ( dict->FindKey( token ) ) {
1275  src.Warning( "'%s' already defined", token.c_str() );
1276  }
1277  dict->Set( token, token2 );
1278  }
1279  info->mapDecls.Append( dict );
1280  }
1281  assert( false );
1282  return NULL;
1283 }
1284 
1285 /*
1286 =================
1287 idFileSystemLocal::LoadZipFile
1288 =================
1289 */
1290 pack_t *idFileSystemLocal::LoadZipFile( const char *zipfile ) {
1291  fileInPack_t * buildBuffer;
1292  pack_t * pack;
1293  unzFile uf;
1294  int err;
1295  unz_global_info gi;
1296  char filename_inzip[MAX_ZIPPED_FILE_NAME];
1297  unz_file_info file_info;
1298  int i;
1299  long hash;
1300  int fs_numHeaderLongs;
1301  int * fs_headerLongs;
1302  FILE *f;
1303  int len;
1304  int confHash;
1305  fileInPack_t *pakFile;
1306 
1307  f = OpenOSFile( zipfile, "rb" );
1308  if ( !f ) {
1309  return NULL;
1310  }
1311  fseek( f, 0, SEEK_END );
1312  len = ftell( f );
1313  fclose( f );
1314 
1315  fs_numHeaderLongs = 0;
1316 
1317  uf = unzOpen( zipfile );
1318  err = unzGetGlobalInfo( uf, &gi );
1319 
1320  if ( err != UNZ_OK ) {
1321  return NULL;
1322  }
1323 
1324  buildBuffer = new fileInPack_t[gi.number_entry];
1325  pack = new pack_t;
1326  for( i = 0; i < FILE_HASH_SIZE; i++ ) {
1327  pack->hashTable[i] = NULL;
1328  }
1329 
1330  pack->pakFilename = zipfile;
1331  pack->handle = uf;
1332  pack->numfiles = gi.number_entry;
1333  pack->buildBuffer = buildBuffer;
1334  pack->referenced = false;
1335  pack->binary = BINARY_UNKNOWN;
1336  pack->addon = false;
1337  pack->addon_search = false;
1338  pack->addon_info = NULL;
1339  pack->pureStatus = PURE_UNKNOWN;
1340  pack->isNew = false;
1341 
1342  pack->length = len;
1343 
1344  unzGoToFirstFile(uf);
1345  fs_headerLongs = (int *)Mem_ClearedAlloc( gi.number_entry * sizeof(int) );
1346  for ( i = 0; i < (int)gi.number_entry; i++ ) {
1347  err = unzGetCurrentFileInfo( uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0 );
1348  if ( err != UNZ_OK ) {
1349  break;
1350  }
1351  if ( file_info.uncompressed_size > 0 ) {
1352  fs_headerLongs[fs_numHeaderLongs++] = LittleLong( file_info.crc );
1353  }
1354  hash = HashFileName( filename_inzip );
1355  buildBuffer[i].name = filename_inzip;
1356  buildBuffer[i].name.ToLower();
1357  buildBuffer[i].name.BackSlashesToSlashes();
1358  // store the file position in the zip
1359  unzGetCurrentFileInfoPosition( uf, &buildBuffer[i].pos );
1360  // add the file to the hash
1361  buildBuffer[i].next = pack->hashTable[hash];
1362  pack->hashTable[hash] = &buildBuffer[i];
1363  // go to the next file in the zip
1364  unzGoToNextFile(uf);
1365  }
1366 
1367  // check if this is an addon pak
1368  pack->addon = false;
1369  confHash = HashFileName( ADDON_CONFIG );
1370  for ( pakFile = pack->hashTable[confHash]; pakFile; pakFile = pakFile->next ) {
1371  if ( !FilenameCompare( pakFile->name, ADDON_CONFIG ) ) {
1372  pack->addon = true;
1373  idFile_InZip *file = ReadFileFromZip( pack, pakFile, ADDON_CONFIG );
1374  // may be just an empty file if you don't bother about the mapDef
1375  if ( file && file->Length() ) {
1376  char *buf;
1377  buf = new char[ file->Length() + 1 ];
1378  file->Read( (void *)buf, file->Length() );
1379  buf[ file->Length() ] = '\0';
1380  pack->addon_info = ParseAddonDef( buf, file->Length() );
1381  delete[] buf;
1382  }
1383  if ( file ) {
1384  CloseFile( file );
1385  }
1386  break;
1387  }
1388  }
1389 
1390  pack->checksum = MD4_BlockChecksum( fs_headerLongs, 4 * fs_numHeaderLongs );
1391  pack->checksum = LittleLong( pack->checksum );
1392 
1393  Mem_Free( fs_headerLongs );
1394 
1395  return pack;
1396 }
1397 
1398 /*
1399 ===============
1400 idFileSystemLocal::AddZipFile
1401 adds a downloaded pak file to the list so we can work out what we have and what we still need
1402 the isNew flag is set to true, indicating that we cannot add this pak to the search lists without a restart
1403 ===============
1404 */
1406  idStr fullpath = fs_savepath.GetString();
1407  pack_t *pak;
1408  searchpath_t *search, *last;
1409 
1410  fullpath.AppendPath( path );
1411  pak = LoadZipFile( fullpath );
1412  if ( !pak ) {
1413  common->Warning( "AddZipFile %s failed\n", path );
1414  return 0;
1415  }
1416  // insert the pak at the end of the search list - temporary until we restart
1417  pak->isNew = true;
1418  search = new searchpath_t;
1419  search->dir = NULL;
1420  search->pack = pak;
1421  search->next = NULL;
1422  last = searchPaths;
1423  while ( last->next ) {
1424  last = last->next;
1425  }
1426  last->next = search;
1427  common->Printf( "Appended pk4 %s with checksum 0x%x\n", pak->pakFilename.c_str(), pak->checksum );
1428  return pak->checksum;
1429 }
1430 
1431 /*
1432 ===============
1433 idFileSystemLocal::AddUnique
1434 ===============
1435 */
1436 int idFileSystemLocal::AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const {
1437  int i, hashKey;
1438 
1439  hashKey = hashIndex.GenerateKey( name );
1440  for ( i = hashIndex.First( hashKey ); i >= 0; i = hashIndex.Next( i ) ) {
1441  if ( list[i].Icmp( name ) == 0 ) {
1442  return i;
1443  }
1444  }
1445  i = list.Append( name );
1446  hashIndex.Add( hashKey, i );
1447  return i;
1448 }
1449 
1450 /*
1451 ===============
1452 idFileSystemLocal::GetExtensionList
1453 ===============
1454 */
1455 void idFileSystemLocal::GetExtensionList( const char *extension, idStrList &extensionList ) const {
1456  int s, e, l;
1457 
1458  l = idStr::Length( extension );
1459  s = 0;
1460  while( 1 ) {
1461  e = idStr::FindChar( extension, '|', s, l );
1462  if ( e != -1 ) {
1463  extensionList.Append( idStr( extension, s, e ) );
1464  s = e + 1;
1465  } else {
1466  extensionList.Append( idStr( extension, s, l ) );
1467  break;
1468  }
1469  }
1470 }
1471 
1472 /*
1473 ===============
1474 idFileSystemLocal::GetFileList
1475 
1476 Does not clear the list first so this can be used to progressively build a file list.
1477 When 'sort' is true only the new files added to the list are sorted.
1478 ===============
1479 */
1480 int idFileSystemLocal::GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir ) {
1481  searchpath_t * search;
1482  fileInPack_t * buildBuffer;
1483  int i, j;
1484  int pathLength;
1485  int length;
1486  const char * name;
1487  pack_t * pak;
1488  idStr work;
1489 
1490  if ( !searchPaths ) {
1491  common->FatalError( "Filesystem call made without initialization\n" );
1492  }
1493 
1494  if ( !extensions.Num() ) {
1495  return 0;
1496  }
1497 
1498  if ( !relativePath ) {
1499  return 0;
1500  }
1501  pathLength = strlen( relativePath );
1502  if ( pathLength ) {
1503  pathLength++; // for the trailing '/'
1504  }
1505 
1506  // search through the path, one element at a time, adding to list
1507  for( search = searchPaths; search != NULL; search = search->next ) {
1508  if ( search->dir ) {
1509  if(gamedir && strlen(gamedir)) {
1510  if(search->dir->gamedir != gamedir) {
1511  continue;
1512  }
1513  }
1514 
1515  idStrList sysFiles;
1516  idStr netpath;
1517 
1518  netpath = BuildOSPath( search->dir->path, search->dir->gamedir, relativePath );
1519 
1520  for ( i = 0; i < extensions.Num(); i++ ) {
1521 
1522  // scan for files in the filesystem
1523  ListOSFiles( netpath, extensions[i], sysFiles );
1524 
1525  // if we are searching for directories, remove . and ..
1526  if ( extensions[i][0] == '/' && extensions[i][1] == 0 ) {
1527  sysFiles.Remove( "." );
1528  sysFiles.Remove( ".." );
1529  }
1530 
1531  for( j = 0; j < sysFiles.Num(); j++ ) {
1532  // unique the match
1533  if ( fullRelativePath ) {
1534  work = relativePath;
1535  work += "/";
1536  work += sysFiles[j];
1537  AddUnique( work, list, hashIndex );
1538  }
1539  else {
1540  AddUnique( sysFiles[j], list, hashIndex );
1541  }
1542  }
1543  }
1544  } else if ( search->pack ) {
1545  // look through all the pak file elements
1546 
1547  // exclude any extra packs if we have server paks to search
1548  if ( serverPaks.Num() ) {
1549  GetPackStatus( search->pack );
1550  if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
1551  continue; // not on the pure server pak list
1552  }
1553  }
1554 
1555  pak = search->pack;
1556  buildBuffer = pak->buildBuffer;
1557  for( i = 0; i < pak->numfiles; i++ ) {
1558 
1559  length = buildBuffer[i].name.Length();
1560 
1561  // if the name is not long anough to at least contain the path
1562  if ( length <= pathLength ) {
1563  continue;
1564  }
1565 
1566  name = buildBuffer[i].name;
1567 
1568 
1569  // check for a path match without the trailing '/'
1570  if ( pathLength && idStr::Icmpn( name, relativePath, pathLength - 1 ) != 0 ) {
1571  continue;
1572  }
1573 
1574  // ensure we have a path, and not just a filename containing the path
1575  if ( name[ pathLength ] == '\0' || name[pathLength - 1] != '/' ) {
1576  continue;
1577  }
1578 
1579  // make sure the file is not in a subdirectory
1580  for ( j = pathLength; name[j+1] != '\0'; j++ ) {
1581  if ( name[j] == '/' ) {
1582  break;
1583  }
1584  }
1585  if ( name[j+1] ) {
1586  continue;
1587  }
1588 
1589  // check for extension match
1590  for ( j = 0; j < extensions.Num(); j++ ) {
1591  if ( length >= extensions[j].Length() && extensions[j].Icmp( name + length - extensions[j].Length() ) == 0 ) {
1592  break;
1593  }
1594  }
1595  if ( j >= extensions.Num() ) {
1596  continue;
1597  }
1598 
1599  // unique the match
1600  if ( fullRelativePath ) {
1601  work = relativePath;
1602  work += "/";
1603  work += name + pathLength;
1604  work.StripTrailing( '/' );
1605  AddUnique( work, list, hashIndex );
1606  } else {
1607  work = name + pathLength;
1608  work.StripTrailing( '/' );
1609  AddUnique( work, list, hashIndex );
1610  }
1611  }
1612  }
1613  }
1614 
1615  return list.Num();
1616 }
1617 
1618 /*
1619 ===============
1620 idFileSystemLocal::ListFiles
1621 ===============
1622 */
1623 idFileList *idFileSystemLocal::ListFiles( const char *relativePath, const char *extension, bool sort, bool fullRelativePath, const char* gamedir ) {
1624  idHashIndex hashIndex( 4096, 4096 );
1625  idStrList extensionList;
1626 
1627  idFileList *fileList = new idFileList;
1628  fileList->basePath = relativePath;
1629 
1630  GetExtensionList( extension, extensionList );
1631 
1632  GetFileList( relativePath, extensionList, fileList->list, hashIndex, fullRelativePath, gamedir );
1633 
1634  if ( sort ) {
1635  idStrListSortPaths( fileList->list );
1636  }
1637 
1638  return fileList;
1639 }
1640 
1641 /*
1642 ===============
1643 idFileSystemLocal::GetFileListTree
1644 ===============
1645 */
1646 int idFileSystemLocal::GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir ) {
1647  int i;
1648  idStrList slash, folders( 128 );
1649  idHashIndex folderHashIndex( 1024, 128 );
1650 
1651  // recurse through the subdirectories
1652  slash.Append( "/" );
1653  GetFileList( relativePath, slash, folders, folderHashIndex, true, gamedir );
1654  for ( i = 0; i < folders.Num(); i++ ) {
1655  if ( folders[i][0] == '.' ) {
1656  continue;
1657  }
1658  if ( folders[i].Icmp( relativePath ) == 0 ){
1659  continue;
1660  }
1661  GetFileListTree( folders[i], extensions, list, hashIndex, gamedir );
1662  }
1663 
1664  // list files in the current directory
1665  GetFileList( relativePath, extensions, list, hashIndex, true, gamedir );
1666 
1667  return list.Num();
1668 }
1669 
1670 /*
1671 ===============
1672 idFileSystemLocal::ListFilesTree
1673 ===============
1674 */
1675 idFileList *idFileSystemLocal::ListFilesTree( const char *relativePath, const char *extension, bool sort, const char* gamedir ) {
1676  idHashIndex hashIndex( 4096, 4096 );
1677  idStrList extensionList;
1678 
1679  idFileList *fileList = new idFileList();
1680  fileList->basePath = relativePath;
1681  fileList->list.SetGranularity( 4096 );
1682 
1683  GetExtensionList( extension, extensionList );
1684 
1685  GetFileListTree( relativePath, extensionList, fileList->list, hashIndex, gamedir );
1686 
1687  if ( sort ) {
1688  idStrListSortPaths( fileList->list );
1689  }
1690 
1691  return fileList;
1692 }
1693 
1694 /*
1695 ===============
1696 idFileSystemLocal::FreeFileList
1697 ===============
1698 */
1700  delete fileList;
1701 }
1702 
1703 /*
1704 ===============
1705 idFileSystemLocal::ListMods
1706 ===============
1707 */
1709  int i;
1710  const int MAX_DESCRIPTION = 256;
1711  char desc[ MAX_DESCRIPTION ];
1712 
1713  idStrList dirs;
1714  idStrList pk4s;
1715 
1716  idModList *list = new idModList;
1717 
1718  const char *search[ 4 ];
1719  int isearch;
1720 
1721  search[0] = fs_savepath.GetString();
1722  search[1] = fs_devpath.GetString();
1723  search[2] = fs_basepath.GetString();
1724  search[3] = fs_cdpath.GetString();
1725 
1726  for ( isearch = 0; isearch < 4; isearch++ ) {
1727 
1728  dirs.Clear();
1729  pk4s.Clear();
1730 
1731  // scan for directories
1732  ListOSFiles( search[ isearch ], "/", dirs );
1733 
1734  dirs.Remove( "." );
1735  dirs.Remove( ".." );
1736  dirs.Remove( "base" );
1737  dirs.Remove( "pb" );
1738 
1739  // see if there are any pk4 files in each directory
1740  for( i = 0; i < dirs.Num(); i++ ) {
1741  idStr gamepath = BuildOSPath( search[ isearch ], dirs[ i ], "" );
1742  ListOSFiles( gamepath, ".pk4", pk4s );
1743  if ( pk4s.Num() ) {
1744  if ( !list->mods.Find( dirs[ i ] ) ) {
1745  // D3 1.3 #31, only list d3xp if the pak is present
1746  if ( dirs[ i ].Icmp( "d3xp" ) || HasD3XP() ) {
1747  list->mods.Append( dirs[ i ] );
1748  }
1749  }
1750  }
1751  }
1752  }
1753 
1754  list->mods.Sort();
1755 
1756  // read the descriptions for each mod - search all paths
1757  for ( i = 0; i < list->mods.Num(); i++ ) {
1758 
1759  for ( isearch = 0; isearch < 4; isearch++ ) {
1760 
1761  idStr descfile = BuildOSPath( search[ isearch ], list->mods[ i ], "description.txt" );
1762  FILE *f = OpenOSFile( descfile, "r" );
1763  if ( f ) {
1764  if ( fgets( desc, MAX_DESCRIPTION, f ) ) {
1765  list->descriptions.Append( desc );
1766  fclose( f );
1767  break;
1768  } else {
1769  common->DWarning( "Error reading %s", descfile.c_str() );
1770  fclose( f );
1771  continue;
1772  }
1773  }
1774  }
1775 
1776  if ( isearch == 4 ) {
1777  list->descriptions.Append( list->mods[ i ] );
1778  }
1779  }
1780 
1781  list->mods.Insert( "" );
1782  list->descriptions.Insert( "Doom 3" );
1783 
1784  assert( list->mods.Num() == list->descriptions.Num() );
1785 
1786  return list;
1787 }
1788 
1789 /*
1790 ===============
1791 idFileSystemLocal::FreeModList
1792 ===============
1793 */
1795  delete modList;
1796 }
1797 
1798 /*
1799 ===============
1800 idDEntry::Matches
1801 ===============
1802 */
1803 bool idDEntry::Matches(const char *directory, const char *extension) const {
1804  if ( !idDEntry::directory.Icmp( directory ) && !idDEntry::extension.Icmp( extension ) ) {
1805  return true;
1806  }
1807  return false;
1808 }
1809 
1810 /*
1811 ===============
1812 idDEntry::Init
1813 ===============
1814 */
1815 void idDEntry::Init( const char *directory, const char *extension, const idStrList &list ) {
1818  idStrList::operator=(list);
1819 }
1820 
1821 /*
1822 ===============
1823 idDEntry::Clear
1824 ===============
1825 */
1826 void idDEntry::Clear( void ) {
1827  directory.Clear();
1828  extension.Clear();
1829  idStrList::Clear();
1830 }
1831 
1832 /*
1833 ===============
1834 idFileSystemLocal::ListOSFiles
1835 
1836  call to the OS for a listing of files in an OS directory
1837  optionally, perform some caching of the entries
1838 ===============
1839 */
1840 int idFileSystemLocal::ListOSFiles( const char *directory, const char *extension, idStrList &list ) {
1841  int i, j, ret;
1842 
1843  if ( !extension ) {
1844  extension = "";
1845  }
1846 
1847  if ( !fs_caseSensitiveOS.GetBool() ) {
1848  return Sys_ListFiles( directory, extension, list );
1849  }
1850 
1851  // try in cache
1852  i = dir_cache_index - 1;
1853  while( i >= dir_cache_index - dir_cache_count ) {
1854  j = (i+MAX_CACHED_DIRS) % MAX_CACHED_DIRS;
1855  if ( dir_cache[j].Matches( directory, extension ) ) {
1856  if ( fs_debug.GetInteger() ) {
1857  //common->Printf( "idFileSystemLocal::ListOSFiles: cache hit: %s\n", directory );
1858  }
1859  list = dir_cache[j];
1860  return list.Num();
1861  }
1862  i--;
1863  }
1864 
1865  if ( fs_debug.GetInteger() ) {
1866  //common->Printf( "idFileSystemLocal::ListOSFiles: cache miss: %s\n", directory );
1867  }
1868 
1869  ret = Sys_ListFiles( directory, extension, list );
1870 
1871  if ( ret == -1 ) {
1872  return -1;
1873  }
1874 
1875  // push a new entry
1876  dir_cache[dir_cache_index].Init( directory, extension, list );
1878  if ( dir_cache_count < MAX_CACHED_DIRS ) {
1879  dir_cache_count++;
1880  }
1881 
1882  return ret;
1883 }
1884 
1885 /*
1886 ================
1887 idFileSystemLocal::Dir_f
1888 ================
1889 */
1891  idStr relativePath;
1892  idStr extension;
1893  idFileList *fileList;
1894  int i;
1895 
1896  if ( args.Argc() < 2 || args.Argc() > 3 ) {
1897  common->Printf( "usage: dir <directory> [extension]\n" );
1898  return;
1899  }
1900 
1901  if ( args.Argc() == 2 ) {
1902  relativePath = args.Argv( 1 );
1903  extension = "";
1904  }
1905  else {
1906  relativePath = args.Argv( 1 );
1907  extension = args.Argv( 2 );
1908  if ( extension[0] != '.' ) {
1909  common->Warning( "extension should have a leading dot" );
1910  }
1911  }
1912  relativePath.BackSlashesToSlashes();
1913  relativePath.StripTrailing( '/' );
1914 
1915  common->Printf( "Listing of %s/*%s\n", relativePath.c_str(), extension.c_str() );
1916  common->Printf( "---------------\n" );
1917 
1918  fileList = fileSystemLocal.ListFiles( relativePath, extension );
1919 
1920  for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
1921  common->Printf( "%s\n", fileList->GetFile( i ) );
1922  }
1923  common->Printf( "%d files\n", fileList->list.Num() );
1924 
1925  fileSystemLocal.FreeFileList( fileList );
1926 }
1927 
1928 /*
1929 ================
1930 idFileSystemLocal::DirTree_f
1931 ================
1932 */
1934  idStr relativePath;
1935  idStr extension;
1936  idFileList *fileList;
1937  int i;
1938 
1939  if ( args.Argc() < 2 || args.Argc() > 3 ) {
1940  common->Printf( "usage: dirtree <directory> [extension]\n" );
1941  return;
1942  }
1943 
1944  if ( args.Argc() == 2 ) {
1945  relativePath = args.Argv( 1 );
1946  extension = "";
1947  }
1948  else {
1949  relativePath = args.Argv( 1 );
1950  extension = args.Argv( 2 );
1951  if ( extension[0] != '.' ) {
1952  common->Warning( "extension should have a leading dot" );
1953  }
1954  }
1955  relativePath.BackSlashesToSlashes();
1956  relativePath.StripTrailing( '/' );
1957 
1958  common->Printf( "Listing of %s/*%s /s\n", relativePath.c_str(), extension.c_str() );
1959  common->Printf( "---------------\n" );
1960 
1961  fileList = fileSystemLocal.ListFilesTree( relativePath, extension );
1962 
1963  for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
1964  common->Printf( "%s\n", fileList->GetFile( i ) );
1965  }
1966  common->Printf( "%d files\n", fileList->list.Num() );
1967 
1968  fileSystemLocal.FreeFileList( fileList );
1969 }
1970 
1971 /*
1972 ============
1973 idFileSystemLocal::Path_f
1974 ============
1975 */
1977  searchpath_t *sp;
1978  int i;
1979  idStr status;
1980 
1981  common->Printf( "Current search path:\n" );
1982  for ( sp = fileSystemLocal.searchPaths; sp; sp = sp->next ) {
1983  if ( sp->pack ) {
1984  if ( com_developer.GetBool() ) {
1985  sprintf( status, "%s (%i files - 0x%x %s", sp->pack->pakFilename.c_str(), sp->pack->numfiles, sp->pack->checksum, sp->pack->referenced ? "referenced" : "not referenced" );
1986  if ( sp->pack->addon ) {
1987  status += " - addon)\n";
1988  } else {
1989  status += ")\n";
1990  }
1991  common->Printf( status.c_str() );
1992  } else {
1993  common->Printf( "%s (%i files)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles );
1994  }
1995  if ( fileSystemLocal.serverPaks.Num() ) {
1996  if ( fileSystemLocal.serverPaks.Find( sp->pack ) ) {
1997  common->Printf( " on the pure list\n" );
1998  } else {
1999  common->Printf( " not on the pure list\n" );
2000  }
2001  }
2002  } else {
2003  common->Printf( "%s/%s\n", sp->dir->path.c_str(), sp->dir->gamedir.c_str() );
2004  }
2005  }
2006  common->Printf( "game DLL: 0x%x in pak: 0x%x\n", fileSystemLocal.gameDLLChecksum, fileSystemLocal.gamePakChecksum );
2007 #if ID_FAKE_PURE
2008  common->Printf( "Note: ID_FAKE_PURE is enabled\n" );
2009 #endif
2010  for( i = 0; i < MAX_GAME_OS; i++ ) {
2011  if ( fileSystemLocal.gamePakForOS[ i ] ) {
2012  common->Printf( "OS %d - pak 0x%x\n", i, fileSystemLocal.gamePakForOS[ i ] );
2013  }
2014  }
2015  // show addon packs that are *not* in the search lists
2016  common->Printf( "Addon pk4s:\n" );
2017  for ( sp = fileSystemLocal.addonPaks; sp; sp = sp->next ) {
2018  if ( com_developer.GetBool() ) {
2019  common->Printf( "%s (%i files - 0x%x)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles, sp->pack->checksum );
2020  } else {
2021  common->Printf( "%s (%i files)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles );
2022  }
2023  }
2024 }
2025 
2026 /*
2027 ============
2028 idFileSystemLocal::GetOSMask
2029 ============
2030 */
2032  int i, ret = 0;
2033  for( i = 0; i < MAX_GAME_OS; i++ ) {
2034  if ( fileSystemLocal.gamePakForOS[ i ] ) {
2035  ret |= ( 1 << i );
2036  }
2037  }
2038  if ( !ret ) {
2039  return -1;
2040  }
2041  return ret;
2042 }
2043 
2044 /*
2045 ============
2046 idFileSystemLocal::TouchFile_f
2047 
2048 The only purpose of this function is to allow game script files to copy
2049 arbitrary files furing an "fs_copyfiles 1" run.
2050 ============
2051 */
2053  idFile *f;
2054 
2055  if ( args.Argc() != 2 ) {
2056  common->Printf( "Usage: touchFile <file>\n" );
2057  return;
2058  }
2059 
2060  f = fileSystemLocal.OpenFileRead( args.Argv( 1 ) );
2061  if ( f ) {
2062  fileSystemLocal.CloseFile( f );
2063  }
2064 }
2065 
2066 /*
2067 ============
2068 idFileSystemLocal::TouchFileList_f
2069 
2070 Takes a text file and touches every file in it, use one file per line.
2071 ============
2072 */
2074 
2075  if ( args.Argc() != 2 ) {
2076  common->Printf( "Usage: touchFileList <filename>\n" );
2077  return;
2078  }
2079 
2080  const char *buffer = NULL;
2082  if ( fileSystem->ReadFile( args.Argv( 1 ), ( void** )&buffer, NULL ) && buffer ) {
2083  src.LoadMemory( buffer, strlen( buffer ), args.Argv( 1 ) );
2084  if ( src.IsLoaded() ) {
2085  idToken token;
2086  while( src.ReadToken( &token ) ) {
2087  common->Printf( "%s\n", token.c_str() );
2088  session->UpdateScreen();
2089  idFile *f = fileSystemLocal.OpenFileRead( token );
2090  if ( f ) {
2091  fileSystemLocal.CloseFile( f );
2092  }
2093  }
2094  }
2095  }
2096 
2097 }
2098 
2099 
2100 /*
2101 ================
2102 idFileSystemLocal::AddGameDirectory
2103 
2104 Sets gameFolder, adds the directory to the head of the search paths, then loads any pk4 files.
2105 ================
2106 */
2107 void idFileSystemLocal::AddGameDirectory( const char *path, const char *dir ) {
2108  int i;
2109  searchpath_t * search;
2110  pack_t * pak;
2111  idStr pakfile;
2112  idStrList pakfiles;
2113 
2114  // check if the search path already exists
2115  for ( search = searchPaths; search; search = search->next ) {
2116  // if this element is a pak file
2117  if ( !search->dir ) {
2118  continue;
2119  }
2120  if ( search->dir->path.Cmp( path ) == 0 && search->dir->gamedir.Cmp( dir ) == 0 ) {
2121  return;
2122  }
2123  }
2124 
2125  gameFolder = dir;
2126 
2127  //
2128  // add the directory to the search path
2129  //
2130  search = new searchpath_t;
2131  search->dir = new directory_t;
2132  search->pack = NULL;
2133 
2134  search->dir->path = path;
2135  search->dir->gamedir = dir;
2136  search->next = searchPaths;
2137  searchPaths = search;
2138 
2139  // find all pak files in this directory
2140  pakfile = BuildOSPath( path, dir, "" );
2141  pakfile[ pakfile.Length() - 1 ] = 0; // strip the trailing slash
2142 
2143  ListOSFiles( pakfile, ".pk4", pakfiles );
2144 
2145  // sort them so that later alphabetic matches override
2146  // earlier ones. This makes pak1.pk4 override pak0.pk4
2147  pakfiles.Sort();
2148 
2149  for ( i = 0; i < pakfiles.Num(); i++ ) {
2150  pakfile = BuildOSPath( path, dir, pakfiles[i] );
2151  pak = LoadZipFile( pakfile );
2152  if ( !pak ) {
2153  continue;
2154  }
2155  // insert the pak after the directory it comes from
2156  search = new searchpath_t;
2157  search->dir = NULL;
2158  search->pack = pak;
2159  search->next = searchPaths->next;
2160  searchPaths->next = search;
2161  common->Printf( "Loaded pk4 %s with checksum 0x%x\n", pakfile.c_str(), pak->checksum );
2162  }
2163 }
2164 
2165 /*
2166 ================
2167 idFileSystemLocal::SetupGameDirectories
2168 
2169  Takes care of the correct search order.
2170 ================
2171 */
2172 void idFileSystemLocal::SetupGameDirectories( const char *gameName ) {
2173  // setup cdpath
2174  if ( fs_cdpath.GetString()[0] ) {
2175  AddGameDirectory( fs_cdpath.GetString(), gameName );
2176  }
2177 
2178  // setup basepath
2179  if ( fs_basepath.GetString()[0] ) {
2180  AddGameDirectory( fs_basepath.GetString(), gameName );
2181  }
2182 
2183  // setup devpath
2184  if ( fs_devpath.GetString()[0] ) {
2185  AddGameDirectory( fs_devpath.GetString(), gameName );
2186  }
2187 
2188  // setup savepath
2189  if ( fs_savepath.GetString()[0] ) {
2190  AddGameDirectory( fs_savepath.GetString(), gameName );
2191  }
2192 }
2193 
2194 /*
2195 ===============
2196 idFileSystemLocal::FollowDependencies
2197 ===============
2198 */
2200  assert( pak );
2201  if ( !pak->addon_info || !pak->addon_info->depends.Num() ) {
2202  return;
2203  }
2204  int i, num = pak->addon_info->depends.Num();
2205  for ( i = 0; i < num; i++ ) {
2206  pack_t *deppak = GetPackForChecksum( pak->addon_info->depends[ i ], true );
2207  if ( deppak ) {
2208  // make sure it hasn't been marked for search already
2209  if ( !deppak->addon_search ) {
2210  // must clean addonChecksums as we go
2211  int addon_index = addonChecksums.FindIndex( deppak->checksum );
2212  if ( addon_index >= 0 ) {
2213  addonChecksums.RemoveIndex( addon_index );
2214  }
2215  deppak->addon_search = true;
2216  common->Printf( "Addon pk4 %s 0x%x depends on pak %s 0x%x, will be searched\n",
2217  pak->pakFilename.c_str(), pak->checksum,
2218  deppak->pakFilename.c_str(), deppak->checksum );
2219  FollowAddonDependencies( deppak );
2220  }
2221  } else {
2222  common->Printf( "Addon pk4 %s 0x%x depends on unknown pak 0x%x\n",
2223  pak->pakFilename.c_str(), pak->checksum, pak->addon_info->depends[ i ] );
2224  }
2225  }
2226 }
2227 
2228 /*
2229 ================
2230 idFileSystemLocal::Startup
2231 ================
2232 */
2234  searchpath_t **search;
2235  int i;
2236  pack_t *pak;
2237  int addon_index;
2238 
2239  common->Printf( "------ Initializing File System ------\n" );
2240 
2241  if ( restartChecksums.Num() ) {
2242  common->Printf( "restarting in pure mode with %d pak files\n", restartChecksums.Num() );
2243  }
2244  if ( addonChecksums.Num() ) {
2245  common->Printf( "restarting filesystem with %d addon pak file(s) to include\n", addonChecksums.Num() );
2246  }
2247 
2249 
2250  // fs_game_base override
2251  if ( fs_game_base.GetString()[0] &&
2254  }
2255 
2256  // fs_game override
2257  if ( fs_game.GetString()[0] &&
2261  }
2262 
2263  // currently all addons are in the search list - deal with filtering out and dependencies now
2264  // scan through and deal with dependencies
2265  search = &searchPaths;
2266  while ( *search ) {
2267  if ( !( *search )->pack || !( *search )->pack->addon ) {
2268  search = &( ( *search )->next );
2269  continue;
2270  }
2271  pak = ( *search )->pack;
2272  if ( fs_searchAddons.GetBool() ) {
2273  // when we have fs_searchAddons on we should never have addonChecksums
2274  assert( !addonChecksums.Num() );
2275  pak->addon_search = true;
2276  search = &( ( *search )->next );
2277  continue;
2278  }
2279  addon_index = addonChecksums.FindIndex( pak->checksum );
2280  if ( addon_index >= 0 ) {
2281  assert( !pak->addon_search ); // any pak getting flagged as addon_search should also have been removed from addonChecksums already
2282  pak->addon_search = true;
2283  addonChecksums.RemoveIndex( addon_index );
2284  FollowAddonDependencies( pak );
2285  }
2286  search = &( ( *search )->next );
2287  }
2288 
2289  // now scan to filter out addons not marked addon_search
2290  search = &searchPaths;
2291  while ( *search ) {
2292  if ( !( *search )->pack || !( *search )->pack->addon ) {
2293  search = &( ( *search )->next );
2294  continue;
2295  }
2296  assert( !( *search )->dir );
2297  pak = ( *search )->pack;
2298  if ( pak->addon_search ) {
2299  common->Printf( "Addon pk4 %s with checksum 0x%x is on the search list\n",
2300  pak->pakFilename.c_str(), pak->checksum );
2301  search = &( ( *search )->next );
2302  } else {
2303  // remove from search list, put in addons list
2304  searchpath_t *paksearch = *search;
2305  *search = ( *search )->next;
2306  paksearch->next = addonPaks;
2307  addonPaks = paksearch;
2308  common->Printf( "Addon pk4 %s with checksum 0x%x is on addon list\n",
2309  pak->pakFilename.c_str(), pak->checksum );
2310  }
2311  }
2312 
2313  // all addon paks found and accounted for
2314  assert( !addonChecksums.Num() );
2315  addonChecksums.Clear(); // just in case
2316 
2317  if ( restartChecksums.Num() ) {
2318  search = &searchPaths;
2319  while ( *search ) {
2320  if ( !( *search )->pack ) {
2321  search = &( ( *search )->next );
2322  continue;
2323  }
2324  if ( ( i = restartChecksums.FindIndex( ( *search )->pack->checksum ) ) != -1 ) {
2325  if ( i == 0 ) {
2326  // this pak is the next one in the pure search order
2327  serverPaks.Append( ( *search )->pack );
2329  if ( !restartChecksums.Num() ) {
2330  break; // early out, we're done
2331  }
2332  search = &( ( *search )->next );
2333  continue;
2334  } else {
2335  // this pak will be on the pure list, but order is not right yet
2336  searchpath_t *aux;
2337  aux = ( *search )->next;
2338  if ( !aux ) {
2339  // last of the list can't be swapped back
2340  if ( fs_debug.GetBool() ) {
2341  common->Printf( "found pure checksum %x at index %d, but the end of search path is reached\n", ( *search )->pack->checksum, i );
2342  idStr checks;
2343  checks.Clear();
2344  for ( i = 0; i < serverPaks.Num(); i++ ) {
2345  checks += va( "%p ", serverPaks[ i ] );
2346  }
2347  common->Printf( "%d pure paks - %s \n", serverPaks.Num(), checks.c_str() );
2348  checks.Clear();
2349  for ( i = 0; i < restartChecksums.Num(); i++ ) {
2350  checks += va( "%x ", restartChecksums[ i ] );
2351  }
2352  common->Printf( "%d paks left - %s\n", restartChecksums.Num(), checks.c_str() );
2353  }
2354  common->FatalError( "Failed to restart with pure mode restrictions for server connect" );
2355  }
2356  // put this search path at the end of the list
2357  searchpath_t *search_end;
2358  search_end = ( *search )->next;
2359  while ( search_end->next ) {
2360  search_end = search_end->next;
2361  }
2362  search_end->next = *search;
2363  *search = ( *search )->next;
2364  search_end->next->next = NULL;
2365  continue;
2366  }
2367  }
2368  // this pak is not on the pure list
2369  search = &( ( *search )->next );
2370  }
2371  // the list must be empty
2372  if ( restartChecksums.Num() ) {
2373  if ( fs_debug.GetBool() ) {
2374  idStr checks;
2375  checks.Clear();
2376  for ( i = 0; i < serverPaks.Num(); i++ ) {
2377  checks += va( "%p ", serverPaks[ i ] );
2378  }
2379  common->Printf( "%d pure paks - %s \n", serverPaks.Num(), checks.c_str() );
2380  checks.Clear();
2381  for ( i = 0; i < restartChecksums.Num(); i++ ) {
2382  checks += va( "%x ", restartChecksums[ i ] );
2383  }
2384  common->Printf( "%d paks left - %s\n", restartChecksums.Num(), checks.c_str() );
2385  }
2386  common->FatalError( "Failed to restart with pure mode restrictions for server connect" );
2387  }
2388  // also the game pak checksum
2389  // we could check if the game pak is actually present, but we would not be restarting if there wasn't one @ first pure check
2391  }
2392 
2393  // add our commands
2395  cmdSystem->AddCommand( "dirtree", DirTree_f, CMD_FL_SYSTEM, "lists a folder with subfolders" );
2396  cmdSystem->AddCommand( "path", Path_f, CMD_FL_SYSTEM, "lists search paths" );
2397  cmdSystem->AddCommand( "touchFile", TouchFile_f, CMD_FL_SYSTEM, "touches a file" );
2398  cmdSystem->AddCommand( "touchFileList", TouchFileList_f, CMD_FL_SYSTEM, "touches a list of files" );
2399 
2400  // print the current search paths
2401  Path_f( idCmdArgs() );
2402 
2403  common->Printf( "file system initialized.\n" );
2404  common->Printf( "--------------------------------------\n" );
2405 }
2406 
2407 /*
2408 ===================
2409 idFileSystemLocal::SetRestrictions
2410 
2411 Looks for product keys and restricts media add on ability
2412 if the full version is not found
2413 ===================
2414 */
2416 #ifdef ID_DEMO_BUILD
2417  common->Printf( "\nRunning in restricted demo mode.\n\n" );
2418  // make sure that the pak file has the header checksum we expect
2419  searchpath_t *search;
2420  for ( search = searchPaths; search; search = search->next ) {
2421  if ( search->pack ) {
2422  // a tiny attempt to keep the checksum from being scannable from the exe
2423  if ( ( search->pack->checksum ^ 0x84268436u ) != ( DEMO_PAK_CHECKSUM ^ 0x84268436u ) ) {
2424  common->FatalError( "Corrupted %s: 0x%x", search->pack->pakFilename.c_str(), search->pack->checksum );
2425  }
2426  }
2427  }
2428  cvarSystem->SetCVarBool( "fs_restrict", true );
2429 #endif
2430 }
2431 
2432 /*
2433 =====================
2434 idFileSystemLocal::UpdatePureServerChecksums
2435 =====================
2436 */
2438  searchpath_t *search;
2439  int i;
2440  pureStatus_t status;
2441 
2442  serverPaks.Clear();
2443  for ( search = searchPaths; search; search = search->next ) {
2444  // is the element a referenced pak file?
2445  if ( !search->pack ) {
2446  continue;
2447  }
2448  status = GetPackStatus( search->pack );
2449  if ( status == PURE_NEVER ) {
2450  continue;
2451  }
2452  if ( status == PURE_NEUTRAL && !search->pack->referenced ) {
2453  continue;
2454  }
2455  serverPaks.Append( search->pack );
2456  if ( serverPaks.Num() >= MAX_PURE_PAKS ) {
2457  common->FatalError( "MAX_PURE_PAKS ( %d ) exceeded\n", MAX_PURE_PAKS );
2458  }
2459  }
2460  if ( fs_debug.GetBool() ) {
2461  idStr checks;
2462  for ( i = 0; i < serverPaks.Num(); i++ ) {
2463  checks += va( "%x ", serverPaks[ i ]->checksum );
2464  }
2465  common->Printf( "set pure list - %d paks ( %s)\n", serverPaks.Num(), checks.c_str() );
2466  }
2467 }
2468 
2469 /*
2470 =====================
2471 idFileSystemLocal::UpdateGamePakChecksums
2472 =====================
2473 */
2475  searchpath_t *search;
2476  fileInPack_t *pakFile;
2477  int confHash;
2478  idFile *confFile;
2479  char *buf;
2480  idLexer *lexConf;
2481  idToken token;
2482  int id;
2483 
2484  confHash = HashFileName( BINARY_CONFIG );
2485 
2486  memset( gamePakForOS, 0, sizeof( gamePakForOS ) );
2487  for ( search = searchPaths; search; search = search->next ) {
2488  if ( !search->pack ) {
2489  continue;
2490  }
2491  search->pack->binary = BINARY_NO;
2492  for ( pakFile = search->pack->hashTable[confHash]; pakFile; pakFile = pakFile->next ) {
2493  if ( !FilenameCompare( pakFile->name, BINARY_CONFIG ) ) {
2494  search->pack->binary = BINARY_YES;
2495  confFile = ReadFileFromZip( search->pack, pakFile, BINARY_CONFIG );
2496  buf = new char[ confFile->Length() + 1 ];
2497  confFile->Read( (void *)buf, confFile->Length() );
2498  buf[ confFile->Length() ] = '\0';
2499  lexConf = new idLexer( buf, confFile->Length(), confFile->GetFullPath() );
2500  while ( lexConf->ReadToken( &token ) ) {
2501  if ( token.IsNumeric() ) {
2502  id = atoi( token );
2503  if ( id < MAX_GAME_OS && !gamePakForOS[ id ] ) {
2504  if ( fs_debug.GetBool() ) {
2505  common->Printf( "Adding game pak checksum for OS %d: %s 0x%x\n", id, confFile->GetFullPath(), search->pack->checksum );
2506  }
2507  gamePakForOS[ id ] = search->pack->checksum;
2508  }
2509  }
2510  }
2511  CloseFile( confFile );
2512  delete lexConf;
2513  delete[] buf;
2514  }
2515  }
2516  }
2517 
2518  // some sanity checks on the game code references
2519  // make sure that at least the local OS got a pure reference
2520  if ( !gamePakForOS[ BUILD_OS_ID ] ) {
2521  common->Warning( "No game code pak reference found for the local OS" );
2522  return false;
2523  }
2524 
2525  if ( !cvarSystem->GetCVarBool( "net_serverAllowServerMod" ) &&
2526  gamePakChecksum != gamePakForOS[ BUILD_OS_ID ] ) {
2527  common->Warning( "The current game code doesn't match pak files (net_serverAllowServerMod is off)" );
2528  return false;
2529  }
2530 
2531  return true;
2532 }
2533 
2534 /*
2535 =====================
2536 idFileSystemLocal::GetPackForChecksum
2537 =====================
2538 */
2539 pack_t* idFileSystemLocal::GetPackForChecksum( int checksum, bool searchAddons ) {
2540  searchpath_t *search;
2541  for ( search = searchPaths; search; search = search->next ) {
2542  if ( !search->pack ) {
2543  continue;
2544  }
2545  if ( search->pack->checksum == checksum ) {
2546  return search->pack;
2547  }
2548  }
2549  if ( searchAddons ) {
2550  for ( search = addonPaks; search; search = search->next ) {
2551  assert( search->pack && search->pack->addon );
2552  if ( search->pack->checksum == checksum ) {
2553  return search->pack;
2554  }
2555  }
2556  }
2557  return NULL;
2558 }
2559 
2560 /*
2561 ===============
2562 idFileSystemLocal::ValidateDownloadPakForChecksum
2563 ===============
2564 */
2565 int idFileSystemLocal::ValidateDownloadPakForChecksum( int checksum, char path[ MAX_STRING_CHARS ], bool isBinary ) {
2566  int i;
2567  idStrList testList;
2568  idStr name;
2569  idStr relativePath;
2570  bool pakBinary;
2571  pack_t *pak = GetPackForChecksum( checksum );
2572 
2573  if ( !pak ) {
2574  return 0;
2575  }
2576 
2577  // validate this pak for a potential download
2578  // ignore pak*.pk4 for download. those are reserved to distribution and cannot be downloaded
2579  name = pak->pakFilename;
2580  name.StripPath();
2581  if ( strstr( name.c_str(), "pak" ) == name.c_str() ) {
2582  common->DPrintf( "%s is not a donwloadable pak\n", pak->pakFilename.c_str() );
2583  return 0;
2584  }
2585  // check the binary
2586  // a pure server sets the binary flag when starting the game
2587  assert( pak->binary != BINARY_UNKNOWN );
2588  pakBinary = ( pak->binary == BINARY_YES ) ? true : false;
2589  if ( isBinary != pakBinary ) {
2590  common->DPrintf( "%s binary flag mismatch\n", pak->pakFilename.c_str() );
2591  return 0;
2592  }
2593 
2594  // extract a path that includes the fs_game: != OSPathToRelativePath
2595  testList.Append( fs_savepath.GetString() );
2596  testList.Append( fs_devpath.GetString() );
2597  testList.Append( fs_basepath.GetString() );
2598  testList.Append( fs_cdpath.GetString() );
2599  for ( i = 0; i < testList.Num(); i ++ ) {
2600  if ( testList[ i ].Length() && !testList[ i ].Icmpn( pak->pakFilename, testList[ i ].Length() ) ) {
2601  relativePath = pak->pakFilename.c_str() + testList[ i ].Length() + 1;
2602  break;
2603  }
2604  }
2605  if ( i == testList.Num() ) {
2606  common->Warning( "idFileSystem::ValidateDownloadPak: failed to extract relative path for %s", pak->pakFilename.c_str() );
2607  return 0;
2608  }
2609  idStr::Copynz( path, relativePath, MAX_STRING_CHARS );
2610  return pak->length;
2611 }
2612 
2613 /*
2614 =====================
2615 idFileSystemLocal::ClearPureChecksums
2616 =====================
2617 */
2619  common->DPrintf( "Cleared pure server lock\n" );
2620  serverPaks.Clear();
2621 }
2622 
2623 /*
2624 =====================
2625 idFileSystemLocal::SetPureServerChecksums
2626 set the pure paks according to what the server asks
2627 if that's not possible, identify why and build an answer
2628 can be:
2629  loadedFileFromDir - some files were loaded from directories instead of paks (a restart in pure pak-only is required)
2630  missing/wrong checksums - some pak files would need to be installed/updated (downloaded for instance)
2631  some pak files currently referenced are not referenced by the server
2632  wrong order - if the pak order doesn't match, means some stuff could have been loaded from somewhere else
2633 server referenced files are prepended to the list if possible ( that doesn't break pureness )
2634 DLL:
2635  the checksum of the pak containing the DLL is maintained seperately, the server can send different replies by OS
2636 =====================
2637 */
2638 fsPureReply_t idFileSystemLocal::SetPureServerChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int _gamePakChecksum, int missingChecksums[ MAX_PURE_PAKS ], int *missingGamePakChecksum ) {
2639  pack_t *pack;
2640  int i, j, imissing;
2641  bool success = true;
2642  bool canPrepend = true;
2643  char dllName[MAX_OSPATH];
2644  int dllHash;
2645  fileInPack_t * pakFile;
2646 
2647  sys->DLL_GetFileName( "game", dllName, MAX_OSPATH );
2648  dllHash = HashFileName( dllName );
2649 
2650  imissing = 0;
2651  missingChecksums[ 0 ] = 0;
2652  assert( missingGamePakChecksum );
2653  *missingGamePakChecksum = 0;
2654 
2655  if ( pureChecksums[ 0 ] == 0 ) {
2657  return PURE_OK;
2658  }
2659 
2660  if ( !serverPaks.Num() ) {
2661  // there was no pure lockdown yet - lock to what we already have
2663  }
2664  i = 0; j = 0;
2665  while ( pureChecksums[ i ] ) {
2666  if ( j < serverPaks.Num() && serverPaks[ j ]->checksum == pureChecksums[ i ] ) {
2667  canPrepend = false; // once you start matching into the list there is no prepending anymore
2668  i++; j++; // the pak is matched, is in the right order, continue..
2669  } else {
2670  pack = GetPackForChecksum( pureChecksums[ i ], true );
2671  if ( pack && pack->addon && !pack->addon_search ) {
2672  // this is an addon pack, and it's not on our current search list
2673  // setting success to false meaning that a restart including this addon is required
2674  if ( fs_debug.GetBool() ) {
2675  common->Printf( "pak %s checksumed 0x%x is on addon list. Restart required.\n", pack->pakFilename.c_str(), pack->checksum );
2676  }
2677  success = false;
2678  }
2679  if ( pack && pack->isNew ) {
2680  // that's a downloaded pack, we will need to restart
2681  if ( fs_debug.GetBool() ) {
2682  common->Printf( "pak %s checksumed 0x%x is a newly downloaded file. Restart required.\n", pack->pakFilename.c_str(), pack->checksum );
2683  }
2684  success = false;
2685  }
2686  if ( pack ) {
2687  if ( canPrepend ) {
2688  // we still have a chance
2689  if ( fs_debug.GetBool() ) {
2690  common->Printf( "prepend pak %s checksumed 0x%x at index %d\n", pack->pakFilename.c_str(), pack->checksum, j );
2691  }
2692  // NOTE: there is a light possibility this adds at the end of the list if UpdatePureServerChecksums didn't set anything
2693  serverPaks.Insert( pack, j );
2694  i++; j++; // continue..
2695  } else {
2696  success = false;
2697  if ( fs_debug.GetBool() ) {
2698  // verbose the situation
2699  if ( serverPaks.Find( pack ) ) {
2700  common->Printf( "pak %s checksumed 0x%x is in the pure list at wrong index. Current index is %d, found at %d\n", pack->pakFilename.c_str(), pack->checksum, j, serverPaks.FindIndex( pack ) );
2701  } else {
2702  common->Printf( "pak %s checksumed 0x%x can't be added to pure list because of search order\n", pack->pakFilename.c_str(), pack->checksum );
2703  }
2704  }
2705  i++; // advance server checksums only
2706  }
2707  } else {
2708  // didn't find a matching checksum
2709  success = false;
2710  missingChecksums[ imissing++ ] = pureChecksums[ i ];
2711  missingChecksums[ imissing ] = 0;
2712  if ( fs_debug.GetBool() ) {
2713  common->Printf( "checksum not found - 0x%x\n", pureChecksums[ i ] );
2714  }
2715  i++; // advance the server checksums only
2716  }
2717  }
2718  }
2719  while ( j < serverPaks.Num() ) {
2720  success = false; // just in case some extra pak files are referenced at the end of our local list
2721  if ( fs_debug.GetBool() ) {
2722  common->Printf( "pak %s checksumed 0x%x is an extra reference at the end of local pure list\n", serverPaks[ j ]->pakFilename.c_str(), serverPaks[ j ]->checksum );
2723  }
2724  j++;
2725  }
2726 
2727  // DLL checksuming
2728  if ( !_gamePakChecksum ) {
2729  // server doesn't have knowledge of code we can use ( OS issue )
2730  return PURE_NODLL;
2731  }
2733 #if ID_FAKE_PURE
2734  gamePakChecksum = _gamePakChecksum;
2735 #endif
2736  if ( _gamePakChecksum != gamePakChecksum ) {
2737  // current DLL is wrong, search for a pak with the approriate checksum
2738  // ( search all paks, the pure list is not relevant here )
2739  pack = GetPackForChecksum( _gamePakChecksum );
2740  if ( !pack ) {
2741  if ( fs_debug.GetBool() ) {
2742  common->Printf( "missing the game code pak ( 0x%x )\n", _gamePakChecksum );
2743  }
2744  // if there are other paks missing they have also been marked above
2745  *missingGamePakChecksum = _gamePakChecksum;
2746  return PURE_MISSING;
2747  }
2748  // if assets paks are missing, don't try any of the DLL restart / NODLL
2749  if ( imissing ) {
2750  return PURE_MISSING;
2751  }
2752  // we have a matching pak
2753  if ( fs_debug.GetBool() ) {
2754  common->Printf( "server's game code pak candidate is '%s' ( 0x%x )\n", pack->pakFilename.c_str(), pack->checksum );
2755  }
2756  // make sure there is a valid DLL for us
2757  if ( pack->hashTable[ dllHash ] ) {
2758  for ( pakFile = pack->hashTable[ dllHash ]; pakFile; pakFile = pakFile->next ) {
2759  if ( !FilenameCompare( pakFile->name, dllName ) ) {
2760  gamePakChecksum = _gamePakChecksum; // this will be used to extract the DLL in pure mode FindDLL
2761  return PURE_RESTART;
2762  }
2763  }
2764  }
2765  common->Warning( "media is misconfigured. server claims pak '%s' ( 0x%x ) has media for us, but '%s' is not found\n", pack->pakFilename.c_str(), pack->checksum, dllName );
2766  return PURE_NODLL;
2767  }
2768 
2769  // we reply to missing after DLL check so it can be part of the list
2770  if ( imissing ) {
2771  return PURE_MISSING;
2772  }
2773 
2774  // one last check
2775  if ( loadedFileFromDir ) {
2776  success = false;
2777  if ( fs_debug.GetBool() ) {
2778  common->Printf( "SetPureServerChecksums: there are files loaded from dir\n" );
2779  }
2780  }
2781  return ( success ? PURE_OK : PURE_RESTART );
2782 }
2783 
2784 /*
2785 =====================
2786 idFileSystemLocal::GetPureServerChecksums
2787 =====================
2788 */
2789 void idFileSystemLocal::GetPureServerChecksums( int checksums[ MAX_PURE_PAKS ], int OS, int *_gamePakChecksum ) {
2790  int i;
2791 
2792  for ( i = 0; i < serverPaks.Num(); i++ ) {
2793  checksums[ i ] = serverPaks[ i ]->checksum;
2794  }
2795  checksums[ i ] = 0;
2796  if ( _gamePakChecksum ) {
2797  if ( OS >= 0 ) {
2798  *_gamePakChecksum = gamePakForOS[ OS ];
2799  } else {
2800  *_gamePakChecksum = gamePakChecksum;
2801  }
2802  }
2803 }
2804 
2805 /*
2806 =====================
2807 idFileSystemLocal::SetRestartChecksums
2808 =====================
2809 */
2810 void idFileSystemLocal::SetRestartChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int gamePakChecksum ) {
2811  int i;
2812  pack_t *pack;
2813 
2815  i = 0;
2816  while ( pureChecksums[ i ] ) {
2817  pack = GetPackForChecksum( pureChecksums[ i ], true );
2818  if ( !pack ) {
2819  common->FatalError( "SetRestartChecksums failed: no pak for checksum 0x%x\n", pureChecksums[i] );
2820  }
2821  if ( pack->addon && addonChecksums.FindIndex( pack->checksum ) < 0 ) {
2822  // can't mark it pure if we're not even gonna search it :-)
2823  addonChecksums.Append( pack->checksum );
2824  }
2825  restartChecksums.Append( pureChecksums[ i ] );
2826  i++;
2827  }
2829 }
2830 
2831 /*
2832 ================
2833 idFileSystemLocal::Init
2834 
2835 Called only at inital startup, not when the filesystem
2836 is resetting due to a game change
2837 ================
2838 */
2840  // allow command line parms to override our defaults
2841  // we have to specially handle this, because normal command
2842  // line variable sets don't happen until after the filesystem
2843  // has already been initialized
2844  common->StartupVariable( "fs_basepath", false );
2845  common->StartupVariable( "fs_savepath", false );
2846  common->StartupVariable( "fs_cdpath", false );
2847  common->StartupVariable( "fs_devpath", false );
2848  common->StartupVariable( "fs_game", false );
2849  common->StartupVariable( "fs_game_base", false );
2850  common->StartupVariable( "fs_copyfiles", false );
2851  common->StartupVariable( "fs_restrict", false );
2852  common->StartupVariable( "fs_searchAddons", false );
2853 
2854 #if !ID_ALLOW_D3XP
2855  if ( fs_game.GetString()[0] && !idStr::Icmp( fs_game.GetString(), "d3xp" ) ) {
2856  fs_game.SetString( NULL );
2857  }
2858  if ( fs_game_base.GetString()[0] && !idStr::Icmp( fs_game_base.GetString(), "d3xp" ) ) {
2860  }
2861 #endif
2862 
2863  if ( fs_basepath.GetString()[0] == '\0' ) {
2865  }
2866  if ( fs_savepath.GetString()[0] == '\0' ) {
2868  }
2869  if ( fs_cdpath.GetString()[0] == '\0' ) {
2871  }
2872 
2873  if ( fs_devpath.GetString()[0] == '\0' ) {
2874 #ifdef WIN32
2876 #else
2878 #endif
2879  }
2880 
2881  // try to start up normally
2882  Startup( );
2883 
2884  // see if we are going to allow add-ons
2885  SetRestrictions();
2886 
2887  // spawn a thread to handle background file reads
2889 
2890  // if we can't find default.cfg, assume that the paths are
2891  // busted and error out now, rather than getting an unreadable
2892  // graphics screen when the font fails to load
2893  // Dedicated servers can run with no outside files at all
2894  if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
2895  common->FatalError( "Couldn't load default.cfg" );
2896  }
2897 }
2898 
2899 /*
2900 ================
2901 idFileSystemLocal::Restart
2902 ================
2903 */
2905  // free anything we currently have loaded
2906  Shutdown( true );
2907 
2908  Startup( );
2909 
2910  // see if we are going to allow add-ons
2911  SetRestrictions();
2912 
2913  // if we can't find default.cfg, assume that the paths are
2914  // busted and error out now, rather than getting an unreadable
2915  // graphics screen when the font fails to load
2916  if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
2917  common->FatalError( "Couldn't load default.cfg" );
2918  }
2919 }
2920 
2921 /*
2922 ================
2923 idFileSystemLocal::Shutdown
2924 
2925 Frees all resources and closes all files
2926 ================
2927 */
2928 void idFileSystemLocal::Shutdown( bool reloading ) {
2929  searchpath_t *sp, *next, *loop;
2930 
2931  gameFolder.Clear();
2932 
2933  serverPaks.Clear();
2934  if ( !reloading ) {
2937  }
2938  loadedFileFromDir = false;
2939  gameDLLChecksum = 0;
2940  gamePakChecksum = 0;
2941 
2942  ClearDirCache();
2943 
2944  // free everything - loop through searchPaths and addonPaks
2945  for ( loop = searchPaths; loop; loop == searchPaths ? loop = addonPaks : loop = NULL ) {
2946  for ( sp = loop; sp; sp = next ) {
2947  next = sp->next;
2948 
2949  if ( sp->pack ) {
2950  unzClose( sp->pack->handle );
2951  delete [] sp->pack->buildBuffer;
2952  if ( sp->pack->addon_info ) {
2953  sp->pack->addon_info->mapDecls.DeleteContents( true );
2954  delete sp->pack->addon_info;
2955  }
2956  delete sp->pack;
2957  }
2958  if ( sp->dir ) {
2959  delete sp->dir;
2960  }
2961  delete sp;
2962  }
2963  }
2964 
2965  // any FS_ calls will now be an error until reinitialized
2966  searchPaths = NULL;
2967  addonPaks = NULL;
2968 
2969  cmdSystem->RemoveCommand( "path" );
2970  cmdSystem->RemoveCommand( "dir" );
2971  cmdSystem->RemoveCommand( "dirtree" );
2972  cmdSystem->RemoveCommand( "touchFile" );
2973 
2974  mapDict.Clear();
2975 }
2976 
2977 /*
2978 ================
2979 idFileSystemLocal::IsInitialized
2980 ================
2981 */
2983  return ( searchPaths != NULL );
2984 }
2985 
2986 
2987 /*
2988 =================================================================================
2989 
2990 Opening files
2991 
2992 =================================================================================
2993 */
2994 
2995 /*
2996 ===========
2997 idFileSystemLocal::FileAllowedFromDir
2998 ===========
2999 */
3001  unsigned int l;
3002 
3003  l = strlen( path );
3004 
3005  if ( !strcmp( path + l - 4, ".cfg" ) // for config files
3006  || !strcmp( path + l - 4, ".dat" ) // for journal files
3007  || !strcmp( path + l - 4, ".dll" ) // dynamic modules are handled a different way for pure
3008  || !strcmp( path + l - 3, ".so" )
3009  || ( l > 6 && !strcmp( path + l - 6, ".dylib" ) )
3010  || ( l > 10 && !strcmp( path + l - 10, ".scriptcfg" ) ) // configuration script, such as map cycle
3011 #if ID_PURE_ALLOWDDS
3012  || !strcmp( path + l - 4, ".dds" )
3013 #endif
3014  ) {
3015  // note: cd and xp keys, as well as config.spec are opened through an explicit OS path and don't hit this
3016  return true;
3017  }
3018  // savegames
3019  if ( strstr( path, "savegames" ) == path &&
3020  ( !strcmp( path + l - 4, ".tga" ) || !strcmp( path + l -4, ".txt" ) || !strcmp( path + l - 5, ".save" ) ) ) {
3021  return true;
3022  }
3023  // screen shots
3024  if ( strstr( path, "screenshots" ) == path && !strcmp( path + l - 4, ".tga" ) ) {
3025  return true;
3026  }
3027  // objective tgas
3028  if ( strstr( path, "maps/game" ) == path &&
3029  !strcmp( path + l - 4, ".tga" ) ) {
3030  return true;
3031  }
3032  // splash screens extracted from addons
3033  if ( strstr( path, "guis/assets/splash/addon" ) == path &&
3034  !strcmp( path + l -4, ".tga" ) ) {
3035  return true;
3036  }
3037 
3038  return false;
3039 }
3040 
3041 /*
3042 ===========
3043 idFileSystemLocal::GetPackStatus
3044 ===========
3045 */
3047  int i, l, hashindex;
3048  fileInPack_t *file;
3049  bool abrt;
3050  idStr name;
3051 
3052  if ( pak->pureStatus != PURE_UNKNOWN ) {
3053  return pak->pureStatus;
3054  }
3055 
3056  // check content for PURE_NEVER
3057  i = 0;
3058  file = pak->buildBuffer;
3059  for ( hashindex = 0; hashindex < FILE_HASH_SIZE; hashindex++ ) {
3060  abrt = false;
3061  file = pak->hashTable[ hashindex ];
3062  while ( file ) {
3063  abrt = true;
3064  l = file->name.Length();
3065  for ( int j = 0; pureExclusions[j].func != NULL; j++ ) {
3066  if ( pureExclusions[j].func( pureExclusions[j], l, file->name ) ) {
3067  abrt = false;
3068  break;
3069  }
3070  }
3071  if ( abrt ) {
3072  common->DPrintf( "pak '%s' candidate for pure: '%s'\n", pak->pakFilename.c_str(), file->name.c_str() );
3073  break;
3074  }
3075  file = file->next;
3076  i++;
3077  }
3078  if ( abrt ) {
3079  break;
3080  }
3081  }
3082  if ( i == pak->numfiles ) {
3083  pak->pureStatus = PURE_NEVER;
3084  return PURE_NEVER;
3085  }
3086 
3087  // check pak name for PURE_ALWAYS
3088  pak->pakFilename.ExtractFileName( name );
3089  if ( !name.IcmpPrefixPath( "pak" ) ) {
3090  pak->pureStatus = PURE_ALWAYS;
3091  return PURE_ALWAYS;
3092  }
3093 
3094  pak->pureStatus = PURE_NEUTRAL;
3095  return PURE_NEUTRAL;
3096 }
3097 
3098 /*
3099 ===========
3100 idFileSystemLocal::ReadFileFromZip
3101 ===========
3102 */
3103 idFile_InZip * idFileSystemLocal::ReadFileFromZip( pack_t *pak, fileInPack_t *pakFile, const char *relativePath ) {
3104  unz_s * zfi;
3105  FILE * fp;
3106  idFile_InZip *file = new idFile_InZip();
3107 
3108  // open a new file on the pakfile
3109  file->z = unzReOpen( pak->pakFilename, pak->handle );
3110  if ( file->z == NULL ) {
3111  common->FatalError( "Couldn't reopen %s", pak->pakFilename.c_str() );
3112  }
3113  file->name = relativePath;
3114  file->fullPath = pak->pakFilename + "/" + relativePath;
3115  zfi = (unz_s *)file->z;
3116  // in case the file was new
3117  fp = zfi->file;
3118  // set the file position in the zip file (also sets the current file info)
3119  unzSetCurrentFileInfoPosition( pak->handle, pakFile->pos );
3120  // copy the file info into the unzip structure
3121  memcpy( zfi, pak->handle, sizeof(unz_s) );
3122  // we copy this back into the structure
3123  zfi->file = fp;
3124  // open the file in the zip
3125  unzOpenCurrentFile( file->z );
3126  file->zipFilePos = pakFile->pos;
3128  return file;
3129 }
3130 
3131 /*
3132 ===========
3133 idFileSystemLocal::OpenFileReadFlags
3134 
3135 Finds the file in the search path, following search flag recommendations
3136 Returns filesize and an open FILE pointer.
3137 Used for streaming data out of either a
3138 separate file or a ZIP file.
3139 ===========
3140 */
3141 idFile *idFileSystemLocal::OpenFileReadFlags( const char *relativePath, int searchFlags, pack_t **foundInPak, bool allowCopyFiles, const char* gamedir ) {
3142  searchpath_t * search;
3143  idStr netpath;
3144  pack_t * pak;
3145  fileInPack_t * pakFile;
3146  directory_t * dir;
3147  long hash;
3148  FILE * fp;
3149 
3150  if ( !searchPaths ) {
3151  common->FatalError( "Filesystem call made without initialization\n" );
3152  }
3153 
3154  if ( !relativePath ) {
3155  common->FatalError( "idFileSystemLocal::OpenFileRead: NULL 'relativePath' parameter passed\n" );
3156  }
3157 
3158  if ( foundInPak ) {
3159  *foundInPak = NULL;
3160  }
3161 
3162  // qpaths are not supposed to have a leading slash
3163  if ( relativePath[0] == '/' || relativePath[0] == '\\' ) {
3164  relativePath++;
3165  }
3166 
3167  // make absolutely sure that it can't back up the path.
3168  // The searchpaths do guarantee that something will always
3169  // be prepended, so we don't need to worry about "c:" or "//limbo"
3170  if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) {
3171  return NULL;
3172  }
3173 
3174  // edge case
3175  if ( relativePath[0] == '\0' ) {
3176  return NULL;
3177  }
3178 
3179  // make sure the doomkey file is only readable by game at initialization
3180  // any other time the key should only be accessed in memory using the provided functions
3181  if( common->IsInitialized() && ( idStr::Icmp( relativePath, CDKEY_FILE ) == 0 || idStr::Icmp( relativePath, XPKEY_FILE ) == 0 ) ) {
3182  return NULL;
3183  }
3184 
3185  //
3186  // search through the path, one element at a time
3187  //
3188 
3189  hash = HashFileName( relativePath );
3190 
3191  for ( search = searchPaths; search; search = search->next ) {
3192  if ( search->dir && ( searchFlags & FSFLAG_SEARCH_DIRS ) ) {
3193  // check a file in the directory tree
3194 
3195  // if we are running restricted, the only files we
3196  // will allow to come from the directory are .cfg files
3197  if ( fs_restrict.GetBool() || serverPaks.Num() ) {
3198  if ( !FileAllowedFromDir( relativePath ) ) {
3199  continue;
3200  }
3201  }
3202 
3203  dir = search->dir;
3204 
3205  if(gamedir && strlen(gamedir)) {
3206  if(dir->gamedir != gamedir) {
3207  continue;
3208  }
3209  }
3210 
3211  netpath = BuildOSPath( dir->path, dir->gamedir, relativePath );
3212  fp = OpenOSFileCorrectName( netpath, "rb" );
3213  if ( !fp ) {
3214  continue;
3215  }
3216 
3217  idFile_Permanent *file = new idFile_Permanent();
3218  file->o = fp;
3219  file->name = relativePath;
3220  file->fullPath = netpath;
3221  file->mode = ( 1 << FS_READ );
3222  file->fileSize = DirectFileLength( file->o );
3223  if ( fs_debug.GetInteger() ) {
3224  common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s/%s')\n", relativePath, dir->path.c_str(), dir->gamedir.c_str() );
3225  }
3226 
3227  if ( !loadedFileFromDir && !FileAllowedFromDir( relativePath ) ) {
3228  if ( restartChecksums.Num() ) {
3229  common->FatalError( "'%s' loaded from directory: Failed to restart with pure mode restrictions for server connect", relativePath );
3230  }
3231  common->DPrintf( "filesystem: switching to pure mode will require a restart. '%s' loaded from directory.\n", relativePath );
3232  loadedFileFromDir = true;
3233  }
3234 
3235  // if fs_copyfiles is set
3236  if ( allowCopyFiles && fs_copyfiles.GetInteger() ) {
3237 
3238  idStr copypath;
3239  idStr name;
3240  copypath = BuildOSPath( fs_savepath.GetString(), dir->gamedir, relativePath );
3241  netpath.ExtractFileName( name );
3242  copypath.StripFilename( );
3243  copypath += PATHSEPERATOR_STR;
3244  copypath += name;
3245 
3246  bool isFromCDPath = !dir->path.Cmp( fs_cdpath.GetString() );
3247  bool isFromSavePath = !dir->path.Cmp( fs_savepath.GetString() );
3248  bool isFromBasePath = !dir->path.Cmp( fs_basepath.GetString() );
3249 
3250  switch ( fs_copyfiles.GetInteger() ) {
3251  case 1:
3252  // copy from cd path only
3253  if ( isFromCDPath ) {
3254  CopyFile( netpath, copypath );
3255  }
3256  break;
3257  case 2:
3258  // from cd path + timestamps
3259  if ( isFromCDPath ) {
3260  CopyFile( netpath, copypath );
3261  } else if ( isFromSavePath || isFromBasePath ) {
3262  idStr sourcepath;
3263  sourcepath = BuildOSPath( fs_cdpath.GetString(), dir->gamedir, relativePath );
3264  FILE *f1 = OpenOSFile( sourcepath, "r" );
3265  if ( f1 ) {
3266  ID_TIME_T t1 = Sys_FileTimeStamp( f1 );
3267  fclose( f1 );
3268  FILE *f2 = OpenOSFile( copypath, "r" );
3269  if ( f2 ) {
3270  ID_TIME_T t2 = Sys_FileTimeStamp( f2 );
3271  fclose( f2 );
3272  if ( t1 > t2 ) {
3273  CopyFile( sourcepath, copypath );
3274  }
3275  }
3276  }
3277  }
3278  break;
3279  case 3:
3280  if ( isFromCDPath || isFromBasePath ) {
3281  CopyFile( netpath, copypath );
3282  }
3283  break;
3284  case 4:
3285  if ( isFromCDPath && !isFromBasePath ) {
3286  CopyFile( netpath, copypath );
3287  }
3288  break;
3289  }
3290  }
3291 
3292  return file;
3293  } else if ( search->pack && ( searchFlags & FSFLAG_SEARCH_PAKS ) ) {
3294 
3295  if ( !search->pack->hashTable[hash] ) {
3296  continue;
3297  }
3298 
3299  // disregard if it doesn't match one of the allowed pure pak files
3300  if ( serverPaks.Num() ) {
3301  GetPackStatus( search->pack );
3302  if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
3303  continue; // not on the pure server pak list
3304  }
3305  }
3306 
3307  // look through all the pak file elements
3308  pak = search->pack;
3309 
3310  if ( searchFlags & FSFLAG_BINARY_ONLY ) {
3311  // make sure this pak is tagged as a binary file
3312  if ( pak->binary == BINARY_UNKNOWN ) {
3313  int confHash;
3314  fileInPack_t *pakFile;
3315  confHash = HashFileName( BINARY_CONFIG );
3316  pak->binary = BINARY_NO;
3317  for ( pakFile = search->pack->hashTable[confHash]; pakFile; pakFile = pakFile->next ) {
3318  if ( !FilenameCompare( pakFile->name, BINARY_CONFIG ) ) {
3319  pak->binary = BINARY_YES;
3320  break;
3321  }
3322  }
3323  }
3324  if ( pak->binary == BINARY_NO ) {
3325  continue; // not a binary pak, skip
3326  }
3327  }
3328 
3329  for ( pakFile = pak->hashTable[hash]; pakFile; pakFile = pakFile->next ) {
3330  // case and separator insensitive comparisons
3331  if ( !FilenameCompare( pakFile->name, relativePath ) ) {
3332  idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
3333 
3334  if ( foundInPak ) {
3335  *foundInPak = pak;
3336  }
3337 
3338  if ( !pak->referenced && !( searchFlags & FSFLAG_PURE_NOREF ) ) {
3339  // mark this pak referenced
3340  if ( fs_debug.GetInteger( ) ) {
3341  common->Printf( "idFileSystem::OpenFileRead: %s -> adding %s to referenced paks\n", relativePath, pak->pakFilename.c_str() );
3342  }
3343  pak->referenced = true;
3344  }
3345 
3346  if ( fs_debug.GetInteger( ) ) {
3347  common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s')\n", relativePath, pak->pakFilename.c_str() );
3348  }
3349  return file;
3350  }
3351  }
3352  }
3353  }
3354 
3355  if ( searchFlags & FSFLAG_SEARCH_ADDONS ) {
3356  for ( search = addonPaks; search; search = search->next ) {
3357  assert( search->pack );
3358  fileInPack_t *pakFile;
3359  pak = search->pack;
3360  for ( pakFile = pak->hashTable[hash]; pakFile; pakFile = pakFile->next ) {
3361  if ( !FilenameCompare( pakFile->name, relativePath ) ) {
3362  idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
3363  if ( foundInPak ) {
3364  *foundInPak = pak;
3365  }
3366  // we don't toggle pure on paks found in addons - they can't be used without a reloadEngine anyway
3367  if ( fs_debug.GetInteger( ) ) {
3368  common->Printf( "idFileSystem::OpenFileRead: %s (found in addon pk4 '%s')\n", relativePath, search->pack->pakFilename.c_str() );
3369  }
3370  return file;
3371  }
3372  }
3373  }
3374  }
3375 
3376  if ( fs_debug.GetInteger( ) ) {
3377  common->Printf( "Can't find %s\n", relativePath );
3378  }
3379 
3380  return NULL;
3381 }
3382 
3383 /*
3384 ===========
3385 idFileSystemLocal::OpenFileRead
3386 ===========
3387 */
3388 idFile *idFileSystemLocal::OpenFileRead( const char *relativePath, bool allowCopyFiles, const char* gamedir ) {
3389  return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS | FSFLAG_SEARCH_PAKS, NULL, allowCopyFiles, gamedir );
3390 }
3391 
3392 /*
3393 ===========
3394 idFileSystemLocal::OpenFileWrite
3395 ===========
3396 */
3397 idFile *idFileSystemLocal::OpenFileWrite( const char *relativePath, const char *basePath ) {
3398  const char *path;
3399  idStr OSpath;
3401 
3402  if ( !searchPaths ) {
3403  common->FatalError( "Filesystem call made without initialization\n" );
3404  }
3405 
3406  path = cvarSystem->GetCVarString( basePath );
3407  if ( !path[0] ) {
3408  path = fs_savepath.GetString();
3409  }
3410 
3411  OSpath = BuildOSPath( path, gameFolder, relativePath );
3412 
3413  if ( fs_debug.GetInteger() ) {
3414  common->Printf( "idFileSystem::OpenFileWrite: %s\n", OSpath.c_str() );
3415  }
3416 
3417  // if the dir we are writing to is in our current list, it will be outdated
3418  // so just flush everything
3419  ClearDirCache();
3420 
3421  common->DPrintf( "writing to: %s\n", OSpath.c_str() );
3422  CreateOSPath( OSpath );
3423 
3424  f = new idFile_Permanent();
3425  f->o = OpenOSFile( OSpath, "wb" );
3426  if ( !f->o ) {
3427  delete f;
3428  return NULL;
3429  }
3430  f->name = relativePath;
3431  f->fullPath = OSpath;
3432  f->mode = ( 1 << FS_WRITE );
3433  f->handleSync = false;
3434  f->fileSize = 0;
3435 
3436  return f;
3437 }
3438 
3439 /*
3440 ===========
3441 idFileSystemLocal::OpenExplicitFileRead
3442 ===========
3443 */
3446 
3447  if ( !searchPaths ) {
3448  common->FatalError( "Filesystem call made without initialization\n" );
3449  }
3450 
3451  if ( fs_debug.GetInteger() ) {
3452  common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath );
3453  }
3454 
3455  common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath );
3456 
3457  f = new idFile_Permanent();
3458  f->o = OpenOSFile( OSPath, "rb" );
3459  if ( !f->o ) {
3460  delete f;
3461  return NULL;
3462  }
3463  f->name = OSPath;
3464  f->fullPath = OSPath;
3465  f->mode = ( 1 << FS_READ );
3466  f->handleSync = false;
3467  f->fileSize = DirectFileLength( f->o );
3468 
3469  return f;
3470 }
3471 
3472 /*
3473 ===========
3474 idFileSystemLocal::OpenExplicitFileWrite
3475 ===========
3476 */
3479 
3480  if ( !searchPaths ) {
3481  common->FatalError( "Filesystem call made without initialization\n" );
3482  }
3483 
3484  if ( fs_debug.GetInteger() ) {
3485  common->Printf( "idFileSystem::OpenExplicitFileWrite: %s\n", OSPath );
3486  }
3487 
3488  common->DPrintf( "writing to: %s\n", OSPath );
3489  CreateOSPath( OSPath );
3490 
3491  f = new idFile_Permanent();
3492  f->o = OpenOSFile( OSPath, "wb" );
3493  if ( !f->o ) {
3494  delete f;
3495  return NULL;
3496  }
3497  f->name = OSPath;
3498  f->fullPath = OSPath;
3499  f->mode = ( 1 << FS_WRITE );
3500  f->handleSync = false;
3501  f->fileSize = 0;
3502 
3503  return f;
3504 }
3505 
3506 /*
3507 ===========
3508 idFileSystemLocal::OpenFileAppend
3509 ===========
3510 */
3511 idFile *idFileSystemLocal::OpenFileAppend( const char *relativePath, bool sync, const char *basePath ) {
3512  const char *path;
3513  idStr OSpath;
3515 
3516  if ( !searchPaths ) {
3517  common->FatalError( "Filesystem call made without initialization\n" );
3518  }
3519 
3520  path = cvarSystem->GetCVarString( basePath );
3521  if ( !path[0] ) {
3522  path = fs_savepath.GetString();
3523  }
3524 
3525  OSpath = BuildOSPath( path, gameFolder, relativePath );
3526  CreateOSPath( OSpath );
3527 
3528  if ( fs_debug.GetInteger() ) {
3529  common->Printf( "idFileSystem::OpenFileAppend: %s\n", OSpath.c_str() );
3530  }
3531 
3532  f = new idFile_Permanent();
3533  f->o = OpenOSFile( OSpath, "ab" );
3534  if ( !f->o ) {
3535  delete f;
3536  return NULL;
3537  }
3538  f->name = relativePath;
3539  f->fullPath = OSpath;
3540  f->mode = ( 1 << FS_WRITE ) + ( 1 << FS_APPEND );
3541  f->handleSync = sync;
3542  f->fileSize = DirectFileLength( f->o );
3543 
3544  return f;
3545 }
3546 
3547 /*
3548 ================
3549 idFileSystemLocal::OpenFileByMode
3550 ================
3551 */
3553  if ( mode == FS_READ ) {
3554  return OpenFileRead( relativePath );
3555  }
3556  if ( mode == FS_WRITE ) {
3557  return OpenFileWrite( relativePath );
3558  }
3559  if ( mode == FS_APPEND ) {
3560  return OpenFileAppend( relativePath, true );
3561  }
3562  common->FatalError( "idFileSystemLocal::OpenFileByMode: bad mode" );
3563  return NULL;
3564 }
3565 
3566 /*
3567 ==============
3568 idFileSystemLocal::CloseFile
3569 ==============
3570 */
3572  if ( !searchPaths ) {
3573  common->FatalError( "Filesystem call made without initialization\n" );
3574  }
3575  delete f;
3576 }
3577 
3578 
3579 /*
3580 =================================================================================
3581 
3582 back ground loading
3583 
3584 =================================================================================
3585 */
3586 
3587 /*
3588 =================
3589 idFileSystemLocal::CurlWriteFunction
3590 =================
3591 */
3592 size_t idFileSystemLocal::CurlWriteFunction( void *ptr, size_t size, size_t nmemb, void *stream ) {
3593  backgroundDownload_t *bgl = (backgroundDownload_t *)stream;
3594  if ( !bgl->f ) {
3595  return size * nmemb;
3596  }
3597  #ifdef _WIN32
3598  return _write( static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr()->_file, ptr, size * nmemb );
3599  #else
3600  return fwrite( ptr, size, nmemb, static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr() );
3601  #endif
3602 }
3603 
3604 /*
3605 =================
3606 idFileSystemLocal::CurlProgressFunction
3607 =================
3608 */
3609 int idFileSystemLocal::CurlProgressFunction( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow ) {
3610  backgroundDownload_t *bgl = (backgroundDownload_t *)clientp;
3611  if ( bgl->url.status == DL_ABORTING ) {
3612  return 1;
3613  }
3614  bgl->url.dltotal = dltotal;
3615  bgl->url.dlnow = dlnow;
3616  return 0;
3617 }
3618 
3619 /*
3620 ===================
3621 BackgroundDownload
3622 
3623 Reads part of a file from a background thread.
3624 ===================
3625 */
3627  while( 1 ) {
3629  backgroundDownload_t *bgl = fileSystemLocal.backgroundDownloads;
3630  if ( !bgl ) {
3632  Sys_WaitForEvent();
3633  continue;
3634  }
3635  // remove this from the list
3636  fileSystemLocal.backgroundDownloads = bgl->next;
3638 
3639  bgl->next = NULL;
3640 
3641  if ( bgl->opcode == DLTYPE_FILE ) {
3642  // use the low level read function, because fread may allocate memory
3643  #if defined(WIN32)
3644  _read( static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr()->_file, bgl->file.buffer, bgl->file.length );
3645  #else
3646  fread( bgl->file.buffer, bgl->file.length, 1, static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr() );
3647  #endif
3648  bgl->completed = true;
3649  } else {
3650 #if ID_ENABLE_CURL
3651  // DLTYPE_URL
3652  // use a local buffer for curl error since the size define is local
3653  char error_buf[ CURL_ERROR_SIZE ];
3654  bgl->url.dlerror[ 0 ] = '\0';
3656  CURLcode ret;
3657  if ( !session ) {
3659  bgl->url.status = DL_FAILED;
3660  bgl->completed = true;
3661  continue;
3662  }
3663  ret = curl_easy_setopt( session, CURLOPT_ERRORBUFFER, error_buf );
3664  if ( ret ) {
3665  bgl->url.dlstatus = ret;
3666  bgl->url.status = DL_FAILED;
3667  bgl->completed = true;
3668  continue;
3669  }
3670  ret = curl_easy_setopt( session, CURLOPT_URL, bgl->url.url.c_str() );
3671  if ( ret ) {
3672  bgl->url.dlstatus = ret;
3673  bgl->url.status = DL_FAILED;
3674  bgl->completed = true;
3675  continue;
3676  }
3677  ret = curl_easy_setopt( session, CURLOPT_FAILONERROR, 1 );
3678  if ( ret ) {
3679  bgl->url.dlstatus = ret;
3680  bgl->url.status = DL_FAILED;
3681  bgl->completed = true;
3682  continue;
3683  }
3684  ret = curl_easy_setopt( session, CURLOPT_WRITEFUNCTION, idFileSystemLocal::CurlWriteFunction );
3685  if ( ret ) {
3686  bgl->url.dlstatus = ret;
3687  bgl->url.status = DL_FAILED;
3688  bgl->completed = true;
3689  continue;
3690  }
3691  ret = curl_easy_setopt( session, CURLOPT_WRITEDATA, bgl );
3692  if ( ret ) {
3693  bgl->url.dlstatus = ret;
3694  bgl->url.status = DL_FAILED;
3695  bgl->completed = true;
3696  continue;
3697  }
3698  ret = curl_easy_setopt( session, CURLOPT_NOPROGRESS, 0 );
3699  if ( ret ) {
3700  bgl->url.dlstatus = ret;
3701  bgl->url.status = DL_FAILED;
3702  bgl->completed = true;
3703  continue;
3704  }
3705  ret = curl_easy_setopt( session, CURLOPT_PROGRESSFUNCTION, idFileSystemLocal::CurlProgressFunction );
3706  if ( ret ) {
3707  bgl->url.dlstatus = ret;
3708  bgl->url.status = DL_FAILED;
3709  bgl->completed = true;
3710  continue;
3711  }
3712  ret = curl_easy_setopt( session, CURLOPT_PROGRESSDATA, bgl );
3713  if ( ret ) {
3714  bgl->url.dlstatus = ret;
3715  bgl->url.status = DL_FAILED;
3716  bgl->completed = true;
3717  continue;
3718  }
3719  bgl->url.dlnow = 0;
3720  bgl->url.dltotal = 0;
3721  bgl->url.status = DL_INPROGRESS;
3722  ret = curl_easy_perform( session );
3723  if ( ret ) {
3724  Sys_Printf( "curl_easy_perform failed: %s\n", error_buf );
3725  idStr::Copynz( bgl->url.dlerror, error_buf, MAX_STRING_CHARS );
3726  bgl->url.dlstatus = ret;
3727  bgl->url.status = DL_FAILED;
3728  bgl->completed = true;
3729  continue;
3730  }
3731  bgl->url.status = DL_DONE;
3732  bgl->completed = true;
3733 #else
3734  bgl->url.status = DL_FAILED;
3735  bgl->completed = true;
3736 #endif
3737  }
3738  }
3739  return 0;
3740 }
3741 
3742 /*
3743 =================
3744 idFileSystemLocal::StartBackgroundReadThread
3745 =================
3746 */
3748  if ( !backgroundThread.threadHandle ) {
3750  if ( !backgroundThread.threadHandle ) {
3751  common->Warning( "idFileSystemLocal::StartBackgroundDownloadThread: failed" );
3752  }
3753  } else {
3754  common->Printf( "background thread already running\n" );
3755  }
3756 }
3757 
3758 /*
3759 =================
3760 idFileSystemLocal::BackgroundDownload
3761 =================
3762 */
3764  if ( bgl->opcode == DLTYPE_FILE ) {
3765  if ( dynamic_cast<idFile_Permanent *>(bgl->f) ) {
3766  // add the bgl to the background download list
3768  bgl->next = backgroundDownloads;
3769  backgroundDownloads = bgl;
3770  Sys_TriggerEvent();
3772  } else {
3773  // read zipped file directly
3774  bgl->f->Seek( bgl->file.position, FS_SEEK_SET );
3775  bgl->f->Read( bgl->file.buffer, bgl->file.length );
3776  bgl->completed = true;
3777  }
3778  } else {
3780  bgl->next = backgroundDownloads;
3781  backgroundDownloads = bgl;
3782  Sys_TriggerEvent();
3784  }
3785 }
3786 
3787 /*
3788 =================
3789 idFileSystemLocal::PerformingCopyFiles
3790 =================
3791 */
3793  return fs_copyfiles.GetInteger() > 0;
3794 }
3795 
3796 /*
3797 =================
3798 idFileSystemLocal::FindPakForFileChecksum
3799 =================
3800 */
3801 pack_t *idFileSystemLocal::FindPakForFileChecksum( const char *relativePath, int findChecksum, bool bReference ) {
3802  searchpath_t *search;
3803  pack_t *pak;
3804  fileInPack_t *pakFile;
3805  int hash;
3806  assert( !serverPaks.Num() );
3807  hash = HashFileName( relativePath );
3808  for ( search = searchPaths; search; search = search->next ) {
3809  if ( search->pack && search->pack->hashTable[ hash ] ) {
3810  pak = search->pack;
3811  for ( pakFile = pak->hashTable[ hash ]; pakFile; pakFile = pakFile->next ) {
3812  if ( !FilenameCompare( pakFile->name, relativePath ) ) {
3813  idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
3814  if ( findChecksum == GetFileChecksum( file ) ) {
3815  if ( fs_debug.GetBool() ) {
3816  common->Printf( "found '%s' with checksum 0x%x in pak '%s'\n", relativePath, findChecksum, pak->pakFilename.c_str() );
3817  }
3818  if ( bReference ) {
3819  pak->referenced = true;
3820  // FIXME: use dependencies for pak references
3821  }
3822  CloseFile( file );
3823  return pak;
3824  } else if ( fs_debug.GetBool() ) {
3825  common->Printf( "'%s' in pak '%s' has != checksum %x\n", relativePath, pak->pakFilename.c_str(), GetFileChecksum( file ) );
3826  }
3827  CloseFile( file );
3828  }
3829  }
3830  }
3831  }
3832  if ( fs_debug.GetBool() ) {
3833  common->Printf( "no pak file found for '%s' checksumed %x\n", relativePath, findChecksum );
3834  }
3835  return NULL;
3836 }
3837 
3838 /*
3839 =================
3840 idFileSystemLocal::GetFileChecksum
3841 =================
3842 */
3844  int len, ret;
3845  byte *buf;
3846 
3847  file->Seek( 0, FS_SEEK_END );
3848  len = file->Tell();
3849  file->Seek( 0, FS_SEEK_SET );
3850  buf = (byte *)Mem_Alloc( len );
3851  if ( file->Read( buf, len ) != len ) {
3852  common->FatalError( "Short read in idFileSystemLocal::GetFileChecksum()\n" );
3853  }
3854  ret = MD4_BlockChecksum( buf, len );
3855  Mem_Free( buf );
3856  return ret;
3857 }
3858 
3859 /*
3860 =================
3861 idFileSystemLocal::FindDLL
3862 =================
3863 */
3864 void idFileSystemLocal::FindDLL( const char *name, char _dllPath[ MAX_OSPATH ], bool updateChecksum ) {
3865  idFile *dllFile = NULL;
3866  char dllName[MAX_OSPATH];
3867  idStr dllPath;
3868  int dllHash;
3869  pack_t *inPak;
3870  pack_t *pak;
3871  fileInPack_t *pakFile;
3872 
3873  sys->DLL_GetFileName( name, dllName, MAX_OSPATH );
3874  dllHash = HashFileName( dllName );
3875 
3876 #if ID_FAKE_PURE
3877  if ( 1 ) {
3878 #else
3879  if ( !serverPaks.Num() ) {
3880 #endif
3881  // from executable directory first - this is handy for developement
3882  dllPath = Sys_EXEPath( );
3883  dllPath.StripFilename( );
3884  dllPath.AppendPath( dllName );
3885  dllFile = OpenExplicitFileRead( dllPath );
3886  }
3887  if ( !dllFile ) {
3888  if ( !serverPaks.Num() ) {
3889  // not running in pure mode, try to extract from a pak file first
3891  if ( dllFile ) {
3892  common->Printf( "found DLL in pak file: %s\n", dllFile->GetFullPath() );
3893  dllPath = RelativePathToOSPath( dllName, "fs_savepath" );
3894  CopyFile( dllFile, dllPath );
3895  CloseFile( dllFile );
3896  dllFile = OpenFileReadFlags( dllName, FSFLAG_SEARCH_DIRS );
3897  if ( !dllFile ) {
3898  common->Error( "DLL extraction to fs_savepath failed\n" );
3899  } else if ( updateChecksum ) {
3900  gameDLLChecksum = GetFileChecksum( dllFile );
3901  gamePakChecksum = inPak->checksum;
3902  updateChecksum = false; // don't try again below
3903  }
3904  } else {
3905  // didn't find a source in a pak file, try in the directory
3906  dllFile = OpenFileReadFlags( dllName, FSFLAG_SEARCH_DIRS );
3907  if ( dllFile ) {
3908  if ( updateChecksum ) {
3909  gameDLLChecksum = GetFileChecksum( dllFile );
3910  // see if we can mark a pak file
3911  pak = FindPakForFileChecksum( dllName, gameDLLChecksum, false );
3912  pak ? gamePakChecksum = pak->checksum : gamePakChecksum = 0;
3913  updateChecksum = false;
3914  }
3915  }
3916  }
3917  } else {
3918  // we are in pure mode. this path to be reached only for game DLL situations
3919  // with a code pak checksum given by server
3921  assert( updateChecksum );
3923  if ( !pak ) {
3924  // not supposed to happen, bug in pure code?
3925  common->Warning( "FindDLL in pure mode: game pak not found ( 0x%x )\n", gamePakChecksum );
3926  } else {
3927  // extract and copy
3928  for ( pakFile = pak->hashTable[dllHash]; pakFile; pakFile = pakFile->next ) {
3929  if ( !FilenameCompare( pakFile->name, dllName ) ) {
3930  dllFile = ReadFileFromZip( pak, pakFile, dllName );
3931  common->Printf( "found DLL in game pak file: %s\n", pak->pakFilename.c_str() );
3932  dllPath = RelativePathToOSPath( dllName, "fs_savepath" );
3933  CopyFile( dllFile, dllPath );
3934  CloseFile( dllFile );
3935  dllFile = OpenFileReadFlags( dllName, FSFLAG_SEARCH_DIRS );
3936  if ( !dllFile ) {
3937  common->Error( "DLL extraction to fs_savepath failed\n" );
3938  } else {
3939  gameDLLChecksum = GetFileChecksum( dllFile );
3940  updateChecksum = false; // don't try again below
3941  }
3942  }
3943  }
3944  }
3945  }
3946  }
3947  if ( updateChecksum ) {
3948  if ( dllFile ) {
3949  gameDLLChecksum = GetFileChecksum( dllFile );
3950  } else {
3951  gameDLLChecksum = 0;
3952  }
3953  gamePakChecksum = 0;
3954  }
3955  if ( dllFile ) {
3956  dllPath = dllFile->GetFullPath( );
3957  CloseFile( dllFile );
3958  dllFile = NULL;
3959  } else {
3960  dllPath = "";
3961  }
3962  idStr::snPrintf( _dllPath, MAX_OSPATH, dllPath.c_str() );
3963 }
3964 
3965 /*
3966 ================
3967 idFileSystemLocal::ClearDirCache
3968 ================
3969 */
3971  int i;
3972 
3973  dir_cache_index = 0;
3974  dir_cache_count = 0;
3975  for( i = 0; i < MAX_CACHED_DIRS; i++ ) {
3976  dir_cache[ i ].Clear();
3977  }
3978 }
3979 
3980 /*
3981 ===============
3982 idFileSystemLocal::HasD3XP
3983 ===============
3984 */
3986  int i;
3987  idStrList dirs, pk4s;
3988  idStr gamepath;
3989 
3990  if ( d3xp == -1 ) {
3991  return false;
3992  } else if ( d3xp == 1 ) {
3993  return true;
3994  }
3995 
3996 #if 0
3997  // check for a d3xp directory with a pk4 file
3998  // copied over from ListMods - only looks in basepath
3999  ListOSFiles( fs_basepath.GetString(), "/", dirs );
4000  for ( i = 0; i < dirs.Num(); i++ ) {
4001  if ( dirs[i].Icmp( "d3xp" ) == 0 ) {
4002  gamepath = BuildOSPath( fs_basepath.GetString(), dirs[ i ], "" );
4003  ListOSFiles( gamepath, ".pk4", pk4s );
4004  if ( pk4s.Num() ) {
4005  d3xp = 1;
4006  return true;
4007  }
4008  }
4009  }
4010 #elif ID_ALLOW_D3XP
4011  // check for d3xp's d3xp/pak000.pk4 in any search path
4012  // checking wether the pak is loaded by checksum wouldn't be enough:
4013  // we may have a different fs_game right now but still need to reply that it's installed
4014  const char *search[4];
4015  idFile *pakfile;
4016  search[0] = fs_savepath.GetString();
4017  search[1] = fs_devpath.GetString();
4018  search[2] = fs_basepath.GetString();
4019  search[3] = fs_cdpath.GetString();
4020  for ( i = 0; i < 4; i++ ) {
4021  pakfile = OpenExplicitFileRead( BuildOSPath( search[ i ], "d3xp", "pak000.pk4" ) );
4022  if ( pakfile ) {
4023  CloseFile( pakfile );
4024  d3xp = 1;
4025  return true;
4026  }
4027  }
4028 #endif
4029 
4030 #if ID_ALLOW_D3XP
4031  // if we didn't find a pk4 file then the user might have unpacked so look for default.cfg file
4032  // that's the old way mostly used during developement. don't think it hurts to leave it there
4033  ListOSFiles( fs_basepath.GetString(), "/", dirs );
4034  for ( i = 0; i < dirs.Num(); i++ ) {
4035  if ( dirs[i].Icmp( "d3xp" ) == 0 ) {
4036 
4037  gamepath = BuildOSPath( fs_savepath.GetString(), dirs[ i ], "default.cfg" );
4038  idFile* cfg = OpenExplicitFileRead(gamepath);
4039  if(cfg) {
4040  CloseFile(cfg);
4041  d3xp = 1;
4042  return true;
4043  }
4044  }
4045  }
4046 #endif
4047  d3xp = -1;
4048  return false;
4049 }
4050 
4051 /*
4052 ===============
4053 idFileSystemLocal::RunningD3XP
4054 ===============
4055 */
4057  // TODO: mark the checksum of the gold XP and check for it being referenced ( for double mod support )
4058  // a simple fs_game check should be enough for now..
4059  if ( !idStr::Icmp( fs_game.GetString(), "d3xp" ) ||
4060  !idStr::Icmp( fs_game_base.GetString(), "d3xp" ) ) {
4061  return true;
4062  }
4063  return false;
4064 }
4065 
4066 /*
4067 ===============
4068 idFileSystemLocal::MakeTemporaryFile
4069 ===============
4070 */
4072  FILE *f = tmpfile();
4073  if ( !f ) {
4074  common->Warning( "idFileSystem::MakeTemporaryFile failed: %s", strerror( errno ) );
4075  return NULL;
4076  }
4077  idFile_Permanent *file = new idFile_Permanent();
4078  file->o = f;
4079  file->name = "<tempfile>";
4080  file->fullPath = "<tempfile>";
4081  file->mode = ( 1 << FS_READ ) + ( 1 << FS_WRITE );
4082  file->fileSize = 0;
4083  return file;
4084 }
4085 
4086 /*
4087 ===============
4088 idFileSystemLocal::FindFile
4089 ===============
4090 */
4091  findFile_t idFileSystemLocal::FindFile( const char *path, bool scheduleAddons ) {
4092  pack_t *pak;
4094  if ( !f ) {
4095  return FIND_NO;
4096  }
4097  if ( !pak ) {
4098  // found in FS, not even in paks
4099  return FIND_YES;
4100  }
4101  // marking addons for inclusion on reload - may need to do that even when already in the search path
4102  if ( scheduleAddons && pak->addon && addonChecksums.FindIndex( pak->checksum ) < 0 ) {
4103  addonChecksums.Append( pak->checksum );
4104  }
4105  // an addon that's not on search list yet? that will require a restart
4106  if ( pak->addon && !pak->addon_search ) {
4107  delete f;
4108  return FIND_ADDON;
4109  }
4110  delete f;
4111  return FIND_YES;
4112 }
4113 
4114 /*
4115 ===============
4116 idFileSystemLocal::GetNumMaps
4117 account for actual decls and for addon maps
4118 ===============
4119 */
4121  int i;
4122  searchpath_t *search = NULL;
4123  int ret = declManager->GetNumDecls( DECL_MAPDEF );
4124 
4125  // add to this all addon decls - coming from all addon packs ( searched or not )
4126  for ( i = 0; i < 2; i++ ) {
4127  if ( i == 0 ) {
4128  search = searchPaths;
4129  } else if ( i == 1 ) {
4130  search = addonPaks;
4131  }
4132  for ( ; search ; search = search->next ) {
4133  if ( !search->pack || !search->pack->addon || !search->pack->addon_info ) {
4134  continue;
4135  }
4136  ret += search->pack->addon_info->mapDecls.Num();
4137  }
4138  }
4139  return ret;
4140 }
4141 
4142 /*
4143 ===============
4144 idFileSystemLocal::GetMapDecl
4145 retrieve the decl dictionary, add a 'path' value
4146 ===============
4147 */
4149  int i;
4150  const idDecl *mapDecl;
4151  const idDeclEntityDef *mapDef;
4152  int numdecls = declManager->GetNumDecls( DECL_MAPDEF );
4153  searchpath_t *search = NULL;
4154 
4155  if ( idecl < numdecls ) {
4156  mapDecl = declManager->DeclByIndex( DECL_MAPDEF, idecl );
4157  mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
4158  if ( !mapDef ) {
4159  common->Error( "idFileSystemLocal::GetMapDecl %d: not found\n", idecl );
4160  }
4161  mapDict = mapDef->dict;
4162  mapDict.Set( "path", mapDef->GetName() );
4163  return &mapDict;
4164  }
4165  idecl -= numdecls;
4166  for ( i = 0; i < 2; i++ ) {
4167  if ( i == 0 ) {
4168  search = searchPaths;
4169  } else if ( i == 1 ) {
4170  search = addonPaks;
4171  }
4172  for ( ; search ; search = search->next ) {
4173  if ( !search->pack || !search->pack->addon || !search->pack->addon_info ) {
4174  continue;
4175  }
4176  // each addon may have a bunch of map decls
4177  if ( idecl < search->pack->addon_info->mapDecls.Num() ) {
4178  mapDict = *search->pack->addon_info->mapDecls[ idecl ];
4179  return &mapDict;
4180  }
4181  idecl -= search->pack->addon_info->mapDecls.Num();
4182  assert( idecl >= 0 );
4183  }
4184  }
4185  return NULL;
4186 }
4187 
4188 /*
4189 ===============
4190 idFileSystemLocal::FindMapScreenshot
4191 ===============
4192 */
4193 void idFileSystemLocal::FindMapScreenshot( const char *path, char *buf, int len ) {
4194  idFile *file;
4195  idStr mapname = path;
4196 
4197  mapname.StripPath();
4198  mapname.StripFileExtension();
4199 
4200  idStr::snPrintf( buf, len, "guis/assets/splash/%s.tga", mapname.c_str() );
4201  if ( ReadFile( buf, NULL, NULL ) == -1 ) {
4202  // try to extract from an addon
4203  file = OpenFileReadFlags( buf, FSFLAG_SEARCH_ADDONS );
4204  if ( file ) {
4205  // save it out to an addon splash directory
4206  int dlen = file->Length();
4207  char *data = new char[ dlen ];
4208  file->Read( data, dlen );
4209  CloseFile( file );
4210  idStr::snPrintf( buf, len, "guis/assets/splash/addon/%s.tga", mapname.c_str() );
4211  WriteFile( buf, data, dlen );
4212  delete[] data;
4213  } else {
4214  idStr::Copynz( buf, "guis/assets/splash/pdtempa", len );
4215  }
4216  }
4217 }
void Sys_Mkdir(const char *path)
Definition: posix_main.cpp:189
#define UNZ_OK
Definition: Unzip.h:165
void Startup(void)
bool excludeExtension(const pureExclusion_t &excl, int l, const idStr &name)
Definition: FileSystem.cpp:186
static idCVar fs_caseSensitiveOS
Definition: FileSystem.cpp:425
#define strcmp
Definition: Str.h:41
pack_t * FindPakForFileChecksum(const char *relativePath, int fileChecksum, bool bReference)
unsigned int dword
Definition: Lib.h:77
static int snPrintf(char *dest, int size, const char *fmt,...) id_attribute((format(printf
Definition: Str.cpp:1465
struct searchpath_s searchpath_t
static idCVar fs_restrict
Definition: FileSystem.cpp:417
int type
Definition: Token.h:77
int gamePakForOS[MAX_GAME_OS]
Definition: FileSystem.cpp:440
idStr directory
Definition: FileSystem.cpp:336
virtual void UpdatePureServerChecksums(void)
tuple sp
Definition: prepare.py:20
assert(prefInfo.fullscreenBtn)
virtual idFile * OpenFileByMode(const char *relativePath, fsMode_t mode)
bool(* pureExclusionFunc_t)(const struct pureExclusion_s &excl, int l, const idStr &name)
Definition: FileSystem.cpp:176
virtual void ResetReadCount(void)
Definition: FileSystem.cpp:380
#define OS
virtual void FreeModList(idModList *modList)
int Cmp(const char *text) const
Definition: Str.h:652
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
virtual idFileList * ListFilesTree(const char *relativePath, const char *extension, bool sort=false, const char *gamedir=NULL)
int length
Definition: FileSystem.cpp:287
static idCVar fs_debug
Definition: FileSystem.cpp:416
const char * Sys_DefaultSavePath(void)
Definition: main.cpp:119
int unzClose(unzFile file)
Definition: Unzip.cpp:1404
virtual int Length(void)
Definition: File.cpp:1310
int Next(const int index) const
Definition: HashIndex.h:247
void ToLower(void)
Definition: Str.h:817
virtual idFile * MakeTemporaryFile(void)
const int DECL_LEXER_FLAGS
Definition: DeclManager.h:93
virtual void ClearDirCache(void)
fsPureReply_t
Definition: FileSystem.h:66
idList< idStr > & operator=(const idList< idStr > &other)
int GetFileList(const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char *gamedir=NULL)
int ListOSFiles(const char *directory, const char *extension, idStrList &list)
virtual idFile * OpenFileAppend(const char *relativePath, bool sync=false, const char *basePath="fs_basepath")
virtual bool UpdateGamePakChecksums(void)
virtual const char * BuildOSPath(const char *base, const char *game, const char *relativePath)
Definition: FileSystem.cpp:801
static idCVar fs_devpath
Definition: FileSystem.cpp:422
void * z
Definition: File.h:240
urlDownload_t url
Definition: FileSystem.h:117
virtual int ReadFile(const char *relativePath, void **buffer, ID_TIME_T *timestamp=NULL)=0
unzFile unzOpen(const char *path)
Definition: Unzip.cpp:1309
virtual void RemoveFile(const char *relativePath)
Definition: FileSystem.cpp:954
const char * name
Definition: FileSystem.cpp:181
addonInfo_t * addon_info
Definition: FileSystem.cpp:292
virtual void SetRestartChecksums(const int pureChecksums[MAX_PURE_PAKS], int gamePakChecksum)
int Length(void) const
Definition: Str.h:702
virtual void FreeFileList(idFileList *fileList)
char dlerror[MAX_STRING_CHARS]
Definition: FileSystem.h:99
int GetNumFiles(void) const
Definition: FileSystem.h:126
void Init(const char *directory, const char *extension, const idStrList &list)
static idCVar fs_savepath
Definition: FileSystem.cpp:420
void Clear(void)
int DirectFileLength(FILE *o)
Definition: FileSystem.cpp:663
virtual idFile * OpenExplicitFileRead(const char *OSPath)
virtual void ClearPureChecksums(void)
void Sys_Printf(const char *msg,...)
virtual void Init(void)
fileInPack_t * hashTable[FILE_HASH_SIZE]
Definition: FileSystem.cpp:295
unsigned long crc
Definition: Unzip.h:70
struct searchpath_s * next
Definition: FileSystem.cpp:307
virtual int WriteFile(const char *relativePath, const void *buffer, int size, const char *basePath="fs_savepath")
void StripTrailing(const char c)
Definition: Str.cpp:515
void GetExtensionList(const char *extension, idStrList &extensionList) const
case const int
Definition: Callbacks.cpp:52
#define BINARY_CONFIG
Definition: FileSystem.cpp:323
#define CDKEY_FILE
Definition: Licensee.h:110
dlStatus_t status
Definition: FileSystem.h:103
bool FileAllowedFromDir(const char *path)
virtual idModList * ListMods(void)
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
idCVar com_developer("developer","0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT,"developer mode")
void SetGranularity(int newgranularity)
Definition: List.h:305
virtual findFile_t FindFile(const char *path, bool scheduleAddons)
int unzGetGlobalInfo(unzFile file, unz_global_info *pglobal_info)
Definition: Unzip.cpp:1424
CURLcode curl_easy_perform(CURL *curl)
Definition: easy.c:260
CURLcode
Definition: curl.h:209
#define FSFLAG_BINARY_ONLY
Definition: FileSystem.cpp:314
virtual int GetNumMaps()
#define ID_PURE_ALLOWDDS
Definition: BuildDefines.h:63
virtual void GetPureServerChecksums(int checksums[MAX_PURE_PAKS], int OS, int *gamePakChecksum)
const char * GetName(void) const
Definition: DeclManager.h:140
int unzSetCurrentFileInfoPosition(unzFile file, unsigned long pos)
Definition: Unzip.cpp:1691
bool excludeFullName(const pureExclusion_t &excl, int l, const idStr &name)
Definition: FileSystem.cpp:200
virtual void RemoveCommand(const char *cmdName)=0
int AddUnique(const char *name, idStrList &list, idHashIndex &hashIndex) const
idDEntry()
Definition: FileSystem.cpp:328
int LoadMemory(const char *ptr, int length, const char *name)
Definition: Parser.cpp:3049
idStrList list
Definition: FileSystem.h:132
virtual const idDict * GetMapDecl(int i)
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
Definition: Token.h:71
bool handleSync
Definition: File.h:213
virtual int Tell(void)
Definition: File.cpp:217
FILE * o
Definition: File.h:212
void SetFlags(int flags)
Definition: Lexer.h:298
#define TT_STRING
Definition: Token.h:41
GLdouble s
Definition: glext.h:2935
GLuint src
Definition: glext.h:5390
GLenum GLsizei len
Definition: glext.h:3472
void Set(const char *key, const char *value)
Definition: Dict.cpp:275
static size_t CurlWriteFunction(void *ptr, size_t size, size_t nmemb, void *stream)
idStr * list
Definition: List.h:138
CURLcode curl_easy_setopt(CURL *curl, CURLoption option,...)
Definition: easy.c:217
pureStatus_t
Definition: FileSystem.cpp:270
virtual bool FilenameCompare(const char *s1, const char *s2) const
Definition: FileSystem.cpp:557
bool excludePathPrefixAndExtension(const pureExclusion_t &excl, int l, const idStr &name)
Definition: FileSystem.cpp:193
virtual const char * GetName(void)
Definition: File.cpp:161
int i
Definition: process.py:33
int unzGoToNextFile(unzFile file)
Definition: Unzip.cpp:1648
int IsLoaded(void) const
Definition: Parser.h:94
#define ADDON_CONFIG
Definition: FileSystem.cpp:324
GLuint GLuint num
Definition: glext.h:5390
unzFile handle
Definition: FileSystem.cpp:284
virtual idFile * OpenFileReadFlags(const char *relativePath, int searchFlags, pack_t **foundInPak=NULL, bool allowCopyFiles=true, const char *gamedir=NULL)
#define FILE_HASH_SIZE
Definition: FileSystem.cpp:256
idList< int > depends
Definition: FileSystem.cpp:278
int Icmp(const char *text) const
Definition: Str.h:667
pureStatus_t pureStatus
Definition: FileSystem.cpp:293
idStr name
Definition: File.h:236
idStr & BackSlashesToSlashes(void)
Definition: Str.cpp:727
volatile bool completed
Definition: FileSystem.h:118
int First(const int key) const
Definition: HashIndex.h:238
list l
Definition: prepare.py:17
long Sys_FileTimeStamp(FILE *fp)
Definition: posix_main.cpp:433
fileDownload_t file
Definition: FileSystem.h:116
idStr & StripPath(void)
Definition: Str.cpp:885
static idCVar fs_game_base
Definition: FileSystem.cpp:424
bool addon_search
Definition: FileSystem.cpp:291
int Icmpn(const char *text, int n) const
Definition: Str.h:672
Definition: File.h:50
bool Matches(const char *directory, const char *extension) const
int ReadToken(idToken *token)
Definition: Parser.cpp:2338
findFile_t
Definition: FileSystem.h:91
idStr basePath
Definition: FileSystem.h:131
void SetupGameDirectories(const char *gameName)
int zipFilePos
Definition: File.h:238
Definition: Lexer.h:137
void Sort(cmp_t *compare=(cmp_t *)&idListSortCompare< type >)
Definition: List.h:898
int Sys_ListFiles(const char *directory, const char *extension, idStrList &list)
Definition: posix_main.cpp:198
virtual void Shutdown(bool reloading)
#define SEEK_END
Definition: Unzip.cpp:131
void ExtractFileName(idStr &dest) const
Definition: Str.cpp:921
int unzOpenCurrentFile(unzFile file)
Definition: Unzip.cpp:1852
virtual idFile * OpenFileRead(const char *relativePath, bool allowCopyFiles=true, const char *gamedir=NULL)
#define FSFLAG_SEARCH_DIRS
Definition: FileSystem.cpp:311
virtual void Flush(void)
Definition: File.cpp:234
addonInfo_t * ParseAddonDef(const char *buf, const int len)
void SetString(const char *value)
Definition: CVarSystem.h:146
virtual const char * RelativePathToOSPath(const char *relativePath, const char *basePath)
Definition: FileSystem.cpp:941
pack_t * LoadZipFile(const char *zipfile)
virtual int ValidateDownloadPakForChecksum(int checksum, char path[MAX_STRING_CHARS], bool isBinary)
static int CurlProgressFunction(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
const GLubyte * c
Definition: glext.h:4677
static void Path_f(const idCmdArgs &args)
idStr & StripFileExtension(void)
Definition: Str.cpp:757
#define BASE_GAMEDIR
Definition: Licensee.h:46
static void Dir_f(const idCmdArgs &args)
long HashFileName(const char *fname) const
Definition: FileSystem.cpp:528
struct backgroundDownload_s * next
Definition: FileSystem.h:113
#define MAX_STRING_CHARS
Definition: Lib.h:95
idSys * sys
Definition: maya_main.cpp:48
unsigned long uncompressed_size
Definition: Unzip.h:72
Definition: FileSystem.cpp:326
idFile_InZip * ReadFileFromZip(pack_t *pak, fileInPack_t *pakFile, const char *relativePath)
GLuint GLuint end
Definition: glext.h:2845
#define MAX_GAME_OS
Definition: FileSystem.cpp:322
virtual void virtual void virtual void DWarning(const char *fmt,...) id_attribute((format(printf
idCommon * common
Definition: Common.cpp:206
bool isNew
Definition: FileSystem.cpp:294
struct fileInPack_s * next
Definition: FileSystem.cpp:261
Definition: Dict.h:65
#define NULL
Definition: Lib.h:88
virtual const char * GetCVarString(const char *name) const =0
void Clear(void)
Definition: Dict.cpp:201
int g_thread_count
searchpath_t * searchPaths
Definition: FileSystem.cpp:406
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
GLuint buffer
Definition: glext.h:3108
virtual void SetCVarBool(const char *name, const bool value, int flags=0)=0
int GetInteger(void) const
Definition: CVarSystem.h:143
pureExclusionFunc_t func
Definition: FileSystem.cpp:183
virtual void AddToReadCount(int c)
Definition: FileSystem.cpp:381
virtual void CreateOSPath(const char *OSPath)
Definition: FileSystem.cpp:681
virtual bool FileIsInPAK(const char *relativePath)
Definition: FileSystem.cpp:973
FILE * OpenOSFile(const char *name, const char *mode, idStr *caseSensitiveName=NULL)
Definition: FileSystem.cpp:592
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
const char * GetFile(int index) const
Definition: FileSystem.h:127
virtual int Read(void *buffer, int len)
Definition: File.cpp:179
static void Copynz(char *dest, const char *src, int destsize)
Definition: Str.cpp:1376
pack_t * pack
Definition: FileSystem.cpp:305
GLint mode
Definition: glext.h:4165
#define FSFLAG_SEARCH_ADDONS
Definition: FileSystem.cpp:315
void * unzFile
Definition: Unzip.h:39
unz_file_info cur_file_info
Definition: Unzip.h:159
idDEntry dir_cache[MAX_CACHED_DIRS]
Definition: FileSystem.cpp:442
virtual void FreeFile(void *buffer)
dword BackgroundDownloadThread(void *parms)
binaryStatus_t binary
Definition: FileSystem.cpp:289
backgroundDownload_t * backgroundDownloads
Definition: FileSystem.cpp:428
type * Find(type const &obj) const
Definition: List.h:782
#define MAX_CACHED_DIRS
Definition: FileSystem.cpp:319
const char * path
Definition: sws.c:117
virtual bool HasD3XP(void)
int Argc(void) const
Definition: CmdArgs.h:48
virtual void StartBackgroundDownloadThread(void)
void DeleteContents(bool clear)
Definition: List.h:207
idStr pakFilename
Definition: FileSystem.cpp:283
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
unsigned long MD4_BlockChecksum(const void *data, int length)
Definition: MD4.cpp:247
virtual bool RunningD3XP(void)
xthreadInfo * g_threads[MAX_THREADS]
void Clear(void)
Definition: Str.h:724
binaryStatus_t
Definition: FileSystem.cpp:264
struct fileInPack_s fileInPack_t
bool addon
Definition: FileSystem.cpp:290
unsigned int(* xthread_t)(void *)
Definition: sys_public.h:480
const idKeyValue * FindKey(const char *key) const
Definition: Dict.cpp:451
virtual void UpdateScreen(bool outOfSequence=true)=0
virtual idFileList * ListFiles(const char *relativePath, const char *extension, bool sort=false, bool fullRelativePath=false, const char *gamedir=NULL)
virtual void Printf(const char *fmt,...) id_attribute((format(printf
int LoadMemory(const char *ptr, int length, const char *name, int startLine=1)
Definition: Lexer.cpp:1646
void AppendPath(const char *text)
Definition: Str.cpp:830
#define MAX_OSPATH
Definition: posix_main.cpp:47
int LittleLong(int l)
Definition: Lib.cpp:281
virtual int Seek(long offset, fsOrigin_t origin)
Definition: File.cpp:242
bool referenced
Definition: FileSystem.cpp:288
virtual ID_TIME_T Timestamp(void)
Definition: File.cpp:208
virtual void StartupVariable(const char *match, bool once)=0
idList< pack_t * > serverPaks
Definition: FileSystem.cpp:432
virtual ~idDEntry()
Definition: FileSystem.cpp:329
idDeclManager * declManager
idEventLoop * eventLoop
Definition: EventLoop.cpp:35
int GenerateKey(const char *string, bool caseSensitive=true) const
Definition: HashIndex.h:379
virtual int ReadFile(const char *relativePath, void **buffer, ID_TIME_T *timestamp)
struct pureExclusion_s pureExclusion_t
ID_INLINE void idStrListSortPaths(idStrList &list)
Definition: StrList.h:178
virtual const char * OSPathToRelativePath(const char *OSPath)
Definition: FileSystem.cpp:851
virtual int GetNumDecls(declType_t type)=0
virtual void Restart(void)
#define XPKEY_FILE
Definition: Licensee.h:111
xthreadInfo backgroundThread
Definition: FileSystem.cpp:430
const char * GetString(void) const
Definition: CVarSystem.h:141
bool IsNumeric(void) const
Definition: Str.h:833
friend dword BackgroundDownloadThread(void *parms)
static int FindChar(const char *str, const char c, int start=0, int end=-1)
Definition: Str.cpp:186
int Insert(const type &obj, int index=0)
Definition: List.h:679
void ReplaceSeparators(idStr &path, char sep=PATHSEPERATOR_CHAR)
Definition: FileSystem.cpp:786
int Append(const type &obj)
Definition: List.h:646
void void Warning(const char *str,...) id_attribute((format(printf
Definition: Lexer.cpp:241
GLdouble GLdouble GLdouble r
Definition: glext.h:2951
virtual int GetReadCount(void)
Definition: FileSystem.cpp:382
int unzGetCurrentFileInfoPosition(unzFile file, unsigned long *pos)
Definition: Unzip.cpp:1675
idList< int > restartChecksums
Definition: FileSystem.cpp:434
int unzGoToFirstFile(unzFile file)
Definition: Unzip.cpp:1627
idList< int > addonChecksums
Definition: FileSystem.cpp:435
unzFile unzReOpen(const char *path, unzFile file)
Definition: Unzip.cpp:1282
idStr fullPath
Definition: File.h:209
static WindowRef ValidModeCallbackProc inCallback OSStatus err
idStr fullPath
Definition: File.h:237
static void TouchFile_f(const idCmdArgs &args)
virtual void FindDLL(const char *basename, char dllPath[MAX_OSPATH], bool updateChecksum)
size_t fread(void *, size_t, size_t, FILE *)
searchpath_t * addonPaks
Definition: FileSystem.cpp:412
static idCVar fs_searchAddons
Definition: FileSystem.cpp:426
idList< idDict * > mapDecls
Definition: FileSystem.cpp:279
unsigned long number_entry
Definition: Unzip.h:57
backgroundDownload_t defaultBackgroundDownload
Definition: FileSystem.cpp:429
#define SEEK_SET
Definition: Unzip.cpp:129
GLuint id
Definition: glext.h:3103
bool GetBool(void) const
Definition: CVarSystem.h:142
static idCVar fs_copyfiles
Definition: FileSystem.cpp:418
virtual const idDecl * DeclByIndex(declType_t type, int index, bool forceParse=true)=0
tuple f
Definition: idal.py:89
int unzGetCurrentFileInfo(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize)
Definition: Unzip.cpp:1612
static void ArgCompletion_FileName(const idCmdArgs &args, void(*callback)(const char *s))
Definition: CmdSystem.h:152
const char * Sys_DefaultCDPath(void)
Definition: posix_main.cpp:429
virtual int GetOSMask(void)
void Sys_TriggerEvent(int index)
virtual void BackgroundDownload(backgroundDownload_t *bgl)
int Num(void) const
Definition: List.h:265
bool RemoveIndex(int index)
Definition: List.h:849
virtual void DLL_GetFileName(const char *baseName, char *dllName, int maxLength)=0
static idCVar fs_cdpath
Definition: FileSystem.cpp:421
unsigned char byte
Definition: Lib.h:75
virtual int AddZipFile(const char *path)
const GLcharARB * name
Definition: glext.h:3629
GLsizeiptr size
Definition: glext.h:3112
#define DEMO_PAK_CHECKSUM
Definition: DemoChecksum.h:39
virtual int Write(const void *buffer, int len)
Definition: File.cpp:189
#define FSFLAG_PURE_NOREF
Definition: FileSystem.cpp:313
idFile * com_journalDataFile
Definition: EventLoop.h:70
static void DirTree_f(const idCmdArgs &args)
Definition: Str.h:116
virtual bool PerformingCopyFiles(void) const
unsigned long pos
Definition: FileSystem.cpp:260
GLsizei const GLcharARB const GLint * length
Definition: glext.h:3599
void AddGameDirectory(const char *path, const char *dir)
virtual idFile * OpenExplicitFileWrite(const char *OSPath)
virtual void FindMapScreenshot(const char *path, char *buf, int len)
void * Mem_ClearedAlloc(const int size)
Definition: Heap.cpp:1149
const char * c_str(void) const
Definition: Str.h:487
int SkipUntilString(const char *string)
Definition: Lexer.cpp:1097
int threadHandle
Definition: sys_public.h:490
int JournalLevel(void) const
Definition: EventLoop.cpp:272
directory_t * dir
Definition: FileSystem.cpp:306
virtual bool IsInitialized(void) const
idStr extension
Definition: FileSystem.cpp:337
Definition: Unzip.h:145
void CURL
Definition: types.h:25
int FindIndex(const type &obj) const
Definition: List.h:761
virtual bool GetCVarBool(const char *name) const =0
#define CURL_ERROR_SIZE
Definition: curl.h:312
int GetFileChecksum(idFile *file)
unsigned char bool
Definition: setup.h:74
const char * Argv(int arg) const
Definition: CmdArgs.h:50
int fileSize
Definition: File.h:211
void Add(const int key, const int index)
Definition: HashIndex.h:193
int IcmpPrefixPath(const char *text) const
Definition: Str.h:697
idFileSystemLocal fileSystemLocal
Definition: FileSystem.cpp:499
idGame * game
Definition: Game_local.cpp:65
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
int numfiles
Definition: FileSystem.cpp:286
GLint j
Definition: qgl.h:264
static idCVar fs_basepath
Definition: FileSystem.cpp:419
virtual bool IsInitialized(void) const =0
idStrList mods
Definition: FileSystem.h:144
idSession * session
Definition: Session.cpp:48
char * va(const char *fmt,...)
Definition: Str.cpp:1568
virtual void CopyFile(const char *fromOSPath, const char *toOSPath)
Definition: FileSystem.cpp:711
virtual int Read(void *buffer, int len)
Definition: File.cpp:1262
idStrList descriptions
Definition: FileSystem.h:145
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
bool HasUpper(void) const
Definition: Str.h:845
virtual void Error(const char *fmt,...) id_attribute((format(printf
idStr name
Definition: File.h:208
virtual fsPureReply_t SetPureServerChecksums(const int pureChecksums[MAX_PURE_PAKS], int gamePakChecksum, int missingChecksums[MAX_PURE_PAKS], int *missingGamePakChecksum)
void FollowAddonDependencies(pack_t *pak)
FILE * OpenOSFileCorrectName(idStr &path, const char *mode)
Definition: FileSystem.cpp:647
pureStatus_t GetPackStatus(pack_t *pak)
fsMode_t
Definition: FileSystem.h:60
int GetFileListTree(const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char *gamedir=NULL)
virtual idFile * OpenFileWrite(const char *relativePath, const char *basePath="fs_savepath")
void SetRestrictions(void)
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
#define CURLOPT_WRITEDATA
Definition: curl.h:805
static void TouchFileList_f(const idCmdArgs &args)
void Sys_LeaveCriticalSection(int index)
void Sys_EnterCriticalSection(int index)
int ReadToken(idToken *token)
Definition: Lexer.cpp:820
size_t fwrite(const void *, size_t, size_t, FILE *)
int sprintf(idStr &string, const char *fmt,...)
Definition: Str.cpp:1528
fileInPack_t * buildBuffer
Definition: FileSystem.cpp:296
pack_t * GetPackForChecksum(int checksum, bool searchAddons=false)
idStr & StripFilename(void)
Definition: Str.cpp:864
virtual int Length(void)
Definition: File.cpp:199
virtual void CloseFile(idFile *f)
void Sys_WaitForEvent(int index)
bool Remove(const type &obj)
Definition: List.h:878
static idCVar fs_game
Definition: FileSystem.cpp:423
virtual const char * GetFullPath(void)
Definition: File.cpp:170
const char * Sys_EXEPath(void)
Definition: main.cpp:132
#define FSFLAG_SEARCH_PAKS
Definition: FileSystem.cpp:312
virtual void AddCommand(const char *cmdName, cmdFunction_t function, int flags, const char *description, argCompletion_t argCompletion=NULL)=0
CURL * curl_easy_init(void)
Definition: easy.c:195
void Sys_CreateThread(xthread_t function, void *parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo **threads, int *thread_count)
const char * Sys_DefaultBasePath(void)
Definition: main.cpp:158
int checksum
Definition: FileSystem.cpp:285
const char * ext
Definition: FileSystem.cpp:182
FILE * file
Definition: Unzip.h:147
#define MAX_ZIPPED_FILE_NAME
Definition: FileSystem.cpp:255
int fileSize
Definition: File.h:239
void Clear(void)
Definition: List.h:184