aboutsummaryrefslogtreecommitdiff
path: root/mp/src/public/filesystem_init.cpp
diff options
context:
space:
mode:
authorJørgen P. Tjernø <[email protected]>2013-12-02 19:31:46 -0800
committerJørgen P. Tjernø <[email protected]>2013-12-02 19:46:31 -0800
commitf56bb35301836e56582a575a75864392a0177875 (patch)
treede61ddd39de3e7df52759711950b4c288592f0dc /mp/src/public/filesystem_init.cpp
parentMark some more files as text. (diff)
downloadsource-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz
source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/public/filesystem_init.cpp')
-rw-r--r--mp/src/public/filesystem_init.cpp2830
1 files changed, 1415 insertions, 1415 deletions
diff --git a/mp/src/public/filesystem_init.cpp b/mp/src/public/filesystem_init.cpp
index 2f8366e4..08e8da17 100644
--- a/mp/src/public/filesystem_init.cpp
+++ b/mp/src/public/filesystem_init.cpp
@@ -1,1415 +1,1415 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================
-
-#undef PROTECTED_THINGS_ENABLE
-#undef PROTECT_FILEIO_FUNCTIONS
-#ifndef POSIX
-#undef fopen
-#endif
-
-#if defined( _WIN32 ) && !defined( _X360 )
-#include <windows.h>
-#include <direct.h>
-#include <io.h>
-#include <process.h>
-#elif defined( POSIX )
-#include <unistd.h>
-#define _chdir chdir
-#define _access access
-#endif
-#include <stdio.h>
-#include <sys/stat.h>
-#include "tier1/strtools.h"
-#include "tier1/utlbuffer.h"
-#include "filesystem_init.h"
-#include "tier0/icommandline.h"
-#include "KeyValues.h"
-#include "appframework/IAppSystemGroup.h"
-#include "tier1/smartptr.h"
-#if defined( _X360 )
-#include "xbox\xbox_win32stubs.h"
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include <tier0/memdbgon.h>
-
-#if !defined( _X360 )
-#define GAMEINFO_FILENAME "gameinfo.txt"
-#else
-// The .xtx file is a TCR requirement, as .txt files cannot live on the DVD.
-// 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.
-// 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.
-// The .xtx concept is private and should only have to occurr here. As a safety measure, if the .xtx file is not found
-// a retry is made with the original .txt name
-#define GAMEINFO_FILENAME "gameinfo.xtx"
-#endif
-#define GAMEINFO_FILENAME_ALTERNATE "gameinfo.txt"
-
-static char g_FileSystemError[256];
-static bool s_bUseVProjectBinDir = false;
-static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG;
-
-// Call this to use a bin directory relative to VPROJECT
-void FileSystem_UseVProjectBinDir( bool bEnable )
-{
- s_bUseVProjectBinDir = bEnable;
-}
-
-// This class lets you modify environment variables, and it restores the original value
-// when it goes out of scope.
-class CTempEnvVar
-{
-public:
- CTempEnvVar( const char *pVarName )
- {
- m_bRestoreOriginalValue = true;
- m_pVarName = pVarName;
-
- const char *pValue = NULL;
-
-#ifdef _WIN32
- // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
- // to the process environment after the DLL was loaded.
- char szBuf[ 4096 ];
- if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0)
- {
- pValue = szBuf;
- }
-#else
- // LINUX BUG: see above
- pValue = getenv( pVarName );
-#endif
-
- if ( pValue )
- {
- m_bExisted = true;
- m_OriginalValue.SetSize( strlen( pValue ) + 1 );
- memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() );
- }
- else
- {
- m_bExisted = false;
- }
- }
-
- ~CTempEnvVar()
- {
- if ( m_bRestoreOriginalValue )
- {
- // Restore the original value.
- if ( m_bExisted )
- {
- SetValue( "%s", m_OriginalValue.Base() );
- }
- else
- {
- ClearValue();
- }
- }
- }
-
- void SetRestoreOriginalValue( bool bRestore )
- {
- m_bRestoreOriginalValue = bRestore;
- }
-
- int GetValue(char *pszBuf, int nBufSize )
- {
- if ( !pszBuf || ( nBufSize <= 0 ) )
- return 0;
-
-#ifdef _WIN32
- // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
- // to the process environment after the DLL was loaded.
- return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize );
-#else
- // LINUX BUG: see above
- const char *pszOut = getenv( m_pVarName );
- if ( !pszOut )
- {
- *pszBuf = '\0';
- return 0;
- }
-
- Q_strncpy( pszBuf, pszOut, nBufSize );
- return Q_strlen( pszBuf );
-#endif
- }
-
- void SetValue( const char *pValue, ... )
- {
- char valueString[4096];
- va_list marker;
- va_start( marker, pValue );
- Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker );
- va_end( marker );
-
-#ifdef WIN32
- char str[4096];
- Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString );
- _putenv( str );
-#else
- setenv( m_pVarName, valueString, 1 );
-#endif
- }
-
- void ClearValue()
- {
-#ifdef WIN32
- char str[512];
- Q_snprintf( str, sizeof( str ), "%s=", m_pVarName );
- _putenv( str );
-#else
- setenv( m_pVarName, "", 1 );
-#endif
- }
-
-private:
- bool m_bRestoreOriginalValue;
- const char *m_pVarName;
- bool m_bExisted;
- CUtlVector<char> m_OriginalValue;
-};
-
-
-class CSteamEnvVars
-{
-public:
- CSteamEnvVars() :
- m_SteamAppId( "SteamAppId" ),
- m_SteamUserPassphrase( "SteamUserPassphrase" ),
- m_SteamAppUser( "SteamAppUser" ),
- m_Path( "path" )
- {
- }
-
- void SetRestoreOriginalValue_ALL( bool bRestore )
- {
- m_SteamAppId.SetRestoreOriginalValue( bRestore );
- m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore );
- m_SteamAppUser.SetRestoreOriginalValue( bRestore );
- m_Path.SetRestoreOriginalValue( bRestore );
- }
-
- CTempEnvVar m_SteamAppId;
- CTempEnvVar m_SteamUserPassphrase;
- CTempEnvVar m_SteamAppUser;
- CTempEnvVar m_Path;
-};
-
-// ---------------------------------------------------------------------------------------------------- //
-// Helpers.
-// ---------------------------------------------------------------------------------------------------- //
-void Q_getwd( char *out, int outSize )
-{
-#if defined( _WIN32 ) || defined( WIN32 )
- _getcwd( out, outSize );
- Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
-#else
- getcwd( out, outSize );
- strcat( out, "/" );
-#endif
- Q_FixSlashes( out );
-}
-
-// ---------------------------------------------------------------------------------------------------- //
-// Module interface.
-// ---------------------------------------------------------------------------------------------------- //
-
-CFSSearchPathsInit::CFSSearchPathsInit()
-{
- m_pDirectoryName = NULL;
- m_pLanguage = NULL;
- m_ModPath[0] = 0;
-}
-
-
-CFSSteamSetupInfo::CFSSteamSetupInfo()
-{
- m_pDirectoryName = NULL;
- m_bOnlyUseDirectoryName = false;
- m_bSteam = false;
- m_bToolsMode = true;
- m_bNoGameInfo = false;
-}
-
-
-CFSLoadModuleInfo::CFSLoadModuleInfo()
-{
- m_pFileSystemDLLName = NULL;
- m_pFileSystem = NULL;
- m_pModule = NULL;
-}
-
-
-CFSMountContentInfo::CFSMountContentInfo()
-{
- m_bToolsMode = true;
- m_pDirectoryName = NULL;
- m_pFileSystem = NULL;
-}
-
-
-const char *FileSystem_GetLastErrorString()
-{
- return g_FileSystemError;
-}
-
-
-KeyValues* ReadKeyValuesFile( const char *pFilename )
-{
- // Read in the gameinfo.txt file and null-terminate it.
- FILE *fp = fopen( pFilename, "rb" );
- if ( !fp )
- return NULL;
- CUtlVector<char> buf;
- fseek( fp, 0, SEEK_END );
- buf.SetSize( ftell( fp ) + 1 );
- fseek( fp, 0, SEEK_SET );
- fread( buf.Base(), 1, buf.Count()-1, fp );
- fclose( fp );
- buf[buf.Count()-1] = 0;
-
- KeyValues *kv = new KeyValues( "" );
- if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
- {
- kv->deleteThis();
- return NULL;
- }
-
- return kv;
-}
-
-static bool Sys_GetExecutableName( char *out, int len )
-{
-#if defined( _WIN32 )
- if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) )
- {
- return false;
- }
-#else
- if ( CommandLine()->GetParm(0) )
- {
- Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) );
- }
- else
- {
- return false;
- }
-#endif
-
- return true;
-}
-
-bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen )
-{
- exedir[0] = 0;
-
- if ( s_bUseVProjectBinDir )
- {
- const char *pProject = GetVProjectCmdLineValue();
- if ( !pProject )
- {
- // Check their registry.
- pProject = getenv( GAMEDIR_TOKEN );
- }
- if ( pProject )
- {
- Q_snprintf( exedir, exeDirLen, "%s%c..%cbin", pProject, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR );
- return true;
- }
- return false;
- }
-
- if ( !Sys_GetExecutableName( exedir, exeDirLen ) )
- return false;
- Q_StripFilename( exedir );
-
- if ( IsX360() )
- {
- // The 360 can have its exe and dlls reside on different volumes
- // use the optional basedir as the exe dir
- if ( CommandLine()->FindParm( "-basedir" ) )
- {
- strcpy( exedir, CommandLine()->ParmValue( "-basedir", "" ) );
- }
- }
-
- Q_FixSlashes( exedir );
-
- // Return the bin directory as the executable dir if it's not in there
- // because that's really where we're running from...
- char ext[MAX_PATH];
- Q_StrRight( exedir, 4, ext, sizeof( ext ) );
- if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 )
- {
- Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS );
- Q_strncat( exedir, "bin", exeDirLen, COPY_ALL_CHARACTERS );
- Q_FixSlashes( exedir );
- }
-
- return true;
-}
-
-static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen )
-{
- if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) )
- {
- Q_StripFilename( baseDir );
- return true;
- }
-
- return false;
-}
-
-void LaunchVConfig()
-{
-#if defined( _WIN32 ) && !defined( _X360 )
- char vconfigExe[MAX_PATH];
- FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) );
- Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) );
- Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS );
-
- char *argv[] =
- {
- vconfigExe,
- "-allowdebug",
- NULL
- };
-
- _spawnv( _P_NOWAIT, vconfigExe, argv );
-#elif defined( _X360 )
- Msg( "Launching vconfig.exe not supported\n" );
-#endif
-}
-
-const char* GetVProjectCmdLineValue()
-{
- return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) );
-}
-
-FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... )
-{
- va_list marker;
- va_start( marker, pMsg );
- Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker );
- va_end( marker );
-
- Warning( "%s\n", g_FileSystemError );
-
- // Run vconfig?
- // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject.
- if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() )
- {
- LaunchVConfig();
- }
-
- if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG )
- {
- Error( "%s\n", g_FileSystemError );
- }
-
- return retVal;
-}
-
-FSReturnCode_t LoadGameInfoFile(
- const char *pDirectoryName,
- KeyValues *&pMainFile,
- KeyValues *&pFileSystemInfo,
- KeyValues *&pSearchPaths )
-{
- // If GameInfo.txt exists under pBaseDir, then this is their game directory.
- // All the filesystem mappings will be in this file.
- char gameinfoFilename[MAX_PATH];
- Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
- Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
- Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
- Q_FixSlashes( gameinfoFilename );
- pMainFile = ReadKeyValuesFile( gameinfoFilename );
- if ( IsX360() && !pMainFile )
- {
- // try again
- Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
- Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
- Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
- Q_FixSlashes( gameinfoFilename );
- pMainFile = ReadKeyValuesFile( gameinfoFilename );
- }
- if ( !pMainFile )
- {
- return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename );
- }
-
- pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
- if ( !pFileSystemInfo )
- {
- pMainFile->deleteThis();
- return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
- }
-
- // Now read in all the search paths.
- pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" );
- if ( !pSearchPaths )
- {
- pMainFile->deleteThis();
- return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
- }
- return FS_OK;
-}
-
-// checks the registry for the low violence setting
-// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3"
-bool IsLowViolenceBuild( void )
-{
-#if defined(_WIN32)
- HKEY hKey;
- char szValue[64];
- unsigned long len = sizeof(szValue) - 1;
- bool retVal = false;
-
- if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS )
- {
- // User Token 2
- if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
- {
- if ( Q_strlen( szValue ) > 0 )
- {
- retVal = true;
- }
- }
-
- if ( !retVal )
- {
- // reset "len" for the next check
- len = sizeof(szValue) - 1;
-
- // User Token 3
- if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
- {
- if ( Q_strlen( szValue ) > 0 )
- {
- retVal = true;
- }
- }
- }
-
- RegCloseKey(hKey);
- }
-
- return retVal;
-#elif POSIX
- return false;
-#else
- #error "Fix me"
-#endif
-}
-
-static void FileSystem_AddLoadedSearchPath(
- CFSSearchPathsInit &initInfo,
- const char *pPathID,
- const char *fullLocationPath,
- bool bLowViolence )
-{
-
- // Check for mounting LV game content in LV builds only
- if ( V_stricmp( pPathID, "game_lv" ) == 0 )
- {
-
- // Not in LV build, don't mount
- if ( !bLowViolence )
- return;
-
- // Mount, as a game path
- pPathID = "game";
- }
-
- // Special processing for ordinary game folders
- if ( V_stristr( fullLocationPath, ".vpk" ) == NULL && Q_stricmp( pPathID, "game" ) == 0 )
- {
- if ( CommandLine()->FindParm( "-tempcontent" ) != 0 )
- {
- char szPath[MAX_PATH];
- Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath );
- initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
- }
- }
-
-
- if ( initInfo.m_pLanguage &&
- Q_stricmp( initInfo.m_pLanguage, "english" ) &&
- V_strstr( fullLocationPath, "_english" ) != NULL )
- {
- char szPath[MAX_PATH];
- char szLangString[MAX_PATH];
-
- // Need to add a language version of this path first
-
- Q_snprintf( szLangString, sizeof(szLangString), "_%s", initInfo.m_pLanguage);
- V_StrSubst( fullLocationPath, "_english", szLangString, szPath, sizeof( szPath ), true );
- initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
- }
-
- initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL );
-}
-
-static int SortStricmp( char * const * sz1, char * const * sz2 )
-{
- return V_stricmp( *sz1, *sz2 );
-}
-
-FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
-{
- if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );
-
- KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
- FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
- if ( retVal != FS_OK )
- return retVal;
-
- // All paths except those marked with |gameinfo_path| are relative to the base dir.
- char baseDir[MAX_PATH];
- if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
-
- // The MOD directory is always the one that contains gameinfo.txt
- Q_strncpy( initInfo.m_ModPath, initInfo.m_pDirectoryName, sizeof( initInfo.m_ModPath ) );
-
- #define GAMEINFOPATH_TOKEN "|gameinfo_path|"
- #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|"
-
- const char *pszExtraSearchPath = CommandLine()->ParmValue( "-insert_search_path" );
- if ( pszExtraSearchPath )
- {
- CUtlStringList vecPaths;
- V_SplitString( pszExtraSearchPath, ",", vecPaths );
- FOR_EACH_VEC( vecPaths, idxExtraPath )
- {
- char szAbsSearchPath[MAX_PATH];
- Q_StripPrecedingAndTrailingWhitespace( vecPaths[ idxExtraPath ] );
- V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), vecPaths[ idxExtraPath ], baseDir );
- V_FixSlashes( szAbsSearchPath );
- if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
- Error( "Bad -insert_search_path - Can't resolve pathname for '%s'", szAbsSearchPath );
- V_StripTrailingSlash( szAbsSearchPath );
- FileSystem_AddLoadedSearchPath( initInfo, "GAME", szAbsSearchPath, false );
- FileSystem_AddLoadedSearchPath( initInfo, "MOD", szAbsSearchPath, false );
- }
- }
-
- bool bLowViolence = IsLowViolenceBuild();
- for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
- {
- const char *pLocation = pCur->GetString();
- const char *pszBaseDir = baseDir;
-
- if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
- {
- pLocation += strlen( GAMEINFOPATH_TOKEN );
- pszBaseDir = initInfo.m_pDirectoryName;
- }
- else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
- {
- // 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.
- // So in Orange Box, if they specified:
- // |all_source_engine_paths|hl2
- // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
- //
- // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
- // 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,
- // so we can just use the path as-is.
- pLocation += strlen( BASESOURCEPATHS_TOKEN );
- }
-
- CUtlStringList vecFullLocationPaths;
- char szAbsSearchPath[MAX_PATH];
- V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), pLocation, pszBaseDir );
-
- // Now resolve any ./'s.
- V_FixSlashes( szAbsSearchPath );
- if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
- Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", szAbsSearchPath );
- V_StripTrailingSlash( szAbsSearchPath );
-
- // Don't bother doing any wildcard expansion unless it has wildcards. This avoids the weird
- // thing with xxx_dir.vpk files being referred to simply as xxx.vpk.
- if ( V_stristr( pLocation, "?") == NULL && V_stristr( pLocation, "*") == NULL )
- {
- vecFullLocationPaths.CopyAndAddToTail( szAbsSearchPath );
- }
- else
- {
- FileFindHandle_t findHandle = NULL;
- const char *pszFoundShortName = initInfo.m_pFileSystem->FindFirst( szAbsSearchPath, &findHandle );
- if ( pszFoundShortName )
- {
- do
- {
-
- // We only know how to mount VPK's and directories
- if ( pszFoundShortName[0] != '.' && ( initInfo.m_pFileSystem->FindIsDirectory( findHandle ) || V_stristr( pszFoundShortName, ".vpk" ) ) )
- {
- char szAbsName[MAX_PATH];
- V_ExtractFilePath( szAbsSearchPath, szAbsName, sizeof( szAbsName ) );
- V_AppendSlash( szAbsName, sizeof(szAbsName) );
- V_strcat_safe( szAbsName, pszFoundShortName );
-
- vecFullLocationPaths.CopyAndAddToTail( szAbsName );
-
- // Check for a common mistake
- if (
- !V_stricmp( pszFoundShortName, "materials" )
- || !V_stricmp( pszFoundShortName, "maps" )
- || !V_stricmp( pszFoundShortName, "resource" )
- || !V_stricmp( pszFoundShortName, "scripts" )
- || !V_stricmp( pszFoundShortName, "sound" )
- || !V_stricmp( pszFoundShortName, "models" ) )
- {
-
- char szReadme[MAX_PATH];
- V_ExtractFilePath( szAbsSearchPath, szReadme, sizeof( szReadme ) );
- V_AppendSlash( szReadme, sizeof(szReadme) );
- V_strcat_safe( szReadme, "readme.txt" );
-
- Error(
- "Tried to add %s as a search path.\n"
- "\nThis is probably not what you intended.\n"
- "\nCheck %s for more info\n",
- szAbsName, szReadme );
- }
-
- }
- pszFoundShortName = initInfo.m_pFileSystem->FindNext( findHandle );
- } while ( pszFoundShortName );
- initInfo.m_pFileSystem->FindClose( findHandle );
- }
-
- // Sort alphabetically. Also note that this will put
- // all the xxx_000.vpk packs just before the corresponding
- // xxx_dir.vpk
- vecFullLocationPaths.Sort( SortStricmp );
-
- // Now for any _dir.vpk files, remove the _nnn.vpk ones.
- int idx = vecFullLocationPaths.Count()-1;
- while ( idx > 0 )
- {
- char szTemp[ MAX_PATH ];
- V_strcpy_safe( szTemp, vecFullLocationPaths[ idx ] );
- --idx;
-
- char *szDirVpk = V_stristr( szTemp, "_dir.vpk" );
- if ( szDirVpk != NULL )
- {
- *szDirVpk = '\0';
- while ( idx >= 0 )
- {
- char *pszPath = vecFullLocationPaths[ idx ];
- if ( V_stristr( pszPath, szTemp ) != pszPath )
- break;
- delete pszPath;
- vecFullLocationPaths.Remove( idx );
- --idx;
- }
- }
- }
- }
-
- // Parse Path ID list
- CUtlStringList vecPathIDs;
- V_SplitString( pCur->GetName(), "+", vecPathIDs );
- FOR_EACH_VEC( vecPathIDs, idxPathID )
- {
- Q_StripPrecedingAndTrailingWhitespace( vecPathIDs[ idxPathID ] );
- }
-
- // Mount them.
- FOR_EACH_VEC( vecFullLocationPaths, idxLocation )
- {
- FOR_EACH_VEC( vecPathIDs, idxPathID )
- {
- FileSystem_AddLoadedSearchPath( initInfo, vecPathIDs[ idxPathID ], vecFullLocationPaths[ idxLocation ], bLowViolence );
- }
- }
- }
-
- pMainFile->deleteThis();
-
- // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
- // when people forget to specify a search path.
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "download", true );
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "game_write", true );
- initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod_write", true );
-
-#ifdef _DEBUG
- // initInfo.m_pFileSystem->PrintSearchPaths();
-#endif
-
- return FS_OK;
-}
-
-bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename )
-{
- char filename[MAX_PATH];
-
- Q_strncpy( filename, pDirectoryName, sizeof( filename ) );
- Q_AppendSlash( filename, sizeof( filename ) );
- Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS );
- Q_FixSlashes( filename );
- bool bExist = ( _access( filename, 0 ) == 0 );
-
- return ( bExist );
-}
-
-namespace
-{
- SuggestGameInfoDirFn_t & GetSuggestGameInfoDirFn( void )
- {
- static SuggestGameInfoDirFn_t s_pfnSuggestGameInfoDir = NULL;
- return s_pfnSuggestGameInfoDir;
- }
-}; // `anonymous` namespace
-
-SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn )
-{
- SuggestGameInfoDirFn_t &rfn = GetSuggestGameInfoDirFn();
- SuggestGameInfoDirFn_t pfnOldFn = rfn;
- rfn = pfnNewFn;
- return pfnOldFn;
-}
-
-static FSReturnCode_t TryLocateGameInfoFile( char *pOutDir, int outDirLen, bool bBubbleDir )
-{
- // Retain a copy of suggested path for further attempts
- CArrayAutoPtr < char > spchCopyNameBuffer( new char [ outDirLen ] );
- Q_strncpy( spchCopyNameBuffer.Get(), pOutDir, outDirLen );
- spchCopyNameBuffer[ outDirLen - 1 ] = 0;
-
- // Make appropriate slashes ('/' - Linux style)
- for ( char *pchFix = spchCopyNameBuffer.Get(),
- *pchEnd = pchFix + outDirLen;
- pchFix < pchEnd; ++ pchFix )
- {
- if ( '\\' == *pchFix )
- {
- *pchFix = '/';
- }
- }
-
- // Have a look in supplied path
- do
- {
- if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
- {
- return FS_OK;
- }
- if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
- {
- return FS_OK;
- }
- }
- while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
-
- // Make an attempt to resolve from "content -> game" directory
- Q_strncpy( pOutDir, spchCopyNameBuffer.Get(), outDirLen );
- pOutDir[ outDirLen - 1 ] = 0;
- if ( char *pchContentFix = Q_stristr( pOutDir, "/content/" ) )
- {
- sprintf( pchContentFix, "/game/" );
- memmove( pchContentFix + 6, pchContentFix + 9, pOutDir + outDirLen - (pchContentFix + 9) );
-
- // Try in the mapped "game" directory
- do
- {
- if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
- {
- return FS_OK;
- }
- if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
- {
- return FS_OK;
- }
- }
- while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
- }
-
- // Could not find it here
- return FS_MISSING_GAMEINFO_FILE;
-}
-
-FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen )
-{
- // Engine and Hammer don't want to search around for it.
- if ( fsInfo.m_bOnlyUseDirectoryName )
- {
- if ( !fsInfo.m_pDirectoryName )
- return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." );
-
- bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME );
- if ( IsX360() && !bExists )
- {
- bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE );
- }
- if ( !bExists )
- {
- if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
- {
- char basePath[MAX_PATH];
- strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
- Q_AppendSlash( basePath, sizeof( basePath ) );
- Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS );
- if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
- {
- Q_strncpy( pOutDir, basePath, outDirLen );
- return FS_OK;
- }
- if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
- {
- Q_strncpy( pOutDir, basePath, outDirLen );
- return FS_OK;
- }
- }
-
- 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 );
- }
-
- Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen );
- return FS_OK;
- }
-
- // First, check for overrides on the command line or environment variables.
- const char *pProject = GetVProjectCmdLineValue();
-
- if ( pProject )
- {
- if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) )
- {
- Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
- return FS_OK;
- }
- if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) )
- {
- Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
- return FS_OK;
- }
-
- if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
- {
- char basePath[MAX_PATH];
- strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
- Q_AppendSlash( basePath, sizeof( basePath ) );
- Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS );
- if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
- {
- Q_strncpy( pOutDir, basePath, outDirLen );
- return FS_OK;
- }
- if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
- {
- Q_strncpy( pOutDir, basePath, outDirLen );
- return FS_OK;
- }
- }
-
- if ( fsInfo.m_bNoGameInfo )
- {
- // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use.
- // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if
- // -game is supplied on the command line.
- Q_strncpy( pOutDir, "", outDirLen );
- return FS_OK;
- }
- else
- {
- // They either specified vproject on the command line or it's in their registry. Either way,
- // we don't want to continue if they've specified it but it's not valid.
- goto ShowError;
- }
- }
-
- if ( fsInfo.m_bNoGameInfo )
- {
- Q_strncpy( pOutDir, "", outDirLen );
- return FS_OK;
- }
-
- // Ask the application if it can provide us with a game info directory
- {
- bool bBubbleDir = true;
- SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn();
- if ( pfnSuggestGameInfoDirFn &&
- ( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) &&
- FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) )
- return FS_OK;
- }
-
- // Try to use the environment variable / registry
- if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL &&
- ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) &&
- FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) )
- return FS_OK;
-
- if ( IsPC() )
- {
- Warning( "Warning: falling back to auto detection of vproject directory.\n" );
-
- // Now look for it in the directory they passed in.
- if ( fsInfo.m_pDirectoryName )
- Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName );
- else
- Q_MakeAbsolutePath( pOutDir, outDirLen, "." );
-
- if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
- return FS_OK;
-
- // Use the CWD
- Q_getwd( pOutDir, outDirLen );
- if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
- return FS_OK;
- }
-
-ShowError:
- return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE,
- "Unable to find %s. Solutions:\n\n"
- "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n"
- "2. Run vconfig to specify which game you're working on.\n"
- "3. Add -game <path> on the command line where <path> is the directory that %s is in.\n",
- GAMEINFO_FILENAME, GAMEINFO_FILENAME );
-}
-
-bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath )
-{
- // Fix the slashes in the input arguments.
- char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH];
- Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) );
- Q_FixSlashes( correctedPathEnvVar );
- pPathEnvVar = correctedPathEnvVar;
-
- Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) );
- Q_FixSlashes( correctedTestPath );
- if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) )
- correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0;
-
- pTestPath = correctedTestPath;
-
- const char *pCurPos = pPathEnvVar;
- while ( 1 )
- {
- const char *pTestPos = Q_stristr( pCurPos, pTestPath );
- if ( !pTestPos )
- return false;
-
- // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon.
- pTestPos += strlen( pTestPath );
- if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') )
- return true;
-
- // Advance our marker..
- pCurPos = pTestPos;
- }
-}
-
-FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings )
-{
- if ( IsConsole() )
- {
- // consoles don't use steam
- return FS_MISSING_STEAM_DLL;
- }
-
- if ( IsPosix() )
- 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
-
- // Start at our bin directory and move up until we find a directory with steam.dll in it.
- char executablePath[MAX_PATH];
- if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
- {
- if ( bErrorsAsWarnings )
- {
- Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" );
- return FS_INVALID_PARAMETERS;
- }
- else
- {
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
- }
- }
-
- Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen );
-#ifdef WIN32
- const char *pchSteamDLL = "steam" DLL_EXT_STRING;
-#elif defined(POSIX)
- // under osx the bin lives in the bin/ folder, so step back one
- Q_StripLastDir( steamInstallPath, steamInstallPathLen );
- const char *pchSteamDLL = "libsteam" DLL_EXT_STRING;
-#else
- #error
-#endif
- while ( 1 )
- {
- // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username.
- // find
- if ( DoesFileExistIn( steamInstallPath, pchSteamDLL ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) )
- break;
-
- if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) )
- {
- if ( bErrorsAsWarnings )
- {
- Warning( "Can't find %s relative to executable path: %s.\n", pchSteamDLL, executablePath );
- return FS_MISSING_STEAM_DLL;
- }
- else
- {
- return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find %s relative to executable path: %s.", pchSteamDLL, executablePath );
- }
- }
- }
-
- // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll.
- char szPath[ 8192 ];
- steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) );
- if ( !DoesPathExistAlready( szPath, steamInstallPath ) )
- {
-#ifdef WIN32
-#define PATH_SEP ";"
-#else
-#define PATH_SEP ":"
-#endif
- steamEnvVars.m_Path.SetValue( "%s%s%s", szPath, PATH_SEP, steamInstallPath );
- }
- return FS_OK;
-}
-
-FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen )
-{
- steamCfgPath[0] = 0;
- char executablePath[MAX_PATH];
- if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
- {
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
- }
- Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen );
- while ( 1 )
- {
- if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) )
- break;
-
- if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) )
- {
- // the file isnt found, thats ok, its not mandatory
- return FS_OK;
- }
- }
- Q_AppendSlash( steamCfgPath, steamCfgPathLen );
- Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS );
-
- return FS_OK;
-}
-
-void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars )
-{
- // Always inherit the Steam user if it's already set, since it probably means we (or the
- // the app that launched us) were launched from Steam.
- char appUser[MAX_PATH];
- if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) )
- return;
-
- const char *pTempAppUser = NULL;
- if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL )
- {
- Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
- }
- else
- {
- // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user
- // by looking in <steam install path>\config\SteamAppData.vdf.
- char fullFilename[MAX_PATH];
- Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) );
- Q_AppendSlash( fullFilename, sizeof( fullFilename ) );
- Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS );
-
- KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename );
- if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL )
- {
- Error( "Can't find steam app user info." );
- }
- Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
-
- pSteamAppData->deleteThis();
- }
-
- Q_strlower( appUser );
- steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser );
-}
-
-void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars )
-{
- // Always inherit the passphrase if it's already set, since it probably means we (or the
- // the app that launched us) were launched from Steam.
- char szPassPhrase[ MAX_PATH ];
- if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) )
- return;
-
- // SteamUserPassphrase.
- const char *pStr;
- if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL )
- {
- steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr );
- }
-}
-
-FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars )
-{
- // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can.
- char steamInfoFile[MAX_PATH];
- Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) );
- Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) );
- Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS );
- KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile );
-
- char steamInstallPath[MAX_PATH];
- FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false );
- if ( ret != FS_OK )
- return ret;
-
- SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars );
- SetSteamUserPassphrase( pSteamInfo, steamEnvVars );
-
- if ( pSteamInfo )
- pSteamInfo->deleteThis();
-
- return FS_OK;
-}
-
-FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem )
-{
- pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" );
-
- char executablePath[MAX_PATH];
- if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
-
- pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" );
-
- if ( !FileSystem_GetBaseDir( executablePath, sizeof( executablePath ) ) )
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
-
- pFileSystem->AddSearchPath( executablePath, "BASE_PATH" );
-
- return FS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// Returns the name of the file system DLL to use
-//-----------------------------------------------------------------------------
-FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
-{
- bSteam = false;
-
- // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
- // is in this same directory with us.
- char executablePath[MAX_PATH];
- if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
-
- // Assume we'll use local files
- Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
-
- #if !defined( _X360 )
-
- // Use filsystem_steam if it exists?
- #if defined( OSX ) || defined( LINUX )
- struct stat statBuf;
- #endif
- if (
- #if defined( OSX ) || defined( LINUX )
- stat( pFileSystemDLL, &statBuf ) != 0
- #else
- _access( pFileSystemDLL, 0 ) != 0
- #endif
- ) {
- Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
- bSteam = true;
- }
- #endif
-
- return FS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// Sets up the steam.dll install path in our PATH env var (so you can then just
-// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special )
-//-----------------------------------------------------------------------------
-FSReturnCode_t FileSystem_SetupSteamInstallPath()
-{
- CSteamEnvVars steamEnvVars;
- char steamInstallPath[MAX_PATH];
- FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
- steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
- return ret;
-}
-
-//-----------------------------------------------------------------------------
-// Sets up the steam environment + gets back the gameinfo.txt path
-//-----------------------------------------------------------------------------
-FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo )
-{
- // First, locate the directory with gameinfo.txt.
- FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) );
- if ( ret != FS_OK )
- return ret;
-
- // This is so that processes spawned by this application will have the same VPROJECT
-#ifdef WIN32
- char pEnvBuf[MAX_PATH+32];
- Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath );
- _putenv( pEnvBuf );
-#else
- setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 );
-#endif
-
- CSteamEnvVars steamEnvVars;
- if ( fsInfo.m_bSteam )
- {
- if ( fsInfo.m_bToolsMode )
- {
- // Now, load gameinfo.txt (to make sure it's there)
- KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
- ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths );
- if ( ret != FS_OK )
- return ret;
-
- // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll.
- // There are two command line parameters for Steam:
- // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend)
- // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD)
-
- // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam.
- ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars );
- if ( ret != FS_OK )
- return ret;
-
- steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
-
- // We're done with main file
- pMainFile->deleteThis();
- }
- else if ( fsInfo.m_bSetSteamDLLPath )
- {
- // This is used by the engine to automatically set the path to their steam.dll when running the engine,
- // so they can debug it without having to copy steam.dll up into their hl2.exe folder.
- char steamInstallPath[MAX_PATH];
- ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
- steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
- }
- }
-
- return FS_OK;
-}
-
-
-//-----------------------------------------------------------------------------
-// Loads the file system module
-//-----------------------------------------------------------------------------
-FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo )
-{
- // First, locate the directory with gameinfo.txt.
- FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo );
- if ( ret != FS_OK )
- return ret;
-
- // Now that the environment is setup, load the filesystem module.
- if ( !Sys_LoadInterface(
- fsInfo.m_pFileSystemDLLName,
- FILESYSTEM_INTERFACE_VERSION,
- &fsInfo.m_pModule,
- (void**)&fsInfo.m_pFileSystem ) )
- {
- return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName );
- }
-
- if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) )
- return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName );
-
- if ( fsInfo.m_pFileSystem->Init() != INIT_OK )
- return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName );
-
- return FS_OK;
-}
-
-
-//-----------------------------------------------------------------------------
-// Mounds a particular steam cache
-//-----------------------------------------------------------------------------
-FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo )
-{
- // This part is Steam-only.
- if ( mountContentInfo.m_pFileSystem->IsSteam() )
- {
- return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "Should not be using filesystem_steam anymore!" );
-
-// // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem
-// // like HL2, then mount the SDK content (tools materials and models, etc) in addition.
-// int nExtraAppId = -1;
-// if ( mountContentInfo.m_bToolsMode )
-// {
-// // !FIXME! Here we need to mount the tools content (VPK's) in some way...?
-// }
-//
-// // Set our working directory temporarily so Steam can remember it.
-// // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk
-// // to get to the relative part of the path.
-// char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH];
-// if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
-// return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
-//
-// Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) );
-// _chdir( baseDir );
-//
-// // Filesystem_tools needs to add dependencies in here beforehand.
-// FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId );
-//
-// _chdir( oldWorkingDir );
-//
-// if ( retVal != FILESYSTEM_MOUNT_OK )
-// return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" );
- }
-
- return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem );
-}
-
-void FileSystem_SetErrorMode( FSErrorMode_t errorMode )
-{
- g_FileSystemErrorMode = errorMode;
-}
-
-void FileSystem_ClearSteamEnvVars()
-{
- CSteamEnvVars envVars;
-
- // Change the values and don't restore the originals.
- envVars.m_SteamAppId.SetValue( "" );
- envVars.m_SteamUserPassphrase.SetValue( "" );
- envVars.m_SteamAppUser.SetValue( "" );
-
- envVars.SetRestoreOriginalValue_ALL( false );
-}
-
-//-----------------------------------------------------------------------------
-// Adds the platform folder to the search path.
-//-----------------------------------------------------------------------------
-void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath )
-{
- char platform[MAX_PATH];
- Q_strncpy( platform, szGameInfoPath, MAX_PATH );
- Q_StripTrailingSlash( platform );
- Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
-
- pFileSystem->AddSearchPath( platform, "PLATFORM" );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#undef PROTECTED_THINGS_ENABLE
+#undef PROTECT_FILEIO_FUNCTIONS
+#ifndef POSIX
+#undef fopen
+#endif
+
+#if defined( _WIN32 ) && !defined( _X360 )
+#include <windows.h>
+#include <direct.h>
+#include <io.h>
+#include <process.h>
+#elif defined( POSIX )
+#include <unistd.h>
+#define _chdir chdir
+#define _access access
+#endif
+#include <stdio.h>
+#include <sys/stat.h>
+#include "tier1/strtools.h"
+#include "tier1/utlbuffer.h"
+#include "filesystem_init.h"
+#include "tier0/icommandline.h"
+#include "KeyValues.h"
+#include "appframework/IAppSystemGroup.h"
+#include "tier1/smartptr.h"
+#if defined( _X360 )
+#include "xbox\xbox_win32stubs.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#if !defined( _X360 )
+#define GAMEINFO_FILENAME "gameinfo.txt"
+#else
+// The .xtx file is a TCR requirement, as .txt files cannot live on the DVD.
+// 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.
+// 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.
+// The .xtx concept is private and should only have to occurr here. As a safety measure, if the .xtx file is not found
+// a retry is made with the original .txt name
+#define GAMEINFO_FILENAME "gameinfo.xtx"
+#endif
+#define GAMEINFO_FILENAME_ALTERNATE "gameinfo.txt"
+
+static char g_FileSystemError[256];
+static bool s_bUseVProjectBinDir = false;
+static FSErrorMode_t g_FileSystemErrorMode = FS_ERRORMODE_VCONFIG;
+
+// Call this to use a bin directory relative to VPROJECT
+void FileSystem_UseVProjectBinDir( bool bEnable )
+{
+ s_bUseVProjectBinDir = bEnable;
+}
+
+// This class lets you modify environment variables, and it restores the original value
+// when it goes out of scope.
+class CTempEnvVar
+{
+public:
+ CTempEnvVar( const char *pVarName )
+ {
+ m_bRestoreOriginalValue = true;
+ m_pVarName = pVarName;
+
+ const char *pValue = NULL;
+
+#ifdef _WIN32
+ // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
+ // to the process environment after the DLL was loaded.
+ char szBuf[ 4096 ];
+ if ( GetEnvironmentVariable( m_pVarName, szBuf, sizeof( szBuf ) ) != 0)
+ {
+ pValue = szBuf;
+ }
+#else
+ // LINUX BUG: see above
+ pValue = getenv( pVarName );
+#endif
+
+ if ( pValue )
+ {
+ m_bExisted = true;
+ m_OriginalValue.SetSize( strlen( pValue ) + 1 );
+ memcpy( m_OriginalValue.Base(), pValue, m_OriginalValue.Count() );
+ }
+ else
+ {
+ m_bExisted = false;
+ }
+ }
+
+ ~CTempEnvVar()
+ {
+ if ( m_bRestoreOriginalValue )
+ {
+ // Restore the original value.
+ if ( m_bExisted )
+ {
+ SetValue( "%s", m_OriginalValue.Base() );
+ }
+ else
+ {
+ ClearValue();
+ }
+ }
+ }
+
+ void SetRestoreOriginalValue( bool bRestore )
+ {
+ m_bRestoreOriginalValue = bRestore;
+ }
+
+ int GetValue(char *pszBuf, int nBufSize )
+ {
+ if ( !pszBuf || ( nBufSize <= 0 ) )
+ return 0;
+
+#ifdef _WIN32
+ // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
+ // to the process environment after the DLL was loaded.
+ return GetEnvironmentVariable( m_pVarName, pszBuf, nBufSize );
+#else
+ // LINUX BUG: see above
+ const char *pszOut = getenv( m_pVarName );
+ if ( !pszOut )
+ {
+ *pszBuf = '\0';
+ return 0;
+ }
+
+ Q_strncpy( pszBuf, pszOut, nBufSize );
+ return Q_strlen( pszBuf );
+#endif
+ }
+
+ void SetValue( const char *pValue, ... )
+ {
+ char valueString[4096];
+ va_list marker;
+ va_start( marker, pValue );
+ Q_vsnprintf( valueString, sizeof( valueString ), pValue, marker );
+ va_end( marker );
+
+#ifdef WIN32
+ char str[4096];
+ Q_snprintf( str, sizeof( str ), "%s=%s", m_pVarName, valueString );
+ _putenv( str );
+#else
+ setenv( m_pVarName, valueString, 1 );
+#endif
+ }
+
+ void ClearValue()
+ {
+#ifdef WIN32
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "%s=", m_pVarName );
+ _putenv( str );
+#else
+ setenv( m_pVarName, "", 1 );
+#endif
+ }
+
+private:
+ bool m_bRestoreOriginalValue;
+ const char *m_pVarName;
+ bool m_bExisted;
+ CUtlVector<char> m_OriginalValue;
+};
+
+
+class CSteamEnvVars
+{
+public:
+ CSteamEnvVars() :
+ m_SteamAppId( "SteamAppId" ),
+ m_SteamUserPassphrase( "SteamUserPassphrase" ),
+ m_SteamAppUser( "SteamAppUser" ),
+ m_Path( "path" )
+ {
+ }
+
+ void SetRestoreOriginalValue_ALL( bool bRestore )
+ {
+ m_SteamAppId.SetRestoreOriginalValue( bRestore );
+ m_SteamUserPassphrase.SetRestoreOriginalValue( bRestore );
+ m_SteamAppUser.SetRestoreOriginalValue( bRestore );
+ m_Path.SetRestoreOriginalValue( bRestore );
+ }
+
+ CTempEnvVar m_SteamAppId;
+ CTempEnvVar m_SteamUserPassphrase;
+ CTempEnvVar m_SteamAppUser;
+ CTempEnvVar m_Path;
+};
+
+// ---------------------------------------------------------------------------------------------------- //
+// Helpers.
+// ---------------------------------------------------------------------------------------------------- //
+void Q_getwd( char *out, int outSize )
+{
+#if defined( _WIN32 ) || defined( WIN32 )
+ _getcwd( out, outSize );
+ Q_strncat( out, "\\", outSize, COPY_ALL_CHARACTERS );
+#else
+ getcwd( out, outSize );
+ strcat( out, "/" );
+#endif
+ Q_FixSlashes( out );
+}
+
+// ---------------------------------------------------------------------------------------------------- //
+// Module interface.
+// ---------------------------------------------------------------------------------------------------- //
+
+CFSSearchPathsInit::CFSSearchPathsInit()
+{
+ m_pDirectoryName = NULL;
+ m_pLanguage = NULL;
+ m_ModPath[0] = 0;
+}
+
+
+CFSSteamSetupInfo::CFSSteamSetupInfo()
+{
+ m_pDirectoryName = NULL;
+ m_bOnlyUseDirectoryName = false;
+ m_bSteam = false;
+ m_bToolsMode = true;
+ m_bNoGameInfo = false;
+}
+
+
+CFSLoadModuleInfo::CFSLoadModuleInfo()
+{
+ m_pFileSystemDLLName = NULL;
+ m_pFileSystem = NULL;
+ m_pModule = NULL;
+}
+
+
+CFSMountContentInfo::CFSMountContentInfo()
+{
+ m_bToolsMode = true;
+ m_pDirectoryName = NULL;
+ m_pFileSystem = NULL;
+}
+
+
+const char *FileSystem_GetLastErrorString()
+{
+ return g_FileSystemError;
+}
+
+
+KeyValues* ReadKeyValuesFile( const char *pFilename )
+{
+ // Read in the gameinfo.txt file and null-terminate it.
+ FILE *fp = fopen( pFilename, "rb" );
+ if ( !fp )
+ return NULL;
+ CUtlVector<char> buf;
+ fseek( fp, 0, SEEK_END );
+ buf.SetSize( ftell( fp ) + 1 );
+ fseek( fp, 0, SEEK_SET );
+ fread( buf.Base(), 1, buf.Count()-1, fp );
+ fclose( fp );
+ buf[buf.Count()-1] = 0;
+
+ KeyValues *kv = new KeyValues( "" );
+ if ( !kv->LoadFromBuffer( pFilename, buf.Base() ) )
+ {
+ kv->deleteThis();
+ return NULL;
+ }
+
+ return kv;
+}
+
+static bool Sys_GetExecutableName( char *out, int len )
+{
+#if defined( _WIN32 )
+ if ( !::GetModuleFileName( ( HINSTANCE )GetModuleHandle( NULL ), out, len ) )
+ {
+ return false;
+ }
+#else
+ if ( CommandLine()->GetParm(0) )
+ {
+ Q_MakeAbsolutePath( out, len, CommandLine()->GetParm(0) );
+ }
+ else
+ {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+bool FileSystem_GetExecutableDir( char *exedir, int exeDirLen )
+{
+ exedir[0] = 0;
+
+ if ( s_bUseVProjectBinDir )
+ {
+ const char *pProject = GetVProjectCmdLineValue();
+ if ( !pProject )
+ {
+ // Check their registry.
+ pProject = getenv( GAMEDIR_TOKEN );
+ }
+ if ( pProject )
+ {
+ Q_snprintf( exedir, exeDirLen, "%s%c..%cbin", pProject, CORRECT_PATH_SEPARATOR, CORRECT_PATH_SEPARATOR );
+ return true;
+ }
+ return false;
+ }
+
+ if ( !Sys_GetExecutableName( exedir, exeDirLen ) )
+ return false;
+ Q_StripFilename( exedir );
+
+ if ( IsX360() )
+ {
+ // The 360 can have its exe and dlls reside on different volumes
+ // use the optional basedir as the exe dir
+ if ( CommandLine()->FindParm( "-basedir" ) )
+ {
+ strcpy( exedir, CommandLine()->ParmValue( "-basedir", "" ) );
+ }
+ }
+
+ Q_FixSlashes( exedir );
+
+ // Return the bin directory as the executable dir if it's not in there
+ // because that's really where we're running from...
+ char ext[MAX_PATH];
+ Q_StrRight( exedir, 4, ext, sizeof( ext ) );
+ if ( ext[0] != CORRECT_PATH_SEPARATOR || Q_stricmp( ext+1, "bin" ) != 0 )
+ {
+ Q_strncat( exedir, CORRECT_PATH_SEPARATOR_S, exeDirLen, COPY_ALL_CHARACTERS );
+ Q_strncat( exedir, "bin", exeDirLen, COPY_ALL_CHARACTERS );
+ Q_FixSlashes( exedir );
+ }
+
+ return true;
+}
+
+static bool FileSystem_GetBaseDir( char *baseDir, int baseDirLen )
+{
+ if ( FileSystem_GetExecutableDir( baseDir, baseDirLen ) )
+ {
+ Q_StripFilename( baseDir );
+ return true;
+ }
+
+ return false;
+}
+
+void LaunchVConfig()
+{
+#if defined( _WIN32 ) && !defined( _X360 )
+ char vconfigExe[MAX_PATH];
+ FileSystem_GetExecutableDir( vconfigExe, sizeof( vconfigExe ) );
+ Q_AppendSlash( vconfigExe, sizeof( vconfigExe ) );
+ Q_strncat( vconfigExe, "vconfig.exe", sizeof( vconfigExe ), COPY_ALL_CHARACTERS );
+
+ char *argv[] =
+ {
+ vconfigExe,
+ "-allowdebug",
+ NULL
+ };
+
+ _spawnv( _P_NOWAIT, vconfigExe, argv );
+#elif defined( _X360 )
+ Msg( "Launching vconfig.exe not supported\n" );
+#endif
+}
+
+const char* GetVProjectCmdLineValue()
+{
+ return CommandLine()->ParmValue( "-vproject", CommandLine()->ParmValue( "-game" ) );
+}
+
+FSReturnCode_t SetupFileSystemError( bool bRunVConfig, FSReturnCode_t retVal, const char *pMsg, ... )
+{
+ va_list marker;
+ va_start( marker, pMsg );
+ Q_vsnprintf( g_FileSystemError, sizeof( g_FileSystemError ), pMsg, marker );
+ va_end( marker );
+
+ Warning( "%s\n", g_FileSystemError );
+
+ // Run vconfig?
+ // Don't do it if they specifically asked for it not to, or if they manually specified a vconfig with -game or -vproject.
+ if ( bRunVConfig && g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG && !CommandLine()->FindParm( CMDLINEOPTION_NOVCONFIG ) && !GetVProjectCmdLineValue() )
+ {
+ LaunchVConfig();
+ }
+
+ if ( g_FileSystemErrorMode == FS_ERRORMODE_AUTO || g_FileSystemErrorMode == FS_ERRORMODE_VCONFIG )
+ {
+ Error( "%s\n", g_FileSystemError );
+ }
+
+ return retVal;
+}
+
+FSReturnCode_t LoadGameInfoFile(
+ const char *pDirectoryName,
+ KeyValues *&pMainFile,
+ KeyValues *&pFileSystemInfo,
+ KeyValues *&pSearchPaths )
+{
+ // If GameInfo.txt exists under pBaseDir, then this is their game directory.
+ // All the filesystem mappings will be in this file.
+ char gameinfoFilename[MAX_PATH];
+ Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
+ Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
+ Q_strncat( gameinfoFilename, GAMEINFO_FILENAME, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( gameinfoFilename );
+ pMainFile = ReadKeyValuesFile( gameinfoFilename );
+ if ( IsX360() && !pMainFile )
+ {
+ // try again
+ Q_strncpy( gameinfoFilename, pDirectoryName, sizeof( gameinfoFilename ) );
+ Q_AppendSlash( gameinfoFilename, sizeof( gameinfoFilename ) );
+ Q_strncat( gameinfoFilename, GAMEINFO_FILENAME_ALTERNATE, sizeof( gameinfoFilename ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( gameinfoFilename );
+ pMainFile = ReadKeyValuesFile( gameinfoFilename );
+ }
+ if ( !pMainFile )
+ {
+ return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE, "%s is missing.", gameinfoFilename );
+ }
+
+ pFileSystemInfo = pMainFile->FindKey( "FileSystem" );
+ if ( !pFileSystemInfo )
+ {
+ pMainFile->deleteThis();
+ return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
+ }
+
+ // Now read in all the search paths.
+ pSearchPaths = pFileSystemInfo->FindKey( "SearchPaths" );
+ if ( !pSearchPaths )
+ {
+ pMainFile->deleteThis();
+ return SetupFileSystemError( true, FS_INVALID_GAMEINFO_FILE, "%s is not a valid format.", gameinfoFilename );
+ }
+ return FS_OK;
+}
+
+// checks the registry for the low violence setting
+// Check "HKEY_CURRENT_USER\Software\Valve\Source\Settings" and "User Token 2" or "User Token 3"
+bool IsLowViolenceBuild( void )
+{
+#if defined(_WIN32)
+ HKEY hKey;
+ char szValue[64];
+ unsigned long len = sizeof(szValue) - 1;
+ bool retVal = false;
+
+ if ( IsPC() && RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Valve\\Source\\Settings", NULL, KEY_READ, &hKey) == ERROR_SUCCESS )
+ {
+ // User Token 2
+ if ( RegQueryValueEx( hKey, "User Token 2", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
+ {
+ if ( Q_strlen( szValue ) > 0 )
+ {
+ retVal = true;
+ }
+ }
+
+ if ( !retVal )
+ {
+ // reset "len" for the next check
+ len = sizeof(szValue) - 1;
+
+ // User Token 3
+ if ( RegQueryValueEx( hKey, "User Token 3", NULL, NULL, (unsigned char*)szValue, &len ) == ERROR_SUCCESS )
+ {
+ if ( Q_strlen( szValue ) > 0 )
+ {
+ retVal = true;
+ }
+ }
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ return retVal;
+#elif POSIX
+ return false;
+#else
+ #error "Fix me"
+#endif
+}
+
+static void FileSystem_AddLoadedSearchPath(
+ CFSSearchPathsInit &initInfo,
+ const char *pPathID,
+ const char *fullLocationPath,
+ bool bLowViolence )
+{
+
+ // Check for mounting LV game content in LV builds only
+ if ( V_stricmp( pPathID, "game_lv" ) == 0 )
+ {
+
+ // Not in LV build, don't mount
+ if ( !bLowViolence )
+ return;
+
+ // Mount, as a game path
+ pPathID = "game";
+ }
+
+ // Special processing for ordinary game folders
+ if ( V_stristr( fullLocationPath, ".vpk" ) == NULL && Q_stricmp( pPathID, "game" ) == 0 )
+ {
+ if ( CommandLine()->FindParm( "-tempcontent" ) != 0 )
+ {
+ char szPath[MAX_PATH];
+ Q_snprintf( szPath, sizeof(szPath), "%s_tempcontent", fullLocationPath );
+ initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
+ }
+ }
+
+
+ if ( initInfo.m_pLanguage &&
+ Q_stricmp( initInfo.m_pLanguage, "english" ) &&
+ V_strstr( fullLocationPath, "_english" ) != NULL )
+ {
+ char szPath[MAX_PATH];
+ char szLangString[MAX_PATH];
+
+ // Need to add a language version of this path first
+
+ Q_snprintf( szLangString, sizeof(szLangString), "_%s", initInfo.m_pLanguage);
+ V_StrSubst( fullLocationPath, "_english", szLangString, szPath, sizeof( szPath ), true );
+ initInfo.m_pFileSystem->AddSearchPath( szPath, pPathID, PATH_ADD_TO_TAIL );
+ }
+
+ initInfo.m_pFileSystem->AddSearchPath( fullLocationPath, pPathID, PATH_ADD_TO_TAIL );
+}
+
+static int SortStricmp( char * const * sz1, char * const * sz2 )
+{
+ return V_stricmp( *sz1, *sz2 );
+}
+
+FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo )
+{
+ if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName )
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." );
+
+ KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
+ FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths );
+ if ( retVal != FS_OK )
+ return retVal;
+
+ // All paths except those marked with |gameinfo_path| are relative to the base dir.
+ char baseDir[MAX_PATH];
+ if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
+
+ // The MOD directory is always the one that contains gameinfo.txt
+ Q_strncpy( initInfo.m_ModPath, initInfo.m_pDirectoryName, sizeof( initInfo.m_ModPath ) );
+
+ #define GAMEINFOPATH_TOKEN "|gameinfo_path|"
+ #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|"
+
+ const char *pszExtraSearchPath = CommandLine()->ParmValue( "-insert_search_path" );
+ if ( pszExtraSearchPath )
+ {
+ CUtlStringList vecPaths;
+ V_SplitString( pszExtraSearchPath, ",", vecPaths );
+ FOR_EACH_VEC( vecPaths, idxExtraPath )
+ {
+ char szAbsSearchPath[MAX_PATH];
+ Q_StripPrecedingAndTrailingWhitespace( vecPaths[ idxExtraPath ] );
+ V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), vecPaths[ idxExtraPath ], baseDir );
+ V_FixSlashes( szAbsSearchPath );
+ if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
+ Error( "Bad -insert_search_path - Can't resolve pathname for '%s'", szAbsSearchPath );
+ V_StripTrailingSlash( szAbsSearchPath );
+ FileSystem_AddLoadedSearchPath( initInfo, "GAME", szAbsSearchPath, false );
+ FileSystem_AddLoadedSearchPath( initInfo, "MOD", szAbsSearchPath, false );
+ }
+ }
+
+ bool bLowViolence = IsLowViolenceBuild();
+ for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() )
+ {
+ const char *pLocation = pCur->GetString();
+ const char *pszBaseDir = baseDir;
+
+ if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation )
+ {
+ pLocation += strlen( GAMEINFOPATH_TOKEN );
+ pszBaseDir = initInfo.m_pDirectoryName;
+ }
+ else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation )
+ {
+ // 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.
+ // So in Orange Box, if they specified:
+ // |all_source_engine_paths|hl2
+ // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder.
+ //
+ // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places.
+ // 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,
+ // so we can just use the path as-is.
+ pLocation += strlen( BASESOURCEPATHS_TOKEN );
+ }
+
+ CUtlStringList vecFullLocationPaths;
+ char szAbsSearchPath[MAX_PATH];
+ V_MakeAbsolutePath( szAbsSearchPath, sizeof( szAbsSearchPath ), pLocation, pszBaseDir );
+
+ // Now resolve any ./'s.
+ V_FixSlashes( szAbsSearchPath );
+ if ( !V_RemoveDotSlashes( szAbsSearchPath ) )
+ Error( "FileSystem_AddLoadedSearchPath - Can't resolve pathname for '%s'", szAbsSearchPath );
+ V_StripTrailingSlash( szAbsSearchPath );
+
+ // Don't bother doing any wildcard expansion unless it has wildcards. This avoids the weird
+ // thing with xxx_dir.vpk files being referred to simply as xxx.vpk.
+ if ( V_stristr( pLocation, "?") == NULL && V_stristr( pLocation, "*") == NULL )
+ {
+ vecFullLocationPaths.CopyAndAddToTail( szAbsSearchPath );
+ }
+ else
+ {
+ FileFindHandle_t findHandle = NULL;
+ const char *pszFoundShortName = initInfo.m_pFileSystem->FindFirst( szAbsSearchPath, &findHandle );
+ if ( pszFoundShortName )
+ {
+ do
+ {
+
+ // We only know how to mount VPK's and directories
+ if ( pszFoundShortName[0] != '.' && ( initInfo.m_pFileSystem->FindIsDirectory( findHandle ) || V_stristr( pszFoundShortName, ".vpk" ) ) )
+ {
+ char szAbsName[MAX_PATH];
+ V_ExtractFilePath( szAbsSearchPath, szAbsName, sizeof( szAbsName ) );
+ V_AppendSlash( szAbsName, sizeof(szAbsName) );
+ V_strcat_safe( szAbsName, pszFoundShortName );
+
+ vecFullLocationPaths.CopyAndAddToTail( szAbsName );
+
+ // Check for a common mistake
+ if (
+ !V_stricmp( pszFoundShortName, "materials" )
+ || !V_stricmp( pszFoundShortName, "maps" )
+ || !V_stricmp( pszFoundShortName, "resource" )
+ || !V_stricmp( pszFoundShortName, "scripts" )
+ || !V_stricmp( pszFoundShortName, "sound" )
+ || !V_stricmp( pszFoundShortName, "models" ) )
+ {
+
+ char szReadme[MAX_PATH];
+ V_ExtractFilePath( szAbsSearchPath, szReadme, sizeof( szReadme ) );
+ V_AppendSlash( szReadme, sizeof(szReadme) );
+ V_strcat_safe( szReadme, "readme.txt" );
+
+ Error(
+ "Tried to add %s as a search path.\n"
+ "\nThis is probably not what you intended.\n"
+ "\nCheck %s for more info\n",
+ szAbsName, szReadme );
+ }
+
+ }
+ pszFoundShortName = initInfo.m_pFileSystem->FindNext( findHandle );
+ } while ( pszFoundShortName );
+ initInfo.m_pFileSystem->FindClose( findHandle );
+ }
+
+ // Sort alphabetically. Also note that this will put
+ // all the xxx_000.vpk packs just before the corresponding
+ // xxx_dir.vpk
+ vecFullLocationPaths.Sort( SortStricmp );
+
+ // Now for any _dir.vpk files, remove the _nnn.vpk ones.
+ int idx = vecFullLocationPaths.Count()-1;
+ while ( idx > 0 )
+ {
+ char szTemp[ MAX_PATH ];
+ V_strcpy_safe( szTemp, vecFullLocationPaths[ idx ] );
+ --idx;
+
+ char *szDirVpk = V_stristr( szTemp, "_dir.vpk" );
+ if ( szDirVpk != NULL )
+ {
+ *szDirVpk = '\0';
+ while ( idx >= 0 )
+ {
+ char *pszPath = vecFullLocationPaths[ idx ];
+ if ( V_stristr( pszPath, szTemp ) != pszPath )
+ break;
+ delete pszPath;
+ vecFullLocationPaths.Remove( idx );
+ --idx;
+ }
+ }
+ }
+ }
+
+ // Parse Path ID list
+ CUtlStringList vecPathIDs;
+ V_SplitString( pCur->GetName(), "+", vecPathIDs );
+ FOR_EACH_VEC( vecPathIDs, idxPathID )
+ {
+ Q_StripPrecedingAndTrailingWhitespace( vecPathIDs[ idxPathID ] );
+ }
+
+ // Mount them.
+ FOR_EACH_VEC( vecFullLocationPaths, idxLocation )
+ {
+ FOR_EACH_VEC( vecPathIDs, idxPathID )
+ {
+ FileSystem_AddLoadedSearchPath( initInfo, vecPathIDs[ idxPathID ], vecFullLocationPaths[ idxLocation ], bLowViolence );
+ }
+ }
+ }
+
+ pMainFile->deleteThis();
+
+ // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them
+ // when people forget to specify a search path.
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true );
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true );
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "download", true );
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true );
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "game_write", true );
+ initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod_write", true );
+
+#ifdef _DEBUG
+ // initInfo.m_pFileSystem->PrintSearchPaths();
+#endif
+
+ return FS_OK;
+}
+
+bool DoesFileExistIn( const char *pDirectoryName, const char *pFilename )
+{
+ char filename[MAX_PATH];
+
+ Q_strncpy( filename, pDirectoryName, sizeof( filename ) );
+ Q_AppendSlash( filename, sizeof( filename ) );
+ Q_strncat( filename, pFilename, sizeof( filename ), COPY_ALL_CHARACTERS );
+ Q_FixSlashes( filename );
+ bool bExist = ( _access( filename, 0 ) == 0 );
+
+ return ( bExist );
+}
+
+namespace
+{
+ SuggestGameInfoDirFn_t & GetSuggestGameInfoDirFn( void )
+ {
+ static SuggestGameInfoDirFn_t s_pfnSuggestGameInfoDir = NULL;
+ return s_pfnSuggestGameInfoDir;
+ }
+}; // `anonymous` namespace
+
+SuggestGameInfoDirFn_t SetSuggestGameInfoDirFn( SuggestGameInfoDirFn_t pfnNewFn )
+{
+ SuggestGameInfoDirFn_t &rfn = GetSuggestGameInfoDirFn();
+ SuggestGameInfoDirFn_t pfnOldFn = rfn;
+ rfn = pfnNewFn;
+ return pfnOldFn;
+}
+
+static FSReturnCode_t TryLocateGameInfoFile( char *pOutDir, int outDirLen, bool bBubbleDir )
+{
+ // Retain a copy of suggested path for further attempts
+ CArrayAutoPtr < char > spchCopyNameBuffer( new char [ outDirLen ] );
+ Q_strncpy( spchCopyNameBuffer.Get(), pOutDir, outDirLen );
+ spchCopyNameBuffer[ outDirLen - 1 ] = 0;
+
+ // Make appropriate slashes ('/' - Linux style)
+ for ( char *pchFix = spchCopyNameBuffer.Get(),
+ *pchEnd = pchFix + outDirLen;
+ pchFix < pchEnd; ++ pchFix )
+ {
+ if ( '\\' == *pchFix )
+ {
+ *pchFix = '/';
+ }
+ }
+
+ // Have a look in supplied path
+ do
+ {
+ if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
+ {
+ return FS_OK;
+ }
+ if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
+ {
+ return FS_OK;
+ }
+ }
+ while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
+
+ // Make an attempt to resolve from "content -> game" directory
+ Q_strncpy( pOutDir, spchCopyNameBuffer.Get(), outDirLen );
+ pOutDir[ outDirLen - 1 ] = 0;
+ if ( char *pchContentFix = Q_stristr( pOutDir, "/content/" ) )
+ {
+ sprintf( pchContentFix, "/game/" );
+ memmove( pchContentFix + 6, pchContentFix + 9, pOutDir + outDirLen - (pchContentFix + 9) );
+
+ // Try in the mapped "game" directory
+ do
+ {
+ if ( DoesFileExistIn( pOutDir, GAMEINFO_FILENAME ) )
+ {
+ return FS_OK;
+ }
+ if ( IsX360() && DoesFileExistIn( pOutDir, GAMEINFO_FILENAME_ALTERNATE ) )
+ {
+ return FS_OK;
+ }
+ }
+ while ( bBubbleDir && Q_StripLastDir( pOutDir, outDirLen ) );
+ }
+
+ // Could not find it here
+ return FS_MISSING_GAMEINFO_FILE;
+}
+
+FSReturnCode_t LocateGameInfoFile( const CFSSteamSetupInfo &fsInfo, char *pOutDir, int outDirLen )
+{
+ // Engine and Hammer don't want to search around for it.
+ if ( fsInfo.m_bOnlyUseDirectoryName )
+ {
+ if ( !fsInfo.m_pDirectoryName )
+ return SetupFileSystemError( false, FS_MISSING_GAMEINFO_FILE, "bOnlyUseDirectoryName=1 and pDirectoryName=NULL." );
+
+ bool bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME );
+ if ( IsX360() && !bExists )
+ {
+ bExists = DoesFileExistIn( fsInfo.m_pDirectoryName, GAMEINFO_FILENAME_ALTERNATE );
+ }
+ if ( !bExists )
+ {
+ if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
+ {
+ char basePath[MAX_PATH];
+ strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
+ Q_AppendSlash( basePath, sizeof( basePath ) );
+ Q_strncat( basePath, fsInfo.m_pDirectoryName, sizeof( basePath ), COPY_ALL_CHARACTERS );
+ if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
+ {
+ Q_strncpy( pOutDir, basePath, outDirLen );
+ return FS_OK;
+ }
+ if ( IsX360() && DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
+ {
+ Q_strncpy( pOutDir, basePath, outDirLen );
+ return FS_OK;
+ }
+ }
+
+ 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 );
+ }
+
+ Q_strncpy( pOutDir, fsInfo.m_pDirectoryName, outDirLen );
+ return FS_OK;
+ }
+
+ // First, check for overrides on the command line or environment variables.
+ const char *pProject = GetVProjectCmdLineValue();
+
+ if ( pProject )
+ {
+ if ( DoesFileExistIn( pProject, GAMEINFO_FILENAME ) )
+ {
+ Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
+ return FS_OK;
+ }
+ if ( IsX360() && DoesFileExistIn( pProject, GAMEINFO_FILENAME_ALTERNATE ) )
+ {
+ Q_MakeAbsolutePath( pOutDir, outDirLen, pProject );
+ return FS_OK;
+ }
+
+ if ( IsX360() && CommandLine()->FindParm( "-basedir" ) )
+ {
+ char basePath[MAX_PATH];
+ strcpy( basePath, CommandLine()->ParmValue( "-basedir", "" ) );
+ Q_AppendSlash( basePath, sizeof( basePath ) );
+ Q_strncat( basePath, pProject, sizeof( basePath ), COPY_ALL_CHARACTERS );
+ if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME ) )
+ {
+ Q_strncpy( pOutDir, basePath, outDirLen );
+ return FS_OK;
+ }
+ if ( DoesFileExistIn( basePath, GAMEINFO_FILENAME_ALTERNATE ) )
+ {
+ Q_strncpy( pOutDir, basePath, outDirLen );
+ return FS_OK;
+ }
+ }
+
+ if ( fsInfo.m_bNoGameInfo )
+ {
+ // fsInfo.m_bNoGameInfo is set by the Steam dedicated server, before it knows which mod to use.
+ // Steam dedicated server doesn't need a gameinfo.txt, because we'll ask which mod to use, even if
+ // -game is supplied on the command line.
+ Q_strncpy( pOutDir, "", outDirLen );
+ return FS_OK;
+ }
+ else
+ {
+ // They either specified vproject on the command line or it's in their registry. Either way,
+ // we don't want to continue if they've specified it but it's not valid.
+ goto ShowError;
+ }
+ }
+
+ if ( fsInfo.m_bNoGameInfo )
+ {
+ Q_strncpy( pOutDir, "", outDirLen );
+ return FS_OK;
+ }
+
+ // Ask the application if it can provide us with a game info directory
+ {
+ bool bBubbleDir = true;
+ SuggestGameInfoDirFn_t pfnSuggestGameInfoDirFn = GetSuggestGameInfoDirFn();
+ if ( pfnSuggestGameInfoDirFn &&
+ ( * pfnSuggestGameInfoDirFn )( &fsInfo, pOutDir, outDirLen, &bBubbleDir ) &&
+ FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, bBubbleDir ) )
+ return FS_OK;
+ }
+
+ // Try to use the environment variable / registry
+ if ( ( pProject = getenv( GAMEDIR_TOKEN ) ) != NULL &&
+ ( Q_MakeAbsolutePath( pOutDir, outDirLen, pProject ), 1 ) &&
+ FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, false ) )
+ return FS_OK;
+
+ if ( IsPC() )
+ {
+ Warning( "Warning: falling back to auto detection of vproject directory.\n" );
+
+ // Now look for it in the directory they passed in.
+ if ( fsInfo.m_pDirectoryName )
+ Q_MakeAbsolutePath( pOutDir, outDirLen, fsInfo.m_pDirectoryName );
+ else
+ Q_MakeAbsolutePath( pOutDir, outDirLen, "." );
+
+ if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
+ return FS_OK;
+
+ // Use the CWD
+ Q_getwd( pOutDir, outDirLen );
+ if ( FS_OK == TryLocateGameInfoFile( pOutDir, outDirLen, true ) )
+ return FS_OK;
+ }
+
+ShowError:
+ return SetupFileSystemError( true, FS_MISSING_GAMEINFO_FILE,
+ "Unable to find %s. Solutions:\n\n"
+ "1. Read http://www.valve-erc.com/srcsdk/faq.html#NoGameDir\n"
+ "2. Run vconfig to specify which game you're working on.\n"
+ "3. Add -game <path> on the command line where <path> is the directory that %s is in.\n",
+ GAMEINFO_FILENAME, GAMEINFO_FILENAME );
+}
+
+bool DoesPathExistAlready( const char *pPathEnvVar, const char *pTestPath )
+{
+ // Fix the slashes in the input arguments.
+ char correctedPathEnvVar[8192], correctedTestPath[MAX_PATH];
+ Q_strncpy( correctedPathEnvVar, pPathEnvVar, sizeof( correctedPathEnvVar ) );
+ Q_FixSlashes( correctedPathEnvVar );
+ pPathEnvVar = correctedPathEnvVar;
+
+ Q_strncpy( correctedTestPath, pTestPath, sizeof( correctedTestPath ) );
+ Q_FixSlashes( correctedTestPath );
+ if ( strlen( correctedTestPath ) > 0 && PATHSEPARATOR( correctedTestPath[strlen(correctedTestPath)-1] ) )
+ correctedTestPath[ strlen(correctedTestPath) - 1 ] = 0;
+
+ pTestPath = correctedTestPath;
+
+ const char *pCurPos = pPathEnvVar;
+ while ( 1 )
+ {
+ const char *pTestPos = Q_stristr( pCurPos, pTestPath );
+ if ( !pTestPos )
+ return false;
+
+ // Ok, we found pTestPath in the path, but it's only valid if it's followed by an optional slash and a semicolon.
+ pTestPos += strlen( pTestPath );
+ if ( pTestPos[0] == 0 || pTestPos[0] == ';' || (PATHSEPARATOR( pTestPos[0] ) && pTestPos[1] == ';') )
+ return true;
+
+ // Advance our marker..
+ pCurPos = pTestPos;
+ }
+}
+
+FSReturnCode_t SetSteamInstallPath( char *steamInstallPath, int steamInstallPathLen, CSteamEnvVars &steamEnvVars, bool bErrorsAsWarnings )
+{
+ if ( IsConsole() )
+ {
+ // consoles don't use steam
+ return FS_MISSING_STEAM_DLL;
+ }
+
+ if ( IsPosix() )
+ 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
+
+ // Start at our bin directory and move up until we find a directory with steam.dll in it.
+ char executablePath[MAX_PATH];
+ if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
+ {
+ if ( bErrorsAsWarnings )
+ {
+ Warning( "SetSteamInstallPath: FileSystem_GetExecutableDir failed.\n" );
+ return FS_INVALID_PARAMETERS;
+ }
+ else
+ {
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
+ }
+ }
+
+ Q_strncpy( steamInstallPath, executablePath, steamInstallPathLen );
+#ifdef WIN32
+ const char *pchSteamDLL = "steam" DLL_EXT_STRING;
+#elif defined(POSIX)
+ // under osx the bin lives in the bin/ folder, so step back one
+ Q_StripLastDir( steamInstallPath, steamInstallPathLen );
+ const char *pchSteamDLL = "libsteam" DLL_EXT_STRING;
+#else
+ #error
+#endif
+ while ( 1 )
+ {
+ // Ignore steamapp.cfg here in case they're debugging. We still need to know the real steam path so we can find their username.
+ // find
+ if ( DoesFileExistIn( steamInstallPath, pchSteamDLL ) && !DoesFileExistIn( steamInstallPath, "steamapp.cfg" ) )
+ break;
+
+ if ( !Q_StripLastDir( steamInstallPath, steamInstallPathLen ) )
+ {
+ if ( bErrorsAsWarnings )
+ {
+ Warning( "Can't find %s relative to executable path: %s.\n", pchSteamDLL, executablePath );
+ return FS_MISSING_STEAM_DLL;
+ }
+ else
+ {
+ return SetupFileSystemError( false, FS_MISSING_STEAM_DLL, "Can't find %s relative to executable path: %s.", pchSteamDLL, executablePath );
+ }
+ }
+ }
+
+ // Also, add the install path to their PATH environment variable, so filesystem_steam.dll can get to steam.dll.
+ char szPath[ 8192 ];
+ steamEnvVars.m_Path.GetValue( szPath, sizeof( szPath ) );
+ if ( !DoesPathExistAlready( szPath, steamInstallPath ) )
+ {
+#ifdef WIN32
+#define PATH_SEP ";"
+#else
+#define PATH_SEP ":"
+#endif
+ steamEnvVars.m_Path.SetValue( "%s%s%s", szPath, PATH_SEP, steamInstallPath );
+ }
+ return FS_OK;
+}
+
+FSReturnCode_t GetSteamCfgPath( char *steamCfgPath, int steamCfgPathLen )
+{
+ steamCfgPath[0] = 0;
+ char executablePath[MAX_PATH];
+ if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
+ {
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
+ }
+ Q_strncpy( steamCfgPath, executablePath, steamCfgPathLen );
+ while ( 1 )
+ {
+ if ( DoesFileExistIn( steamCfgPath, "steam.cfg" ) )
+ break;
+
+ if ( !Q_StripLastDir( steamCfgPath, steamCfgPathLen) )
+ {
+ // the file isnt found, thats ok, its not mandatory
+ return FS_OK;
+ }
+ }
+ Q_AppendSlash( steamCfgPath, steamCfgPathLen );
+ Q_strncat( steamCfgPath, "steam.cfg", steamCfgPathLen, COPY_ALL_CHARACTERS );
+
+ return FS_OK;
+}
+
+void SetSteamAppUser( KeyValues *pSteamInfo, const char *steamInstallPath, CSteamEnvVars &steamEnvVars )
+{
+ // Always inherit the Steam user if it's already set, since it probably means we (or the
+ // the app that launched us) were launched from Steam.
+ char appUser[MAX_PATH];
+ if ( steamEnvVars.m_SteamAppUser.GetValue( appUser, sizeof( appUser ) ) )
+ return;
+
+ const char *pTempAppUser = NULL;
+ if ( pSteamInfo && (pTempAppUser = pSteamInfo->GetString( "SteamAppUser", NULL )) != NULL )
+ {
+ Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
+ }
+ else
+ {
+ // They don't have SteamInfo.txt, or it's missing SteamAppUser. Try to figure out the user
+ // by looking in <steam install path>\config\SteamAppData.vdf.
+ char fullFilename[MAX_PATH];
+ Q_strncpy( fullFilename, steamInstallPath, sizeof( fullFilename ) );
+ Q_AppendSlash( fullFilename, sizeof( fullFilename ) );
+ Q_strncat( fullFilename, "config\\SteamAppData.vdf", sizeof( fullFilename ), COPY_ALL_CHARACTERS );
+
+ KeyValues *pSteamAppData = ReadKeyValuesFile( fullFilename );
+ if ( !pSteamAppData || (pTempAppUser = pSteamAppData->GetString( "AutoLoginUser", NULL )) == NULL )
+ {
+ Error( "Can't find steam app user info." );
+ }
+ Q_strncpy( appUser, pTempAppUser, sizeof( appUser ) );
+
+ pSteamAppData->deleteThis();
+ }
+
+ Q_strlower( appUser );
+ steamEnvVars.m_SteamAppUser.SetValue( "%s", appUser );
+}
+
+void SetSteamUserPassphrase( KeyValues *pSteamInfo, CSteamEnvVars &steamEnvVars )
+{
+ // Always inherit the passphrase if it's already set, since it probably means we (or the
+ // the app that launched us) were launched from Steam.
+ char szPassPhrase[ MAX_PATH ];
+ if ( steamEnvVars.m_SteamUserPassphrase.GetValue( szPassPhrase, sizeof( szPassPhrase ) ) )
+ return;
+
+ // SteamUserPassphrase.
+ const char *pStr;
+ if ( pSteamInfo && (pStr = pSteamInfo->GetString( "SteamUserPassphrase", NULL )) != NULL )
+ {
+ steamEnvVars.m_SteamUserPassphrase.SetValue( "%s", pStr );
+ }
+}
+
+FSReturnCode_t SetupSteamStartupEnvironment( KeyValues *pFileSystemInfo, const char *pGameInfoDirectory, CSteamEnvVars &steamEnvVars )
+{
+ // Ok, we're going to run Steam. See if they have SteamInfo.txt. If not, we'll try to deduce what we can.
+ char steamInfoFile[MAX_PATH];
+ Q_strncpy( steamInfoFile, pGameInfoDirectory, sizeof( steamInfoFile ) );
+ Q_AppendSlash( steamInfoFile, sizeof( steamInfoFile ) );
+ Q_strncat( steamInfoFile, "steaminfo.txt", sizeof( steamInfoFile ), COPY_ALL_CHARACTERS );
+ KeyValues *pSteamInfo = ReadKeyValuesFile( steamInfoFile );
+
+ char steamInstallPath[MAX_PATH];
+ FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, false );
+ if ( ret != FS_OK )
+ return ret;
+
+ SetSteamAppUser( pSteamInfo, steamInstallPath, steamEnvVars );
+ SetSteamUserPassphrase( pSteamInfo, steamEnvVars );
+
+ if ( pSteamInfo )
+ pSteamInfo->deleteThis();
+
+ return FS_OK;
+}
+
+FSReturnCode_t FileSystem_SetBasePaths( IFileSystem *pFileSystem )
+{
+ pFileSystem->RemoveSearchPaths( "EXECUTABLE_PATH" );
+
+ char executablePath[MAX_PATH];
+ if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
+
+ pFileSystem->AddSearchPath( executablePath, "EXECUTABLE_PATH" );
+
+ if ( !FileSystem_GetBaseDir( executablePath, sizeof( executablePath ) ) )
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
+
+ pFileSystem->AddSearchPath( executablePath, "BASE_PATH" );
+
+ return FS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the name of the file system DLL to use
+//-----------------------------------------------------------------------------
+FSReturnCode_t FileSystem_GetFileSystemDLLName( char *pFileSystemDLL, int nMaxLen, bool &bSteam )
+{
+ bSteam = false;
+
+ // Inside of here, we don't have a filesystem yet, so we have to assume that the filesystem_stdio or filesystem_steam
+ // is in this same directory with us.
+ char executablePath[MAX_PATH];
+ if ( !FileSystem_GetExecutableDir( executablePath, sizeof( executablePath ) ) )
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetExecutableDir failed." );
+
+ // Assume we'll use local files
+ Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_stdio" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
+
+ #if !defined( _X360 )
+
+ // Use filsystem_steam if it exists?
+ #if defined( OSX ) || defined( LINUX )
+ struct stat statBuf;
+ #endif
+ if (
+ #if defined( OSX ) || defined( LINUX )
+ stat( pFileSystemDLL, &statBuf ) != 0
+ #else
+ _access( pFileSystemDLL, 0 ) != 0
+ #endif
+ ) {
+ Q_snprintf( pFileSystemDLL, nMaxLen, "%s%cfilesystem_steam" DLL_EXT_STRING, executablePath, CORRECT_PATH_SEPARATOR );
+ bSteam = true;
+ }
+ #endif
+
+ return FS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the steam.dll install path in our PATH env var (so you can then just
+// LoadLibrary() on filesystem_steam.dll without having to copy steam.dll anywhere special )
+//-----------------------------------------------------------------------------
+FSReturnCode_t FileSystem_SetupSteamInstallPath()
+{
+ CSteamEnvVars steamEnvVars;
+ char steamInstallPath[MAX_PATH];
+ FSReturnCode_t ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
+ steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
+ return ret;
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the steam environment + gets back the gameinfo.txt path
+//-----------------------------------------------------------------------------
+FSReturnCode_t FileSystem_SetupSteamEnvironment( CFSSteamSetupInfo &fsInfo )
+{
+ // First, locate the directory with gameinfo.txt.
+ FSReturnCode_t ret = LocateGameInfoFile( fsInfo, fsInfo.m_GameInfoPath, sizeof( fsInfo.m_GameInfoPath ) );
+ if ( ret != FS_OK )
+ return ret;
+
+ // This is so that processes spawned by this application will have the same VPROJECT
+#ifdef WIN32
+ char pEnvBuf[MAX_PATH+32];
+ Q_snprintf( pEnvBuf, sizeof(pEnvBuf), "%s=%s", GAMEDIR_TOKEN, fsInfo.m_GameInfoPath );
+ _putenv( pEnvBuf );
+#else
+ setenv( GAMEDIR_TOKEN, fsInfo.m_GameInfoPath, 1 );
+#endif
+
+ CSteamEnvVars steamEnvVars;
+ if ( fsInfo.m_bSteam )
+ {
+ if ( fsInfo.m_bToolsMode )
+ {
+ // Now, load gameinfo.txt (to make sure it's there)
+ KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths;
+ ret = LoadGameInfoFile( fsInfo.m_GameInfoPath, pMainFile, pFileSystemInfo, pSearchPaths );
+ if ( ret != FS_OK )
+ return ret;
+
+ // If filesystem_stdio.dll is missing or -steam is specified, then load filesystem_steam.dll.
+ // There are two command line parameters for Steam:
+ // 1) -steam (runs Steam in remote filesystem mode; requires Steam backend)
+ // 2) -steamlocal (runs Steam in local filesystem mode (all content off HDD)
+
+ // Setup all the environment variables related to Steam so filesystem_steam.dll knows how to initialize Steam.
+ ret = SetupSteamStartupEnvironment( pFileSystemInfo, fsInfo.m_GameInfoPath, steamEnvVars );
+ if ( ret != FS_OK )
+ return ret;
+
+ steamEnvVars.m_SteamAppId.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
+
+ // We're done with main file
+ pMainFile->deleteThis();
+ }
+ else if ( fsInfo.m_bSetSteamDLLPath )
+ {
+ // This is used by the engine to automatically set the path to their steam.dll when running the engine,
+ // so they can debug it without having to copy steam.dll up into their hl2.exe folder.
+ char steamInstallPath[MAX_PATH];
+ ret = SetSteamInstallPath( steamInstallPath, sizeof( steamInstallPath ), steamEnvVars, true );
+ steamEnvVars.m_Path.SetRestoreOriginalValue( false ); // We want to keep the change to the path going forward.
+ }
+ }
+
+ return FS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Loads the file system module
+//-----------------------------------------------------------------------------
+FSReturnCode_t FileSystem_LoadFileSystemModule( CFSLoadModuleInfo &fsInfo )
+{
+ // First, locate the directory with gameinfo.txt.
+ FSReturnCode_t ret = FileSystem_SetupSteamEnvironment( fsInfo );
+ if ( ret != FS_OK )
+ return ret;
+
+ // Now that the environment is setup, load the filesystem module.
+ if ( !Sys_LoadInterface(
+ fsInfo.m_pFileSystemDLLName,
+ FILESYSTEM_INTERFACE_VERSION,
+ &fsInfo.m_pModule,
+ (void**)&fsInfo.m_pFileSystem ) )
+ {
+ return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "Can't load %s.", fsInfo.m_pFileSystemDLLName );
+ }
+
+ if ( !fsInfo.m_pFileSystem->Connect( fsInfo.m_ConnectFactory ) )
+ return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Connect failed.", fsInfo.m_pFileSystemDLLName );
+
+ if ( fsInfo.m_pFileSystem->Init() != INIT_OK )
+ return SetupFileSystemError( false, FS_UNABLE_TO_INIT, "%s IFileSystem::Init failed.", fsInfo.m_pFileSystemDLLName );
+
+ return FS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Mounds a particular steam cache
+//-----------------------------------------------------------------------------
+FSReturnCode_t FileSystem_MountContent( CFSMountContentInfo &mountContentInfo )
+{
+ // This part is Steam-only.
+ if ( mountContentInfo.m_pFileSystem->IsSteam() )
+ {
+ return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "Should not be using filesystem_steam anymore!" );
+
+// // Find out the "extra app id". This is for tools, which want to mount a base app's filesystem
+// // like HL2, then mount the SDK content (tools materials and models, etc) in addition.
+// int nExtraAppId = -1;
+// if ( mountContentInfo.m_bToolsMode )
+// {
+// // !FIXME! Here we need to mount the tools content (VPK's) in some way...?
+// }
+//
+// // Set our working directory temporarily so Steam can remember it.
+// // This is what Steam strips off absolute filenames like c:\program files\valve\steam\steamapps\username\sourcesdk
+// // to get to the relative part of the path.
+// char baseDir[MAX_PATH], oldWorkingDir[MAX_PATH];
+// if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) )
+// return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." );
+//
+// Q_getwd( oldWorkingDir, sizeof( oldWorkingDir ) );
+// _chdir( baseDir );
+//
+// // Filesystem_tools needs to add dependencies in here beforehand.
+// FilesystemMountRetval_t retVal = mountContentInfo.m_pFileSystem->MountSteamContent( nExtraAppId );
+//
+// _chdir( oldWorkingDir );
+//
+// if ( retVal != FILESYSTEM_MOUNT_OK )
+// return SetupFileSystemError( true, FS_UNABLE_TO_INIT, "Unable to mount Steam content in the file system" );
+ }
+
+ return FileSystem_SetBasePaths( mountContentInfo.m_pFileSystem );
+}
+
+void FileSystem_SetErrorMode( FSErrorMode_t errorMode )
+{
+ g_FileSystemErrorMode = errorMode;
+}
+
+void FileSystem_ClearSteamEnvVars()
+{
+ CSteamEnvVars envVars;
+
+ // Change the values and don't restore the originals.
+ envVars.m_SteamAppId.SetValue( "" );
+ envVars.m_SteamUserPassphrase.SetValue( "" );
+ envVars.m_SteamAppUser.SetValue( "" );
+
+ envVars.SetRestoreOriginalValue_ALL( false );
+}
+
+//-----------------------------------------------------------------------------
+// Adds the platform folder to the search path.
+//-----------------------------------------------------------------------------
+void FileSystem_AddSearchPath_Platform( IFileSystem *pFileSystem, const char *szGameInfoPath )
+{
+ char platform[MAX_PATH];
+ Q_strncpy( platform, szGameInfoPath, MAX_PATH );
+ Q_StripTrailingSlash( platform );
+ Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
+
+ pFileSystem->AddSearchPath( platform, "PLATFORM" );
+}