diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/public/filesystem_init.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/public/filesystem_init.cpp')
| -rw-r--r-- | mp/src/public/filesystem_init.cpp | 1423 |
1 files changed, 1423 insertions, 0 deletions
diff --git a/mp/src/public/filesystem_init.cpp b/mp/src/public/filesystem_init.cpp new file mode 100644 index 00000000..5ca97fb3 --- /dev/null +++ b/mp/src/public/filesystem_init.cpp @@ -0,0 +1,1423 @@ +//========= 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];
+ if ( pFileSystem->IsSteam() )
+ {
+ // Steam doesn't support relative paths
+ Q_strncpy( platform, "platform", MAX_PATH );
+ }
+ else
+ {
+ Q_strncpy( platform, szGameInfoPath, MAX_PATH );
+ Q_StripTrailingSlash( platform );
+ Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
+ }
+
+ pFileSystem->AddSearchPath( platform, "PLATFORM" );
+}
|