From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/public/filesystem_init.cpp | 2830 ++++++++++++++++++------------------- 1 file changed, 1415 insertions(+), 1415 deletions(-) (limited to 'mp/src/public/filesystem_init.cpp') 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 -#include -#include -#include -#elif defined( POSIX ) -#include -#define _chdir chdir -#define _access access -#endif -#include -#include -#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 - -#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 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 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 on the command line where 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 \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 +#include +#include +#include +#elif defined( POSIX ) +#include +#define _chdir chdir +#define _access access +#endif +#include +#include +#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 + +#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 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 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 on the command line where 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 \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" ); +} -- cgit v1.2.3