Team Fortress 2 Source Code as on 22/4/2020
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

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