Counter Strike : Global Offensive Source Code
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.

1502 lines
41 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "basefilesystem.h"
  8. #include "SteamCommon.h"
  9. #include "SteamInterface.h"
  10. #include "tier0/dbg.h"
  11. #include "tier0/icommandline.h"
  12. #include "steam/steam_api.h"
  13. #include "tier0/vprof.h"
  14. #ifdef POSIX
  15. #include <fcntl.h>
  16. #ifdef LINUX
  17. #include <sys/file.h>
  18. #endif
  19. #include <dlfcn.h>
  20. #define _S_IWRITE S_IWRITE
  21. #define _S_IWRITE S_IWRITE
  22. #define _S_IFREG S_IFREG
  23. #define FILE_ATTRIBUTE_OFFLINE 0x1000
  24. #endif
  25. // NOTE: This has to be the last file included!
  26. #include "tier0/memdbgon.h"
  27. #ifdef _WIN32
  28. DECLSPEC_IMPORT BOOL WINAPI IsDebuggerPresent(void);
  29. #else
  30. static bool IsDebuggerPresent( void )
  31. {
  32. return false;
  33. }
  34. #endif
  35. ISteamInterface *steam = NULL;
  36. static SteamHandle_t g_pLastErrorFile;
  37. static TSteamError g_tLastError;
  38. static TSteamError g_tLastErrorNoFile;
  39. void CheckError( SteamHandle_t fp, TSteamError & steamError)
  40. {
  41. if (steamError.eSteamError == eSteamErrorContentServerConnect)
  42. {
  43. // fatal error
  44. #ifdef WIN32
  45. // kill the current window so the user can see the error
  46. HWND hwnd = GetForegroundWindow();
  47. if (hwnd)
  48. {
  49. DestroyWindow(hwnd);
  50. }
  51. // show the error
  52. MessageBox(NULL, "Could not acquire necessary game files because the connection to Steam servers was lost.", "Source - Fatal Error", MB_OK | MB_ICONEXCLAMATION);
  53. // get out of here immediately
  54. TerminateProcess(GetCurrentProcess(), 0);
  55. #else
  56. fprintf( stderr, "Could not acquire necessary game files because the connection to Steam servers was lost." );
  57. exit(-1);
  58. #endif
  59. return;
  60. }
  61. if (fp)
  62. {
  63. if (steamError.eSteamError != eSteamErrorNone || g_tLastError.eSteamError != eSteamErrorNone)
  64. {
  65. g_pLastErrorFile = fp;
  66. g_tLastError = steamError;
  67. }
  68. }
  69. else
  70. {
  71. // write to the NULL error checker
  72. if (steamError.eSteamError != eSteamErrorNone || g_tLastErrorNoFile.eSteamError != eSteamErrorNone)
  73. {
  74. g_tLastErrorNoFile = steamError;
  75. }
  76. }
  77. }
  78. #ifdef POSIX
  79. class CSteamFile
  80. {
  81. public:
  82. explicit CSteamFile( SteamHandle_t file, bool bWriteable, const char *pchName ) : m_File( file ), m_bWriteable( bWriteable ), m_FileName(pchName) {}
  83. ~CSteamFile() {}
  84. SteamHandle_t Handle() { return m_File; }
  85. bool BWriteable() { return m_bWriteable; }
  86. CUtlSymbol GetFileName() { return m_FileName; }
  87. private:
  88. SteamHandle_t m_File;
  89. bool m_bWriteable;
  90. CUtlSymbol m_FileName;
  91. };
  92. #endif
  93. class CFileSystem_Steam : public CBaseFileSystem
  94. {
  95. public:
  96. CFileSystem_Steam();
  97. ~CFileSystem_Steam();
  98. // Methods of IAppSystem
  99. virtual InitReturnVal_t Init();
  100. virtual void Shutdown();
  101. virtual void * QueryInterface( const char *pInterfaceName );
  102. // Higher level filesystem methods requiring specific behavior
  103. virtual void GetLocalCopy( const char *pFileName );
  104. virtual int HintResourceNeed( const char *hintlist, int forgetEverything );
  105. virtual CSysModule * LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly );
  106. virtual bool IsFileImmediatelyAvailable(const char *pFileName);
  107. // resource waiting
  108. virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
  109. virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
  110. virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
  111. virtual bool IsSteam() const { return true; }
  112. virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 );
  113. protected:
  114. // implementation of CBaseFileSystem virtual functions
  115. virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo );
  116. virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
  117. virtual void FS_fclose( FILE *fp );
  118. virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
  119. virtual long FS_ftell( FILE *fp );
  120. virtual int FS_feof( FILE *fp );
  121. virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
  122. virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
  123. virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
  124. virtual int FS_ferror( FILE *fp );
  125. virtual int FS_fflush( FILE *fp );
  126. virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
  127. virtual int FS_stat( const char *path, struct _stat *buf );
  128. virtual int FS_chmod( const char *path, int pmode );
  129. virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
  130. virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
  131. virtual bool FS_FindClose(HANDLE handle);
  132. private:
  133. bool IsFileInSteamCache( const char *file );
  134. bool IsFileInSteamCache2( const char *file );
  135. void ViewSteamCache( const char* szDir, bool bRecurse );
  136. bool m_bSteamInitialized;
  137. bool m_bCurrentlyLoading;
  138. bool m_bAssertFilesImmediatelyAvailable;
  139. bool m_bCanAsync;
  140. bool m_bSelfMounted;
  141. bool m_bContentLoaded;
  142. bool m_bSDKToolMode;
  143. SteamCallHandle_t m_hWaitForResourcesCallHandle;
  144. int m_iCurrentReturnedCallHandle;
  145. HMODULE m_hSteamDLL;
  146. void LoadAndStartSteam();
  147. #ifdef POSIX
  148. static CUtlMap< int, CInterlockedInt > m_LockedFDMap;
  149. #endif
  150. };
  151. #ifdef POSIX
  152. CUtlMap< int, CInterlockedInt> CFileSystem_Steam::m_LockedFDMap;
  153. #endif
  154. //-----------------------------------------------------------------------------
  155. // singleton
  156. //-----------------------------------------------------------------------------
  157. static CFileSystem_Steam g_FileSystem_Steam;
  158. #if defined(DEDICATED)
  159. CBaseFileSystem *BaseFileSystem_Steam( void )
  160. {
  161. return &g_FileSystem_Steam;
  162. }
  163. #endif
  164. #ifdef DEDICATED // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere
  165. IFileSystem *g_pFileSystemSteam = &g_FileSystem_Steam;
  166. IBaseFileSystem *g_pBaseFileSystemSteam = &g_FileSystem_Steam;
  167. #else
  168. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );
  169. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );
  170. #endif
  171. //-----------------------------------------------------------------------------
  172. // constructor
  173. //-----------------------------------------------------------------------------
  174. CFileSystem_Steam::CFileSystem_Steam()
  175. {
  176. m_bSteamInitialized = false;
  177. m_bCurrentlyLoading = false;
  178. m_bAssertFilesImmediatelyAvailable = false;
  179. m_bCanAsync = true;
  180. m_bContentLoaded = false;
  181. m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
  182. m_iCurrentReturnedCallHandle = 1;
  183. m_hSteamDLL = NULL;
  184. m_bSDKToolMode = false;
  185. #ifdef POSIX
  186. SetDefLessFunc( m_LockedFDMap );
  187. #endif
  188. }
  189. //-----------------------------------------------------------------------------
  190. // Purpose:
  191. //-----------------------------------------------------------------------------
  192. CFileSystem_Steam::~CFileSystem_Steam()
  193. {
  194. m_bSteamInitialized = false;
  195. }
  196. bool CFileSystem_Steam::IsFileInSteamCache2( const char *file )
  197. {
  198. if ( !m_bContentLoaded || m_bSDKToolMode)
  199. {
  200. return true;
  201. }
  202. // see if the file exists
  203. TSteamElemInfo info;
  204. TSteamError error;
  205. SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
  206. if ( h == STEAM_INVALID_HANDLE )
  207. {
  208. return false;
  209. }
  210. else
  211. {
  212. steam->FindClose( h, &error );
  213. }
  214. return true;
  215. }
  216. void MountDependencies( int iAppId, CUtlVector<unsigned int> &depList )
  217. {
  218. TSteamError steamError;
  219. // Setup the buffers for the TSteamApp structure.
  220. char buffers[4][2048];
  221. TSteamApp steamApp;
  222. steamApp.szName = buffers[0];
  223. steamApp.uMaxNameChars = sizeof( buffers[0] );
  224. steamApp.szLatestVersionLabel = buffers[1];
  225. steamApp.uMaxLatestVersionLabelChars = sizeof( buffers[1] );
  226. steamApp.szCurrentVersionLabel = buffers[2];
  227. steamApp.uMaxCurrentVersionLabelChars = sizeof( buffers[2] );
  228. steamApp.szInstallDirName = buffers[3];
  229. steamApp.uMaxInstallDirNameChars = sizeof( buffers[3] );
  230. // Ask how many caches depend on this app ID.
  231. steam->EnumerateApp( iAppId, &steamApp, &steamError );
  232. if ( steamError.eSteamError != eSteamErrorNone )
  233. Error( "EnumerateApp( %d ) failed: %s", iAppId, steamError.szDesc );
  234. // Mount each cache.
  235. for ( int i=0; i < (int)steamApp.uNumDependencies; i++ )
  236. {
  237. TSteamAppDependencyInfo appDependencyInfo;
  238. steam->EnumerateAppDependency( iAppId, i, &appDependencyInfo, &steamError );
  239. if ( steamError.eSteamError != eSteamErrorNone )
  240. Error( "EnumerateAppDependency( %d, %d ) failed: %s", iAppId, i, steamError.szDesc );
  241. if ( depList.Find( appDependencyInfo.uAppId ) == -1 )
  242. {
  243. depList.AddToTail( appDependencyInfo.uAppId );
  244. // Make sure that the user owns the app before attempting to mount it
  245. int isSubscribed = false, isPending = false;
  246. steam->IsAppSubscribed( appDependencyInfo.uAppId, &isSubscribed, &isPending, &steamError );
  247. if ( isSubscribed )
  248. {
  249. steam->MountFilesystem( appDependencyInfo.uAppId, "", &steamError );
  250. if ( steamError.eSteamError != eSteamErrorNone && steamError.eSteamError != eSteamErrorNotSubscribed )
  251. {
  252. Error( "MountFilesystem( %d ) failed: %s", appDependencyInfo.uAppId, steamError.szDesc );
  253. }
  254. }
  255. }
  256. }
  257. }
  258. //-----------------------------------------------------------------------------
  259. // QueryInterface:
  260. //-----------------------------------------------------------------------------
  261. void *CFileSystem_Steam::QueryInterface( const char *pInterfaceName )
  262. {
  263. // We also implement the IMatSystemSurface interface
  264. if (!Q_strncmp( pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
  265. return (IFileSystem*)this;
  266. return CBaseFileSystem::QueryInterface( pInterfaceName );
  267. }
  268. //-----------------------------------------------------------------------------
  269. // Methods of IAppSystem
  270. //-----------------------------------------------------------------------------
  271. InitReturnVal_t CFileSystem_Steam::Init()
  272. {
  273. m_bSteamInitialized = true;
  274. m_bSelfMounted = false;
  275. LoadAndStartSteam();
  276. return CBaseFileSystem::Init();
  277. }
  278. void CFileSystem_Steam::Shutdown()
  279. {
  280. Assert( m_bSteamInitialized );
  281. if ( !steam )
  282. return;
  283. TSteamError steamError;
  284. // If we're not running Steam in local mode, remove all mount points from the STEAM VFS.
  285. if ( !CommandLine()->CheckParm("-steamlocal") && !m_bSelfMounted && !steam->UnmountAppFilesystem(&steamError) )
  286. {
  287. #ifdef WIN32
  288. OutputDebugString(steamError.szDesc);
  289. #endif
  290. Assert(!("STEAM VFS failed to unmount"));
  291. // just continue on as if nothing happened
  292. // ::MessageBox(NULL, szErrorMsg, "Half-Life FileSystem_Steam Error", MB_OK);
  293. // exit( -1 );
  294. }
  295. steam->Cleanup(&steamError);
  296. if ( m_hSteamDLL )
  297. {
  298. Sys_UnloadModule( (CSysModule *)m_hSteamDLL );
  299. m_hSteamDLL = NULL;
  300. }
  301. m_bSteamInitialized = false;
  302. }
  303. void CFileSystem_Steam::LoadAndStartSteam()
  304. {
  305. if ( !m_hSteamDLL )
  306. {
  307. const char *pchSteamInstallPath = SteamAPI_GetSteamInstallPath();
  308. if ( pchSteamInstallPath )
  309. {
  310. char szSteamDLLPath[ MAX_PATH ];
  311. #ifdef WIN32
  312. V_ComposeFileName( pchSteamInstallPath, "steam.dll", szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
  313. #elif defined(OSX)
  314. V_ComposeFileName( pchSteamInstallPath, "libsteam.dylib", szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
  315. #elif defined(LINUX)
  316. V_ComposeFileName( pchSteamInstallPath, "libsteam.so", szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
  317. #else
  318. #error
  319. #endif
  320. // try to load the steam.dll from the running steam process first
  321. m_hSteamDLL = (HMODULE)Sys_LoadModule( szSteamDLLPath );
  322. }
  323. if ( !m_hSteamDLL )
  324. #ifdef WIN32
  325. m_hSteamDLL = (HMODULE)Sys_LoadModule( "steam.dll" );
  326. #elif defined(OSX)
  327. m_hSteamDLL = (HMODULE)Sys_LoadModule( "libsteam.dylib" );
  328. #elif defined(LINUX)
  329. m_hSteamDLL = (HMODULE)Sys_LoadModule( "libsteam.so" );
  330. #else
  331. #error
  332. #endif
  333. }
  334. if ( m_hSteamDLL )
  335. {
  336. typedef void *(*PFSteamCreateInterface)( const char *pchSteam );
  337. #ifdef WIN32
  338. PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)GetProcAddress( m_hSteamDLL, "_f" );
  339. #else
  340. PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)dlsym( (void *)m_hSteamDLL, "_f" );
  341. #endif
  342. if ( pfnSteamCreateInterface )
  343. steam = (ISteamInterface *)pfnSteamCreateInterface( STEAM_INTERFACE_VERSION );
  344. }
  345. if ( !steam )
  346. {
  347. Error("CFileSystem_Steam::Init() failed: failed to find steam interface\n");
  348. #ifdef WIN32
  349. ::DestroyWindow( GetForegroundWindow() );
  350. ::MessageBox(NULL, "CFileSystem_Steam::Init() failed: failed to find steam interface", "Half-Life FileSystem_Steam Error", MB_OK);
  351. #endif
  352. _exit( -1 );
  353. }
  354. TSteamError steamError;
  355. if (!steam->Startup(STEAM_USING_FILESYSTEM | STEAM_USING_LOGGING | STEAM_USING_USERID | STEAM_USING_ACCOUNT, &steamError))
  356. {
  357. Error("SteamStartup() failed: %s\n", steamError.szDesc);
  358. #ifdef WIN32
  359. ::DestroyWindow( GetForegroundWindow() );
  360. ::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
  361. #endif
  362. _exit( -1 );
  363. }
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Methods of IAppSystem
  367. //-----------------------------------------------------------------------------
  368. FilesystemMountRetval_t CFileSystem_Steam::MountSteamContent( int nExtraAppId )
  369. {
  370. m_bContentLoaded = true;
  371. FilesystemMountRetval_t retval = FILESYSTEM_MOUNT_OK;
  372. // 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;
  373. if ( NULL == steam )
  374. {
  375. LoadAndStartSteam();
  376. }
  377. // only mount if we're already logged in
  378. // if we're not logged in, assume the app will login & mount the cache itself
  379. // this enables both the game and the platform to use this same code, even though they mount caches at different times
  380. int loggedIn = 0;
  381. TSteamError steamError;
  382. int result = steam->IsLoggedIn(&loggedIn, &steamError);
  383. if (!result || loggedIn)
  384. {
  385. if ( nExtraAppId != -1 )
  386. {
  387. m_bSDKToolMode = true;
  388. CUtlVector<unsigned int> depList;
  389. if ( nExtraAppId < -1 )
  390. {
  391. // Special way to tell them to mount a specific App ID's depots.
  392. MountDependencies( -nExtraAppId, depList );
  393. return FILESYSTEM_MOUNT_OK;
  394. }
  395. else
  396. {
  397. const char *pMainAppId = NULL;
  398. // If they specified extra app IDs they want to mount after the main one, then we mount
  399. // the caches manually here.
  400. #ifdef _WIN32
  401. // Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
  402. // to the process environment after the DLL was loaded.
  403. char szMainAppId[128];
  404. if ( GetEnvironmentVariable( "steamappid", szMainAppId, sizeof( szMainAppId ) ) != 0 )
  405. {
  406. pMainAppId = szMainAppId;
  407. }
  408. #else
  409. // LINUX BUG: see above
  410. pMainAppId = getenv( "SteamAppId" );
  411. #endif // _WIN32
  412. if ( !pMainAppId )
  413. Error( "Extra App ID set to %d, but no SteamAppId.", nExtraAppId );
  414. //swapping this mount order ensures the most current engine binaries are used by tools
  415. MountDependencies( nExtraAppId, depList );
  416. MountDependencies( atoi( pMainAppId ), depList );
  417. return FILESYSTEM_MOUNT_OK;
  418. }
  419. }
  420. else if (!steam->MountAppFilesystem(&steamError))
  421. {
  422. Error("MountAppFilesystem() failed: %s\n", steamError.szDesc);
  423. #ifdef WIN32
  424. ::DestroyWindow( GetForegroundWindow() );
  425. ::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
  426. #endif
  427. _exit( -1 );
  428. }
  429. }
  430. else
  431. {
  432. m_bSelfMounted = true;
  433. }
  434. return retval;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose: low-level filesystem wrapper
  438. //-----------------------------------------------------------------------------
  439. FILE *CFileSystem_Steam::FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo )
  440. {
  441. // make sure the file is immediately available
  442. if (m_bAssertFilesImmediatelyAvailable && !m_bCurrentlyLoading)
  443. {
  444. if (!IsFileImmediatelyAvailable(filename))
  445. {
  446. Msg("Steam FS: '%s' not immediately available when not in loading dialog", filename);
  447. }
  448. }
  449. if ( !steam )
  450. {
  451. AssertMsg( 0, "CFileSystem_Steam::FS_fopen used with null steam interface!" );
  452. return NULL;
  453. }
  454. CFileLoadInfo dummyInfo;
  455. if ( !pInfo )
  456. {
  457. dummyInfo.m_bSteamCacheOnly = false;
  458. pInfo = &dummyInfo;
  459. }
  460. SteamHandle_t f = 0;
  461. #ifdef POSIX
  462. FILE *pFile = NULL;
  463. bool bWriteable = false;
  464. if ( strchr(options,'w') || strchr(options,'a') )
  465. bWriteable = true;
  466. if ( bWriteable )
  467. {
  468. pFile = fopen( filename, options );
  469. if (pFile && size)
  470. {
  471. // todo: replace with filelength()?
  472. struct _stat buf;
  473. int rt = _stat( filename, &buf );
  474. if (rt == 0)
  475. {
  476. *size = buf.st_size;
  477. }
  478. }
  479. if ( pFile )
  480. {
  481. // 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)
  482. // so use flock here to mimic that behavior
  483. ThreadId_t curThread = ThreadGetCurrentId();
  484. {
  485. CThreadFastMutex Locklock;
  486. AUTO_LOCK( Locklock );
  487. int fd = fileno_unlocked( pFile );
  488. int iLockID = m_LockedFDMap.Find( fd );
  489. int ret = flock( fd, LOCK_EX | LOCK_NB );
  490. if ( ret < 0 )
  491. {
  492. if ( errno == EWOULDBLOCK )
  493. {
  494. if ( iLockID != m_LockedFDMap.InvalidIndex() &&
  495. m_LockedFDMap[iLockID] != -1 &&
  496. curThread != m_LockedFDMap[iLockID] )
  497. {
  498. ret = flock( fd, LOCK_EX );
  499. if ( ret < 0 )
  500. {
  501. fclose( pFile );
  502. return NULL;
  503. }
  504. }
  505. }
  506. else
  507. {
  508. fclose( pFile );
  509. return NULL;
  510. }
  511. }
  512. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  513. m_LockedFDMap[iLockID] = curThread;
  514. else
  515. m_LockedFDMap.Insert( fd, curThread );
  516. }
  517. rewind( pFile );
  518. }
  519. }
  520. else
  521. {
  522. #endif
  523. TSteamError steamError;
  524. unsigned int fileSize;
  525. int bLocal = 0;
  526. f = steam->OpenFileEx(filename, options, pInfo->m_bSteamCacheOnly, &fileSize, &bLocal, &steamError);
  527. pInfo->m_bLoadedFromSteamCache = (bLocal == 0);
  528. if (size)
  529. {
  530. *size = fileSize;
  531. }
  532. CheckError( f, steamError );
  533. #ifdef POSIX
  534. }
  535. if ( f || pFile )
  536. {
  537. CSteamFile *steamFile = new CSteamFile( pFile ? (SteamHandle_t)pFile : f, bWriteable, filename );
  538. f = (SteamHandle_t)steamFile;
  539. }
  540. #endif
  541. return (FILE *)f;
  542. }
  543. //-----------------------------------------------------------------------------
  544. // Purpose: low-level filesystem wrapper
  545. //-----------------------------------------------------------------------------
  546. void CFileSystem_Steam::FS_setbufsize( FILE *fp, unsigned nBytes )
  547. {
  548. }
  549. //-----------------------------------------------------------------------------
  550. // Purpose: steam call, unnecessary in stdio
  551. //-----------------------------------------------------------------------------
  552. WaitForResourcesHandle_t CFileSystem_Steam::WaitForResources( const char *resourcelist )
  553. {
  554. char szResourceList[MAX_PATH];
  555. Q_strncpy( szResourceList, resourcelist, sizeof(szResourceList) );
  556. Q_DefaultExtension( szResourceList, ".lst", sizeof(szResourceList) );
  557. // cancel any old call
  558. TSteamError steamError;
  559. m_hWaitForResourcesCallHandle = steam->WaitForResources(szResourceList, &steamError);
  560. if (steamError.eSteamError == eSteamErrorNone)
  561. {
  562. // return a new call handle
  563. return (WaitForResourcesHandle_t)(++m_iCurrentReturnedCallHandle);
  564. }
  565. Msg("SteamWaitForResources() failed: %s\n", steamError.szDesc);
  566. return (WaitForResourcesHandle_t)FILESYSTEM_INVALID_HANDLE;
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose: steam call, unnecessary in stdio
  570. //-----------------------------------------------------------------------------
  571. bool CFileSystem_Steam::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
  572. {
  573. VPROF_BUDGET( "GetWaitForResourcesProgress (steam)", VPROF_BUDGETGROUP_STEAM );
  574. // clear the input
  575. *progress = 0.0f;
  576. *complete = true;
  577. // check to see if they're using an old handle
  578. if (m_iCurrentReturnedCallHandle != handle)
  579. return false;
  580. if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
  581. return false;
  582. // get the progress
  583. TSteamError steamError;
  584. TSteamProgress steamProgress;
  585. int result = steam->ProcessCall(m_hWaitForResourcesCallHandle, &steamProgress, &steamError);
  586. if (result && steamError.eSteamError == eSteamErrorNone)
  587. {
  588. // we've finished successfully
  589. m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
  590. *complete = true;
  591. *progress = 1.0f;
  592. return true;
  593. }
  594. else if (steamError.eSteamError != eSteamErrorNotFinishedProcessing)
  595. {
  596. // we have an error, just call it done
  597. m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
  598. Msg("SteamProcessCall(SteamWaitForResources()) failed: %s\n", steamError.szDesc);
  599. return false;
  600. }
  601. // return the progress
  602. if (steamProgress.bValid)
  603. {
  604. *progress = (float)steamProgress.uPercentDone / (100.0f * STEAM_PROGRESS_PERCENT_SCALE);
  605. }
  606. else
  607. {
  608. *progress = 0;
  609. }
  610. *complete = false;
  611. return (steamProgress.bValid != false);
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose: steam call, unnecessary in stdio
  615. //-----------------------------------------------------------------------------
  616. void CFileSystem_Steam::CancelWaitForResources( WaitForResourcesHandle_t handle )
  617. {
  618. // check to see if they're using an old handle
  619. if (m_iCurrentReturnedCallHandle != handle)
  620. return;
  621. if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
  622. return;
  623. TSteamError steamError;
  624. steam->AbortCall(m_hWaitForResourcesCallHandle, &steamError);
  625. m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
  626. }
  627. //-----------------------------------------------------------------------------
  628. // Purpose: helper for posix file handle wrapper
  629. //-----------------------------------------------------------------------------
  630. #ifdef POSIX
  631. FILE *GetFileHandle( CSteamFile *steamFile )
  632. {
  633. if ( !steamFile )
  634. return NULL;
  635. return (FILE *)steamFile->Handle();
  636. }
  637. bool BWriteable( CSteamFile *steamFile )
  638. {
  639. return steamFile && steamFile->BWriteable();
  640. }
  641. #endif
  642. //-----------------------------------------------------------------------------
  643. // Purpose: low-level filesystem wrapper
  644. //-----------------------------------------------------------------------------
  645. void CFileSystem_Steam::FS_fclose( FILE *fp )
  646. {
  647. #ifdef POSIX
  648. CSteamFile *steamFile = (CSteamFile *)fp;
  649. fp = GetFileHandle( steamFile );
  650. if ( BWriteable( steamFile ) )
  651. {
  652. int fd = fileno_unlocked( fp );
  653. fflush( fp );
  654. flock( fd, LOCK_UN );
  655. int iLockID = m_LockedFDMap.Find( fd );
  656. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  657. m_LockedFDMap[ iLockID ] = -1;
  658. fclose( fp );
  659. }
  660. else
  661. {
  662. #endif
  663. TSteamError steamError;
  664. steam->CloseFile((SteamHandle_t)fp, &steamError);
  665. CheckError( (SteamHandle_t)fp, steamError );
  666. #ifdef POSIX
  667. }
  668. #endif
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose: low-level filesystem wrapper
  672. //-----------------------------------------------------------------------------
  673. void CFileSystem_Steam::FS_fseek( FILE *fp, int64 pos, int seekType )
  674. {
  675. #ifdef POSIX
  676. CSteamFile *steamFile = (CSteamFile *)fp;
  677. fp = GetFileHandle( steamFile );
  678. if ( BWriteable( steamFile ) )
  679. {
  680. fseek( fp, pos, seekType );
  681. }
  682. else
  683. {
  684. #endif
  685. TSteamError steamError;
  686. int result;
  687. result = steam->SeekFile((SteamHandle_t)fp, (int32)pos, (ESteamSeekMethod)seekType, &steamError);
  688. CheckError((SteamHandle_t)fp, steamError);
  689. #ifdef POSIX
  690. }
  691. #endif
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose: low-level filesystem wrapper
  695. //-----------------------------------------------------------------------------
  696. long CFileSystem_Steam::FS_ftell( FILE *fp )
  697. {
  698. #ifdef POSIX
  699. CSteamFile *steamFile = (CSteamFile *)fp;
  700. fp = GetFileHandle( steamFile );
  701. if ( BWriteable( steamFile ) )
  702. {
  703. return ftell(fp);
  704. }
  705. else
  706. {
  707. #endif
  708. long steam_offset;
  709. TSteamError steamError;
  710. steam_offset = steam->TellFile((SteamHandle_t)fp, &steamError);
  711. if ( steamError.eSteamError != eSteamErrorNone )
  712. {
  713. CheckError((SteamHandle_t)fp, steamError);
  714. return -1L;
  715. }
  716. return steam_offset;
  717. #ifdef POSIX
  718. }
  719. #endif
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Purpose: low-level filesystem wrapper
  723. //-----------------------------------------------------------------------------
  724. int CFileSystem_Steam::FS_feof( FILE *fp )
  725. {
  726. #ifdef POSIX
  727. CSteamFile *steamFile = (CSteamFile *)fp;
  728. fp = GetFileHandle( steamFile );
  729. if ( BWriteable( steamFile ) )
  730. {
  731. return feof(fp);
  732. }
  733. else
  734. {
  735. #endif
  736. long orig_pos;
  737. // Figure out where in the file we currently are...
  738. orig_pos = FS_ftell(fp);
  739. if ( (SteamHandle_t)fp == g_pLastErrorFile && g_tLastError.eSteamError == eSteamErrorEOF )
  740. return 1;
  741. if ( g_tLastError.eSteamError != eSteamErrorNone )
  742. return 0;
  743. // Jump to the end...
  744. FS_fseek(fp, 0L, SEEK_END);
  745. // If we were already at the end, return true
  746. if ( orig_pos == FS_ftell(fp) )
  747. return 1;
  748. // Otherwise, go back to the original spot and return false.
  749. FS_fseek(fp, orig_pos, SEEK_SET);
  750. return 0;
  751. #ifdef POSIX
  752. }
  753. #endif
  754. }
  755. //-----------------------------------------------------------------------------
  756. // Purpose: low-level filesystem wrapper
  757. //-----------------------------------------------------------------------------
  758. size_t CFileSystem_Steam::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
  759. {
  760. #ifdef POSIX
  761. CSteamFile *steamFile = (CSteamFile *)fp;
  762. fp = GetFileHandle( steamFile );
  763. if ( BWriteable( steamFile ) )
  764. {
  765. return fread( dest, 1, size, fp );
  766. }
  767. else
  768. {
  769. #endif
  770. TSteamError steamError;
  771. int blocksRead = steam->ReadFile(dest, 1, size, (SteamHandle_t)fp, &steamError);
  772. CheckError((SteamHandle_t)fp, steamError);
  773. return blocksRead; // steam reads in atomic blocks of "size" bytes
  774. #ifdef POSIX
  775. }
  776. #endif
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose: low-level filesystem wrapper
  780. //-----------------------------------------------------------------------------
  781. size_t CFileSystem_Steam::FS_fwrite( const void *src, size_t size, FILE *fp )
  782. {
  783. #ifdef POSIX
  784. CSteamFile *steamFile = (CSteamFile *)fp;
  785. fp = GetFileHandle( steamFile );
  786. if ( BWriteable( steamFile ) )
  787. {
  788. #define WRITE_CHUNK (256 * 1024)
  789. if ( size > WRITE_CHUNK )
  790. {
  791. size_t remaining = size;
  792. const byte* current = (const byte *) src;
  793. size_t total = 0;
  794. while ( remaining > 0 )
  795. {
  796. size_t bytesToCopy = MIN(remaining, WRITE_CHUNK);
  797. total += fwrite(current, 1, bytesToCopy, fp);
  798. remaining -= bytesToCopy;
  799. current += bytesToCopy;
  800. }
  801. Assert( total == size );
  802. return total;
  803. }
  804. return fwrite(src, 1, size, fp);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)
  805. }
  806. else
  807. {
  808. #endif
  809. TSteamError steamError;
  810. int result = steam->WriteFile(src, 1, size, (SteamHandle_t)fp, &steamError);
  811. CheckError((SteamHandle_t)fp, steamError);
  812. return result;
  813. #ifdef POSIX
  814. }
  815. #endif
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Purpose: low-level filesystem wrapper
  819. //-----------------------------------------------------------------------------
  820. size_t CFileSystem_Steam::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
  821. {
  822. #ifdef POSIX
  823. CSteamFile *steamFile = (CSteamFile *)fp;
  824. fp = GetFileHandle( steamFile );
  825. if ( BWriteable( steamFile ) )
  826. {
  827. return vfprintf(fp, fmt, list);
  828. }
  829. else
  830. {
  831. #endif
  832. int blen, plen;
  833. char *buf;
  834. if ( !fp || !fmt )
  835. return 0;
  836. // Open the null device...used by vfprintf to determine the length of
  837. // the formatted string.
  838. FILE *nullDeviceFP = fopen("nul:", "w");
  839. if ( !nullDeviceFP )
  840. return 0;
  841. // Figure out how long the formatted string will be...dump formatted
  842. // string to the bit bucket.
  843. blen = vfprintf(nullDeviceFP, fmt, list);
  844. fclose(nullDeviceFP);
  845. if ( !blen )
  846. {
  847. return 0;
  848. }
  849. // Get buffer in which to build the formatted string.
  850. buf = (char *)malloc(blen+1);
  851. if ( !buf )
  852. {
  853. return 0;
  854. }
  855. // Build the formatted string.
  856. plen = _vsnprintf(buf, blen, fmt, list);
  857. va_end(list);
  858. if ( plen != blen )
  859. {
  860. free(buf);
  861. return 0;
  862. }
  863. buf[ blen ] = 0;
  864. // Write out the formatted string.
  865. if ( plen != (int)FS_fwrite(buf, plen, fp) )
  866. {
  867. free(buf);
  868. return 0;
  869. }
  870. free(buf);
  871. return plen;
  872. #ifdef POSIX
  873. }
  874. #endif
  875. }
  876. //-----------------------------------------------------------------------------
  877. // Purpose: low-level filesystem wrapper
  878. //-----------------------------------------------------------------------------
  879. int CFileSystem_Steam::FS_ferror( FILE *fp )
  880. {
  881. if (fp)
  882. {
  883. #ifdef POSIX
  884. CSteamFile *steamFile = (CSteamFile *)fp;
  885. fp = GetFileHandle( steamFile );
  886. if ( BWriteable( steamFile ) )
  887. {
  888. return ferror(fp);
  889. }
  890. else
  891. {
  892. #endif
  893. if ((SteamHandle_t)fp != g_pLastErrorFile)
  894. {
  895. // it's asking for an error for a previous file, return no error
  896. return 0;
  897. }
  898. return ( g_tLastError.eSteamError != eSteamErrorNone );
  899. #ifdef POSIX
  900. }
  901. #endif
  902. }
  903. return g_tLastErrorNoFile.eSteamError != eSteamErrorNone;
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Purpose: low-level filesystem wrapper
  907. //-----------------------------------------------------------------------------
  908. int CFileSystem_Steam::FS_fflush( FILE *fp )
  909. {
  910. #ifdef POSIX
  911. CSteamFile *steamFile = (CSteamFile *)fp;
  912. fp = GetFileHandle( steamFile );
  913. if ( BWriteable( steamFile ) )
  914. {
  915. return fflush(fp);
  916. }
  917. else
  918. {
  919. #endif
  920. TSteamError steamError;
  921. int result = steam->FlushFile((SteamHandle_t)fp, &steamError);
  922. CheckError((SteamHandle_t)fp, steamError);
  923. return result;
  924. #ifdef POSIX
  925. }
  926. #endif
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Purpose: low-level filesystem wrapper
  930. //-----------------------------------------------------------------------------
  931. char *CFileSystem_Steam::FS_fgets( char *dest, int destSize, FILE *fp )
  932. {
  933. #ifdef POSIX
  934. CSteamFile *steamFile = (CSteamFile *)fp;
  935. fp = GetFileHandle( steamFile );
  936. if ( BWriteable( steamFile ) )
  937. {
  938. return fgets(dest, destSize, fp);
  939. }
  940. else
  941. {
  942. #endif
  943. unsigned char c;
  944. int numCharRead = 0;
  945. // Read at most n chars from the file or until a newline
  946. *dest = c = '\0';
  947. while ( (numCharRead < destSize-1) && (c != '\n') )
  948. {
  949. // Read in the next char...
  950. if ( FS_fread(&c, 1, 1, fp) != 1 )
  951. {
  952. if ( g_tLastError.eSteamError != eSteamErrorEOF || numCharRead == 0 )
  953. {
  954. return NULL; // If we hit an error, return NULL.
  955. }
  956. numCharRead = destSize; // Hit EOF, no more to read, all done...
  957. }
  958. else
  959. {
  960. *dest++ = c; // add the char to the string and point to the next pos
  961. *dest = '\0'; // append NULL
  962. numCharRead++; // count the char read
  963. }
  964. }
  965. return dest; // Has a NULL termination...
  966. #ifdef POSIX
  967. }
  968. #endif
  969. }
  970. //-----------------------------------------------------------------------------
  971. // Purpose: low-level filesystem wrapper
  972. //-----------------------------------------------------------------------------
  973. int CFileSystem_Steam::FS_stat( const char *path, struct _stat *buf )
  974. {
  975. TSteamElemInfo Info;
  976. TSteamError steamError;
  977. if ( !steam )
  978. {
  979. // The dedicated server gets here once at startup. When setting up the executable path before loading
  980. // base modules like engine.dll, the filesystem looks for zipX.zip but we haven't mounted steam content
  981. // yet so steam is null.
  982. #if !defined( DEDICATED )
  983. AssertMsg( 0, "CFileSystem_Steam::FS_stat used with null steam interface!" );
  984. #endif
  985. return -1;
  986. }
  987. memset(buf, 0, sizeof(struct _stat));
  988. int returnVal= steam->Stat(path, &Info, &steamError);
  989. if ( returnVal == 0 )
  990. {
  991. if (Info.bIsDir )
  992. {
  993. buf->st_mode |= _S_IFDIR;
  994. buf->st_size = 0;
  995. }
  996. else
  997. {
  998. // Now we want to know if it's writable or not. First see if there is a local copy.
  999. struct _stat testBuf;
  1000. int rt = _stat( path, &testBuf );
  1001. if ( rt == 0 )
  1002. {
  1003. // Ok, there's a local copy. Now check if the copy on our HD is writable.
  1004. if ( testBuf.st_mode & _S_IWRITE )
  1005. buf->st_mode |= _S_IWRITE;
  1006. }
  1007. buf->st_mode |= _S_IFREG;
  1008. buf->st_size = Info.uSizeOrCount;
  1009. }
  1010. buf->st_atime = Info.lLastAccessTime;
  1011. buf->st_mtime = Info.lLastModificationTime;
  1012. buf->st_ctime = Info.lCreationTime;
  1013. }
  1014. CheckError(NULL, steamError);
  1015. return returnVal;
  1016. }
  1017. #ifdef _WIN32
  1018. #include <io.h>
  1019. #endif
  1020. //-----------------------------------------------------------------------------
  1021. // Purpose: low-level filesystem wrapper
  1022. //-----------------------------------------------------------------------------
  1023. int CFileSystem_Steam::FS_chmod( const char *path, int pmode )
  1024. {
  1025. return _chmod( path, pmode );
  1026. }
  1027. //-----------------------------------------------------------------------------
  1028. // Purpose: low-level filesystem wrapper
  1029. //-----------------------------------------------------------------------------
  1030. HANDLE CFileSystem_Steam::FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat)
  1031. {
  1032. TSteamElemInfo steamFindInfo;
  1033. HANDLE hResult = INVALID_HANDLE_VALUE;
  1034. SteamHandle_t steamResult;
  1035. TSteamError steamError;
  1036. steamResult = steam->FindFirst(findname, eSteamFindAll, &steamFindInfo, &steamError);
  1037. CheckError(NULL, steamError);
  1038. if ( steamResult == STEAM_INVALID_HANDLE )
  1039. {
  1040. hResult = INVALID_HANDLE_VALUE;
  1041. }
  1042. else
  1043. {
  1044. hResult = (HANDLE)steamResult;
  1045. strcpy(dat->cFileName, steamFindInfo.cszName);
  1046. // NEED TO DEAL WITH THIS STUFF!!! FORTUNATELY HALF-LIFE DOESN'T USE ANY OF IT
  1047. // AND ARCANUM USES _findfirst() etc.
  1048. //
  1049. // findInfo->ftLastWriteTime = steamFindInfo.lLastModificationTime;
  1050. // findInfo->ftCreationTime = steamFindInfo.lCreationTime;
  1051. // findInfo->ftLastAccessTime = steamFindInfo.lLastAccessTime;
  1052. // findInfo->nFileSizeHigh = ;
  1053. // findInfo->nFileSizeLow = ;
  1054. // Determine if the found object is a directory...
  1055. if ( steamFindInfo.bIsDir )
  1056. dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  1057. else
  1058. dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
  1059. // Determine if the found object was local or remote.
  1060. // ***NOTE*** we are hijacking the FILE_ATTRIBUTE_OFFLINE bit and using it in a different
  1061. // (but similar) manner than the WIN32 documentation indicates ***NOTE***
  1062. if ( steamFindInfo.bIsLocal )
  1063. dat->dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE;
  1064. else
  1065. dat->dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
  1066. }
  1067. return hResult;
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Purpose: low-level filesystem wrapper
  1071. //-----------------------------------------------------------------------------
  1072. bool CFileSystem_Steam::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
  1073. {
  1074. TSteamElemInfo steamFindInfo;
  1075. bool result;
  1076. TSteamError steamError;
  1077. result = (steam->FindNext((SteamHandle_t)handle, &steamFindInfo, &steamError) == 0);
  1078. CheckError(NULL, steamError);
  1079. if ( result )
  1080. {
  1081. strcpy(dat->cFileName, steamFindInfo.cszName);
  1082. if ( steamFindInfo.bIsDir )
  1083. dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
  1084. else
  1085. dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
  1086. }
  1087. return result;
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Purpose: low-level filesystem wrapper
  1091. //-----------------------------------------------------------------------------
  1092. bool CFileSystem_Steam::FS_FindClose(HANDLE handle)
  1093. {
  1094. TSteamError steamError;
  1095. int result = (steam->FindClose((SteamHandle_t)handle, &steamError) == 0);
  1096. CheckError(NULL, steamError);
  1097. return result != 0;
  1098. }
  1099. //-----------------------------------------------------------------------------
  1100. // Purpose: files are always immediately available on disk
  1101. //-----------------------------------------------------------------------------
  1102. bool CFileSystem_Steam::IsFileImmediatelyAvailable(const char *pFileName)
  1103. {
  1104. TSteamError steamError;
  1105. return (steam->IsFileImmediatelyAvailable(pFileName, &steamError) != 0);
  1106. }
  1107. //-----------------------------------------------------------------------------
  1108. // Purpose:
  1109. //-----------------------------------------------------------------------------
  1110. void CFileSystem_Steam::GetLocalCopy( const char *pFileName )
  1111. {
  1112. // Now try to find the dll under Steam so we can do a GetLocalCopy() on it
  1113. TSteamError steamError;
  1114. #ifdef WIN32
  1115. struct _stat StatBuf;
  1116. if ( FS_stat(pFileName, &StatBuf) == -1 )
  1117. {
  1118. // Use the environment search path to try and find it
  1119. char* pPath = getenv("PATH");
  1120. // Use the .EXE name to determine the root directory
  1121. char srchPath[ MAX_PATH ];
  1122. #ifdef WIN32
  1123. HINSTANCE hInstance = ( HINSTANCE )GetModuleHandle( 0 );
  1124. if ( !GetModuleFileName( hInstance, srchPath, MAX_PATH ) )
  1125. {
  1126. ::MessageBox( 0, "Failed calling GetModuleFileName", "Half-Life Steam Filesystem Error", MB_OK );
  1127. return;
  1128. }
  1129. #else
  1130. srchPath[0] = '.';
  1131. srchPath[1] = '\0';
  1132. #endif
  1133. // Get the length of the root directory the .exe is in
  1134. char* pSeperator = strrchr( srchPath, CORRECT_PATH_SEPARATOR );
  1135. int nBaseLen = 0;
  1136. if ( pSeperator )
  1137. {
  1138. nBaseLen = pSeperator - srchPath;
  1139. }
  1140. // Extract each section of the path
  1141. char* pStart = pPath;
  1142. char* pEnd = 0;
  1143. bool bSearch = true;
  1144. while ( pStart && bSearch )
  1145. {
  1146. #ifdef WIN32
  1147. #define PATH_SEP ";"
  1148. #else
  1149. #define PATH_SEP ":"
  1150. #endif
  1151. pEnd = strstr( pStart, PATH_SEP );
  1152. if ( !pEnd )
  1153. bSearch = false;
  1154. int nSize = pEnd - pStart;
  1155. // Is this path even in the base directory?
  1156. if ( nSize > nBaseLen )
  1157. {
  1158. // Create a new path (relative to the base directory)
  1159. Assert( sizeof(srchPath) > nBaseLen + strlen(pFileName) + 2 );
  1160. nSize -= nBaseLen+1;
  1161. memcpy( srchPath, pStart+nBaseLen+1, nSize );
  1162. memcpy( srchPath+nSize, pFileName, strlen(pFileName)+1 );
  1163. if ( FS_stat(srchPath, &StatBuf) == 0 )
  1164. {
  1165. steam->GetLocalFileCopy(srchPath, &steamError);
  1166. break;
  1167. }
  1168. }
  1169. pStart = pEnd+1;
  1170. }
  1171. }
  1172. else
  1173. #endif
  1174. {
  1175. steam->GetLocalFileCopy(pFileName, &steamError);
  1176. }
  1177. }
  1178. //-----------------------------------------------------------------------------
  1179. // Purpose: Load a DLL
  1180. // Input : *path
  1181. //-----------------------------------------------------------------------------
  1182. CSysModule * CFileSystem_Steam::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
  1183. {
  1184. char szNewPath[ MAX_PATH ];
  1185. CBaseFileSystem::ParsePathID( pFileName, pPathID, szNewPath );
  1186. // File must end in .dll
  1187. char szExtension[] = DLL_EXT_STRING;
  1188. Assert( Q_strlen(pFileName) < sizeof(szNewPath) );
  1189. Q_strncpy( szNewPath, pFileName, sizeof( szNewPath ) );
  1190. if ( !Q_stristr(szNewPath, szExtension) )
  1191. {
  1192. Assert( strlen(pFileName) + sizeof(szExtension) < sizeof(szNewPath) );
  1193. Q_strncat( szNewPath, szExtension, sizeof( szNewPath ), COPY_ALL_CHARACTERS );
  1194. }
  1195. LogFileAccess( szNewPath );
  1196. if ( !pPathID )
  1197. {
  1198. pPathID = "EXECUTABLE_PATH"; // default to the bin dir
  1199. }
  1200. CUtlSymbol lookup = g_PathIDTable.AddString( pPathID );
  1201. // a pathID has been specified, find the first match in the path list
  1202. int c = m_SearchPaths.Count();
  1203. for (int i = 0; i < c; i++)
  1204. {
  1205. // pak files are not allowed to be written to...
  1206. if (m_SearchPaths[i].GetPackFile())
  1207. continue;
  1208. if ( m_SearchPaths[i].GetPathID() == lookup )
  1209. {
  1210. char newPathName[MAX_PATH];
  1211. Q_snprintf( newPathName, sizeof(newPathName), "%s%s", m_SearchPaths[i].GetPathString(), szNewPath ); // append the path to this dir.
  1212. // make sure the file exists, and is in the Steam cache
  1213. if ( bValidatedDllOnly && !IsFileInSteamCache(newPathName) )
  1214. continue;
  1215. // Get a local copy from Steam
  1216. bool bGetLocalCopy = true;
  1217. if ( m_bSDKToolMode )
  1218. bGetLocalCopy = false;
  1219. if ( IsDebuggerPresent() )
  1220. bGetLocalCopy = false;
  1221. if ( bGetLocalCopy )
  1222. GetLocalCopy( newPathName );
  1223. CSysModule *module = Sys_LoadModule( newPathName );
  1224. if ( module ) // we found the binary in one of our search paths
  1225. {
  1226. if ( bValidatedDllOnly && !IsFileInSteamCache2(newPathName) )
  1227. {
  1228. return NULL;
  1229. }
  1230. else
  1231. {
  1232. return module;
  1233. }
  1234. }
  1235. }
  1236. }
  1237. if ( bValidatedDllOnly && IsFileInSteamCache(szNewPath) )
  1238. {
  1239. // couldn't load it from any of the search paths, let LoadLibrary try
  1240. return Sys_LoadModule( szNewPath );
  1241. }
  1242. return NULL;
  1243. }
  1244. void CFileSystem_Steam::ViewSteamCache(const char* szDir, bool bRecurse)
  1245. {
  1246. TSteamElemInfo info;
  1247. TSteamError error;
  1248. char szPath[MAX_PATH];
  1249. V_snprintf( szPath, sizeof(szPath),"%s%c*.*", szDir, CORRECT_PATH_SEPARATOR );
  1250. SteamHandle_t h = steam->FindFirst( szPath, eSteamFindRemoteOnly, &info, &error );
  1251. int ret = 0;
  1252. if ( h != STEAM_INVALID_HANDLE )
  1253. {
  1254. do
  1255. {
  1256. Msg( "View Steam Cache: '%s%c%s' \n", szDir, CORRECT_PATH_SEPARATOR, info.cszName );
  1257. if ( bRecurse && info.bIsDir && (0 == V_stristr( info.cszName, "." ) ) )
  1258. {
  1259. V_snprintf( szPath, sizeof(szPath),"%s%c%s", szDir, CORRECT_PATH_SEPARATOR, info.cszName );
  1260. ViewSteamCache( szPath, true );
  1261. }
  1262. ret = steam->FindNext( h, &info, &error );
  1263. } while( 0 == ret );
  1264. steam->FindClose( h, &error );
  1265. }
  1266. }
  1267. // HACK HACK - to allow IsFileInSteamCache() to use the old C exported interface
  1268. extern "C" SteamHandle_t SteamFindFirst( const char *cszPattern, ESteamFindFilter eFilter, TSteamElemInfo *pFindInfo, TSteamError *pError );
  1269. extern "C" int SteamFindClose( SteamHandle_t hDirectory, TSteamError *pError );
  1270. //-----------------------------------------------------------------------------
  1271. // Purpose: returns true if the file exists and is in a mounted Steam cache
  1272. //-----------------------------------------------------------------------------
  1273. bool CFileSystem_Steam::IsFileInSteamCache( const char *file )
  1274. {
  1275. if ( !m_bContentLoaded || m_bSDKToolMode )
  1276. {
  1277. return true;
  1278. }
  1279. // see if the file exists
  1280. TSteamElemInfo info;
  1281. TSteamError error;
  1282. SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
  1283. if ( h == STEAM_INVALID_HANDLE )
  1284. {
  1285. return false;
  1286. }
  1287. else
  1288. {
  1289. steam->FindClose( h, &error );
  1290. }
  1291. return true;
  1292. }
  1293. int CFileSystem_Steam::HintResourceNeed( const char *hintlist, int forgetEverything )
  1294. {
  1295. TSteamError steamError;
  1296. int result = steam->HintResourceNeed( hintlist, forgetEverything, &steamError );
  1297. CheckError(NULL, steamError);
  1298. return result;
  1299. }