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.

1606 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #ifdef _WIN32
  8. #include <io.h>
  9. #include <fcntl.h>
  10. #endif
  11. #include "basefilesystem.h"
  12. #include "packfile.h"
  13. #include "tier0/dbg.h"
  14. #include "tier0/threadtools.h"
  15. #ifdef _WIN32
  16. #include "tier0/tslist.h"
  17. #elif defined(POSIX)
  18. #include <fcntl.h>
  19. #ifdef LINUX
  20. #include <sys/file.h>
  21. #endif
  22. #endif
  23. #include "tier1/convar.h"
  24. #include "tier0/vcrmode.h"
  25. #include "tier0/vprof.h"
  26. #include "tier1/fmtstr.h"
  27. #include "tier1/utlrbtree.h"
  28. #include "vstdlib/osversion.h"
  29. #ifdef _X360
  30. #undef WaitForSingleObject
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. ASSERT_INVARIANT( SEEK_CUR == FILESYSTEM_SEEK_CURRENT );
  35. ASSERT_INVARIANT( SEEK_SET == FILESYSTEM_SEEK_HEAD );
  36. ASSERT_INVARIANT( SEEK_END == FILESYSTEM_SEEK_TAIL );
  37. //-----------------------------------------------------------------------------
  38. class CFileSystem_Stdio : public CBaseFileSystem
  39. {
  40. public:
  41. CFileSystem_Stdio();
  42. ~CFileSystem_Stdio();
  43. // Used to get at older versions
  44. void *QueryInterface( const char *pInterfaceName );
  45. // Higher level filesystem methods requiring specific behavior
  46. virtual void GetLocalCopy( const char *pFileName );
  47. virtual int HintResourceNeed( const char *hintlist, int forgetEverything );
  48. virtual bool IsFileImmediatelyAvailable(const char *pFileName);
  49. virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
  50. virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
  51. virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
  52. virtual bool IsSteam() const { return false; }
  53. virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 ) { return FILESYSTEM_MOUNT_OK; }
  54. bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign );
  55. void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset );
  56. void FreeOptimalReadBuffer( void *p );
  57. protected:
  58. // implementation of CBaseFileSystem virtual functions
  59. virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size );
  60. virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
  61. virtual void FS_fclose( FILE *fp );
  62. virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
  63. virtual long FS_ftell( FILE *fp );
  64. virtual int FS_feof( FILE *fp );
  65. virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
  66. virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
  67. virtual bool FS_setmode( FILE *fp, FileMode_t mode );
  68. virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
  69. virtual int FS_ferror( FILE *fp );
  70. virtual int FS_fflush( FILE *fp );
  71. virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
  72. virtual int FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache=NULL );
  73. virtual int FS_chmod( const char *path, int pmode );
  74. virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
  75. virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
  76. virtual bool FS_FindClose(HANDLE handle);
  77. virtual int FS_GetSectorSize( FILE * );
  78. private:
  79. bool CanAsync() const
  80. {
  81. return m_bCanAsync;
  82. }
  83. bool m_bMounted;
  84. bool m_bCanAsync;
  85. };
  86. //-----------------------------------------------------------------------------
  87. // Per-file worker classes
  88. //-----------------------------------------------------------------------------
  89. abstract_class CStdFilesystemFile
  90. {
  91. public:
  92. virtual ~CStdFilesystemFile() {}
  93. virtual void FS_setbufsize( unsigned nBytes ) = 0;
  94. virtual void FS_fclose() = 0;
  95. virtual void FS_fseek( int64 pos, int seekType ) = 0;
  96. virtual long FS_ftell() = 0;
  97. virtual int FS_feof() = 0;
  98. virtual size_t FS_fread( void *dest, size_t destSize, size_t size ) = 0;
  99. virtual size_t FS_fwrite( const void *src, size_t size ) = 0;
  100. virtual bool FS_setmode( FileMode_t mode ) = 0;
  101. virtual size_t FS_vfprintf( const char *fmt, va_list list ) = 0;
  102. virtual int FS_ferror() = 0;
  103. virtual int FS_fflush() = 0;
  104. virtual char *FS_fgets( char *dest, int destSize ) = 0;
  105. virtual int FS_GetSectorSize() { return 1; }
  106. };
  107. //---------------------------------------------------------
  108. class CStdioFile : public CStdFilesystemFile
  109. {
  110. public:
  111. static CStdioFile *FS_fopen( const char *filename, const char *options, int64 *size );
  112. virtual void FS_setbufsize( unsigned nBytes );
  113. virtual void FS_fclose();
  114. virtual void FS_fseek( int64 pos, int seekType );
  115. virtual long FS_ftell();
  116. virtual int FS_feof();
  117. virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
  118. virtual size_t FS_fwrite( const void *src, size_t size );
  119. virtual bool FS_setmode( FileMode_t mode );
  120. virtual size_t FS_vfprintf( const char *fmt, va_list list );
  121. virtual int FS_ferror();
  122. virtual int FS_fflush();
  123. virtual char *FS_fgets( char *dest, int destSize );
  124. #ifdef POSIX
  125. static CUtlMap< ino_t, CThreadMutex * > m_LockedFDMap;
  126. static CThreadMutex m_MutexLockedFD;
  127. #endif
  128. private:
  129. CStdioFile( FILE *pFile, bool bWriteable )
  130. : m_pFile( pFile ), m_bWriteable( bWriteable )
  131. {
  132. }
  133. FILE *m_pFile;
  134. bool m_bWriteable;
  135. };
  136. #ifdef POSIX
  137. CUtlMap< ino_t, CThreadMutex * > CStdioFile::m_LockedFDMap;
  138. CThreadMutex CStdioFile::m_MutexLockedFD;
  139. #endif
  140. //-----------------------------------------------------------------------------
  141. #ifdef _WIN32
  142. class CWin32ReadOnlyFile : public CStdFilesystemFile
  143. {
  144. public:
  145. static bool CanOpen( const char *filename, const char *options );
  146. static CWin32ReadOnlyFile *FS_fopen( const char *filename, const char *options, int64 *size );
  147. virtual void FS_setbufsize( unsigned nBytes ) {}
  148. virtual void FS_fclose();
  149. virtual void FS_fseek( int64 pos, int seekType );
  150. virtual long FS_ftell();
  151. virtual int FS_feof();
  152. virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
  153. virtual size_t FS_fwrite( const void *src, size_t size ) { return 0; }
  154. virtual bool FS_setmode( FileMode_t mode ) { Error( "Can't set mode, open a second file in right mode\n" ); return false; }
  155. virtual size_t FS_vfprintf( const char *fmt, va_list list ) { return 0; }
  156. virtual int FS_ferror() { return 0; }
  157. virtual int FS_fflush() { return 0; }
  158. virtual char *FS_fgets( char *dest, int destSize );
  159. virtual int FS_GetSectorSize() { return m_SectorSize; }
  160. private:
  161. CWin32ReadOnlyFile( HANDLE hFileUnbuffered, HANDLE hFileBuffered, int sectorSize, int64 fileSize, bool bOverlapped )
  162. : m_hFileUnbuffered( hFileUnbuffered ),
  163. m_hFileBuffered( hFileBuffered ),
  164. m_ReadPos( 0 ),
  165. m_Size( fileSize ),
  166. m_SectorSize( sectorSize ),
  167. m_bOverlapped( bOverlapped )
  168. {
  169. }
  170. int64 m_ReadPos;
  171. int64 m_Size;
  172. HANDLE m_hFileUnbuffered;
  173. HANDLE m_hFileBuffered;
  174. CThreadFastMutex m_Mutex;
  175. int m_SectorSize;
  176. bool m_bOverlapped;
  177. };
  178. #endif
  179. //-----------------------------------------------------------------------------
  180. // singleton
  181. //-----------------------------------------------------------------------------
  182. CFileSystem_Stdio g_FileSystem_Stdio;
  183. #if defined(_WIN32) && defined(DEDICATED)
  184. CBaseFileSystem *BaseFileSystem_Stdio( void )
  185. {
  186. return &g_FileSystem_Stdio;
  187. }
  188. #endif
  189. #ifdef DEDICATED // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere
  190. IFileSystem *g_pFileSystem = &g_FileSystem_Stdio;
  191. IBaseFileSystem *g_pBaseFileSystem = &g_FileSystem_Stdio;
  192. #else
  193. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
  194. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
  195. #endif
  196. //-----------------------------------------------------------------------------
  197. #ifndef _RETAIL
  198. bool UseOptimalBufferAllocation()
  199. {
  200. static bool bUseOptimalBufferAllocation = ( IsX360() || ( !IsLinux() && Q_stristr( Plat_GetCommandLine(), "-unbuffered_io" ) != NULL ) );
  201. return bUseOptimalBufferAllocation;
  202. }
  203. ConVar filesystem_unbuffered_io( "filesystem_unbuffered_io", "1", 0, "" );
  204. #define UseUnbufferedIO() ( UseOptimalBufferAllocation() && filesystem_unbuffered_io.GetBool() )
  205. #else
  206. #define UseUnbufferedIO() true
  207. #endif
  208. ConVar filesystem_native( "filesystem_native", "1", 0, "Use native FS or STDIO" );
  209. ConVar filesystem_max_stdio_read( "filesystem_max_stdio_read", IsX360() ? "64" : "16", 0, "" );
  210. ConVar filesystem_report_buffered_io( "filesystem_report_buffered_io", "0" );
  211. //-----------------------------------------------------------------------------
  212. // constructor
  213. //-----------------------------------------------------------------------------
  214. CFileSystem_Stdio::CFileSystem_Stdio()
  215. {
  216. m_bMounted = false;
  217. m_bCanAsync = true;
  218. #ifdef POSIX
  219. SetDefLessFunc( CStdioFile::m_LockedFDMap );
  220. #endif
  221. }
  222. //-----------------------------------------------------------------------------
  223. // Purpose:
  224. //-----------------------------------------------------------------------------
  225. CFileSystem_Stdio::~CFileSystem_Stdio()
  226. {
  227. #ifdef POSIX
  228. FOR_EACH_MAP_FAST( CStdioFile::m_LockedFDMap, i )
  229. {
  230. Assert( CStdioFile::m_LockedFDMap[ i ] );
  231. delete CStdioFile::m_LockedFDMap[ i ];
  232. }
  233. CStdioFile::m_LockedFDMap.RemoveAll();
  234. #endif
  235. Assert(!m_bMounted);
  236. }
  237. //-----------------------------------------------------------------------------
  238. // QueryInterface:
  239. //-----------------------------------------------------------------------------
  240. void *CFileSystem_Stdio::QueryInterface( const char *pInterfaceName )
  241. {
  242. // We also implement the IMatSystemSurface interface
  243. if (!Q_strncmp( pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
  244. return (IFileSystem*)this;
  245. return CBaseFileSystem::QueryInterface( pInterfaceName );
  246. }
  247. //-----------------------------------------------------------------------------
  248. //-----------------------------------------------------------------------------
  249. bool CFileSystem_Stdio::GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign )
  250. {
  251. unsigned sectorSize;
  252. if ( hFile && UseOptimalBufferAllocation() )
  253. {
  254. CFileHandle *fh = ( CFileHandle *)hFile;
  255. sectorSize = fh->GetSectorSize();
  256. if ( !sectorSize || ( fh->m_pPackFileHandle && ( fh->m_pPackFileHandle->AbsoluteBaseOffset() % sectorSize ) ) )
  257. {
  258. sectorSize = 1;
  259. }
  260. }
  261. else
  262. {
  263. sectorSize = 1;
  264. }
  265. if ( pOffsetAlign )
  266. {
  267. *pOffsetAlign = sectorSize;
  268. }
  269. if ( pSizeAlign )
  270. {
  271. *pSizeAlign = sectorSize;
  272. }
  273. if ( pBufferAlign )
  274. {
  275. if ( IsX360() )
  276. {
  277. *pBufferAlign = 4;
  278. }
  279. else
  280. {
  281. *pBufferAlign = sectorSize;
  282. }
  283. }
  284. return ( sectorSize > 1 );
  285. }
  286. //-----------------------------------------------------------------------------
  287. //-----------------------------------------------------------------------------
  288. void *CFileSystem_Stdio::AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset )
  289. {
  290. if ( !UseOptimalBufferAllocation() )
  291. {
  292. return CBaseFileSystem::AllocOptimalReadBuffer( hFile, nSize, nOffset );
  293. }
  294. unsigned sectorSize;
  295. if ( hFile != FILESYSTEM_INVALID_HANDLE )
  296. {
  297. CFileHandle *fh = ( CFileHandle *)hFile;
  298. sectorSize = fh->GetSectorSize();
  299. if ( !nSize )
  300. {
  301. nSize = fh->Size();
  302. }
  303. if ( fh->m_pPackFileHandle )
  304. {
  305. nOffset += fh->m_pPackFileHandle->AbsoluteBaseOffset();
  306. }
  307. }
  308. else
  309. {
  310. // an invalid handle gets a fake "optimal" but valid buffer
  311. // this path is for a caller that isn't doing i/o,
  312. // but needs an "optimal" buffer that can end up passed to FreeOptimalReadBuffer()
  313. sectorSize = 4;
  314. }
  315. bool bOffsetIsAligned = ( nOffset % sectorSize == 0 );
  316. unsigned nAllocSize = ( bOffsetIsAligned ) ? AlignValue( nSize, sectorSize ) : nSize;
  317. if ( IsX360() )
  318. {
  319. return malloc( nAllocSize );
  320. }
  321. else
  322. {
  323. unsigned nAllocAlignment = ( bOffsetIsAligned ) ? sectorSize : 4;
  324. return _aligned_malloc( nAllocSize, nAllocAlignment );
  325. }
  326. }
  327. //-----------------------------------------------------------------------------
  328. //-----------------------------------------------------------------------------
  329. void CFileSystem_Stdio::FreeOptimalReadBuffer( void *p )
  330. {
  331. if ( !UseOptimalBufferAllocation() )
  332. {
  333. CBaseFileSystem::FreeOptimalReadBuffer( p );
  334. return;
  335. }
  336. if ( p )
  337. {
  338. if ( IsX360() )
  339. {
  340. free( p );
  341. }
  342. else
  343. {
  344. _aligned_free( p );
  345. }
  346. }
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose: low-level filesystem wrapper
  350. //-----------------------------------------------------------------------------
  351. FILE *CFileSystem_Stdio::FS_fopen( const char *filenameT, const char *options, unsigned flags, int64 *size )
  352. {
  353. CStdFilesystemFile *pFile = NULL;
  354. char filename[ MAX_PATH ];
  355. CBaseFileSystem::FixUpPath ( filenameT, filename, sizeof( filename ) );
  356. #ifdef _WIN32
  357. if ( CWin32ReadOnlyFile::CanOpen( filename, options ) )
  358. {
  359. pFile = CWin32ReadOnlyFile::FS_fopen( filename, options, size );
  360. if ( pFile )
  361. {
  362. return (FILE *)pFile;
  363. }
  364. }
  365. #endif
  366. pFile = CStdioFile::FS_fopen( filename, options, size );
  367. return (FILE *)pFile;
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose: low-level filesystem wrapper
  371. //-----------------------------------------------------------------------------
  372. void CFileSystem_Stdio::FS_setbufsize( FILE *fp, unsigned nBytes )
  373. {
  374. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  375. pFile->FS_setbufsize( nBytes );
  376. }
  377. //-----------------------------------------------------------------------------
  378. // Purpose: low-level filesystem wrapper
  379. //-----------------------------------------------------------------------------
  380. void CFileSystem_Stdio::FS_fclose( FILE *fp )
  381. {
  382. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  383. pFile->FS_fclose();
  384. delete pFile;
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose: low-level filesystem wrapper
  388. //-----------------------------------------------------------------------------
  389. void CFileSystem_Stdio::FS_fseek( FILE *fp, int64 pos, int seekType )
  390. {
  391. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  392. pFile->FS_fseek( pos, seekType );
  393. }
  394. //-----------------------------------------------------------------------------
  395. // Purpose: low-level filesystem wrapper
  396. //-----------------------------------------------------------------------------
  397. long CFileSystem_Stdio::FS_ftell( FILE *fp )
  398. {
  399. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  400. return pFile->FS_ftell();
  401. }
  402. //-----------------------------------------------------------------------------
  403. // Purpose: low-level filesystem wrapper
  404. //-----------------------------------------------------------------------------
  405. int CFileSystem_Stdio::FS_feof( FILE *fp )
  406. {
  407. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  408. return pFile->FS_feof();
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Purpose: low-level filesystem wrapper
  412. //-----------------------------------------------------------------------------
  413. size_t CFileSystem_Stdio::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
  414. {
  415. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  416. if( ThreadInMainThread() )
  417. {
  418. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
  419. }
  420. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  421. size_t nBytesRead = pFile->FS_fread( dest, destSize, size);
  422. Trace_FRead( nBytesRead, fp );
  423. return nBytesRead;
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose: low-level filesystem wrapper
  427. //-----------------------------------------------------------------------------
  428. size_t CFileSystem_Stdio::FS_fwrite( const void *src, size_t size, FILE *fp )
  429. {
  430. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
  431. if( ThreadInMainThread() )
  432. {
  433. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesWrite" );
  434. }
  435. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  436. size_t nBytesWritten = pFile->FS_fwrite(src, size);
  437. return nBytesWritten;
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose: low-level filesystem wrapper
  441. //-----------------------------------------------------------------------------
  442. bool CFileSystem_Stdio::FS_setmode( FILE *fp, FileMode_t mode )
  443. {
  444. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  445. return pFile->FS_setmode( mode );
  446. }
  447. //-----------------------------------------------------------------------------
  448. // Purpose: low-level filesystem wrapper
  449. //-----------------------------------------------------------------------------
  450. size_t CFileSystem_Stdio::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
  451. {
  452. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  453. return pFile->FS_vfprintf(fmt, list);
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Purpose: low-level filesystem wrapper
  457. //-----------------------------------------------------------------------------
  458. int CFileSystem_Stdio::FS_ferror( FILE *fp )
  459. {
  460. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  461. return pFile->FS_ferror();
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Purpose: low-level filesystem wrapper
  465. //-----------------------------------------------------------------------------
  466. int CFileSystem_Stdio::FS_fflush( FILE *fp )
  467. {
  468. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  469. return pFile->FS_fflush();
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose: low-level filesystem wrapper
  473. //-----------------------------------------------------------------------------
  474. char *CFileSystem_Stdio::FS_fgets( char *dest, int destSize, FILE *fp )
  475. {
  476. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  477. return pFile->FS_fgets(dest, destSize);
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose:
  481. // Input : *path -
  482. // pmode -
  483. // Output : int
  484. //-----------------------------------------------------------------------------
  485. int CFileSystem_Stdio::FS_chmod( const char *pathT, int pmode )
  486. {
  487. if ( !pathT )
  488. return -1;
  489. char path[ MAX_PATH ];
  490. CBaseFileSystem::FixUpPath ( pathT, path, sizeof( path ) );
  491. int rt = _chmod( path, pmode );
  492. #if defined(LINUX)
  493. if (rt==-1)
  494. {
  495. char caseFixedName[ MAX_PATH ];
  496. const bool found = findFileInDirCaseInsensitive_safe( path, caseFixedName );
  497. if ( found )
  498. {
  499. rt=_chmod( caseFixedName, pmode );
  500. }
  501. }
  502. #endif
  503. return rt;
  504. }
  505. //-----------------------------------------------------------------------------
  506. // Purpose: A replacement for _stat() backed by GetFileAttributesEx for XP users
  507. //
  508. // Workaround for:
  509. // https://connect.microsoft.com/VisualStudio/feedback/details/1600505/stat-not-working-on-windows-xp-using-v14-xp-platform-toolset-vs2015
  510. //
  511. // This is not well tested or meant to be a proper implementation of stat(), but rather a band-aid for XP users only
  512. // until microsoft pushes a runtime update to fix above issue :-/
  513. //-----------------------------------------------------------------------------
  514. #if defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
  515. static int WindowsXPStatShim( const char *pathT, struct _stat *buf )
  516. {
  517. WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
  518. if ( !GetFileAttributesEx(pathT, GetFileExInfoStandard, &fileAttributes) )
  519. {
  520. *_errno() = ENOENT;
  521. return -1;
  522. }
  523. memset( buf, 0, sizeof(struct _stat) );
  524. // Mode
  525. unsigned short permBits = _S_IREAD; // If GetFileAttributes let us see it, we can read it. I think.
  526. if ( fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  527. {
  528. buf->st_mode |= _S_IFDIR;
  529. permBits |= _S_IEXEC;
  530. }
  531. else
  532. {
  533. buf->st_mode |= _S_IFREG;
  534. const char *pExt = V_GetFileExtension( pathT );
  535. if ( V_strcasecmp( pExt, "exe" ) == 0 ||
  536. V_strcasecmp( pExt, "bat" ) == 0 ||
  537. V_strcasecmp( pExt, "com" ) == 0 ||
  538. V_strcasecmp( pExt, "cmd" ) == 0 )
  539. {
  540. // Windows stat seems to set this flag for these extensions
  541. permBits |= _S_IEXEC;
  542. }
  543. }
  544. if ( fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY )
  545. {
  546. permBits |= S_IWRITE;
  547. }
  548. // Duplicate permission bits to user/group/world (windows stat doesn't care)
  549. buf->st_mode |= permBits | permBits >> 3 | permBits >> 6;
  550. // Device is just drive-letter-index according to msdn
  551. char driveLetter = tolower( pathT[ 0 ] );
  552. if ( driveLetter >= 'a' && driveLetter <= 'z' && pathT[1] == ':' )
  553. {
  554. unsigned char driveIdx = driveLetter - 'a';
  555. buf->st_dev = driveIdx;
  556. buf->st_rdev = driveIdx;
  557. }
  558. else
  559. {
  560. buf->st_dev = _getdrive();
  561. buf->st_rdev = _getdrive();
  562. }
  563. buf->st_nlink = 1;
  564. buf->st_size = (uint64_t)fileAttributes.nFileSizeHigh << 32 | fileAttributes.nFileSizeLow;
  565. // The 90s was a hell of a time I guess.
  566. uint64_t actualAccessTime = (uint64_t)fileAttributes.ftLastAccessTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftLastAccessTime.dwLowDateTime;
  567. uint64_t actualModTime = (uint64_t)fileAttributes.ftLastWriteTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftLastWriteTime.dwLowDateTime;
  568. uint64_t actualCreationTime = (uint64_t)fileAttributes.ftCreationTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftCreationTime.dwLowDateTime;
  569. uint64_t ullMSUniverseToEveryoneElseUniverse = (369 * 365 + 89) * 86400ull; // okay
  570. buf->st_atime = actualAccessTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
  571. buf->st_mtime = actualModTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
  572. buf->st_ctime = actualCreationTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
  573. // st_uid/st_gid always 0 on windows
  574. return 0;
  575. }
  576. #endif // defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
  577. //-----------------------------------------------------------------------------
  578. // Purpose: low-level filesystem wrapper
  579. //-----------------------------------------------------------------------------
  580. int CFileSystem_Stdio::FS_stat( const char *pathT, struct _stat *buf, bool *pbLoadedFromSteamCache )
  581. {
  582. if ( pbLoadedFromSteamCache )
  583. *pbLoadedFromSteamCache = false;
  584. if ( !pathT )
  585. {
  586. return -1;
  587. }
  588. char path[ MAX_PATH ];
  589. CBaseFileSystem::FixUpPath ( pathT, path, sizeof( path ) );
  590. int rt = _stat( path, buf );
  591. // Workaround bug wherein stat() randomly fails on Windows XP. See comment on function.
  592. #if defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
  593. if ( rt == -1 )
  594. {
  595. EOSType eOSType = GetOSType();
  596. if ( eOSType == k_eWin2000 || eOSType == k_eWinXP || eOSType == k_eWin2003 )
  597. {
  598. rt = WindowsXPStatShim( path, buf );
  599. }
  600. }
  601. #endif // defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
  602. #if defined(LINUX)
  603. if ( rt == -1 )
  604. {
  605. char caseFixedName[ MAX_PATH ];
  606. bool found = findFileInDirCaseInsensitive_safe( path, caseFixedName );
  607. if ( found )
  608. {
  609. rt = _stat( caseFixedName, buf );
  610. }
  611. }
  612. #endif
  613. return rt;
  614. }
  615. //-----------------------------------------------------------------------------
  616. // Purpose: low-level filesystem wrapper
  617. //-----------------------------------------------------------------------------
  618. HANDLE CFileSystem_Stdio::FS_FindFirstFile(const char *findnameT, WIN32_FIND_DATA *dat)
  619. {
  620. char findname[ MAX_PATH ];
  621. CBaseFileSystem::FixUpPath ( findnameT, findname, sizeof( findname ) );
  622. return ::FindFirstFile(findname, dat);
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Purpose: low-level filesystem wrapper
  626. //-----------------------------------------------------------------------------
  627. bool CFileSystem_Stdio::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
  628. {
  629. if (INVALID_HANDLE_VALUE == handle) // invalid handle should return false
  630. return false;
  631. return (::FindNextFile(handle, dat) != 0);
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose: low-level filesystem wrapper
  635. //-----------------------------------------------------------------------------
  636. bool CFileSystem_Stdio::FS_FindClose(HANDLE handle)
  637. {
  638. return (::FindClose(handle) != 0);
  639. }
  640. //-----------------------------------------------------------------------------
  641. //
  642. //-----------------------------------------------------------------------------
  643. int CFileSystem_Stdio::FS_GetSectorSize( FILE *fp )
  644. {
  645. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  646. return pFile->FS_GetSectorSize();
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Purpose: files are always immediately available on disk
  650. //-----------------------------------------------------------------------------
  651. bool CFileSystem_Stdio::IsFileImmediatelyAvailable(const char *pFileName)
  652. {
  653. return true;
  654. }
  655. // enable this if you want the stdio filesystem to pretend it's steam, and make people wait for resources
  656. //#define DEBUG_WAIT_FOR_RESOURCES_API
  657. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  658. static float g_flDebugProgress = 0.0f;
  659. #endif
  660. //-----------------------------------------------------------------------------
  661. // Purpose: steam call, unnecessary in stdio
  662. //-----------------------------------------------------------------------------
  663. WaitForResourcesHandle_t CFileSystem_Stdio::WaitForResources( const char *resourcelist )
  664. {
  665. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  666. g_flDebugProgress = 0.0f;
  667. #endif
  668. return 1;
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose: steam call, unnecessary in stdio
  672. //-----------------------------------------------------------------------------
  673. bool CFileSystem_Stdio::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
  674. {
  675. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  676. g_flDebugProgress += 0.002f;
  677. if (g_flDebugProgress < 1.0f)
  678. {
  679. *progress = g_flDebugProgress;
  680. *complete = false;
  681. return true;
  682. }
  683. #endif
  684. // always return that we're complete
  685. *progress = 0.0f;
  686. *complete = true;
  687. return true;
  688. }
  689. //-----------------------------------------------------------------------------
  690. // Purpose: steam call, unnecessary in stdio
  691. //-----------------------------------------------------------------------------
  692. void CFileSystem_Stdio::CancelWaitForResources( WaitForResourcesHandle_t handle )
  693. {
  694. }
  695. //-----------------------------------------------------------------------------
  696. // Purpose:
  697. //-----------------------------------------------------------------------------
  698. void CFileSystem_Stdio::GetLocalCopy( const char *pFileName )
  699. {
  700. // do nothing. . everything is local.
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose:
  704. //-----------------------------------------------------------------------------
  705. int CFileSystem_Stdio::HintResourceNeed( const char *hintlist, int forgetEverything )
  706. {
  707. // do nothing. . everything is local.
  708. return 0;
  709. }
  710. //-----------------------------------------------------------------------------
  711. // Purpose: low-level filesystem wrapper
  712. //-----------------------------------------------------------------------------
  713. CStdioFile *CStdioFile::FS_fopen( const char *filenameT, const char *options, int64 *size )
  714. {
  715. FILE *pFile = NULL;
  716. char *p = NULL;
  717. char filename[MAX_PATH];
  718. struct _stat buf;
  719. V_strncpy( filename, filenameT, sizeof(filename) );
  720. // stop newline characters at end of filename
  721. p = strchr( filename, '\n' );
  722. if ( p )
  723. *p = '\0';
  724. p = strchr( filename, '\r' );
  725. if ( p )
  726. *p = '\0';
  727. pFile = fopen(filename, options);
  728. if (pFile && size)
  729. {
  730. // todo: replace with filelength()?
  731. int rt = _stat( filename, &buf );
  732. if (rt == 0)
  733. {
  734. *size = buf.st_size;
  735. }
  736. }
  737. #if defined(LINUX)
  738. if(!pFile && !strchr(options,'w') && !strchr(options,'+') ) // try opening the lower cased version
  739. {
  740. char caseFixedName[ MAX_PATH ];
  741. bool found = findFileInDirCaseInsensitive_safe( filename, caseFixedName );
  742. if ( found )
  743. {
  744. pFile = fopen( caseFixedName, options );
  745. if (pFile && size)
  746. {
  747. // todo: replace with filelength()?
  748. struct _stat buf;
  749. int rt = _stat( caseFixedName, &buf );
  750. if (rt == 0)
  751. {
  752. *size = buf.st_size;
  753. }
  754. }
  755. }
  756. }
  757. #endif
  758. if ( pFile )
  759. {
  760. bool bWriteable = false;
  761. if ( strchr(options,'w') || strchr(options,'a') )
  762. bWriteable = true;
  763. #if defined POSIX
  764. if ( bWriteable )
  765. {
  766. CThreadMutex *pMutex = NULL;
  767. {
  768. AUTO_LOCK( m_MutexLockedFD );
  769. // 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)
  770. // so add a lock here to mimic that behavior
  771. int iLockID = m_LockedFDMap.Find( buf.st_ino );
  772. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  773. {
  774. pMutex = m_LockedFDMap[iLockID];
  775. }
  776. else
  777. {
  778. CThreadMutex *newMutex = new CThreadMutex;
  779. pMutex = m_LockedFDMap[m_LockedFDMap.Insert( buf.st_ino, newMutex )];
  780. }
  781. }
  782. // grab the lock once we have UNLOCKED m_MutexLockedFD so we don't deadlock on a close
  783. pMutex->Lock();
  784. rewind( pFile );
  785. // we need to get the file size again after the lock returns
  786. if (pFile && size)
  787. {
  788. int rt = _stat( filename, &buf );
  789. if (rt == 0)
  790. {
  791. *size = buf.st_size;
  792. }
  793. }
  794. }
  795. #endif
  796. return new CStdioFile( pFile, bWriteable );
  797. }
  798. return NULL;
  799. }
  800. //-----------------------------------------------------------------------------
  801. // Purpose: low-level filesystem wrapper
  802. //-----------------------------------------------------------------------------
  803. void CStdioFile::FS_setbufsize( unsigned nBytes )
  804. {
  805. #ifdef _WIN32
  806. if ( nBytes )
  807. {
  808. setvbuf( m_pFile, NULL, _IOFBF, 32768 );
  809. }
  810. else
  811. {
  812. setvbuf( m_pFile, NULL, _IONBF, 0 );
  813. #if defined(_MSC_VER) && ( _MSC_VER < 1900 )
  814. // hack to make microsoft stdio not always read one stray byte on odd sized files
  815. m_pFile->_bufsiz = 1;
  816. #endif
  817. }
  818. #endif
  819. }
  820. //-----------------------------------------------------------------------------
  821. // Purpose: low-level filesystem wrapper
  822. //-----------------------------------------------------------------------------
  823. void CStdioFile::FS_fclose()
  824. {
  825. #ifdef POSIX
  826. if ( m_bWriteable )
  827. {
  828. AUTO_LOCK( m_MutexLockedFD );
  829. struct _stat buf;
  830. int fd = fileno_unlocked( m_pFile );
  831. fstat( fd, &buf );
  832. fflush( m_pFile );
  833. int iLockID = m_LockedFDMap.Find( buf.st_ino );
  834. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  835. {
  836. m_LockedFDMap[iLockID]->Unlock();
  837. }
  838. }
  839. #endif
  840. fclose(m_pFile);
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose: low-level filesystem wrapper
  844. //-----------------------------------------------------------------------------
  845. void CStdioFile::FS_fseek( int64 pos, int seekType )
  846. {
  847. fseek( m_pFile, pos, seekType );
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose: low-level filesystem wrapper
  851. //-----------------------------------------------------------------------------
  852. long CStdioFile::FS_ftell()
  853. {
  854. return ftell(m_pFile);
  855. }
  856. //-----------------------------------------------------------------------------
  857. // Purpose: low-level filesystem wrapper
  858. //-----------------------------------------------------------------------------
  859. int CStdioFile::FS_feof()
  860. {
  861. return feof(m_pFile);
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Purpose: low-level filesystem wrapper
  865. //-----------------------------------------------------------------------------
  866. size_t CStdioFile::FS_fread( void *dest, size_t destSize, size_t size )
  867. {
  868. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
  869. if( ThreadInMainThread() )
  870. {
  871. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
  872. }
  873. // read (size) of bytes to ensure truncated reads returns bytes read and not 0
  874. return fread( dest, 1, size, m_pFile );
  875. }
  876. #define WRITE_CHUNK (256 * 1024)
  877. //-----------------------------------------------------------------------------
  878. // Purpose: low-level filesystem wrapper
  879. //
  880. // This routine breaks data into chunks if the amount to be written is beyond WRITE_CHUNK (256kb)
  881. // Windows can fail on monolithic writes of ~12MB or more, so we work around that here
  882. //-----------------------------------------------------------------------------
  883. size_t CStdioFile::FS_fwrite( const void *src, size_t size )
  884. {
  885. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
  886. if( ThreadInMainThread() )
  887. {
  888. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesWrite" );
  889. }
  890. if ( size > WRITE_CHUNK )
  891. {
  892. size_t remaining = size;
  893. const byte* current = (const byte *) src;
  894. size_t total = 0;
  895. while ( remaining > 0 )
  896. {
  897. size_t bytesToCopy = min(remaining, (size_t)WRITE_CHUNK);
  898. total += fwrite(current, 1, bytesToCopy, m_pFile);
  899. remaining -= bytesToCopy;
  900. current += bytesToCopy;
  901. }
  902. Assert( total == size );
  903. return total;
  904. }
  905. return fwrite(src, 1, size, m_pFile);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)
  906. }
  907. //-----------------------------------------------------------------------------
  908. // Purpose: low-level filesystem wrapper
  909. //-----------------------------------------------------------------------------
  910. bool CStdioFile::FS_setmode( FileMode_t mode )
  911. {
  912. #ifdef _WIN32
  913. int fd = _fileno( m_pFile );
  914. int newMode = ( mode == FM_BINARY ) ? _O_BINARY : _O_TEXT;
  915. return ( _setmode( fd, newMode) != -1 );
  916. #else
  917. return false;
  918. #endif
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose: low-level filesystem wrapper
  922. //-----------------------------------------------------------------------------
  923. size_t CStdioFile::FS_vfprintf( const char *fmt, va_list list )
  924. {
  925. return vfprintf(m_pFile, fmt, list);
  926. }
  927. //-----------------------------------------------------------------------------
  928. // Purpose: low-level filesystem wrapper
  929. //-----------------------------------------------------------------------------
  930. int CStdioFile::FS_ferror()
  931. {
  932. return ferror(m_pFile);
  933. }
  934. //-----------------------------------------------------------------------------
  935. // Purpose: low-level filesystem wrapper
  936. //-----------------------------------------------------------------------------
  937. int CStdioFile::FS_fflush()
  938. {
  939. return fflush(m_pFile);
  940. }
  941. //-----------------------------------------------------------------------------
  942. // Purpose: low-level filesystem wrapper
  943. //-----------------------------------------------------------------------------
  944. char *CStdioFile::FS_fgets( char *dest, int destSize )
  945. {
  946. return fgets(dest, destSize, m_pFile);
  947. }
  948. //-----------------------------------------------------------------------------
  949. //
  950. //-----------------------------------------------------------------------------
  951. #ifdef _WIN32
  952. ConVar filesystem_use_overlapped_io( "filesystem_use_overlapped_io", "1", 0, "" );
  953. #define UseOverlappedIO() filesystem_use_overlapped_io.GetBool()
  954. //-----------------------------------------------------------------------------
  955. //
  956. //-----------------------------------------------------------------------------
  957. int GetSectorSize( const char *pszFilename )
  958. {
  959. if ( ( !pszFilename[0] || !pszFilename[1] ) ||
  960. ( pszFilename[0] == '\\' && pszFilename[1] == '\\' ) ||
  961. ( pszFilename[0] == '/' && pszFilename[1] == '/' ) )
  962. {
  963. // Cannot determine sector size with a UNC path (need volume identifier)
  964. return 0;
  965. }
  966. if ( IsX360() )
  967. {
  968. // purposely dvd centric, which is also the worst case
  969. return XBOX_DVD_SECTORSIZE;
  970. }
  971. #if defined( _WIN32 ) && !defined( FILESYSTEM_STEAM ) && !defined( _X360 )
  972. char szAbsoluteFilename[MAX_FILEPATH];
  973. if ( pszFilename[1] != ':' )
  974. {
  975. Q_MakeAbsolutePath( szAbsoluteFilename, sizeof(szAbsoluteFilename), pszFilename );
  976. pszFilename = szAbsoluteFilename;
  977. }
  978. DWORD sectorSize = 1;
  979. struct DriveSectorSize_t
  980. {
  981. char volume;
  982. DWORD sectorSize;
  983. };
  984. static DriveSectorSize_t cachedSizes[4];
  985. char volume = tolower( *pszFilename );
  986. int i;
  987. for ( i = 0; i < ARRAYSIZE(cachedSizes) && cachedSizes[i].volume; i++ )
  988. {
  989. if ( cachedSizes[i].volume == volume )
  990. {
  991. sectorSize = cachedSizes[i].sectorSize;
  992. break;
  993. }
  994. }
  995. if ( sectorSize == 1 )
  996. {
  997. char root[4] = "X:\\";
  998. root[0] = *pszFilename;
  999. DWORD ignored;
  1000. if ( !GetDiskFreeSpace( root, &ignored, &sectorSize, &ignored, &ignored ) )
  1001. {
  1002. sectorSize = 0;
  1003. }
  1004. if ( i < ARRAYSIZE(cachedSizes) )
  1005. {
  1006. cachedSizes[i].volume = volume;
  1007. cachedSizes[i].sectorSize = sectorSize;
  1008. }
  1009. }
  1010. return sectorSize;
  1011. #else
  1012. return 0;
  1013. #endif
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. //
  1017. //-----------------------------------------------------------------------------
  1018. class CThreadIOEventPool
  1019. {
  1020. public:
  1021. ~CThreadIOEventPool()
  1022. {
  1023. CThreadEvent *pEvent;
  1024. while ( m_Events.PopItem( &pEvent ) )
  1025. {
  1026. delete pEvent;
  1027. }
  1028. }
  1029. CThreadEvent *GetEvent()
  1030. {
  1031. CThreadEvent *pEvent;
  1032. if ( m_Events.PopItem( &pEvent ) )
  1033. {
  1034. return pEvent;
  1035. }
  1036. return new CThreadEvent;
  1037. }
  1038. void ReleaseEvent( CThreadEvent *pEvent )
  1039. {
  1040. m_Events.PushItem( pEvent );
  1041. }
  1042. private:
  1043. CTSList<CThreadEvent *> m_Events;
  1044. };
  1045. CThreadIOEventPool g_ThreadIOEvents;
  1046. //-----------------------------------------------------------------------------
  1047. //
  1048. //-----------------------------------------------------------------------------
  1049. bool CWin32ReadOnlyFile::CanOpen( const char *filename, const char *options )
  1050. {
  1051. return ( options[0] == 'r' && options[1] == 'b' && options[2] == 0 && filesystem_native.GetBool() );
  1052. }
  1053. //-----------------------------------------------------------------------------
  1054. //
  1055. //-----------------------------------------------------------------------------
  1056. static HANDLE OpenWin32File( const char *filename, bool bOverlapped, bool bUnbuffered, int64 *pFileSize )
  1057. {
  1058. HANDLE hFile;
  1059. DWORD createFlags = FILE_ATTRIBUTE_NORMAL;
  1060. if ( bOverlapped )
  1061. {
  1062. createFlags |= FILE_FLAG_OVERLAPPED;
  1063. }
  1064. if ( bUnbuffered )
  1065. {
  1066. createFlags |= FILE_FLAG_NO_BUFFERING;
  1067. }
  1068. hFile = ::CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, createFlags, NULL );
  1069. if ( hFile != INVALID_HANDLE_VALUE && !*pFileSize )
  1070. {
  1071. LARGE_INTEGER fileSize;
  1072. if ( !GetFileSizeEx( hFile, &fileSize ) )
  1073. {
  1074. CloseHandle( hFile );
  1075. hFile = INVALID_HANDLE_VALUE;
  1076. }
  1077. *pFileSize = fileSize.QuadPart;
  1078. }
  1079. return hFile;
  1080. }
  1081. CWin32ReadOnlyFile *CWin32ReadOnlyFile::FS_fopen( const char *filename, const char *options, int64 *size )
  1082. {
  1083. Assert( CanOpen( filename, options ) );
  1084. int sectorSize = 0;
  1085. bool bTryUnbuffered = ( UseUnbufferedIO() && ( sectorSize = GetSectorSize( filename ) ) != 0 );
  1086. bool bOverlapped = UseOverlappedIO();
  1087. HANDLE hFileUnbuffered = INVALID_HANDLE_VALUE;
  1088. int64 fileSize = 0;
  1089. if ( bTryUnbuffered )
  1090. {
  1091. hFileUnbuffered = OpenWin32File( filename, bOverlapped, true, &fileSize );
  1092. if ( hFileUnbuffered == INVALID_HANDLE_VALUE )
  1093. {
  1094. return NULL;
  1095. }
  1096. }
  1097. HANDLE hFileBuffered = OpenWin32File( filename, bOverlapped, false, &fileSize );
  1098. if ( hFileBuffered == INVALID_HANDLE_VALUE )
  1099. {
  1100. if ( hFileUnbuffered != INVALID_HANDLE_VALUE )
  1101. {
  1102. CloseHandle( hFileUnbuffered );
  1103. }
  1104. return NULL;
  1105. }
  1106. if ( size )
  1107. {
  1108. *size = fileSize;
  1109. }
  1110. return new CWin32ReadOnlyFile( hFileUnbuffered, hFileBuffered, ( sectorSize ) ? sectorSize : 1, fileSize, bOverlapped );
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose: low-level filesystem wrapper
  1114. //-----------------------------------------------------------------------------
  1115. void CWin32ReadOnlyFile::FS_fclose()
  1116. {
  1117. if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
  1118. {
  1119. CloseHandle( m_hFileUnbuffered );
  1120. }
  1121. if ( m_hFileBuffered != INVALID_HANDLE_VALUE )
  1122. {
  1123. CloseHandle( m_hFileBuffered );
  1124. }
  1125. }
  1126. //-----------------------------------------------------------------------------
  1127. // Purpose: low-level filesystem wrapper
  1128. //-----------------------------------------------------------------------------
  1129. void CWin32ReadOnlyFile::FS_fseek( int64 pos, int seekType )
  1130. {
  1131. switch ( seekType )
  1132. {
  1133. case SEEK_SET:
  1134. m_ReadPos = pos;
  1135. break;
  1136. case SEEK_CUR:
  1137. m_ReadPos += pos;
  1138. break;
  1139. case SEEK_END:
  1140. m_ReadPos = m_Size - pos;
  1141. break;
  1142. }
  1143. }
  1144. //-----------------------------------------------------------------------------
  1145. // Purpose: low-level filesystem wrapper
  1146. //-----------------------------------------------------------------------------
  1147. long CWin32ReadOnlyFile::FS_ftell()
  1148. {
  1149. return m_ReadPos;
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. // Purpose: low-level filesystem wrapper
  1153. //-----------------------------------------------------------------------------
  1154. int CWin32ReadOnlyFile::FS_feof()
  1155. {
  1156. return ( m_ReadPos >= m_Size );
  1157. }
  1158. // ends up on a thread's stack, don't blindly increase without awareness of that implication
  1159. // 360 threads have small stacks, using small buffer of the worst case quantum sector size
  1160. #if !defined( _X360 )
  1161. #define READ_TEMP_BUFFER ( 32*1024 )
  1162. #else
  1163. #define READ_TEMP_BUFFER ( 2*XBOX_DVD_SECTORSIZE )
  1164. #endif
  1165. //-----------------------------------------------------------------------------
  1166. // Purpose: low-level filesystem wrapper
  1167. //-----------------------------------------------------------------------------
  1168. size_t CWin32ReadOnlyFile::FS_fread( void *dest, size_t destSize, size_t size )
  1169. {
  1170. VPROF_BUDGET( "CWin32ReadOnlyFile::FS_fread", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  1171. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
  1172. if( ThreadInMainThread() )
  1173. {
  1174. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
  1175. }
  1176. if ( !size || ( m_hFileUnbuffered == INVALID_HANDLE_VALUE && m_hFileBuffered == INVALID_HANDLE_VALUE ) )
  1177. {
  1178. return 0;
  1179. }
  1180. CThreadEvent *pEvent = NULL;
  1181. if ( destSize == (size_t)-1 )
  1182. {
  1183. destSize = size;
  1184. }
  1185. byte tempBuffer[READ_TEMP_BUFFER];
  1186. HANDLE hReadFile = m_hFileBuffered;
  1187. int nBytesToRead = size;
  1188. byte *pDest = (byte *)dest;
  1189. int64 offset = m_ReadPos;
  1190. if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
  1191. {
  1192. const int destBaseAlign = ( IsX360() ) ? 4 : m_SectorSize;
  1193. bool bDestBaseIsAligned = ( (DWORD)dest % destBaseAlign == 0 );
  1194. bool bCanReadUnbufferedDirect = ( bDestBaseIsAligned && ( destSize % m_SectorSize == 0 ) && ( m_ReadPos % m_SectorSize == 0 ) );
  1195. if ( bCanReadUnbufferedDirect )
  1196. {
  1197. // fastest path, unbuffered
  1198. nBytesToRead = AlignValue( size, m_SectorSize );
  1199. hReadFile = m_hFileUnbuffered;
  1200. }
  1201. else
  1202. {
  1203. // not properly aligned, snap to alignments
  1204. // attempt to perform single unbuffered operation using stack buffer
  1205. int64 alignedOffset = AlignValue( ( m_ReadPos - m_SectorSize ) + 1, m_SectorSize );
  1206. unsigned int alignedBytesToRead = AlignValue( ( m_ReadPos - alignedOffset ) + size, m_SectorSize );
  1207. if ( alignedBytesToRead <= sizeof( tempBuffer ) - destBaseAlign )
  1208. {
  1209. // read operation can be performed as unbuffered follwed by a post fixup
  1210. nBytesToRead = alignedBytesToRead;
  1211. offset = alignedOffset;
  1212. pDest = AlignValue( tempBuffer, destBaseAlign );
  1213. hReadFile = m_hFileUnbuffered;
  1214. }
  1215. }
  1216. }
  1217. OVERLAPPED overlapped = { 0 };
  1218. if ( m_bOverlapped )
  1219. {
  1220. pEvent = g_ThreadIOEvents.GetEvent();
  1221. overlapped.hEvent = *pEvent;
  1222. }
  1223. #ifdef REPORT_BUFFERED_IO
  1224. if ( hReadFile == m_hFileBuffered && filesystem_report_buffered_io.GetBool() )
  1225. {
  1226. Msg( "Buffered Operation :(\n" );
  1227. }
  1228. #endif
  1229. // some disk drivers will fail if read is too large
  1230. static int MAX_READ = filesystem_max_stdio_read.GetInt()*1024*1024;
  1231. const int MIN_READ = 64*1024;
  1232. bool bReadOk = true;
  1233. DWORD nBytesRead = 0;
  1234. size_t result = 0;
  1235. int64 currentOffset = offset;
  1236. while ( bReadOk && nBytesToRead > 0 )
  1237. {
  1238. int nCurBytesToRead = min( nBytesToRead, MAX_READ );
  1239. DWORD nCurBytesRead = 0;
  1240. overlapped.Offset = currentOffset & 0xFFFFFFFF;
  1241. overlapped.OffsetHigh = ( currentOffset >> 32 ) & 0xFFFFFFFF;
  1242. bReadOk = ( ::ReadFile( hReadFile, pDest + nBytesRead, nCurBytesToRead, &nCurBytesRead, &overlapped ) != 0 );
  1243. if ( !bReadOk )
  1244. {
  1245. if ( m_bOverlapped && GetLastError() == ERROR_IO_PENDING )
  1246. {
  1247. bReadOk = true;
  1248. }
  1249. }
  1250. if ( bReadOk )
  1251. {
  1252. if ( !m_bOverlapped || GetOverlappedResult( hReadFile, &overlapped, &nCurBytesRead, true ) )
  1253. {
  1254. nBytesRead += nCurBytesRead;
  1255. nBytesToRead -= nCurBytesRead;
  1256. currentOffset += nCurBytesRead;
  1257. }
  1258. else
  1259. {
  1260. if ( m_bOverlapped )
  1261. {
  1262. if ( GetLastError() == ERROR_HANDLE_EOF )
  1263. {
  1264. nBytesToRead = 0; // we have hit the end of the file
  1265. }
  1266. else
  1267. {
  1268. bReadOk = false;
  1269. }
  1270. }
  1271. else
  1272. {
  1273. bReadOk = false;
  1274. }
  1275. }
  1276. if ( !m_bOverlapped && nCurBytesRead == 0 )
  1277. {
  1278. nBytesToRead = 0; // we have hit the end of the file
  1279. }
  1280. }
  1281. if ( nBytesToRead > 0 && nCurBytesRead == 0 ) // if you failed to ready anything this time then bail the loop
  1282. {
  1283. DevMsg( "Got zero length read" );
  1284. bReadOk = false;
  1285. }
  1286. else if ( !bReadOk )
  1287. {
  1288. DWORD dwError = GetLastError();
  1289. if ( IsX360() )
  1290. {
  1291. if ( dwError == ERROR_DISK_CORRUPT || dwError == ERROR_FILE_CORRUPT )
  1292. {
  1293. FSDirtyDiskReportFunc_t func = g_FileSystem_Stdio.GetDirtyDiskReportFunc();
  1294. if ( func )
  1295. {
  1296. func();
  1297. result = 0;
  1298. }
  1299. }
  1300. }
  1301. if ( dwError == ERROR_NO_SYSTEM_RESOURCES && MAX_READ > MIN_READ )
  1302. {
  1303. MAX_READ /= 2;
  1304. bReadOk = true;
  1305. DevMsg( "ERROR_NO_SYSTEM_RESOURCES: Reducing max read to %d bytes\n", MAX_READ );
  1306. }
  1307. else
  1308. {
  1309. DevMsg( "Unknown read error %d\n", dwError );
  1310. }
  1311. }
  1312. }
  1313. if ( bReadOk )
  1314. {
  1315. if ( nBytesRead && hReadFile == m_hFileUnbuffered && pDest != dest )
  1316. {
  1317. int nBytesExtra = ( m_ReadPos - offset );
  1318. nBytesRead -= nBytesExtra;
  1319. if ( nBytesRead )
  1320. {
  1321. memcpy( dest, (byte *)pDest + nBytesExtra, size );
  1322. }
  1323. }
  1324. result = min( (size_t)nBytesRead, size );
  1325. }
  1326. if ( m_bOverlapped )
  1327. {
  1328. pEvent->Reset();
  1329. g_ThreadIOEvents.ReleaseEvent( pEvent );
  1330. }
  1331. m_ReadPos += result;
  1332. return result;
  1333. }
  1334. //-----------------------------------------------------------------------------
  1335. // Purpose: low-level filesystem wrapper
  1336. //-----------------------------------------------------------------------------
  1337. char *CWin32ReadOnlyFile::FS_fgets( char *dest, int destSize )
  1338. {
  1339. if ( FS_feof() )
  1340. {
  1341. return NULL;
  1342. }
  1343. int nStartPos = m_ReadPos;
  1344. int nBytesRead = FS_fread( dest, destSize, destSize );
  1345. if ( !nBytesRead )
  1346. {
  1347. return NULL;
  1348. }
  1349. dest[min( nBytesRead, destSize - 1)] = 0;
  1350. char *pNewline = strchr( dest, '\n' );
  1351. if ( pNewline )
  1352. {
  1353. // advance past, leave \n
  1354. pNewline++;
  1355. *pNewline = 0;
  1356. }
  1357. else
  1358. {
  1359. pNewline = &dest[min( nBytesRead, destSize - 1)];
  1360. }
  1361. m_ReadPos = nStartPos + ( pNewline - dest ) + 1;
  1362. return dest;
  1363. }
  1364. #endif // _WIN32