Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1241 lines
37 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #undef PROTECTED_THINGS_ENABLE
  7. #undef PROTECT_FILEIO_FUNCTIONS
  8. #ifndef POSIX
  9. #undef fopen
  10. #endif
  11. #if defined( _WIN32 ) && !defined( _X360 )
  12. #include <windows.h>
  13. #include <direct.h>
  14. #include <io.h>
  15. #include <process.h>
  16. #elif defined( POSIX )
  17. #include <unistd.h>
  18. #define _chdir chdir
  19. #define _access access
  20. #endif
  21. #include <stdio.h>
  22. #include <sys/stat.h>
  23. #include "tier1/strtools.h"
  24. #include "tier1/utlbuffer.h"
  25. #include "filesystem_init.h"
  26. #include "tier0/icommandline.h"
  27. #include "KeyValues.h"
  28. #include "appframework/IAppSystemGroup.h"
  29. #include "tier1/smartptr.h"
  30. #if defined( _X360 )
  31. #include "xbox\xbox_win32stubs.h"
  32. #endif
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include <tier0/memdbgon.h>
  35. #if !defined( _X360 )
  36. #define GAMEINFO_FILENAME "gameinfo.txt"
  37. #else
  38. // The .xtx file is a TCR requirement, as .txt files cannot live on the DVD.
  39. // The .xtx file only exists outside the zips (same as .txt and is made during the image build) and is read to setup the search paths.
  40. // So all other code should be able to safely expect gameinfo.txt after the zip is mounted as the .txt file exists inside the zips.
  41. // The .xtx concept is private and should only have to occurr here. As a safety measure, if the .xtx file is not found
  42. // a retry is made with the original .txt name
  43. #define GAMEINFO_FILENAME "gameinfo.xtx"
  44. #endif
  45. #define GAMEINFO_FILENAME_ALTERNATE "gameinfo.txt"
  46. static char g_FileSystemError[256];
  47. static bool s_bUseVProjectBinDir = false;
  48. static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG;
  49. // Call this to use a bin directory relative to VPROJECT
  50. void FileSystem_UseVProjectBinDir( bool bEnable )
  51. {
  52. s_bUseVProjectBinDir = bEnable;
  53. }
  54. // This class lets you modify environment variables, and it restores the original value
  55. // when it goes out of scope.
  56. class CTempEnvVar
  57. {
  58. public:
  59. CTempEnvVar( const char *pVarName )
  60. {
  61. m_bRestoreOriginalValue = true;
  62. m_pVarName = pVarName;
  63. const char *pValue = NULL;
  64. #ifdef _WIN32
  65. // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
  66. // to the process environment after the DLL was loaded.
  67. char szBuf[ 4096 ];
  68. if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0)
  69. {
  70. pValue = szBuf;
  71. }
  72. #else
  73. // LINUX BUG: see above
  74. pValue = getenv( pVarName );
  75. #endif
  76. if ( pValue )
  77. {
  78. m_bExisted = true;
  79. m_OriginalValue.SetSize( Q_strlen( pValue ) + 1 );
  80. memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() );
  81. }
  82. else
  83. {
  84. m_bExisted = false;
  85. }
  86. }
  87. ~CTempEnvVar()
  88. {
  89. if ( m_bRestoreOriginalValue )
  90. {
  91. // Restore the original value.
  92. if ( m_bExisted )
  93. {
  94. SetValue( "%s", m_OriginalValue.Base() );
  95. }
  96. else
  97. {
  98. ClearValue();
  99. }
  100. }
  101. }
  102. void SetRestoreOriginalValue( bool bRestore )
  103. {
  104. m_bRestoreOriginalValue = bRestore;
  105. }
  106. int GetValue(char *pszBuf, int nBufSize )
  107. {
  108. if ( !pszBuf || ( nBufSize <= 0 ) )
  109. return 0;
  110. #ifdef _WIN32
  111. // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
  112. // to the process environment after the DLL was loaded.
  113. return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize );
  114. #else
  115. // LINUX BUG: see above
  116. const char *pszOut = getenv( m_pVarName );
  117. if ( !pszOut )
  118. {
  119. *pszBuf = '\0';
  120. return 0;
  121. }
  122. Q_strncpy( pszBuf, pszOut, nBufSize );
  123. return Q_strlen( pszBuf );
  124. #endif
  125. }
  126. void SetValue( const char *pValue, ... )
  127. {
  128. char valueString[4096];
  129. va_list marker;
  130. va_start( marker, pValue );
  131. Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker );
  132. va_end( marker );
  133. #ifdef WIN32
  134. char str[4096];
  135. Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString );
  136. _putenv( str );
  137. #else
  138. setenv( m_pVarName, valueString, 1 );
  139. #endif
  140. }
  141. void ClearValue()
  142. {
  143. #ifdef WIN32
  144. char str[512];
  145. Q_snprintf( str, sizeof( str ), "%s=", m_pVarName );
  146. _putenv( str );
  147. #else
  148. setenv( m_pVarName, "", 1 );
  149. #endif
  150. }
  151. private:
  152. bool m_bRestoreOriginalValue;
  153. const char *m_pVarName;
  154. bool m_bExisted;
  155. CUtlVector<char> m_OriginalValue;
  156. };
  157. class CSteamEnvVars
  158. {
  159. public:
  160. CSteamEnvVars() :
  161. m_SteamAppId( "SteamAppId" ),
  162. m_SteamUserPassphrase( "SteamUserPassphrase" ),
  163. m_SteamAppUser( "SteamAppUser" ),
  164. m_Path( "path" )
  165. {
  166. }
  167. void SetRestoreOriginalValue_ALL( bool bRestore )
  168. {
  169. m_SteamAppId.SetRestoreOriginalValue( bRestore );
  170. m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore );
  171. m_SteamAppUser.SetRestoreOriginalValue( bRestore );
  172. m_Path.SetRestoreOriginalValue( bRestore );
  173. }
  174. CTempEnvVar m_SteamAppId;
  175. CTempEnvVar m_SteamUserPassphrase;
  176. CTempEnvVar m_SteamAppUser;
  177. CTempEnvVar m_Path;
  178. };
  179. // ---------------------------------------------------------------------------------------------------- //
  180. // Helpers.
  181. // ---------------------------------------------------------------------------------------------------- //
  182. void Q_getwd( char *out, int outSize )
  183. {
  184. #if defined( _WIN32 ) || defined( WIN32 )
  185. _getcwd( out, outSize );
  186. Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
  187. #else
  188. getcwd( out, outSize );
  189. strcat( out, "/" );
  190. #endif
  191. Q_FixSlashes( out );
  192. }
  193. // ---------------------------------------------------------------------------------------------------- //
  194. // Module interface.
  195. // ---------------------------------------------------------------------------------------------------- //
  196. CFSSearchPathsInit::CFSSearchPathsInit()
  197. {
  198. m_pDirectoryName = NULL;
  199. m_pLanguage = NULL;
  200. m_ModPath[0] = 0;
  201. m_bMountHDContent = m_bLowViolence = false;
  202. }
  203. CFSSteamSetupInfo::CFSSteamSetupInfo()
  204. {
  205. m_pDirectoryName = NULL;
  206. m_bOnlyUseDirectoryName = false;
  207. m_bSteam = false;
  208. m_bToolsMode = true;
  209. m_bNoGameInfo = false;
  210. }
  211. CFSLoadModuleInfo::CFSLoadModuleInfo()
  212. {
  213. m_pFileSystemDLLName = NULL;
  214. m_pFileSystem = NULL;
  215. m_pModule = NULL;
  216. }
  217. CFSMountContentInfo::CFSMountContentInfo()
  218. {
  219. m_bToolsMode = true;
  220. m_pDirectoryName = NULL;
  221. m_pFileSystem = NULL;
  222. }
  223. const char *FileSystem_GetLastErrorString()
  224. {
  225. return g_FileSystemError;
  226. }
  227. KeyValues* ReadKeyValuesFile( const char *pFilename )
  228. {
  229. // Read in the gameinfo.txt file and null-terminate it.
  230. FILE *fp = fopen( pFilename, "rb" );
  231. if ( !fp )
  232. return NULL;
  233. CUtlVector<char> buf;
  234. fseek( fp, 0, SEEK_END );
  235. buf.SetSize( ftell( fp ) + 1 );
  236. fseek( fp, 0, SEEK_SET );
  237. fread( buf.Base(), 1, buf.Count()-1, fp );
  238. fclose( fp );
  239. buf[buf.Count()-1] = 0;
  240. KeyValues *kv = new KeyValues( "" );
  241. if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
  242. {
  243. kv->deleteThis();
  244. return NULL;
  245. }
  246. return kv;
  247. }
  248. static bool Sys_GetExecutableName( char *out, int len )
  249. {
  250. #if defined( _WIN32 )
  251. if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) )
  252. {
  253. return false;
  254. }
  255. #else
  256. if ( CommandLine()->GetParm(0) )
  257. {
  258. Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) );
  259. }
  260. else
  261. {
  262. return false;
  263. }
  264. #endif
  265. return true;
  266. }
  267. bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen )
  268. {
  269. exedir[0] = 0;
  270. if ( s_bUseVProjectBinDir )
  271. {
  272. const char *pProject = GetVProjectCmdLineValue();
  273. if ( !pProject )
  274. {
  275. // Check their registry.
  276. pProject = getenv( GAMEDIR_TOKEN );
  277. }
  278. if ( pProject )
  279. {
  280. Q_snprintf( exedir, exeDirLen, "%s%c..%cbin", pProject, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR );
  281. return true;
  282. }
  283. return false;
  284. }
  285. if ( !Sys_GetExecutableName( exedir, exeDirLen ) )
  286. return false;
  287. Q_StripFilename( exedir );
  288. if ( IsX360() )
  289. {
  290. // The 360 can have its exe and dlls reside on different volumes
  291. // use the optional basedir as the exe dir
  292. if ( CommandLine()->FindParm( "-basedir" ) )
  293. {
  294. strcpy( exedir, CommandLine()->ParmValue( "-basedir", "" ) );
  295. }
  296. }
  297. Q_FixSlashes( exedir );
  298. // Return the bin directory as the executable dir if it's not in there
  299. // because that's really where we're running from...
  300. char ext[MAX_PATH];
  301. Q_StrRight( exedir, 4, ext, sizeof( ext ) );
  302. if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 )
  303. {
  304. Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS );
  305. Q_strncat( exedir, "bin", exeDirLen, COPY_ALL_CHARACTERS );
  306. Q_FixSlashes( exedir );
  307. }
  308. return true;
  309. }
  310. static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen )
  311. {
  312. if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) )
  313. {
  314. Q_StripFilename( baseDir );
  315. return true;
  316. }
  317. return false;
  318. }
  319. void LaunchVConfig()
  320. {
  321. #if defined( _WIN32 ) && !defined( _X360 )
  322. char vconfigExe[MAX_PATH];
  323. FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) );
  324. Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) );
  325. Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS );
  326. char *argv[] =
  327. {
  328. vconfigExe,
  329. "-allowdebug",
  330. NULL
  331. };
  332. _spawnv( _P_NOWAIT, vconfigExe, argv );
  333. #elif defined( _X360 )
  334. Msg( "Launching vconfig.exe not supported\n" );
  335. #endif
  336. }
  337. const char* GetVProjectCmdLineValue()
  338. {
  339. return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) );
  340. }
  341. FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... )
  342. {
  343. va_list marker;
  344. va_start( marker, pMsg );
  345. Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker );
  346. va_end( marker );
  347. Warning( "%s\n", g_FileSystemError );
  348. // Run vconfig?
  349. // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject.
  350. if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() )
  351. {
  352. LaunchVConfig();
  353. }
  354. if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG )
  355. {
  356. Error( "%s\n", g_FileSystemError );
  357. }
  358. return retVal;
  359. }
  360. FSReturnCode_t LoadGameInfoFile(
  361. const char *pDirectoryName,
  362. KeyValues *&pMainFile,
  363. KeyValues *&pFileSystemInfo,
  364. KeyValues *&pSearchPaths )
  365. {
  366. // If GameInfo.txt exists under pBaseDir, then this is their game directory.
  367. // All the filesystem mappings will be in this file.
  368. char gameinfoFilename[MAX_PATH];
  369. Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
  370. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  371. Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
  372. Q_FixSlashes( gameinfoFilename );
  373. pMainFile = ReadKeyValuesFile( gameinfoFilename );
  374. if ( IsX360() && !pMainFile )
  375. {
  376. // try again
  377. Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
  378. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  379. Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
  380. Q_FixSlashes( gameinfoFilename );
  381. pMainFile = ReadKeyValuesFile( gameinfoFilename );
  382. }
  383. if ( !pMainFile )
  384. {
  385. return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename );
  386. }
  387. pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
  388. if ( !pFileSystemInfo )
  389. {
  390. pMainFile->deleteThis();
  391. return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
  392. }
  393. // Now read in all the search paths.
  394. pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" );
  395. if ( !pSearchPaths )
  396. {
  397. pMainFile->deleteThis();
  398. return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
  399. }
  400. return FS_OK;
  401. }
  402. static void FileSystem_AddLoadedSearchPath(
  403. CFSSearchPathsInit &initInfo,
  404. const char *pPathID,
  405. const char *fullLocationPath,
  406. bool bLowViolence )
  407. {
  408. // Check for mounting LV game content in LV builds only
  409. if ( V_stricmp( pPathID, "game_lv" ) == 0 )
  410. {
  411. // Not in LV build, don't mount
  412. if ( !initInfo.m_bLowViolence )
  413. return;
  414. // Mount, as a game path
  415. pPathID = "game";
  416. }
  417. // Check for mounting HD game content if enabled
  418. if ( V_stricmp( pPathID, "game_hd" ) == 0 )
  419. {
  420. // Not in LV build, don't mount
  421. if ( !initInfo.m_bMountHDContent )
  422. return;
  423. // Mount, as a game path
  424. pPathID = "game";
  425. }
  426. // Special processing for ordinary game folders
  427. if ( V_stristr( fullLocationPath, ".vpk" ) == NULL && Q_stricmp( pPathID, "game" ) == 0 )
  428. {
  429. if ( CommandLine()->FindParm( "-tempcontent" ) != 0 )
  430. {
  431. char szPath[MAX_PATH];
  432. Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath );
  433. initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
  434. }
  435. }
  436. if ( initInfo.m_pLanguage &&
  437. Q_stricmp( initInfo.m_pLanguage, "english" ) &&
  438. V_strstr( fullLocationPath, "_english" ) != NULL )
  439. {
  440. char szPath[MAX_PATH];
  441. char szLangString[MAX_PATH];
  442. // Need to add a language version of this path first
  443. Q_snprintf( szLangString, sizeof(szLangString), "_%s", initInfo.m_pLanguage);
  444. V_StrSubst( fullLocationPath, "_english", szLangString, szPath, sizeof( szPath ), true );
  445. initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
  446. }
  447. initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL );
  448. }
  449. static int SortStricmp( char * const * sz1, char * const * sz2 )
  450. {
  451. return V_stricmp( *sz1, *sz2 );
  452. }
  453. FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
  454. {
  455. if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
  456. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );
  457. KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
  458. FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
  459. if ( retVal != FS_OK )
  460. return retVal;
  461. // All paths except those marked with |gameinfo_path| are relative to the base dir.
  462. char baseDir[MAX_PATH];
  463. if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
  464. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
  465. // The MOD directory is always the one that contains gameinfo.txt
  466. Q_strncpy( initInfo.m_ModPath, initInfo.m_pDirectoryName, sizeof( initInfo.m_ModPath ) );
  467. #define GAMEINFOPATH_TOKEN "|gameinfo_path|"
  468. #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|"
  469. const char *pszExtraSearchPath = CommandLine()->ParmValue( "-insert_search_path" );
  470. if ( pszExtraSearchPath )
  471. {
  472. CUtlStringList vecPaths;
  473. V_SplitString( pszExtraSearchPath, ",", vecPaths );
  474. FOR_EACH_VEC( vecPaths, idxExtraPath )
  475. {
  476. char szAbsSearchPath[MAX_PATH];
  477. Q_StripPrecedingAndTrailingWhitespace( vecPaths[ idxExtraPath ] );
  478. V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), vecPaths[ idxExtraPath ], baseDir );
  479. V_FixSlashes( szAbsSearchPath );
  480. if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
  481. Error( "Bad -insert_search_path - Can't resolve pathname for '%s'", szAbsSearchPath );
  482. V_StripTrailingSlash( szAbsSearchPath );
  483. FileSystem_AddLoadedSearchPath( initInfo, "GAME", szAbsSearchPath, false );
  484. FileSystem_AddLoadedSearchPath( initInfo, "MOD", szAbsSearchPath, false );
  485. }
  486. }
  487. bool bLowViolence = initInfo.m_bLowViolence;
  488. for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
  489. {
  490. const char *pLocation = pCur->GetString();
  491. const char *pszBaseDir = baseDir;
  492. if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
  493. {
  494. pLocation += strlen( GAMEINFOPATH_TOKEN );
  495. pszBaseDir = initInfo.m_pDirectoryName;
  496. }
  497. else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
  498. {
  499. // This is a special identifier that tells it to add the specified path for all source engine versions equal to or prior to this version.
  500. // So in Orange Box, if they specified:
  501. // |all_source_engine_paths|hl2
  502. // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
  503. //
  504. // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
  505. // In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots,
  506. // so we can just use the path as-is.
  507. pLocation += strlen( BASESOURCEPATHS_TOKEN );
  508. }
  509. CUtlStringList vecFullLocationPaths;
  510. char szAbsSearchPath[MAX_PATH];
  511. V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), pLocation, pszBaseDir );
  512. // Now resolve any ./'s.
  513. V_FixSlashes( szAbsSearchPath );
  514. if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
  515. Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", szAbsSearchPath );
  516. V_StripTrailingSlash( szAbsSearchPath );
  517. // Don't bother doing any wildcard expansion unless it has wildcards. This avoids the weird
  518. // thing with xxx_dir.vpk files being referred to simply as xxx.vpk.
  519. if ( V_stristr( pLocation, "?") == NULL && V_stristr( pLocation, "*") == NULL )
  520. {
  521. vecFullLocationPaths.CopyAndAddToTail( szAbsSearchPath );
  522. }
  523. else
  524. {
  525. FileFindHandle_t findHandle = NULL;
  526. const char *pszFoundShortName = initInfo.m_pFileSystem->FindFirst( szAbsSearchPath, &findHandle );
  527. if ( pszFoundShortName )
  528. {
  529. do
  530. {
  531. // We only know how to mount VPK's and directories
  532. if ( pszFoundShortName[0] != '.' && ( initInfo.m_pFileSystem->FindIsDirectory( findHandle ) || V_stristr( pszFoundShortName, ".vpk" ) ) )
  533. {
  534. char szAbsName[MAX_PATH];
  535. V_ExtractFilePath( szAbsSearchPath, szAbsName, sizeof( szAbsName ) );
  536. V_AppendSlash( szAbsName, sizeof(szAbsName) );
  537. V_strcat_safe( szAbsName, pszFoundShortName );
  538. vecFullLocationPaths.CopyAndAddToTail( szAbsName );
  539. // Check for a common mistake
  540. if (
  541. !V_stricmp( pszFoundShortName, "materials" )
  542. || !V_stricmp( pszFoundShortName, "maps" )
  543. || !V_stricmp( pszFoundShortName, "resource" )
  544. || !V_stricmp( pszFoundShortName, "scripts" )
  545. || !V_stricmp( pszFoundShortName, "sound" )
  546. || !V_stricmp( pszFoundShortName, "models" ) )
  547. {
  548. char szReadme[MAX_PATH];
  549. V_ExtractFilePath( szAbsSearchPath, szReadme, sizeof( szReadme ) );
  550. V_AppendSlash( szReadme, sizeof(szReadme) );
  551. V_strcat_safe( szReadme, "readme.txt" );
  552. Error(
  553. "Tried to add %s as a search path.\n"
  554. "\nThis is probably not what you intended.\n"
  555. "\nCheck %s for more info\n",
  556. szAbsName, szReadme );
  557. }
  558. }
  559. pszFoundShortName = initInfo.m_pFileSystem->FindNext( findHandle );
  560. } while ( pszFoundShortName );
  561. initInfo.m_pFileSystem->FindClose( findHandle );
  562. }
  563. // Sort alphabetically. Also note that this will put
  564. // all the xxx_000.vpk packs just before the corresponding
  565. // xxx_dir.vpk
  566. vecFullLocationPaths.Sort( SortStricmp );
  567. // Now for any _dir.vpk files, remove the _nnn.vpk ones.
  568. int idx = vecFullLocationPaths.Count()-1;
  569. while ( idx > 0 )
  570. {
  571. char szTemp[ MAX_PATH ];
  572. V_strcpy_safe( szTemp, vecFullLocationPaths[ idx ] );
  573. --idx;
  574. char *szDirVpk = V_stristr( szTemp, "_dir.vpk" );
  575. if ( szDirVpk != NULL )
  576. {
  577. *szDirVpk = '\0';
  578. while ( idx >= 0 )
  579. {
  580. char *pszPath = vecFullLocationPaths[ idx ];
  581. if ( V_stristr( pszPath, szTemp ) != pszPath )
  582. break;
  583. delete pszPath;
  584. vecFullLocationPaths.Remove( idx );
  585. --idx;
  586. }
  587. }
  588. }
  589. }
  590. // Parse Path ID list
  591. CUtlStringList vecPathIDs;
  592. V_SplitString( pCur->GetName(), "+", vecPathIDs );
  593. FOR_EACH_VEC( vecPathIDs, idxPathID )
  594. {
  595. Q_StripPrecedingAndTrailingWhitespace( vecPathIDs[ idxPathID ] );
  596. }
  597. // Mount them.
  598. FOR_EACH_VEC( vecFullLocationPaths, idxLocation )
  599. {
  600. FOR_EACH_VEC( vecPathIDs, idxPathID )
  601. {
  602. FileSystem_AddLoadedSearchPath( initInfo, vecPathIDs[ idxPathID ], vecFullLocationPaths[ idxLocation ], bLowViolence );
  603. }
  604. }
  605. }
  606. pMainFile->deleteThis();
  607. // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
  608. // when people forget to specify a search path.
  609. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
  610. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
  611. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "download", true );
  612. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );
  613. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "game_write", true );
  614. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod_write", true );
  615. #ifdef _DEBUG
  616. // initInfo.m_pFileSystem->PrintSearchPaths();
  617. #endif
  618. return FS_OK;
  619. }
  620. bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename )
  621. {
  622. char filename[MAX_PATH];
  623. Q_strncpy( filename, pDirectoryName, sizeof( filename ) );
  624. Q_AppendSlash( filename, sizeof( filename ) );
  625. Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS );
  626. Q_FixSlashes( filename );
  627. bool bExist = ( _access( filename, 0 ) == 0 );
  628. return ( bExist );
  629. }
  630. namespace
  631. {
  632. SuggestGameInfoDirFn_t & GetSuggestGameInfoDirFn( void )
  633. {
  634. static SuggestGameInfoDirFn_t s_pfnSuggestGameInfoDir = NULL;
  635. return s_pfnSuggestGameInfoDir;
  636. }
  637. }; // `anonymous` namespace
  638. SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn )
  639. {
  640. SuggestGameInfoDirFn_t &rfn = GetSuggestGameInfoDirFn();
  641. SuggestGameInfoDirFn_t pfnOldFn = rfn;
  642. rfn = pfnNewFn;
  643. return pfnOldFn;
  644. }
  645. static FSReturnCode_t TryLocateGameInfoFile( char *pOutDir, int outDirLen, bool bBubbleDir )
  646. {
  647. // Retain a copy of suggested path for further attempts
  648. CArrayAutoPtr < char > spchCopyNameBuffer( new char [ outDirLen ] );
  649. Q_strncpy( spchCopyNameBuffer.Get(), pOutDir, outDirLen );
  650. spchCopyNameBuffer[ outDirLen - 1 ] = 0;
  651. // Make appropriate slashes ('/' - Linux style)
  652. for ( char *pchFix = spchCopyNameBuffer.Get(),
  653. *pchEnd = pchFix + outDirLen;
  654. pchFix < pchEnd; ++ pchFix )
  655. {
  656. if ( '\\' == *pchFix )
  657. {
  658. *pchFix = '/';
  659. }
  660. }
  661. // Have a look in supplied path
  662. do
  663. {
  664. if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
  665. {
  666. return FS_OK;
  667. }
  668. if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
  669. {
  670. return FS_OK;
  671. }
  672. }
  673. while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
  674. // Make an attempt to resolve from "content -> game" directory
  675. Q_strncpy( pOutDir, spchCopyNameBuffer.Get(), outDirLen );
  676. pOutDir[ outDirLen - 1 ] = 0;
  677. if ( char *pchContentFix = Q_stristr( pOutDir, "/content/" ) )
  678. {
  679. sprintf( pchContentFix, "/game/" );
  680. memmove( pchContentFix + 6, pchContentFix + 9, pOutDir + outDirLen - (pchContentFix + 9) );
  681. // Try in the mapped "game" directory
  682. do
  683. {
  684. if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
  685. {
  686. return FS_OK;
  687. }
  688. if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
  689. {
  690. return FS_OK;
  691. }
  692. }
  693. while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
  694. }
  695. // Could not find it here
  696. return FS_MISSING_GAMEINFO_FILE;
  697. }
  698. FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen )
  699. {
  700. // Engine and Hammer don't want to search around for it.
  701. if ( fsInfo.m_bOnlyUseDirectoryName )
  702. {
  703. if ( !fsInfo.m_pDirectoryName )
  704. return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." );
  705. bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME );
  706. if ( IsX360() && !bExists )
  707. {
  708. bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE );
  709. }
  710. if ( !bExists )
  711. {
  712. if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
  713. {
  714. char basePath[MAX_PATH];
  715. strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
  716. Q_AppendSlash( basePath, sizeof( basePath ) );
  717. Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS );
  718. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
  719. {
  720. Q_strncpy( pOutDir, basePath, outDirLen );
  721. return FS_OK;
  722. }
  723. if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
  724. {
  725. Q_strncpy( pOutDir, basePath, outDirLen );
  726. return FS_OK;
  727. }
  728. }
  729. return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "Setup file '%s' doesn't exist in subdirectory '%s'.\nCheck your -game parameter or VCONFIG setting.", GAMEINFO_FILENAME, fsInfo.m_pDirectoryName );
  730. }
  731. Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen );
  732. return FS_OK;
  733. }
  734. // First, check for overrides on the command line or environment variables.
  735. const char *pProject = GetVProjectCmdLineValue();
  736. if ( pProject )
  737. {
  738. if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) )
  739. {
  740. Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
  741. return FS_OK;
  742. }
  743. if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) )
  744. {
  745. Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
  746. return FS_OK;
  747. }
  748. if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
  749. {
  750. char basePath[MAX_PATH];
  751. strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
  752. Q_AppendSlash( basePath, sizeof( basePath ) );
  753. Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS );
  754. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
  755. {
  756. Q_strncpy( pOutDir, basePath, outDirLen );
  757. return FS_OK;
  758. }
  759. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
  760. {
  761. Q_strncpy( pOutDir, basePath, outDirLen );
  762. return FS_OK;
  763. }
  764. }
  765. if ( fsInfo.m_bNoGameInfo )
  766. {
  767. // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use.
  768. // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if
  769. // -game is supplied on the command line.
  770. Q_strncpy( pOutDir, "", outDirLen );
  771. return FS_OK;
  772. }
  773. else
  774. {
  775. // They either specified vproject on the command line or it's in their registry. Either way,
  776. // we don't want to continue if they've specified it but it's not valid.
  777. goto ShowError;
  778. }
  779. }
  780. if ( fsInfo.m_bNoGameInfo )
  781. {
  782. Q_strncpy( pOutDir, "", outDirLen );
  783. return FS_OK;
  784. }
  785. // Ask the application if it can provide us with a game info directory
  786. {
  787. bool bBubbleDir = true;
  788. SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn();
  789. if ( pfnSuggestGameInfoDirFn &&
  790. ( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) &&
  791. FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) )
  792. return FS_OK;
  793. }
  794. // Try to use the environment variable / registry
  795. if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL &&
  796. ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) &&
  797. FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) )
  798. return FS_OK;
  799. if ( IsPC() )
  800. {
  801. Warning( "Warning: falling back to auto detection of vproject directory.\n" );
  802. // Now look for it in the directory they passed in.
  803. if ( fsInfo.m_pDirectoryName )
  804. Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName );
  805. else
  806. Q_MakeAbsolutePath( pOutDir, outDirLen, "." );
  807. if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
  808. return FS_OK;
  809. // Use the CWD
  810. Q_getwd( pOutDir, outDirLen );
  811. if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
  812. return FS_OK;
  813. }
  814. ShowError:
  815. return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE,
  816. "Unable to find %s. Solutions:\n\n"
  817. "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n"
  818. "2. Run vconfig to specify which game you're working on.\n"
  819. "3. Add -game <path> on the command line where <path> is the directory that %s is in.\n",
  820. GAMEINFO_FILENAME, GAMEINFO_FILENAME );
  821. }
  822. bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath )
  823. {
  824. // Fix the slashes in the input arguments.
  825. char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH];
  826. Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) );
  827. Q_FixSlashes( correctedPathEnvVar );
  828. pPathEnvVar = correctedPathEnvVar;
  829. Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) );
  830. Q_FixSlashes( correctedTestPath );
  831. if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) )
  832. correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0;
  833. pTestPath = correctedTestPath;
  834. const char *pCurPos = pPathEnvVar;
  835. while ( 1 )
  836. {
  837. const char *pTestPos = Q_stristr( pCurPos, pTestPath );
  838. if ( !pTestPos )
  839. return false;
  840. // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon.
  841. pTestPos += strlen( pTestPath );
  842. if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') )
  843. return true;
  844. // Advance our marker..
  845. pCurPos = pTestPos;
  846. }
  847. }
  848. FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen )
  849. {
  850. steamCfgPath[0] = 0;
  851. char executablePath[MAX_PATH];
  852. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  853. {
  854. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  855. }
  856. Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen );
  857. while ( 1 )
  858. {
  859. if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) )
  860. break;
  861. if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) )
  862. {
  863. // the file isnt found, thats ok, its not mandatory
  864. return FS_OK;
  865. }
  866. }
  867. Q_AppendSlash( steamCfgPath, steamCfgPathLen );
  868. Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS );
  869. return FS_OK;
  870. }
  871. void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars )
  872. {
  873. // Always inherit the Steam user if it's already set, since it probably means we (or the
  874. // the app that launched us) were launched from Steam.
  875. char appUser[MAX_PATH];
  876. if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) )
  877. return;
  878. const char *pTempAppUser = NULL;
  879. if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL )
  880. {
  881. Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
  882. }
  883. else
  884. {
  885. // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user
  886. // by looking in <steam install path>\config\SteamAppData.vdf.
  887. char fullFilename[MAX_PATH];
  888. Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) );
  889. Q_AppendSlash( fullFilename, sizeof( fullFilename ) );
  890. Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS );
  891. KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename );
  892. if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL )
  893. {
  894. Error( "Can't find steam app user info." );
  895. }
  896. Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
  897. pSteamAppData->deleteThis();
  898. }
  899. Q_strlower( appUser );
  900. steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser );
  901. }
  902. void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars )
  903. {
  904. // Always inherit the passphrase if it's already set, since it probably means we (or the
  905. // the app that launched us) were launched from Steam.
  906. char szPassPhrase[ MAX_PATH ];
  907. if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) )
  908. return;
  909. // SteamUserPassphrase.
  910. const char *pStr;
  911. if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL )
  912. {
  913. steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr );
  914. }
  915. }
  916. FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem )
  917. {
  918. pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" );
  919. char executablePath[MAX_PATH];
  920. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  921. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  922. pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" );
  923. if ( !FileSystem_GetBaseDir( executablePath, sizeof( executablePath ) ) )
  924. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
  925. pFileSystem->AddSearchPath( executablePath, "BASE_PATH" );
  926. return FS_OK;
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Returns the name of the file system DLL to use
  930. //-----------------------------------------------------------------------------
  931. FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
  932. {
  933. bSteam = false;
  934. // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
  935. // is in this same directory with us.
  936. char executablePath[MAX_PATH];
  937. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  938. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  939. // Assume we'll use local files
  940. Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
  941. #if !defined( _X360 )
  942. // Use filsystem_steam if it exists?
  943. #if defined( OSX ) || defined( LINUX )
  944. struct stat statBuf;
  945. #endif
  946. if (
  947. #if defined( OSX ) || defined( LINUX )
  948. stat( pFileSystemDLL, &statBuf ) != 0
  949. #else
  950. _access( pFileSystemDLL, 0 ) != 0
  951. #endif
  952. ) {
  953. Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
  954. bSteam = true;
  955. }
  956. #endif
  957. return FS_OK;
  958. }
  959. //-----------------------------------------------------------------------------
  960. // Sets up the steam environment + gets back the gameinfo.txt path
  961. //-----------------------------------------------------------------------------
  962. FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo )
  963. {
  964. // First, locate the directory with gameinfo.txt.
  965. FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) );
  966. if ( ret != FS_OK )
  967. return ret;
  968. // This is so that processes spawned by this application will have the same VPROJECT
  969. #ifdef WIN32
  970. char pEnvBuf[MAX_PATH+32];
  971. Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath );
  972. _putenv( pEnvBuf );
  973. #else
  974. setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 );
  975. #endif
  976. return FS_OK;
  977. }
  978. //-----------------------------------------------------------------------------
  979. // Loads the file system module
  980. //-----------------------------------------------------------------------------
  981. FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo )
  982. {
  983. // First, locate the directory with gameinfo.txt.
  984. FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo );
  985. if ( ret != FS_OK )
  986. return ret;
  987. // Now that the environment is setup, load the filesystem module.
  988. if ( !Sys_LoadInterface(
  989. fsInfo.m_pFileSystemDLLName,
  990. FILESYSTEM_INTERFACE_VERSION,
  991. &fsInfo.m_pModule,
  992. (void**)&fsInfo.m_pFileSystem ) )
  993. {
  994. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName );
  995. }
  996. if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) )
  997. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName );
  998. if ( fsInfo.m_pFileSystem->Init() != INIT_OK )
  999. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName );
  1000. return FS_OK;
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Mounds a particular steam cache
  1004. //-----------------------------------------------------------------------------
  1005. FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo )
  1006. {
  1007. // This part is Steam-only.
  1008. if ( mountContentInfo.m_pFileSystem->IsSteam() )
  1009. {
  1010. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "Should not be using filesystem_steam anymore!" );
  1011. // // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem
  1012. // // like HL2, then mount the SDK content (tools materials and models, etc) in addition.
  1013. // int nExtraAppId = -1;
  1014. // if ( mountContentInfo.m_bToolsMode )
  1015. // {
  1016. // // !FIXME! Here we need to mount the tools content (VPK's) in some way...?
  1017. // }
  1018. //
  1019. // // Set our working directory temporarily so Steam can remember it.
  1020. // // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk
  1021. // // to get to the relative part of the path.
  1022. // char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH];
  1023. // if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
  1024. // return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
  1025. //
  1026. // Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) );
  1027. // _chdir( baseDir );
  1028. //
  1029. // // Filesystem_tools needs to add dependencies in here beforehand.
  1030. // FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId );
  1031. //
  1032. // _chdir( oldWorkingDir );
  1033. //
  1034. // if ( retVal != FILESYSTEM_MOUNT_OK )
  1035. // return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" );
  1036. }
  1037. return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem );
  1038. }
  1039. void FileSystem_SetErrorMode( FSErrorMode_t errorMode )
  1040. {
  1041. g_FileSystemErrorMode = errorMode;
  1042. }
  1043. void FileSystem_ClearSteamEnvVars()
  1044. {
  1045. CSteamEnvVars envVars;
  1046. // Change the values and don't restore the originals.
  1047. envVars.m_SteamAppId.SetValue( "" );
  1048. envVars.m_SteamUserPassphrase.SetValue( "" );
  1049. envVars.m_SteamAppUser.SetValue( "" );
  1050. envVars.SetRestoreOriginalValue_ALL( false );
  1051. }
  1052. //-----------------------------------------------------------------------------
  1053. // Adds the platform folder to the search path.
  1054. //-----------------------------------------------------------------------------
  1055. void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath )
  1056. {
  1057. char platform[MAX_PATH];
  1058. Q_strncpy( platform, szGameInfoPath, MAX_PATH );
  1059. Q_StripTrailingSlash( platform );
  1060. Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
  1061. pFileSystem->AddSearchPath( platform, "PLATFORM" );
  1062. }