summaryrefslogtreecommitdiff
path: root/filesystem/filesystem_steam.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filesystem/filesystem_steam.cpp')
-rw-r--r--filesystem/filesystem_steam.cpp1536
1 files changed, 1536 insertions, 0 deletions
diff --git a/filesystem/filesystem_steam.cpp b/filesystem/filesystem_steam.cpp
new file mode 100644
index 0000000..8456f85
--- /dev/null
+++ b/filesystem/filesystem_steam.cpp
@@ -0,0 +1,1536 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "basefilesystem.h"
+#include "steamcommon.h"
+#include "SteamInterface.h"
+#include "tier0/dbg.h"
+#include "tier0/icommandline.h"
+#include "steam/steam_api.h"
+#ifdef POSIX
+#include <fcntl.h>
+#ifdef LINUX
+#include <sys/file.h>
+#endif
+#include <dlfcn.h>
+#define _S_IWRITE S_IWRITE
+#define _S_IWRITE S_IWRITE
+#define _S_IFREG S_IFREG
+#define FILE_ATTRIBUTE_OFFLINE 0x1000
+#endif
+
+#ifdef _WIN32
+ extern "C"
+ {
+ __declspec(dllimport) int __stdcall IsDebuggerPresent();
+ }
+#endif
+
+ISteamInterface *steam = NULL;
+static SteamHandle_t g_pLastErrorFile;
+static TSteamError g_tLastError;
+static TSteamError g_tLastErrorNoFile;
+
+void CheckError( SteamHandle_t fp, TSteamError & steamError)
+{
+ if (steamError.eSteamError == eSteamErrorContentServerConnect)
+ {
+ // fatal error
+#ifdef WIN32
+ // kill the current window so the user can see the error
+ HWND hwnd = GetForegroundWindow();
+ if (hwnd)
+ {
+ DestroyWindow(hwnd);
+ }
+
+ // show the error
+ MessageBox(NULL, "Could not acquire necessary game files because the connection to Steam servers was lost.", "Source - Fatal Error", MB_OK | MB_ICONEXCLAMATION);
+
+ // get out of here immediately
+ TerminateProcess(GetCurrentProcess(), 0);
+#else
+ fprintf( stderr, "Could not acquire necessary game files because the connection to Steam servers was lost." );
+ exit(-1);
+#endif
+ return;
+ }
+
+ if (fp)
+ {
+ if (steamError.eSteamError != eSteamErrorNone || g_tLastError.eSteamError != eSteamErrorNone)
+ {
+ g_pLastErrorFile = fp;
+ g_tLastError = steamError;
+ }
+ }
+ else
+ {
+ // write to the NULL error checker
+ if (steamError.eSteamError != eSteamErrorNone || g_tLastErrorNoFile.eSteamError != eSteamErrorNone)
+ {
+ g_tLastErrorNoFile = steamError;
+ }
+ }
+}
+
+
+#ifdef POSIX
+class CSteamFile
+{
+public:
+ explicit CSteamFile( SteamHandle_t file, bool bWriteable, const char *pchName ) : m_File( file ), m_bWriteable( bWriteable ), m_FileName(pchName) {}
+ ~CSteamFile() {}
+ SteamHandle_t Handle() { return m_File; }
+ bool BWriteable() { return m_bWriteable; }
+ CUtlSymbol GetFileName() { return m_FileName; }
+private:
+ SteamHandle_t m_File;
+ bool m_bWriteable;
+ CUtlSymbol m_FileName;
+};
+#endif
+
+
+class CFileSystem_Steam : public CBaseFileSystem
+{
+public:
+ CFileSystem_Steam();
+ ~CFileSystem_Steam();
+
+ // Methods of IAppSystem
+ virtual InitReturnVal_t Init();
+ virtual void Shutdown();
+ virtual void * QueryInterface( const char *pInterfaceName );
+
+ // Higher level filesystem methods requiring specific behavior
+ virtual void GetLocalCopy( const char *pFileName );
+ virtual int HintResourceNeed( const char *hintlist, int forgetEverything );
+ virtual CSysModule * LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly );
+ virtual bool IsFileImmediatelyAvailable(const char *pFileName);
+
+ // resource waiting
+ virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
+ virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
+ virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
+ virtual bool IsSteam() const { return true; }
+ virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 );
+
+protected:
+ // implementation of CBaseFileSystem virtual functions
+ virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo );
+ virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
+ virtual void FS_fclose( FILE *fp );
+ virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
+ virtual long FS_ftell( FILE *fp );
+ virtual int FS_feof( FILE *fp );
+ virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
+ virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
+ virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
+ virtual int FS_ferror( FILE *fp );
+ virtual int FS_fflush( FILE *fp );
+ virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
+ virtual int FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache=NULL );
+ virtual int FS_chmod( const char *path, int pmode );
+ virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
+ virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
+ virtual bool FS_FindClose(HANDLE handle);
+
+private:
+ bool IsFileInSteamCache( const char *file );
+ bool IsFileInSteamCache2( const char *file );
+ void ViewSteamCache( const char* szDir, bool bRecurse );
+ bool m_bSteamInitialized;
+ bool m_bCurrentlyLoading;
+ bool m_bAssertFilesImmediatelyAvailable;
+ bool m_bCanAsync;
+ bool m_bSelfMounted;
+ bool m_bContentLoaded;
+ bool m_bSDKToolMode;
+
+ SteamCallHandle_t m_hWaitForResourcesCallHandle;
+ int m_iCurrentReturnedCallHandle;
+ HMODULE m_hSteamDLL;
+ void LoadAndStartSteam();
+#ifdef POSIX
+ static CUtlMap< int, CInterlockedInt > m_LockedFDMap;
+#endif
+};
+
+#ifdef POSIX
+CUtlMap< int, CInterlockedInt> CFileSystem_Steam::m_LockedFDMap;
+#endif
+
+
+//-----------------------------------------------------------------------------
+// singleton
+//-----------------------------------------------------------------------------
+static CFileSystem_Steam g_FileSystem_Steam;
+#if defined(DEDICATED)
+CBaseFileSystem *BaseFileSystem_Steam( void )
+{
+ return &g_FileSystem_Steam;
+}
+#endif
+
+#ifdef DEDICATED // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere
+
+IFileSystem *g_pFileSystemSteam = &g_FileSystem_Steam;
+IBaseFileSystem *g_pBaseFileSystemSteam = &g_FileSystem_Steam;
+
+#else
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CFileSystem_Steam::CFileSystem_Steam()
+{
+ m_bSteamInitialized = false;
+ m_bCurrentlyLoading = false;
+ m_bAssertFilesImmediatelyAvailable = false;
+ m_bCanAsync = true;
+ m_bContentLoaded = false;
+ m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
+ m_iCurrentReturnedCallHandle = 1;
+ m_hSteamDLL = NULL;
+ m_bSDKToolMode = false;
+#ifdef POSIX
+ SetDefLessFunc( m_LockedFDMap );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CFileSystem_Steam::~CFileSystem_Steam()
+{
+ m_bSteamInitialized = false;
+}
+
+bool CFileSystem_Steam::IsFileInSteamCache2( const char *file )
+{
+ if ( !m_bContentLoaded || m_bSDKToolMode)
+ {
+ return true;
+ }
+
+ // see if the file exists
+ TSteamElemInfo info;
+ TSteamError error;
+
+ SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
+ if ( h == STEAM_INVALID_HANDLE )
+ {
+ return false;
+ }
+ else
+ {
+ steam->FindClose( h, &error );
+ }
+
+ return true;
+}
+
+
+void MountDependencies( int iAppId, CUtlVector<unsigned int> &depList )
+{
+ TSteamError steamError;
+
+ // Setup the buffers for the TSteamApp structure.
+ char buffers[4][2048];
+ TSteamApp steamApp;
+ steamApp.szName = buffers[0];
+ steamApp.uMaxNameChars = sizeof( buffers[0] );
+ steamApp.szLatestVersionLabel = buffers[1];
+ steamApp.uMaxLatestVersionLabelChars = sizeof( buffers[1] );
+ steamApp.szCurrentVersionLabel = buffers[2];
+ steamApp.uMaxCurrentVersionLabelChars = sizeof( buffers[2] );
+ steamApp.szInstallDirName = buffers[3];
+ steamApp.uMaxInstallDirNameChars = sizeof( buffers[3] );
+
+ // Ask how many caches depend on this app ID.
+ steam->EnumerateApp( iAppId, &steamApp, &steamError );
+ if ( steamError.eSteamError != eSteamErrorNone )
+ Error( "EnumerateApp( %d ) failed: %s", iAppId, steamError.szDesc );
+
+ // Mount each cache.
+ for ( int i=0; i < (int)steamApp.uNumDependencies; i++ )
+ {
+ TSteamAppDependencyInfo appDependencyInfo;
+ steam->EnumerateAppDependency( iAppId, i, &appDependencyInfo, &steamError );
+ if ( steamError.eSteamError != eSteamErrorNone )
+ Error( "EnumerateAppDependency( %d, %d ) failed: %s", iAppId, i, steamError.szDesc );
+
+ if ( depList.Find( appDependencyInfo.uAppId ) == -1 )
+ {
+ depList.AddToTail( appDependencyInfo.uAppId );
+
+ // Make sure that the user owns the app before attempting to mount it
+ int isSubscribed = false, isPending = false;
+ steam->IsAppSubscribed( appDependencyInfo.uAppId, &isSubscribed, &isPending, &steamError );
+ if ( isSubscribed )
+ {
+ steam->MountFilesystem( appDependencyInfo.uAppId, "", &steamError );
+ if ( steamError.eSteamError != eSteamErrorNone && steamError.eSteamError != eSteamErrorNotSubscribed )
+ {
+ Error( "MountFilesystem( %d ) failed: %s", appDependencyInfo.uAppId, steamError.szDesc );
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// QueryInterface:
+//-----------------------------------------------------------------------------
+void *CFileSystem_Steam::QueryInterface( const char *pInterfaceName )
+{
+ // We also implement the IMatSystemSurface interface
+ if (!Q_strncmp( pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
+ return (IFileSystem*)this;
+
+ return CBaseFileSystem::QueryInterface( pInterfaceName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of IAppSystem
+//-----------------------------------------------------------------------------
+InitReturnVal_t CFileSystem_Steam::Init()
+{
+ m_bSteamInitialized = true;
+ m_bSelfMounted = false;
+
+ LoadAndStartSteam();
+
+ return CBaseFileSystem::Init();
+}
+
+void CFileSystem_Steam::Shutdown()
+{
+ Assert( m_bSteamInitialized );
+
+ if ( !steam )
+ return;
+
+
+ TSteamError steamError;
+
+ // If we're not running Steam in local mode, remove all mount points from the STEAM VFS.
+ if ( !CommandLine()->CheckParm("-steamlocal") && !m_bSelfMounted && !steam->UnmountAppFilesystem(&steamError) )
+ {
+#ifdef WIN32
+ OutputDebugString(steamError.szDesc);
+#endif
+ Assert(!("STEAM VFS failed to unmount"));
+
+ // just continue on as if nothing happened
+ // ::MessageBox(NULL, szErrorMsg, "Half-Life FileSystem_Steam Error", MB_OK);
+ // exit( -1 );
+ }
+
+ steam->Cleanup(&steamError);
+
+ if ( m_hSteamDLL )
+ {
+ Sys_UnloadModule( (CSysModule *)m_hSteamDLL );
+ m_hSteamDLL = NULL;
+ }
+ m_bSteamInitialized = false;
+}
+
+
+void CFileSystem_Steam::LoadAndStartSteam()
+{
+ if ( !m_hSteamDLL )
+ {
+ const char *pchSteamInstallPath = SteamAPI_GetSteamInstallPath();
+ if ( pchSteamInstallPath )
+ {
+ char szSteamDLLPath[ MAX_PATH ];
+#ifdef WIN32
+ V_ComposeFileName( pchSteamInstallPath, "steam" DLL_EXT_STRING, szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
+#elif defined(POSIX)
+ V_ComposeFileName( pchSteamInstallPath, "libsteam" DLL_EXT_STRING, szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
+#else
+#error
+#endif
+ // try to load the steam.dll from the running steam process first
+ m_hSteamDLL = (HMODULE)Sys_LoadModule( szSteamDLLPath );
+ }
+
+ if ( !m_hSteamDLL )
+#ifdef WIN32
+ m_hSteamDLL = (HMODULE)Sys_LoadModule( "steam" DLL_EXT_STRING );
+#elif defined(POSIX)
+ m_hSteamDLL = (HMODULE)Sys_LoadModule( "libsteam" DLL_EXT_STRING );
+#else
+#error
+#endif
+ }
+
+ if ( m_hSteamDLL )
+ {
+ typedef void *(*PFSteamCreateInterface)( const char *pchSteam );
+#ifdef WIN32
+ PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)GetProcAddress( m_hSteamDLL, "_f" );
+#else
+ PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)dlsym( (void *)m_hSteamDLL, "_f" );
+#endif
+ if ( pfnSteamCreateInterface )
+ steam = (ISteamInterface *)pfnSteamCreateInterface( STEAM_INTERFACE_VERSION );
+ }
+
+ if ( !steam )
+ {
+ Error("CFileSystem_Steam::Init() failed: failed to find steam interface\n");
+#ifdef WIN32
+ ::DestroyWindow( GetForegroundWindow() );
+ ::MessageBox(NULL, "CFileSystem_Steam::Init() failed: failed to find steam interface", "Half-Life FileSystem_Steam Error", MB_OK);
+#endif
+ _exit( -1 );
+ }
+
+ TSteamError steamError;
+ if (!steam->Startup(STEAM_USING_FILESYSTEM | STEAM_USING_LOGGING | STEAM_USING_USERID | STEAM_USING_ACCOUNT, &steamError))
+ {
+ Error("SteamStartup() failed: %s\n", steamError.szDesc);
+#ifdef WIN32
+ ::DestroyWindow( GetForegroundWindow() );
+ ::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
+#endif
+ _exit( -1 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of IAppSystem
+//-----------------------------------------------------------------------------
+FilesystemMountRetval_t CFileSystem_Steam::MountSteamContent( int nExtraAppId )
+{
+ m_bContentLoaded = true;
+ FilesystemMountRetval_t retval = FILESYSTEM_MOUNT_OK;
+
+ // MWD: This is here because of Hammer's funky startup sequence that requires MountSteamContent() be called in CHammerApp::PreInit(). Once that root problem is addressed this will be removed;
+ if ( NULL == steam )
+ {
+ LoadAndStartSteam();
+ }
+
+ // only mount if we're already logged in
+ // if we're not logged in, assume the app will login & mount the cache itself
+ // this enables both the game and the platform to use this same code, even though they mount caches at different times
+ int loggedIn = 0;
+ TSteamError steamError;
+ int result = steam->IsLoggedIn(&loggedIn, &steamError);
+ if (!result || loggedIn)
+ {
+ if ( nExtraAppId != -1 )
+ {
+ m_bSDKToolMode = true;
+
+ CUtlVector<unsigned int> depList;
+ if ( nExtraAppId < -1 )
+ {
+ // Special way to tell them to mount a specific App ID's depots.
+ MountDependencies( -nExtraAppId, depList );
+ return FILESYSTEM_MOUNT_OK;
+ }
+ else
+ {
+ const char *pMainAppId = NULL;
+
+ // If they specified extra app IDs they want to mount after the main one, then we mount
+ // the caches manually here.
+#ifdef _WIN32
+ // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
+ // to the process environment after the DLL was loaded.
+ char szMainAppId[128];
+ if ( GetEnvironmentVariable( "steamappid", szMainAppId, sizeof( szMainAppId ) ) != 0 )
+ {
+ pMainAppId = szMainAppId;
+ }
+#else
+ // LINUX BUG: see above
+ pMainAppId = getenv( "SteamAppId" );
+#endif // _WIN32
+
+ if ( !pMainAppId )
+ Error( "Extra App ID set to %d, but no SteamAppId.", nExtraAppId );
+
+ //swapping this mount order ensures the most current engine binaries are used by tools
+ MountDependencies( nExtraAppId, depList );
+ MountDependencies( atoi( pMainAppId ), depList );
+ return FILESYSTEM_MOUNT_OK;
+ }
+ }
+ else if (!steam->MountAppFilesystem(&steamError))
+ {
+ Error("MountAppFilesystem() failed: %s\n", steamError.szDesc);
+#ifdef WIN32
+ ::DestroyWindow( GetForegroundWindow() );
+ ::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
+#endif
+ _exit( -1 );
+ }
+
+ }
+ else
+ {
+ m_bSelfMounted = true;
+ }
+
+ return retval;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+FILE *CFileSystem_Steam::FS_fopen( const char *filenameT, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo )
+{
+ char filename[MAX_PATH];
+
+ FixUpPath ( filenameT, filename, sizeof( filename ) );
+
+ // make sure the file is immediately available
+ if (m_bAssertFilesImmediatelyAvailable && !m_bCurrentlyLoading)
+ {
+ if (!IsFileImmediatelyAvailable(filename))
+ {
+ Msg("Steam FS: '%s' not immediately available when not in loading dialog", filename);
+ }
+ }
+
+ if ( !steam )
+ {
+ AssertMsg( 0, "CFileSystem_Steam::FS_fopen used with null steam interface!" );
+ return NULL;
+ }
+
+ CFileLoadInfo dummyInfo;
+ if ( !pInfo )
+ {
+ dummyInfo.m_bSteamCacheOnly = false;
+ pInfo = &dummyInfo;
+ }
+
+ SteamHandle_t f = 0;
+
+#ifdef POSIX
+ FILE *pFile = NULL;
+ bool bWriteable = false;
+ if ( strchr(options,'w') || strchr(options,'a') )
+ bWriteable = true;
+
+ if ( bWriteable )
+ {
+ pFile = fopen( filename, options );
+ if (pFile && size)
+ {
+ // todo: replace with filelength()?
+ struct _stat buf;
+ int rt = _stat( filename, &buf );
+ if (rt == 0)
+ {
+ *size = buf.st_size;
+ }
+ }
+ if ( pFile )
+ {
+
+ // Win32 has an undocumented feature that is serialized ALL writes to a file across threads (i.e only 1 thread can open a file at a time)
+ // so use flock here to mimic that behavior
+
+ ThreadId_t curThread = ThreadGetCurrentId();
+
+ {
+ CThreadFastMutex Locklock;
+ AUTO_LOCK( Locklock );
+ int fd = fileno_unlocked( pFile );
+ int iLockID = m_LockedFDMap.Find( fd );
+ int ret = flock( fd, LOCK_EX | LOCK_NB );
+ if ( ret < 0 )
+ {
+ if ( errno == EWOULDBLOCK )
+ {
+ if ( iLockID != m_LockedFDMap.InvalidIndex() &&
+ m_LockedFDMap[iLockID] != -1 &&
+ curThread != m_LockedFDMap[iLockID] )
+ {
+ ret = flock( fd, LOCK_EX );
+ if ( ret < 0 )
+ {
+ fclose( pFile );
+ return NULL;
+ }
+ }
+ }
+ else
+ {
+ fclose( pFile );
+ return NULL;
+ }
+ }
+
+ if ( iLockID != m_LockedFDMap.InvalidIndex() )
+ m_LockedFDMap[iLockID] = curThread;
+ else
+ m_LockedFDMap.Insert( fd, curThread );
+
+ }
+ rewind( pFile );
+ }
+ }
+ else
+ {
+#endif
+
+ TSteamError steamError;
+ unsigned int fileSize;
+ int bLocal = 0;
+ f = steam->OpenFileEx(filename, options, pInfo->m_bSteamCacheOnly, &fileSize, &bLocal, &steamError);
+
+ pInfo->m_bLoadedFromSteamCache = (bLocal == 0);
+ if (size)
+ {
+ *size = fileSize;
+ }
+
+ CheckError( f, steamError );
+
+#ifdef POSIX
+ }
+
+ if ( f || pFile )
+ {
+ CSteamFile *steamFile = new CSteamFile( pFile ? (SteamHandle_t)pFile : f, bWriteable, filename );
+ f = (SteamHandle_t)steamFile;
+ }
+#endif
+ return (FILE *)f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Steam::FS_setbufsize( FILE *fp, unsigned nBytes )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+WaitForResourcesHandle_t CFileSystem_Steam::WaitForResources( const char *resourcelist )
+{
+ char szResourceList[MAX_PATH];
+ Q_strncpy( szResourceList, resourcelist, sizeof(szResourceList) );
+ Q_DefaultExtension( szResourceList, ".lst", sizeof(szResourceList) );
+
+ // cancel any old call
+ TSteamError steamError;
+ m_hWaitForResourcesCallHandle = steam->WaitForResources(szResourceList, &steamError);
+ if (steamError.eSteamError == eSteamErrorNone)
+ {
+ // return a new call handle
+ return (WaitForResourcesHandle_t)(++m_iCurrentReturnedCallHandle);
+ }
+
+ Msg("SteamWaitForResources() failed: %s\n", steamError.szDesc);
+ return (WaitForResourcesHandle_t)FILESYSTEM_INVALID_HANDLE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+bool CFileSystem_Steam::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
+{
+ // clear the input
+ *progress = 0.0f;
+ *complete = true;
+
+ // check to see if they're using an old handle
+ if (m_iCurrentReturnedCallHandle != handle)
+ return false;
+ if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
+ return false;
+
+ // get the progress
+ TSteamError steamError;
+ TSteamProgress steamProgress;
+ int result = steam->ProcessCall(m_hWaitForResourcesCallHandle, &steamProgress, &steamError);
+ if (result && steamError.eSteamError == eSteamErrorNone)
+ {
+ // we've finished successfully
+ m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
+ *complete = true;
+ *progress = 1.0f;
+ return true;
+ }
+ else if (steamError.eSteamError != eSteamErrorNotFinishedProcessing)
+ {
+ // we have an error, just call it done
+ m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
+ Msg("SteamProcessCall(SteamWaitForResources()) failed: %s\n", steamError.szDesc);
+ return false;
+ }
+
+ // return the progress
+ if (steamProgress.bValid)
+ {
+ *progress = (float)steamProgress.uPercentDone / (100.0f * STEAM_PROGRESS_PERCENT_SCALE);
+ }
+ else
+ {
+ *progress = 0;
+ }
+ *complete = false;
+
+ return (steamProgress.bValid != false);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+void CFileSystem_Steam::CancelWaitForResources( WaitForResourcesHandle_t handle )
+{
+ // check to see if they're using an old handle
+ if (m_iCurrentReturnedCallHandle != handle)
+ return;
+ if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
+ return;
+
+ TSteamError steamError;
+ steam->AbortCall(m_hWaitForResourcesCallHandle, &steamError);
+ m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: helper for posix file handle wrapper
+//-----------------------------------------------------------------------------
+#ifdef POSIX
+FILE *GetFileHandle( CSteamFile *steamFile )
+{
+ if ( !steamFile )
+ return NULL;
+
+ return (FILE *)steamFile->Handle();
+}
+bool BWriteable( CSteamFile *steamFile )
+{
+ return steamFile && steamFile->BWriteable();
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Steam::FS_fclose( FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ int fd = fileno_unlocked( fp );
+ fflush( fp );
+ flock( fd, LOCK_UN );
+ int iLockID = m_LockedFDMap.Find( fd );
+ if ( iLockID != m_LockedFDMap.InvalidIndex() )
+ m_LockedFDMap[ iLockID ] = -1;
+
+ fclose( fp );
+ }
+ else
+ {
+#endif
+ TSteamError steamError;
+ steam->CloseFile((SteamHandle_t)fp, &steamError);
+ CheckError( (SteamHandle_t)fp, steamError );
+#ifdef POSIX
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Steam::FS_fseek( FILE *fp, int64 pos, int seekType )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ fseek( fp, pos, seekType );
+ }
+ else
+ {
+#endif
+ TSteamError steamError;
+ int result;
+ result = steam->SeekFile((SteamHandle_t)fp, (int32)pos, (ESteamSeekMethod)seekType, &steamError);
+ CheckError((SteamHandle_t)fp, steamError);
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+long CFileSystem_Steam::FS_ftell( FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return ftell(fp);
+ }
+ else
+ {
+#endif
+ long steam_offset;
+ TSteamError steamError;
+
+ steam_offset = steam->TellFile((SteamHandle_t)fp, &steamError);
+ if ( steamError.eSteamError != eSteamErrorNone )
+ {
+ CheckError((SteamHandle_t)fp, steamError);
+ return -1L;
+ }
+
+ return steam_offset;
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Steam::FS_feof( FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return feof(fp);
+ }
+ else
+ {
+#endif
+ long orig_pos;
+
+ // Figure out where in the file we currently are...
+ orig_pos = FS_ftell(fp);
+
+ if ( (SteamHandle_t)fp == g_pLastErrorFile && g_tLastError.eSteamError == eSteamErrorEOF )
+ return 1;
+
+ if ( g_tLastError.eSteamError != eSteamErrorNone )
+ return 0;
+
+ // Jump to the end...
+ FS_fseek(fp, 0L, SEEK_END);
+
+ // If we were already at the end, return true
+ if ( orig_pos == FS_ftell(fp) )
+ return 1;
+
+ // Otherwise, go back to the original spot and return false.
+ FS_fseek(fp, orig_pos, SEEK_SET);
+ return 0;
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Steam::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return fread( dest, 1, size, fp );
+ }
+ else
+ {
+#endif
+ TSteamError steamError;
+ int blocksRead = steam->ReadFile(dest, 1, size, (SteamHandle_t)fp, &steamError);
+ CheckError((SteamHandle_t)fp, steamError);
+ return blocksRead; // steam reads in atomic blocks of "size" bytes
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Steam::FS_fwrite( const void *src, size_t size, FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+#define WRITE_CHUNK (256 * 1024)
+ if ( size > WRITE_CHUNK )
+ {
+ size_t remaining = size;
+ const byte* current = (const byte *) src;
+ size_t total = 0;
+
+ while ( remaining > 0 )
+ {
+ size_t bytesToCopy = min(remaining, WRITE_CHUNK);
+
+ total += fwrite(current, 1, bytesToCopy, fp);
+
+ remaining -= bytesToCopy;
+ current += bytesToCopy;
+ }
+
+ Assert( total == size );
+ return total;
+ }
+
+ return fwrite(src, 1, size, fp);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)
+ }
+ else
+ {
+#endif
+ TSteamError steamError;
+ int result = steam->WriteFile(src, 1, size, (SteamHandle_t)fp, &steamError);
+ CheckError((SteamHandle_t)fp, steamError);
+ return result;
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Steam::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return vfprintf(fp, fmt, list);
+ }
+ else
+ {
+#endif
+ int blen, plen;
+ char *buf;
+
+ if ( !fp || !fmt )
+ return 0;
+
+ // Open the null device...used by vfprintf to determine the length of
+ // the formatted string.
+ FILE *nullDeviceFP = fopen("nul:", "w");
+ if ( !nullDeviceFP )
+ return 0;
+
+ // Figure out how long the formatted string will be...dump formatted
+ // string to the bit bucket.
+ blen = vfprintf(nullDeviceFP, fmt, list);
+ fclose(nullDeviceFP);
+ if ( !blen )
+ {
+ return 0;
+ }
+
+ // Get buffer in which to build the formatted string.
+ buf = (char *)malloc(blen+1);
+ if ( !buf )
+ {
+ return 0;
+ }
+
+ // Build the formatted string.
+ plen = _vsnprintf(buf, blen, fmt, list);
+ va_end(list);
+ if ( plen != blen )
+ {
+ free(buf);
+ return 0;
+ }
+
+ buf[ blen ] = 0;
+
+ // Write out the formatted string.
+ if ( plen != (int)FS_fwrite(buf, plen, fp) )
+ {
+ free(buf);
+ return 0;
+ }
+
+ free(buf);
+ return plen;
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Steam::FS_ferror( FILE *fp )
+{
+ if (fp)
+ {
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return ferror(fp);
+ }
+ else
+ {
+#endif
+ if ((SteamHandle_t)fp != g_pLastErrorFile)
+ {
+ // it's asking for an error for a previous file, return no error
+ return 0;
+ }
+
+ return ( g_tLastError.eSteamError != eSteamErrorNone );
+#ifdef POSIX
+ }
+#endif
+ }
+ return g_tLastErrorNoFile.eSteamError != eSteamErrorNone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Steam::FS_fflush( FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return fflush(fp);
+ }
+ else
+ {
+#endif
+ TSteamError steamError;
+ int result = steam->FlushFile((SteamHandle_t)fp, &steamError);
+ CheckError((SteamHandle_t)fp, steamError);
+ return result;
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+char *CFileSystem_Steam::FS_fgets( char *dest, int destSize, FILE *fp )
+{
+#ifdef POSIX
+ CSteamFile *steamFile = (CSteamFile *)fp;
+ fp = GetFileHandle( steamFile );
+ if ( BWriteable( steamFile ) )
+ {
+ return fgets(dest, destSize, fp);
+ }
+ else
+ {
+#endif
+ unsigned char c;
+ int numCharRead = 0;
+
+ // Read at most n chars from the file or until a newline
+ *dest = c = '\0';
+ while ( (numCharRead < destSize-1) && (c != '\n') )
+ {
+ // Read in the next char...
+ if ( FS_fread(&c, 1, 1, fp) != 1 )
+ {
+ if ( g_tLastError.eSteamError != eSteamErrorEOF || numCharRead == 0 )
+ {
+ return NULL; // If we hit an error, return NULL.
+ }
+
+ numCharRead = destSize; // Hit EOF, no more to read, all done...
+ }
+
+ else
+ {
+ *dest++ = c; // add the char to the string and point to the next pos
+ *dest = '\0'; // append NULL
+ numCharRead++; // count the char read
+ }
+ }
+ return dest; // Has a NULL termination...
+#ifdef POSIX
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Steam::FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache )
+{
+ TSteamElemInfo Info;
+ TSteamError steamError;
+
+ if ( pbLoadedFromSteamCache )
+ *pbLoadedFromSteamCache = false;
+
+ if ( !steam )
+ {
+ // The dedicated server gets here once at startup. When setting up the executable path before loading
+ // base modules like engine.dll, the filesystem looks for zipX.zip but we haven't mounted steam content
+ // yet so steam is null.
+#if !defined( DEDICATED )
+ AssertMsg( 0, "CFileSystem_Steam::FS_stat used with null steam interface!" );
+#endif
+ return -1;
+ }
+
+ memset(buf, 0, sizeof(struct _stat));
+ int returnVal= steam->Stat(path, &Info, &steamError);
+ if ( returnVal == 0 )
+ {
+ if (Info.bIsDir )
+ {
+ buf->st_mode |= _S_IFDIR;
+ buf->st_size = 0;
+ }
+ else
+ {
+ if ( pbLoadedFromSteamCache )
+ *pbLoadedFromSteamCache = ( Info.bIsLocal == 0 );
+
+ // Now we want to know if it's writable or not. First see if there is a local copy.
+ struct _stat testBuf;
+ int rt = _stat( path, &testBuf );
+ if ( rt == 0 )
+ {
+ // Ok, there's a local copy. Now check if the copy on our HD is writable.
+ if ( testBuf.st_mode & _S_IWRITE )
+ buf->st_mode |= _S_IWRITE;
+ }
+
+ buf->st_mode |= _S_IFREG;
+ buf->st_size = Info.uSizeOrCount;
+ }
+
+ buf->st_atime = Info.lLastAccessTime;
+ buf->st_mtime = Info.lLastModificationTime;
+ buf->st_ctime = Info.lCreationTime;
+ }
+
+ CheckError(NULL, steamError);
+ return returnVal;
+}
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Steam::FS_chmod( const char *path, int pmode )
+{
+ return _chmod( path, pmode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+HANDLE CFileSystem_Steam::FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat)
+{
+ TSteamElemInfo steamFindInfo;
+ HANDLE hResult = INVALID_HANDLE_VALUE;
+ SteamHandle_t steamResult;
+ TSteamError steamError;
+
+ steamResult = steam->FindFirst(findname, eSteamFindAll, &steamFindInfo, &steamError);
+ CheckError(NULL, steamError);
+
+ if ( steamResult == STEAM_INVALID_HANDLE )
+ {
+ hResult = INVALID_HANDLE_VALUE;
+ }
+ else
+ {
+ hResult = (HANDLE)steamResult;
+ strcpy(dat->cFileName, steamFindInfo.cszName);
+
+// NEED TO DEAL WITH THIS STUFF!!! FORTUNATELY HALF-LIFE DOESN'T USE ANY OF IT
+// AND ARCANUM USES _findfirst() etc.
+//
+// findInfo->ftLastWriteTime = steamFindInfo.lLastModificationTime;
+// findInfo->ftCreationTime = steamFindInfo.lCreationTime;
+// findInfo->ftLastAccessTime = steamFindInfo.lLastAccessTime;
+// findInfo->nFileSizeHigh = ;
+// findInfo->nFileSizeLow = ;
+
+ // Determine if the found object is a directory...
+ if ( steamFindInfo.bIsDir )
+ dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ else
+ dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+ // Determine if the found object was local or remote.
+ // ***NOTE*** we are hijacking the FILE_ATTRIBUTE_OFFLINE bit and using it in a different
+ // (but similar) manner than the WIN32 documentation indicates ***NOTE***
+ if ( steamFindInfo.bIsLocal )
+ dat->dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE;
+ else
+ dat->dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
+ }
+
+ return hResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CFileSystem_Steam::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
+{
+ TSteamElemInfo steamFindInfo;
+ bool result;
+ TSteamError steamError;
+
+ result = (steam->FindNext((SteamHandle_t)handle, &steamFindInfo, &steamError) == 0);
+ CheckError(NULL, steamError);
+
+ if ( result )
+ {
+ strcpy(dat->cFileName, steamFindInfo.cszName);
+ if ( steamFindInfo.bIsDir )
+ dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
+ else
+ dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
+ }
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CFileSystem_Steam::FS_FindClose(HANDLE handle)
+{
+ TSteamError steamError;
+ int result = (steam->FindClose((SteamHandle_t)handle, &steamError) == 0);
+ CheckError(NULL, steamError);
+ return result != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: files are always immediately available on disk
+//-----------------------------------------------------------------------------
+bool CFileSystem_Steam::IsFileImmediatelyAvailable(const char *pFileName)
+{
+ TSteamError steamError;
+ return (steam->IsFileImmediatelyAvailable(pFileName, &steamError) != 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFileSystem_Steam::GetLocalCopy( const char *pFileName )
+{
+ // Now try to find the dll under Steam so we can do a GetLocalCopy() on it
+ TSteamError steamError;
+
+/*
+#ifdef WIN32
+ struct _stat StatBuf;
+ if ( FS_stat(pFileName, &StatBuf) == -1 )
+ {
+ // Use the environment search path to try and find it
+ char* pPath = getenv("PATH");
+
+ // Use the .EXE name to determine the root directory
+ char srchPath[ MAX_PATH ];
+#ifdef WIN32
+ HINSTANCE hInstance = ( HINSTANCE )GetModuleHandle( 0 );
+ if ( !GetModuleFileName( hInstance, srchPath, MAX_PATH ) )
+ {
+ ::MessageBox( 0, "Failed calling GetModuleFileName", "Half-Life Steam Filesystem Error", MB_OK );
+ return;
+ }
+#else
+ srchPath[0] = '.';
+ srchPath[1] = '\0';
+#endif
+
+ // Get the length of the root directory the .exe is in
+ char* pSeperator = strrchr( srchPath, CORRECT_PATH_SEPARATOR );
+ int nBaseLen = 0;
+ if ( pSeperator )
+ {
+ nBaseLen = pSeperator - srchPath;
+ }
+
+ // Extract each section of the path
+ char* pStart = pPath;
+ char* pEnd = 0;
+ bool bSearch = true;
+ while ( bSearch )
+ {
+#ifdef WIN32
+#define PATH_SEP ";"
+#else
+#define PATH_SEP ":"
+#endif
+ pEnd = strstr( pStart, PATH_SEP );
+ int nSize = pEnd - pStart;
+ if ( !pEnd )
+ {
+ bSearch = false;
+ // If pEnd is NULL then nSize will be rubbish, so calculate
+ // it sensibly.
+ nSize = strlen( pStart );
+ }
+
+ // Is this path even potentially in the base directory?
+ if ( nSize > nBaseLen )
+ {
+ // Create a new path (relative to the base directory) by stripping off
+ // nBaseLen characters and therefore doing FS_stat relative to the current
+ // directory, which should be the base directory.
+ Assert( sizeof(srchPath) > nBaseLen + strlen(pFileName) + 2 );
+ nSize -= nBaseLen+1;
+ memcpy( srchPath, pStart+nBaseLen+1, nSize );
+ memcpy( srchPath+nSize, pFileName, strlen(pFileName)+1 );
+ // If the path starts with a directory separator then we won't get a
+ // relative path, so skip the check.
+ if ( srchPath[0] != CORRECT_PATH_SEPARATOR )
+ {
+ if ( FS_stat(srchPath, &StatBuf) == 0 )
+ {
+ steam->GetLocalFileCopy(srchPath, &steamError);
+ break;
+ }
+ }
+ }
+ pStart = pEnd+1;
+ }
+ }
+ else
+#endif
+*/
+ {
+ // Convert _srv.so to .so...
+ const char *pDllStringExtension = V_GetFileExtension( DLL_EXT_STRING );
+ const char *pModuleExtension = pDllStringExtension ? ( pDllStringExtension - 1 ) : DLL_EXT_STRING;
+
+ // If we got an extension, and this filename has it, then check if it's loaded.
+ if( pModuleExtension && V_stristr( pFileName, pModuleExtension ) )
+ {
+ // We can't be copying files over the top of .so files if they're already loaded
+ // in memory. mmap2( ... MAP_PRIVATE ... ) says "it is unspecified whether changes
+ // made to the file after the mmap() call are visible in the mapped region." Testing
+ // and lots of debugging (thanks Pierre-Loup!) has shown that they are, in fact,
+ // blasted right over your nicely loaded and fixed up object.
+ CSysModule *module = Sys_LoadModule( pFileName, SYS_NOLOAD );
+
+ if( module )
+ {
+ // Sys_LoadModule( SYS_NOLOAD ) increments the refcount, so bump that back down.
+ Sys_UnloadModule( module );
+ return;
+ }
+ }
+
+ steam->GetLocalFileCopy(pFileName, &steamError);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Load a DLL
+// Input : *path
+//-----------------------------------------------------------------------------
+CSysModule * CFileSystem_Steam::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
+{
+ char szNewPath[ MAX_PATH ];
+ CBaseFileSystem::ParsePathID( pFileName, pPathID, szNewPath );
+
+ // File must end in .dll
+ char szExtension[] = DLL_EXT_STRING;
+ Assert( Q_strlen(pFileName) < sizeof(szNewPath) );
+
+ Q_strncpy( szNewPath, pFileName, sizeof( szNewPath ) );
+ if ( !Q_stristr(szNewPath, szExtension) )
+ {
+ Assert( strlen(pFileName) + sizeof(szExtension) < sizeof(szNewPath) );
+ Q_strncat( szNewPath, szExtension, sizeof( szNewPath ), COPY_ALL_CHARACTERS );
+ }
+
+ LogFileAccess( szNewPath );
+ if ( !pPathID )
+ {
+ pPathID = "EXECUTABLE_PATH"; // default to the bin dir
+ }
+
+ CUtlSymbol lookup = g_PathIDTable.AddString( pPathID );
+
+ // a pathID has been specified, find the first match in the path list
+ int c = m_SearchPaths.Count();
+ for (int i = 0; i < c; i++)
+ {
+ // pak files are not allowed to be written to...
+ if (m_SearchPaths[i].GetPackFile())
+ continue;
+
+ if ( m_SearchPaths[i].GetPathID() == lookup )
+ {
+ char newPathName[MAX_PATH];
+ Q_snprintf( newPathName, sizeof(newPathName), "%s%s", m_SearchPaths[i].GetPathString(), szNewPath ); // append the path to this dir.
+
+ // make sure the file exists, and is in the Steam cache
+
+ if ( bValidatedDllOnly && !IsFileInSteamCache(newPathName) )
+ continue;
+
+ // Get a local copy from Steam
+ bool bGetLocalCopy = true;
+
+ if ( m_bSDKToolMode )
+ bGetLocalCopy = false;
+#ifdef _WIN32
+ if ( IsDebuggerPresent() )
+ bGetLocalCopy = false;
+#endif
+ if ( bGetLocalCopy )
+ GetLocalCopy( newPathName );
+
+ CSysModule *module = Sys_LoadModule( newPathName );
+ if ( module ) // we found the binary in one of our search paths
+ {
+ if ( bValidatedDllOnly && !IsFileInSteamCache2(newPathName) )
+ {
+ return NULL;
+ }
+ else
+ {
+ return module;
+ }
+ }
+ }
+ }
+
+ if ( bValidatedDllOnly && IsFileInSteamCache(szNewPath) )
+ {
+ // couldn't load it from any of the search paths, let LoadLibrary try
+ return Sys_LoadModule( szNewPath );
+ }
+
+ return NULL;
+}
+
+void CFileSystem_Steam::ViewSteamCache(const char* szDir, bool bRecurse)
+{
+ TSteamElemInfo info;
+ TSteamError error;
+ char szPath[MAX_PATH];
+
+ V_snprintf( szPath, sizeof(szPath),"%s%c*.*", szDir, CORRECT_PATH_SEPARATOR );
+
+ SteamHandle_t h = steam->FindFirst( szPath, eSteamFindRemoteOnly, &info, &error );
+ int ret = 0;
+
+ if ( h != STEAM_INVALID_HANDLE )
+ {
+ do
+ {
+ Msg( "View Steam Cache: '%s%c%s' \n", szDir, CORRECT_PATH_SEPARATOR, info.cszName );
+
+ if ( bRecurse && info.bIsDir && (0 == V_stristr( info.cszName, "." ) ) )
+ {
+ V_snprintf( szPath, sizeof(szPath),"%s%c%s", szDir, CORRECT_PATH_SEPARATOR, info.cszName );
+ ViewSteamCache( szPath, true );
+ }
+
+ ret = steam->FindNext( h, &info, &error );
+
+ } while( 0 == ret );
+
+ steam->FindClose( h, &error );
+ }
+}
+
+
+// HACK HACK - to allow IsFileInSteamCache() to use the old C exported interface
+extern "C" SteamHandle_t SteamFindFirst( const char *cszPattern, ESteamFindFilter eFilter, TSteamElemInfo *pFindInfo, TSteamError *pError );
+extern "C" int SteamFindClose( SteamHandle_t hDirectory, TSteamError *pError );
+
+//-----------------------------------------------------------------------------
+// Purpose: returns true if the file exists and is in a mounted Steam cache
+//-----------------------------------------------------------------------------
+bool CFileSystem_Steam::IsFileInSteamCache( const char *file )
+{
+ if ( !m_bContentLoaded || m_bSDKToolMode )
+ {
+ return true;
+ }
+
+ // see if the file exists
+ TSteamElemInfo info;
+ TSteamError error;
+
+ SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
+ if ( h == STEAM_INVALID_HANDLE )
+ {
+ return false;
+ }
+ else
+ {
+ steam->FindClose( h, &error );
+ }
+
+ return true;
+}
+
+
+int CFileSystem_Steam::HintResourceNeed( const char *hintlist, int forgetEverything )
+{
+ TSteamError steamError;
+ int result = steam->HintResourceNeed( hintlist, forgetEverything, &steamError );
+ CheckError(NULL, steamError);
+ return result;
+}
+
+