You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1536 lines
42 KiB
1536 lines
42 KiB
//========= 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; |
|
} |
|
|
|
|
|
|