Counter Strike : Global Offensive Source Code
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.

1610 lines
49 KiB

  1. //====== Copyright 1996-2004, 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. #endif
  19. #include <stdio.h>
  20. #include <sys/stat.h>
  21. #include "tier0/platform.h"
  22. #include "tier1/strtools.h"
  23. #include "filesystem_init.h"
  24. #include "tier0/icommandline.h"
  25. #include "tier0/stacktools.h"
  26. #include "keyvalues.h"
  27. #include "appframework/IAppSystemGroup.h"
  28. #include "tier1/smartptr.h"
  29. #if defined( _X360 )
  30. #include "xbox\xbox_win32stubs.h"
  31. #endif
  32. #if defined( _PS3 )
  33. #include "ps3/ps3_win32stubs.h"
  34. #include "ps3/ps3_helpers.h"
  35. #include "ps3_pathinfo.h"
  36. #endif
  37. #include "tier2/tier2.h"
  38. // memdbgon must be the last include file in a .cpp file!!!
  39. #include <tier0/memdbgon.h>
  40. #if !defined( _X360 )
  41. #define GAMEINFO_FILENAME "gameinfo.txt"
  42. #else
  43. // The .xtx file is a TCR requirement, as .txt files cannot live on the DVD.
  44. // 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.
  45. // 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.
  46. // The .xtx concept is private and should only have to occurr here. As a safety measure, if the .xtx file is not found
  47. // a retry is made with the original .txt name
  48. #define GAMEINFO_FILENAME "gameinfo.xtx"
  49. #endif
  50. #define GAMEINFO_FILENAME_ALTERNATE "gameinfo.txt"
  51. static char g_FileSystemError[256];
  52. static bool s_bUseVProjectBinDir = false;
  53. static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG;
  54. // Call this to use a bin directory relative to VPROJECT
  55. void FileSystem_UseVProjectBinDir( bool bEnable )
  56. {
  57. s_bUseVProjectBinDir = bEnable;
  58. }
  59. // This class lets you modify environment variables, and it restores the original value
  60. // when it goes out of scope.
  61. class CTempEnvVar
  62. {
  63. public:
  64. CTempEnvVar( const char *pVarName )
  65. {
  66. m_bRestoreOriginalValue = true;
  67. m_pVarName = pVarName;
  68. const char *pValue = NULL;
  69. #if defined( _WIN32 ) || defined( _GAMECONSOLE )
  70. // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
  71. // to the process environment after the DLL was loaded.
  72. char szBuf[ 4096 ];
  73. if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0)
  74. {
  75. pValue = szBuf;
  76. }
  77. #else
  78. // LINUX BUG: see above
  79. pValue = getenv( pVarName );
  80. #endif
  81. if ( pValue )
  82. {
  83. m_bExisted = true;
  84. m_OriginalValue.SetSize( strlen( pValue ) + 1 );
  85. memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() );
  86. }
  87. else
  88. {
  89. m_bExisted = false;
  90. }
  91. }
  92. ~CTempEnvVar()
  93. {
  94. if ( m_bRestoreOriginalValue )
  95. {
  96. // Restore the original value.
  97. if ( m_bExisted )
  98. {
  99. SetValue( "%s", m_OriginalValue.Base() );
  100. }
  101. else
  102. {
  103. ClearValue();
  104. }
  105. }
  106. }
  107. void SetRestoreOriginalValue( bool bRestore )
  108. {
  109. m_bRestoreOriginalValue = bRestore;
  110. }
  111. int GetValue(char *pszBuf, int nBufSize )
  112. {
  113. if ( !pszBuf || ( nBufSize <= 0 ) )
  114. return 0;
  115. #if defined( _WIN32 ) || defined( _GAMECONSOLE )
  116. // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
  117. // to the process environment after the DLL was loaded.
  118. return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize );
  119. #else
  120. // LINUX BUG: see above
  121. const char *pszOut = getenv( m_pVarName );
  122. if ( !pszOut )
  123. {
  124. *pszBuf = '\0';
  125. return 0;
  126. }
  127. Q_strncpy( pszBuf, pszOut, nBufSize );
  128. return Q_strlen( pszBuf );
  129. #endif
  130. }
  131. void SetValue( const char *pValue, ... )
  132. {
  133. char valueString[4096];
  134. va_list marker;
  135. va_start( marker, pValue );
  136. Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker );
  137. va_end( marker );
  138. #if defined( WIN32 ) || defined( _GAMECONSOLE )
  139. char str[4096];
  140. Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString );
  141. _putenv( str );
  142. #else
  143. setenv( m_pVarName, valueString, 1 );
  144. #endif
  145. }
  146. void ClearValue()
  147. {
  148. #if defined( WIN32 ) || defined( _GAMECONSOLE )
  149. char str[512];
  150. Q_snprintf( str, sizeof( str ), "%s=", m_pVarName );
  151. _putenv( str );
  152. #else
  153. setenv( m_pVarName, "", 1 );
  154. #endif
  155. }
  156. private:
  157. bool m_bRestoreOriginalValue;
  158. const char *m_pVarName;
  159. bool m_bExisted;
  160. CUtlVector<char> m_OriginalValue;
  161. };
  162. class CSteamEnvVars
  163. {
  164. public:
  165. CSteamEnvVars() :
  166. m_SteamAppId( "SteamAppId" ),
  167. m_SteamUserPassphrase( "SteamUserPassphrase" ),
  168. m_SteamAppUser( "SteamAppUser" ),
  169. m_Path( "path" )
  170. {
  171. }
  172. void SetRestoreOriginalValue_ALL( bool bRestore )
  173. {
  174. m_SteamAppId.SetRestoreOriginalValue( bRestore );
  175. m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore );
  176. m_SteamAppUser.SetRestoreOriginalValue( bRestore );
  177. m_Path.SetRestoreOriginalValue( bRestore );
  178. }
  179. CTempEnvVar m_SteamAppId;
  180. CTempEnvVar m_SteamUserPassphrase;
  181. CTempEnvVar m_SteamAppUser;
  182. CTempEnvVar m_Path;
  183. };
  184. // ---------------------------------------------------------------------------------------------------- //
  185. // Helpers.
  186. // ---------------------------------------------------------------------------------------------------- //
  187. void Q_getwd( char *out, int outSize )
  188. {
  189. #if defined( _WIN32 ) || defined( WIN32 )
  190. _getcwd( out, outSize );
  191. Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
  192. #elif defined( _PS3 )
  193. Assert( 0 );
  194. if ( outSize > 0 )
  195. out[0] = '\0';
  196. #else
  197. getcwd( out, outSize );
  198. strcat( out, "/" );
  199. #endif
  200. Q_FixSlashes( out );
  201. }
  202. // ---------------------------------------------------------------------------------------------------- //
  203. // Module interface.
  204. // ---------------------------------------------------------------------------------------------------- //
  205. CFSSearchPathsInit::CFSSearchPathsInit()
  206. {
  207. m_pDirectoryName = NULL;
  208. m_pLanguage = NULL;
  209. m_ModPath[0] = 0;
  210. }
  211. CFSSteamSetupInfo::CFSSteamSetupInfo()
  212. {
  213. m_pDirectoryName = NULL;
  214. m_bOnlyUseDirectoryName = false;
  215. m_bSteam = false;
  216. m_bToolsMode = true;
  217. m_bNoGameInfo = false;
  218. }
  219. CFSLoadModuleInfo::CFSLoadModuleInfo()
  220. {
  221. m_pFileSystemDLLName = NULL;
  222. m_pFileSystem = NULL;
  223. m_pModule = NULL;
  224. }
  225. CFSMountContentInfo::CFSMountContentInfo()
  226. {
  227. m_bToolsMode = true;
  228. m_pDirectoryName = NULL;
  229. m_pFileSystem = NULL;
  230. }
  231. const char *FileSystem_GetLastErrorString()
  232. {
  233. return g_FileSystemError;
  234. }
  235. void AddLanguageGameDir( IFileSystem *pFileSystem, const char *pLocation, const char *pLanguage )
  236. {
  237. #if !defined( DEDICATED )
  238. char temp[MAX_PATH];
  239. Q_snprintf( temp, sizeof(temp), "%s_%s", pLocation, pLanguage );
  240. pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL );
  241. if ( IsPC() )
  242. {
  243. // also look in "..\localization\<folder>" if that directory exists
  244. char baseDir[MAX_PATH];
  245. char *tempPtr = NULL, *gameDir = NULL;
  246. Q_strncpy( baseDir, pLocation, sizeof(baseDir) );
  247. #ifdef WIN32
  248. tempPtr = Q_strstr( baseDir, "\\game\\" );
  249. #else
  250. tempPtr = Q_strstr( baseDir, "/game/" );
  251. #endif
  252. if ( tempPtr )
  253. {
  254. #ifdef WIN32
  255. gameDir = tempPtr + Q_strlen( "\\game\\" );
  256. #else
  257. gameDir = tempPtr + Q_strlen( "/game/" );
  258. #endif
  259. *tempPtr = 0;
  260. Q_snprintf( temp, sizeof(temp), "%s%clocalization%c%s_%s", baseDir, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR, gameDir, pLanguage );
  261. if ( pFileSystem->IsDirectory( temp ) )
  262. {
  263. pFileSystem->AddSearchPath( temp, "GAME", PATH_ADD_TO_TAIL );
  264. }
  265. }
  266. }
  267. #endif
  268. }
  269. void AddGameBinDir( IFileSystem *pFileSystem, const char *pLocation, bool bForceHead = false )
  270. {
  271. char temp[MAX_PATH];
  272. Q_snprintf( temp, sizeof(temp), "%s%cbin", pLocation, CORRECT_PATH_SEPARATOR );
  273. pFileSystem->AddSearchPath( temp, "GAMEBIN", bForceHead ? PATH_ADD_TO_HEAD : PATH_ADD_TO_TAIL );
  274. }
  275. KeyValues* ReadKeyValuesFile( const char *pFilename )
  276. {
  277. MEM_ALLOC_CREDIT();
  278. // Read in the gameinfo.txt file and null-terminate it.
  279. FILE *fp = fopen( pFilename, "rb" );
  280. if ( !fp )
  281. return NULL;
  282. CUtlVector<char> buf;
  283. fseek( fp, 0, SEEK_END );
  284. buf.SetSize( ftell( fp ) + 1 );
  285. fseek( fp, 0, SEEK_SET );
  286. fread( buf.Base(), 1, buf.Count()-1, fp );
  287. fclose( fp );
  288. buf[buf.Count()-1] = 0;
  289. KeyValues *kv = new KeyValues( "" );
  290. if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
  291. {
  292. kv->deleteThis();
  293. return NULL;
  294. }
  295. return kv;
  296. }
  297. static bool Sys_GetExecutableName( char *out, int len )
  298. {
  299. #if defined( _WIN32 )
  300. if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) )
  301. {
  302. return false;
  303. }
  304. // Fix up the path if Windows gave us a messy one.
  305. if ( !Q_RemoveDotSlashes( out ) )
  306. {
  307. Error( "V_MakeAbsolutePath: tried to \"..\" past the root." );
  308. return false;
  309. }
  310. Q_FixSlashes( out );
  311. #elif defined( _PS3 )
  312. Q_snprintf( out, len, "%s/valve.self", g_pPS3PathInfo->SelfDirectory() );
  313. #else
  314. if ( CommandLine()->GetParm(0) )
  315. {
  316. Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) );
  317. }
  318. else
  319. {
  320. return false;
  321. }
  322. #endif
  323. return true;
  324. }
  325. bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen )
  326. {
  327. exedir[0] = 0;
  328. if ( s_bUseVProjectBinDir )
  329. {
  330. const char *pProject = GetVProjectCmdLineValue();
  331. if ( !pProject )
  332. {
  333. // Check their registry.
  334. pProject = getenv( GAMEDIR_TOKEN );
  335. }
  336. if ( pProject )
  337. {
  338. Q_snprintf( exedir, exeDirLen, "%s%c..%cbin", pProject, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR );
  339. return true;
  340. }
  341. return false;
  342. }
  343. if ( !Sys_GetExecutableName( exedir, exeDirLen ) )
  344. return false;
  345. Q_StripFilename( exedir );
  346. if ( IsX360() )
  347. {
  348. // The 360 can have its exe and dlls reside on different volumes
  349. // use the optional basedir as the exe dir
  350. if ( CommandLine()->FindParm( "-basedir" ) )
  351. {
  352. strcpy( exedir, CommandLine()->ParmValue( "-basedir", "" ) );
  353. }
  354. }
  355. Q_FixSlashes( exedir );
  356. // Return the bin directory as the executable dir if it's not in there
  357. // because that's really where we're running from...
  358. char ext[MAX_PATH];
  359. Q_StrRight( exedir, 4, ext, sizeof( ext ) );
  360. if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 )
  361. {
  362. Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS );
  363. Q_strncat( exedir, "bin", exeDirLen, COPY_ALL_CHARACTERS );
  364. #ifdef PLATFORM_64BITS
  365. #ifdef _WIN64
  366. const char *pPlatPath = "x64";
  367. #elif OSX
  368. const char *pPlatPath = "osx64";
  369. #elif LINUX
  370. const char *pPlatPath = "linux64";
  371. #endif
  372. Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS );
  373. Q_strncat( exedir, pPlatPath, exeDirLen, COPY_ALL_CHARACTERS );
  374. #endif
  375. Q_FixSlashes( exedir );
  376. }
  377. return true;
  378. }
  379. static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen )
  380. {
  381. #ifdef _PS3
  382. V_strncpy( baseDir, g_pPS3PathInfo->GameImagePath(), baseDirLen );
  383. return baseDir[0] != 0;
  384. #else
  385. if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) )
  386. {
  387. Q_StripFilename( baseDir );
  388. return true;
  389. }
  390. return false;
  391. #endif
  392. }
  393. void LaunchVConfig()
  394. {
  395. #if defined( _WIN32 ) && !defined( _X360 )
  396. char vconfigExe[MAX_PATH];
  397. FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) );
  398. Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) );
  399. Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS );
  400. char *argv[] =
  401. {
  402. vconfigExe,
  403. "-allowdebug",
  404. NULL
  405. };
  406. _spawnv( _P_NOWAIT, vconfigExe, argv );
  407. #elif defined( _X360 )
  408. Msg( "Launching vconfig.exe not supported\n" );
  409. #endif
  410. }
  411. const char* GetVProjectCmdLineValue()
  412. {
  413. return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) );
  414. }
  415. FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... )
  416. {
  417. va_list marker;
  418. va_start( marker, pMsg );
  419. Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker );
  420. va_end( marker );
  421. Warning( "%s\n", g_FileSystemError );
  422. // Run vconfig?
  423. // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject.
  424. if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() )
  425. {
  426. LaunchVConfig();
  427. }
  428. if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG )
  429. {
  430. // this may happen when we eject disk while loading the game.
  431. if( IsPS3() )
  432. {
  433. return FS_UNABLE_TO_INIT;
  434. }
  435. else
  436. {
  437. Error( "%s\n", g_FileSystemError );
  438. }
  439. }
  440. return retVal;
  441. }
  442. FSReturnCode_t LoadGameInfoFile(
  443. const char *pDirectoryName,
  444. KeyValues *&pMainFile,
  445. KeyValues *&pFileSystemInfo,
  446. KeyValues *&pSearchPaths )
  447. {
  448. // If GameInfo.txt exists under pBaseDir, then this is their game directory.
  449. // All the filesystem mappings will be in this file.
  450. char gameinfoFilename[MAX_PATH];
  451. Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
  452. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  453. Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
  454. Q_FixSlashes( gameinfoFilename );
  455. pMainFile = ReadKeyValuesFile( gameinfoFilename );
  456. #if defined( _PS3 )
  457. if ( IsPS3() && !pMainFile )
  458. {
  459. Q_strncpy( gameinfoFilename, g_pPS3PathInfo->GameImagePath(), sizeof( gameinfoFilename ) );
  460. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  461. Q_strncat( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
  462. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  463. Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
  464. Q_FixSlashes( gameinfoFilename );
  465. Msg("Attempting %s\n", gameinfoFilename);
  466. pMainFile = ReadKeyValuesFile( gameinfoFilename );
  467. }
  468. #endif
  469. if ( IsX360() && !pMainFile )
  470. {
  471. // try again
  472. Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
  473. Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
  474. Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
  475. Q_FixSlashes( gameinfoFilename );
  476. pMainFile = ReadKeyValuesFile( gameinfoFilename );
  477. }
  478. if ( !pMainFile )
  479. {
  480. return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename );
  481. }
  482. pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
  483. if ( !pFileSystemInfo )
  484. {
  485. pMainFile->deleteThis();
  486. return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
  487. }
  488. // Now read in all the search paths.
  489. pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" );
  490. if ( !pSearchPaths )
  491. {
  492. pMainFile->deleteThis();
  493. return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
  494. }
  495. return FS_OK;
  496. }
  497. // checks the registry for the low violence setting
  498. // Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3"
  499. bool IsLowViolenceBuild( void )
  500. {
  501. #if defined( _LOWVIOLENCE )
  502. // a low violence build can not be-undone
  503. return true;
  504. #endif
  505. // Users can opt into low violence mode on the command-line.
  506. if ( CommandLine()->FindParm( "-lv" ) != 0 )
  507. return true;
  508. #if defined(_WIN32)
  509. HKEY hKey;
  510. char szValue[64];
  511. unsigned long len = sizeof(szValue) - 1;
  512. bool retVal = false;
  513. if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS )
  514. {
  515. // User Token 2
  516. if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
  517. {
  518. if ( Q_strlen( szValue ) > 0 )
  519. {
  520. retVal = true;
  521. }
  522. }
  523. if ( !retVal )
  524. {
  525. // reset "len" for the next check
  526. len = sizeof(szValue) - 1;
  527. // User Token 3
  528. if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
  529. {
  530. if ( Q_strlen( szValue ) > 0 )
  531. {
  532. retVal = true;
  533. }
  534. }
  535. }
  536. RegCloseKey(hKey);
  537. }
  538. return retVal;
  539. #elif defined( _GAMECONSOLE )
  540. // console builds must be compiled with _LOWVIOLENCE
  541. return false;
  542. #elif POSIX
  543. return false;
  544. #else
  545. #error "Fix me"
  546. #endif
  547. }
  548. static void FileSystem_AddLoadedSearchPath(
  549. CFSSearchPathsInit &initInfo,
  550. const char *pPathID,
  551. bool *bFirstGamePath,
  552. const char *pBaseDir,
  553. const char *pLocation,
  554. bool bLowViolence )
  555. {
  556. char fullLocationPath[MAX_PATH];
  557. Q_MakeAbsolutePath( fullLocationPath, sizeof( fullLocationPath ), pLocation, pBaseDir );
  558. // Now resolve any ./'s.
  559. V_FixSlashes( fullLocationPath );
  560. if ( !V_RemoveDotSlashes( fullLocationPath ) )
  561. Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", fullLocationPath );
  562. // Add language, mod, and gamebin search paths automatically.
  563. if ( Q_stricmp( pPathID, "game" ) == 0 )
  564. {
  565. bool bDoAllPaths = true;
  566. #if defined( _X360 ) && defined( LEFT4DEAD )
  567. // hl2 is a vestigal mistake due to shaders, xbox needs to prevent any search path bloat
  568. if ( V_stristr( fullLocationPath, "\\hl2" ) )
  569. {
  570. bDoAllPaths = false;
  571. }
  572. #endif
  573. // add the language path, needs to be topmost, generally only contains audio
  574. // and the language localized movies (there are 2 version one normal, one LV)
  575. // this trumps the LV english movie as desired for the language
  576. if ( initInfo.m_pLanguage && bDoAllPaths )
  577. {
  578. AddLanguageGameDir( initInfo.m_pFileSystem, fullLocationPath, initInfo.m_pLanguage );
  579. }
  580. // next add the low violence path
  581. if ( bLowViolence && bDoAllPaths )
  582. {
  583. char szPath[MAX_PATH];
  584. Q_snprintf( szPath, sizeof(szPath), "%s_lv", fullLocationPath );
  585. initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
  586. }
  587. if ( CommandLine()->FindParm( "-tempcontent" ) != 0 && bDoAllPaths )
  588. {
  589. char szPath[MAX_PATH];
  590. Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath );
  591. initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
  592. }
  593. // mark the first "game" dir as the "MOD" dir
  594. if ( *bFirstGamePath )
  595. {
  596. *bFirstGamePath = false;
  597. initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, "MOD", PATH_ADD_TO_TAIL );
  598. Q_strncpy( initInfo.m_ModPath, fullLocationPath, sizeof( initInfo.m_ModPath ) );
  599. }
  600. if ( bDoAllPaths )
  601. {
  602. // add the game bin
  603. AddGameBinDir( initInfo.m_pFileSystem, fullLocationPath );
  604. }
  605. }
  606. initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL );
  607. }
  608. bool FileSystem_IsHldsUpdateToolDedicatedServer()
  609. {
  610. // To determine this, we see if the directory our executable was launched from is "orangebox".
  611. // We only are under "orangebox" if we're run from hldsupdatetool.
  612. char baseDir[MAX_PATH];
  613. if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
  614. return false;
  615. V_FixSlashes( baseDir );
  616. V_StripTrailingSlash( baseDir );
  617. const char *pLastDir = V_UnqualifiedFileName( baseDir );
  618. return ( pLastDir && V_stricmp( pLastDir, "orangebox" ) == 0 );
  619. }
  620. #ifdef ENGINE_DLL
  621. extern void FileSystem_UpdateAddonSearchPaths( IFileSystem *pFileSystem );
  622. #endif
  623. FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
  624. {
  625. if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
  626. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );
  627. KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
  628. FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
  629. if ( retVal != FS_OK )
  630. return retVal;
  631. // All paths except those marked with |gameinfo_path| are relative to the base dir.
  632. char baseDir[MAX_PATH];
  633. if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
  634. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
  635. initInfo.m_ModPath[0] = 0;
  636. #define GAMEINFOPATH_TOKEN "|gameinfo_path|"
  637. #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|"
  638. bool bLowViolence = IsLowViolenceBuild();
  639. bool bFirstGamePath = true;
  640. for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
  641. {
  642. const char *pPathID = pCur->GetName();
  643. const char *pLocation = pCur->GetString();
  644. if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
  645. {
  646. pLocation += strlen( GAMEINFOPATH_TOKEN );
  647. FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence );
  648. }
  649. else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
  650. {
  651. // 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.
  652. // So in Orange Box, if they specified:
  653. // |all_source_engine_paths|hl2
  654. // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
  655. //
  656. // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
  657. // 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,
  658. // so we can just use the path as-is.
  659. // In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder).
  660. pLocation += strlen( BASESOURCEPATHS_TOKEN );
  661. // Add the Orange-box path (which also will include whatever the depots mapped in as well if we're
  662. // running a Steam-launched app).
  663. FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );
  664. if ( FileSystem_IsHldsUpdateToolDedicatedServer() )
  665. {
  666. // If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too.
  667. char ep1EraPath[MAX_PATH];
  668. V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation );
  669. FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence );
  670. }
  671. }
  672. else
  673. {
  674. FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence );
  675. }
  676. }
  677. #ifdef _PS3
  678. // Always base GAMEBIN PRX location off of main PRX path (unless content and PRX paths are same)
  679. if ( Q_strncmp( g_pPS3PathInfo->GameImagePath(), g_pPS3PathInfo->PrxPath(), Q_strlen( g_pPS3PathInfo->GameImagePath() ) ) )
  680. {
  681. char gamebindir[CELL_GAME_PATH_MAX];
  682. // get the moddirname
  683. const char *pModName;
  684. for ( pModName = initInfo.m_ModPath + strlen(initInfo.m_ModPath) - 1 ;
  685. pModName > initInfo.m_ModPath && *(pModName-1) != '/' ;
  686. pModName--);
  687. V_snprintf( gamebindir, CELL_GAME_PATH_MAX, "%s/../%s", g_pPS3PathInfo->PrxPath(), pModName );
  688. AddGameBinDir( initInfo.m_pFileSystem, gamebindir, true );
  689. }
  690. #endif
  691. pMainFile->deleteThis();
  692. //
  693. // Set up search paths for add-ons
  694. //
  695. if ( IsPC() )
  696. {
  697. #ifdef ENGINE_DLL
  698. FileSystem_UpdateAddonSearchPaths( initInfo.m_pFileSystem );
  699. #endif
  700. }
  701. // Add the "platform" directory as a game searchable path
  702. char pPlatformPath[MAX_PATH];
  703. V_ComposeFileName( baseDir, "platform", pPlatformPath, sizeof(pPlatformPath) );
  704. initInfo.m_pFileSystem->AddSearchPath( pPlatformPath, "GAME", PATH_ADD_TO_TAIL );
  705. // these specialized tool paths are not used on 360 and cause a costly constant perf tax, so inhibited
  706. if ( IsPC() )
  707. {
  708. // Create a content search path based on the game search path
  709. char szContentRoot[MAX_PATH];
  710. V_strncpy( szContentRoot, baseDir, sizeof(szContentRoot) );
  711. char *pRootEnd = V_strrchr( szContentRoot, '\\' );
  712. if ( pRootEnd )
  713. {
  714. *pRootEnd = '\0';
  715. }
  716. V_strncat( szContentRoot, "\\content", sizeof( szContentRoot ) );
  717. int nLen = initInfo.m_pFileSystem->GetSearchPath( "GAME", false, NULL, 0 );
  718. char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) );
  719. initInfo.m_pFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen );
  720. char *pPath = pSearchPath;
  721. while( pPath )
  722. {
  723. char *pSemiColon = strchr( pPath, ';' );
  724. if ( pSemiColon )
  725. {
  726. *pSemiColon = 0;
  727. }
  728. Q_StripTrailingSlash( pPath );
  729. Q_FixSlashes( pPath );
  730. const char *pCurPath = pPath;
  731. pPath = pSemiColon ? pSemiColon + 1 : NULL;
  732. char pRelativePath[MAX_PATH];
  733. char pContentPath[MAX_PATH];
  734. if ( !Q_MakeRelativePath( pCurPath, baseDir, pRelativePath, sizeof(pRelativePath) ) )
  735. continue;
  736. Q_ComposeFileName( szContentRoot, pRelativePath, pContentPath, sizeof(pContentPath) );
  737. initInfo.m_pFileSystem->AddSearchPath( pContentPath, "CONTENT" );
  738. }
  739. // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
  740. // when people forget to specify a search path.
  741. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "content", true );
  742. }
  743. // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
  744. // when people forget to specify a search path.
  745. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
  746. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
  747. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );
  748. // Add the write path last.
  749. if ( initInfo.m_ModPath[0] != 0 )
  750. {
  751. initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL );
  752. }
  753. #ifdef _DEBUG
  754. initInfo.m_pFileSystem->PrintSearchPaths();
  755. #endif
  756. #if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) && !defined( _GAMECONSOLE )
  757. //copy search paths to stack tools so it can grab pdb's from all over. But only on P4 or Steam Beta builds
  758. if( (CommandLine()->FindParm( "-steam" ) == 0) || //not steam
  759. (CommandLine()->FindParm( "-internalbuild" ) != 0) ) //steam beta is ok
  760. {
  761. char szSearchPaths[4096];
  762. //int CBaseFileSystem::GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen )
  763. int iLength1 = initInfo.m_pFileSystem->GetSearchPath( "EXECUTABLE_PATH", false, szSearchPaths, 4096 );
  764. if( iLength1 == 1 )
  765. iLength1 = 0;
  766. int iLength2 = initInfo.m_pFileSystem->GetSearchPath( "GAMEBIN", false, szSearchPaths + iLength1, 4096 - iLength1 );
  767. if( (iLength2 > 1) && (iLength1 > 1) )
  768. {
  769. szSearchPaths[iLength1 - 1] = ';'; //replace first null terminator
  770. }
  771. const char *szAdditionalPath = CommandLine()->ParmValue( "-AdditionalPDBSearchPath" );
  772. if( szAdditionalPath && szAdditionalPath[0] )
  773. {
  774. int iLength = iLength1;
  775. if( iLength2 > 1 )
  776. iLength += iLength2;
  777. if( iLength != 0 )
  778. {
  779. szSearchPaths[iLength - 1] = ';'; //replaces null terminator
  780. }
  781. V_strncpy( &szSearchPaths[iLength], szAdditionalPath, 4096 - iLength );
  782. }
  783. //Append the perforce symbol server last. Documentation says that "srv*\\perforce\symbols" should work, but it doesn't.
  784. //"symsrv*symsrv.dll*\\perforce\symbols" which the docs say is the same statement, works.
  785. {
  786. V_strncat( szSearchPaths, ";symsrv*symsrv.dll*\\\\perforce\\symbols", 4096 );
  787. }
  788. SetStackTranslationSymbolSearchPath( szSearchPaths );
  789. //MessageBox( NULL, szSearchPaths, "Search Paths", 0 );
  790. }
  791. #endif
  792. return FS_OK;
  793. }
  794. bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename )
  795. {
  796. char filename[MAX_PATH];
  797. Q_strncpy( filename, pDirectoryName, sizeof( filename ) );
  798. Q_AppendSlash( filename, sizeof( filename ) );
  799. Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS );
  800. Q_FixSlashes( filename );
  801. #ifdef _PS3
  802. Assert( 0 );
  803. bool bExist = false;
  804. #else // !_PS3
  805. bool bExist = ( _access( filename, 0 ) == 0 );
  806. #endif // _PS3
  807. return ( bExist );
  808. }
  809. namespace
  810. {
  811. SuggestGameInfoDirFn_t & GetSuggestGameInfoDirFn( void )
  812. {
  813. static SuggestGameInfoDirFn_t s_pfnSuggestGameInfoDir = NULL;
  814. return s_pfnSuggestGameInfoDir;
  815. }
  816. }; // `anonymous` namespace
  817. SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn )
  818. {
  819. SuggestGameInfoDirFn_t &rfn = GetSuggestGameInfoDirFn();
  820. SuggestGameInfoDirFn_t pfnOldFn = rfn;
  821. rfn = pfnNewFn;
  822. return pfnOldFn;
  823. }
  824. static FSReturnCode_t TryLocateGameInfoFile( char *pOutDir, int outDirLen, bool bBubbleDir )
  825. {
  826. // Retain a copy of suggested path for further attempts
  827. CArrayAutoPtr < char > spchCopyNameBuffer( new char [ outDirLen ] );
  828. Q_strncpy( spchCopyNameBuffer.Get(), pOutDir, outDirLen );
  829. spchCopyNameBuffer[ outDirLen - 1 ] = 0;
  830. // Make appropriate slashes ('/' - Linux style)
  831. for ( char *pchFix = spchCopyNameBuffer.Get(),
  832. *pchEnd = pchFix + outDirLen;
  833. pchFix < pchEnd; ++ pchFix )
  834. {
  835. if ( '\\' == *pchFix )
  836. {
  837. *pchFix = '/';
  838. }
  839. }
  840. // Have a look in supplied path
  841. do
  842. {
  843. if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
  844. {
  845. return FS_OK;
  846. }
  847. if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
  848. {
  849. return FS_OK;
  850. }
  851. }
  852. while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
  853. // Make an attempt to resolve from "content -> game" directory
  854. Q_strncpy( pOutDir, spchCopyNameBuffer.Get(), outDirLen );
  855. pOutDir[ outDirLen - 1 ] = 0;
  856. if ( char *pchContentFix = Q_stristr( pOutDir, "/content/" ) )
  857. {
  858. V_strcpy( pchContentFix, "/game/" );
  859. memmove( pchContentFix + 6, pchContentFix + 9, pOutDir + outDirLen - (pchContentFix + 9) );
  860. // Try in the mapped "game" directory
  861. do
  862. {
  863. if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
  864. {
  865. return FS_OK;
  866. }
  867. if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
  868. {
  869. return FS_OK;
  870. }
  871. }
  872. while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
  873. }
  874. // Could not find it here
  875. return FS_MISSING_GAMEINFO_FILE;
  876. }
  877. FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen )
  878. {
  879. // Engine and Hammer don't want to search around for it.
  880. if ( fsInfo.m_bOnlyUseDirectoryName )
  881. {
  882. if ( !fsInfo.m_pDirectoryName )
  883. return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." );
  884. bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME );
  885. if ( IsX360() && !bExists )
  886. {
  887. bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE );
  888. }
  889. if ( !bExists )
  890. {
  891. if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
  892. {
  893. char basePath[MAX_PATH];
  894. strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
  895. Q_AppendSlash( basePath, sizeof( basePath ) );
  896. Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS );
  897. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
  898. {
  899. Q_strncpy( pOutDir, basePath, outDirLen );
  900. return FS_OK;
  901. }
  902. if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
  903. {
  904. Q_strncpy( pOutDir, basePath, outDirLen );
  905. return FS_OK;
  906. }
  907. }
  908. 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 );
  909. }
  910. Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen );
  911. return FS_OK;
  912. }
  913. // First, check for overrides on the command line or environment variables.
  914. const char *pProject = GetVProjectCmdLineValue();
  915. if ( pProject )
  916. {
  917. if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) )
  918. {
  919. Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
  920. return FS_OK;
  921. }
  922. if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) )
  923. {
  924. Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
  925. return FS_OK;
  926. }
  927. if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
  928. {
  929. char basePath[MAX_PATH];
  930. strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
  931. Q_AppendSlash( basePath, sizeof( basePath ) );
  932. Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS );
  933. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
  934. {
  935. Q_strncpy( pOutDir, basePath, outDirLen );
  936. return FS_OK;
  937. }
  938. if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
  939. {
  940. Q_strncpy( pOutDir, basePath, outDirLen );
  941. return FS_OK;
  942. }
  943. }
  944. if ( fsInfo.m_bNoGameInfo )
  945. {
  946. // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use.
  947. // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if
  948. // -game is supplied on the command line.
  949. Q_strncpy( pOutDir, "", outDirLen );
  950. return FS_OK;
  951. }
  952. else
  953. {
  954. // They either specified vproject on the command line or it's in their registry. Either way,
  955. // we don't want to continue if they've specified it but it's not valid.
  956. goto ShowError;
  957. }
  958. }
  959. if ( fsInfo.m_bNoGameInfo )
  960. {
  961. Q_strncpy( pOutDir, "", outDirLen );
  962. return FS_OK;
  963. }
  964. // Ask the application if it can provide us with a game info directory
  965. {
  966. bool bBubbleDir = true;
  967. SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn();
  968. if ( pfnSuggestGameInfoDirFn &&
  969. ( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) &&
  970. FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) )
  971. return FS_OK;
  972. }
  973. // Try to use the environment variable / registry
  974. if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL &&
  975. ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) &&
  976. FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) )
  977. return FS_OK;
  978. if ( IsPC() )
  979. {
  980. Warning( "Warning: falling back to auto detection of vproject directory.\n" );
  981. // Now look for it in the directory they passed in.
  982. if ( fsInfo.m_pDirectoryName )
  983. Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName );
  984. else
  985. Q_MakeAbsolutePath( pOutDir, outDirLen, "." );
  986. if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
  987. return FS_OK;
  988. // Use the CWD
  989. Q_getwd( pOutDir, outDirLen );
  990. if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
  991. return FS_OK;
  992. }
  993. ShowError:
  994. return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE,
  995. "Unable to find %s. Solutions:\n\n"
  996. "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n"
  997. "2. Run vconfig to specify which game you're working on.\n"
  998. "3. Add -game <path> on the command line where <path> is the directory that %s is in.\n",
  999. GAMEINFO_FILENAME, GAMEINFO_FILENAME );
  1000. }
  1001. bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath )
  1002. {
  1003. // Fix the slashes in the input arguments.
  1004. char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH];
  1005. Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) );
  1006. Q_FixSlashes( correctedPathEnvVar );
  1007. pPathEnvVar = correctedPathEnvVar;
  1008. Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) );
  1009. Q_FixSlashes( correctedTestPath );
  1010. if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) )
  1011. correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0;
  1012. pTestPath = correctedTestPath;
  1013. const char *pCurPos = pPathEnvVar;
  1014. while ( 1 )
  1015. {
  1016. const char *pTestPos = Q_stristr( pCurPos, pTestPath );
  1017. if ( !pTestPos )
  1018. return false;
  1019. // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon.
  1020. pTestPos += strlen( pTestPath );
  1021. if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') )
  1022. return true;
  1023. // Advance our marker..
  1024. pCurPos = pTestPos;
  1025. }
  1026. }
  1027. FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings )
  1028. {
  1029. if ( IsGameConsole() )
  1030. {
  1031. // consoles don't use steam
  1032. return FS_MISSING_STEAM_DLL;
  1033. }
  1034. if ( IsPosix() )
  1035. return FS_OK; // under posix the content does not live with steam.dll up the path, rely on the environment already being set by steam
  1036. // Start at our bin directory and move up until we find a directory with steam.dll in it.
  1037. char executablePath[MAX_PATH];
  1038. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  1039. {
  1040. if ( bErrorsAsWarnings )
  1041. {
  1042. Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" );
  1043. return FS_INVALID_PARAMETERS;
  1044. }
  1045. else
  1046. {
  1047. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  1048. }
  1049. }
  1050. Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen );
  1051. #ifdef WIN32
  1052. const char *pchSteamDLL = "steam" DLL_EXT_STRING;
  1053. #elif defined(OSX) || defined(LINUX)
  1054. const char *pchSteamDLL = "libsteam" DLL_EXT_STRING;
  1055. // under Linux & OSX the bin lives in the bin/ folder, so step back one
  1056. Q_StripLastDir( steamInstallPath, steamInstallPathLen );
  1057. #elif defined( _PS3 )
  1058. const char *pchSteamDLL = "steam_ps3.ps3";
  1059. #else
  1060. #error
  1061. #endif
  1062. while ( 1 )
  1063. {
  1064. // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username.
  1065. // find
  1066. if ( DoesFileExistIn( steamInstallPath, pchSteamDLL ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) )
  1067. break;
  1068. if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) )
  1069. {
  1070. if ( bErrorsAsWarnings )
  1071. {
  1072. Warning( "Can't find %s relative to executable path: %s.\n", pchSteamDLL, executablePath );
  1073. return FS_MISSING_STEAM_DLL;
  1074. }
  1075. else
  1076. {
  1077. return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find %s relative to executable path: %s.", pchSteamDLL, executablePath );
  1078. }
  1079. }
  1080. }
  1081. // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll.
  1082. char szPath[ 8192 ];
  1083. steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) );
  1084. if ( !DoesPathExistAlready( szPath, steamInstallPath ) )
  1085. {
  1086. #ifdef WIN32
  1087. #define PATH_SEP ";"
  1088. #else
  1089. #define PATH_SEP ":"
  1090. #endif
  1091. steamEnvVars.m_Path.SetValue( "%s%s%s", szPath, PATH_SEP, steamInstallPath );
  1092. }
  1093. return FS_OK;
  1094. }
  1095. FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen )
  1096. {
  1097. steamCfgPath[0] = 0;
  1098. char executablePath[MAX_PATH];
  1099. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  1100. {
  1101. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  1102. }
  1103. Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen );
  1104. while ( 1 )
  1105. {
  1106. if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) )
  1107. break;
  1108. if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) )
  1109. {
  1110. // the file isnt found, thats ok, its not mandatory
  1111. return FS_OK;
  1112. }
  1113. }
  1114. Q_AppendSlash( steamCfgPath, steamCfgPathLen );
  1115. Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS );
  1116. return FS_OK;
  1117. }
  1118. void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars )
  1119. {
  1120. // Always inherit the Steam user if it's already set, since it probably means we (or the
  1121. // the app that launched us) were launched from Steam.
  1122. char appUser[MAX_PATH];
  1123. if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) )
  1124. return;
  1125. const char *pTempAppUser = NULL;
  1126. if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL )
  1127. {
  1128. Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
  1129. }
  1130. else
  1131. {
  1132. // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user
  1133. // by looking in <steam install path>\config\SteamAppData.vdf.
  1134. char fullFilename[MAX_PATH];
  1135. Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) );
  1136. Q_AppendSlash( fullFilename, sizeof( fullFilename ) );
  1137. Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS );
  1138. KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename );
  1139. if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL )
  1140. {
  1141. Error( "Can't find steam app user info." );
  1142. }
  1143. Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
  1144. pSteamAppData->deleteThis();
  1145. }
  1146. Q_strlower( appUser );
  1147. steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser );
  1148. }
  1149. void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars )
  1150. {
  1151. // Always inherit the passphrase if it's already set, since it probably means we (or the
  1152. // the app that launched us) were launched from Steam.
  1153. char szPassPhrase[ MAX_PATH ];
  1154. if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) )
  1155. return;
  1156. // SteamUserPassphrase.
  1157. const char *pStr;
  1158. if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL )
  1159. {
  1160. steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr );
  1161. }
  1162. }
  1163. void SetSteamAppId( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars )
  1164. {
  1165. // SteamAppId is in gameinfo.txt->FileSystem->FileSystemInfo_Steam->SteamAppId.
  1166. int iAppId = pFileSystemInfo->GetInt( "SteamAppId", -1 );
  1167. if ( iAppId == -1 )
  1168. Error( "Missing SteamAppId in %s\\%s.", pGameInfoDirectory, GAMEINFO_FILENAME );
  1169. steamEnvVars.m_SteamAppId.SetValue( "%d", iAppId );
  1170. }
  1171. FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars )
  1172. {
  1173. // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can.
  1174. char steamInfoFile[MAX_PATH];
  1175. Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) );
  1176. Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) );
  1177. Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS );
  1178. KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile );
  1179. char steamInstallPath[MAX_PATH];
  1180. FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false );
  1181. if ( ret != FS_OK )
  1182. return ret;
  1183. SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars );
  1184. SetSteamUserPassphrase( pSteamInfo, steamEnvVars );
  1185. SetSteamAppId( pFileSystemInfo, pGameInfoDirectory, steamEnvVars );
  1186. if ( pSteamInfo )
  1187. pSteamInfo->deleteThis();
  1188. return FS_OK;
  1189. }
  1190. FSReturnCode_t GetSteamExtraAppId( const char *pDirectoryName, int *nExtraAppId )
  1191. {
  1192. // Now, load gameinfo.txt (to make sure it's there)
  1193. KeyValues *pMainFile = NULL, *pFileSystemInfo = NULL, *pSearchPaths = NULL;
  1194. FSReturnCode_t ret = LoadGameInfoFile( pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
  1195. if ( ret != FS_OK )
  1196. return ret;
  1197. *nExtraAppId = pFileSystemInfo->GetInt( "ToolsAppId", -1 );
  1198. pMainFile->deleteThis();
  1199. return FS_OK;
  1200. }
  1201. FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem )
  1202. {
  1203. pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" );
  1204. #ifndef _PS3
  1205. char executablePath[MAX_PATH];
  1206. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  1207. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  1208. pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" );
  1209. #endif
  1210. return FS_OK;
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. // Returns the name of the file system DLL to use
  1214. //-----------------------------------------------------------------------------
  1215. FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
  1216. {
  1217. bSteam = false;
  1218. // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
  1219. // is in this same directory with us.
  1220. char executablePath[MAX_PATH];
  1221. if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
  1222. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
  1223. #if defined( _PS3 )
  1224. Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, g_pPS3PathInfo->PrxPath(), CORRECT_PATH_SEPARATOR );
  1225. #else
  1226. Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
  1227. #endif
  1228. return FS_OK;
  1229. }
  1230. //-----------------------------------------------------------------------------
  1231. // Sets up the steam.dll install path in our PATH env var (so you can then just
  1232. // LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special )
  1233. //-----------------------------------------------------------------------------
  1234. FSReturnCode_t FileSystem_SetupSteamInstallPath()
  1235. {
  1236. CSteamEnvVars steamEnvVars;
  1237. char steamInstallPath[MAX_PATH];
  1238. FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
  1239. steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
  1240. return ret;
  1241. }
  1242. //-----------------------------------------------------------------------------
  1243. // Sets up the steam environment + gets back the gameinfo.txt path
  1244. //-----------------------------------------------------------------------------
  1245. FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo )
  1246. {
  1247. // First, locate the directory with gameinfo.txt.
  1248. FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) );
  1249. if ( ret != FS_OK )
  1250. return ret;
  1251. // This is so that processes spawned by this application will have the same VPROJECT
  1252. #if defined( WIN32 ) || defined( _GAMECONSOLE )
  1253. char pEnvBuf[MAX_PATH+32];
  1254. Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath );
  1255. _putenv( pEnvBuf );
  1256. #else
  1257. setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 );
  1258. #endif
  1259. CSteamEnvVars steamEnvVars;
  1260. if ( fsInfo.m_bSteam )
  1261. {
  1262. if ( fsInfo.m_bToolsMode )
  1263. {
  1264. // Now, load gameinfo.txt (to make sure it's there)
  1265. KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
  1266. ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths );
  1267. if ( ret != FS_OK )
  1268. return ret;
  1269. // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam.
  1270. ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars );
  1271. if ( ret != FS_OK )
  1272. return ret;
  1273. steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
  1274. // We're done with main file
  1275. pMainFile->deleteThis();
  1276. }
  1277. else if ( fsInfo.m_bSetSteamDLLPath )
  1278. {
  1279. // This is used by the engine to automatically set the path to their steam.dll when running the engine,
  1280. // so they can debug it without having to copy steam.dll up into their hl2.exe folder.
  1281. char steamInstallPath[MAX_PATH];
  1282. ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
  1283. steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
  1284. }
  1285. }
  1286. return FS_OK;
  1287. }
  1288. //-----------------------------------------------------------------------------
  1289. // Loads the file system module
  1290. //-----------------------------------------------------------------------------
  1291. FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo )
  1292. {
  1293. // First, locate the directory with gameinfo.txt.
  1294. FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo );
  1295. if ( ret != FS_OK )
  1296. return ret;
  1297. // Now that the environment is setup, load the filesystem module.
  1298. if ( !Sys_LoadInterface(
  1299. fsInfo.m_pFileSystemDLLName,
  1300. FILESYSTEM_INTERFACE_VERSION,
  1301. &fsInfo.m_pModule,
  1302. (void**)&fsInfo.m_pFileSystem ) )
  1303. {
  1304. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName );
  1305. }
  1306. if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) )
  1307. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName );
  1308. if ( fsInfo.m_pFileSystem->Init() != INIT_OK )
  1309. return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName );
  1310. return FS_OK;
  1311. }
  1312. //-----------------------------------------------------------------------------
  1313. // Mounds a particular steam cache
  1314. //-----------------------------------------------------------------------------
  1315. FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo )
  1316. {
  1317. // This part is Steam-only.
  1318. if ( mountContentInfo.m_pFileSystem->IsSteam() )
  1319. {
  1320. // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem
  1321. // like HL2, then mount the SDK content (tools materials and models, etc) in addition.
  1322. int nExtraAppId = -1;
  1323. if ( mountContentInfo.m_bToolsMode )
  1324. {
  1325. FSReturnCode_t ret = GetSteamExtraAppId( mountContentInfo.m_pDirectoryName, &nExtraAppId );
  1326. if ( ret != FS_OK )
  1327. return ret;
  1328. }
  1329. // Set our working directory temporarily so Steam can remember it.
  1330. // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk
  1331. // to get to the relative part of the path.
  1332. char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH];
  1333. if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
  1334. return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
  1335. Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) );
  1336. _chdir( baseDir );
  1337. // Filesystem_tools needs to add dependencies in here beforehand.
  1338. FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId );
  1339. _chdir( oldWorkingDir );
  1340. if ( retVal != FILESYSTEM_MOUNT_OK )
  1341. return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" );
  1342. }
  1343. return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem );
  1344. }
  1345. void FileSystem_SetErrorMode( FSErrorMode_t errorMode )
  1346. {
  1347. g_FileSystemErrorMode = errorMode;
  1348. }
  1349. void FileSystem_ClearSteamEnvVars()
  1350. {
  1351. CSteamEnvVars envVars;
  1352. // Change the values and don't restore the originals.
  1353. envVars.m_SteamAppId.SetValue( "" );
  1354. envVars.m_SteamUserPassphrase.SetValue( "" );
  1355. envVars.m_SteamAppUser.SetValue( "" );
  1356. envVars.SetRestoreOriginalValue_ALL( false );
  1357. }
  1358. //-----------------------------------------------------------------------------
  1359. // Adds the platform folder to the search path.
  1360. //-----------------------------------------------------------------------------
  1361. void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath )
  1362. {
  1363. char platform[MAX_PATH];
  1364. if ( pFileSystem->IsSteam() )
  1365. {
  1366. // Steam doesn't support relative paths
  1367. Q_strncpy( platform, "platform", MAX_PATH );
  1368. }
  1369. else
  1370. {
  1371. Q_strncpy( platform, szGameInfoPath, MAX_PATH );
  1372. Q_StripTrailingSlash( platform );
  1373. #ifdef _PS3
  1374. Q_strncat( platform, "/platform", MAX_PATH, MAX_PATH );
  1375. #else // _PS3
  1376. Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
  1377. #endif // !_PS3
  1378. }
  1379. pFileSystem->AddSearchPath( platform, "PLATFORM" );
  1380. }