FileSystem.cpp 95 KB

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