Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2125 lines
57 KiB

  1. //====== Copyright 1996-2005, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "tier0/platform.h"
  8. #ifdef _WIN32
  9. #include <io.h>
  10. #include <fcntl.h>
  11. #endif
  12. #include "basefilesystem.h"
  13. #ifndef _PS3
  14. #include "filesystemasync.h"
  15. #endif
  16. #include "tier0/dbg.h"
  17. #include "tier0/threadtools.h"
  18. #include "tier0/icommandline.h"
  19. #ifdef _WIN32
  20. #include "tier0/tslist.h"
  21. #elif defined(POSIX)
  22. #include <fcntl.h>
  23. #ifdef LINUX
  24. #include <sys/file.h>
  25. #endif
  26. #endif
  27. #include "tier1/convar.h"
  28. #include "tier0/vprof.h"
  29. #include "tier1/fmtstr.h"
  30. #include "tier1/utlrbtree.h"
  31. #define GAMEINFO_FILENAME "GAMEINFO.TXT"
  32. bool ShouldFailIo()
  33. {
  34. #if defined( _CERT ) || !defined( _PS3 )
  35. return false;
  36. #else
  37. static float s_flFailIoAfter = CommandLine()->ParmValue( "-failioafter", 0.0f );
  38. return ( s_flFailIoAfter > 0 && Plat_FloatTime() > s_flFailIoAfter );
  39. #endif
  40. }
  41. #if defined( _PS3 )
  42. #include <cell/cell_fs.h>
  43. #include <cell/sysmodule.h>
  44. #include <tier0/memalloc.h>
  45. #include <sys/process.h>
  46. #include <sys/memory.h>
  47. #include <sys/timer.h>
  48. #include <sysutil/sysutil_gamecontent.h>
  49. #include "ps3/ps3_console.h"
  50. // #include "ps3/ps3_gamedata.h"
  51. #include "tls_ps3.h"
  52. #include "ps3_pathinfo.h"
  53. #include <dirent.h>
  54. #include <cell/fios.h>
  55. #if 0 // defined( _PS3 )
  56. #include "MemMgr/inc/MemMgr.h"
  57. #include "FileGroup.h"
  58. #include "const.h"
  59. #include <sys/sys_time.h>
  60. #include "memmgr\inc\PS3VirtualAlloc.h"
  61. char gSrcGameDataPath[MAX_PATH];
  62. bool g_bUseBdvdGameData = false;
  63. extern uint g_ioThreadId;
  64. CFileGroupSystem g_fileGroupSystem;
  65. int g_levelLoadGroup = -1;
  66. #endif //_PS3
  67. #endif
  68. #ifdef _X360
  69. #undef WaitForSingleObject
  70. #endif
  71. // memdbgon must be the last include file in a .cpp file!!!
  72. #include "tier0/memdbgon.h"
  73. ASSERT_INVARIANT( SEEK_CUR == FILESYSTEM_SEEK_CURRENT );
  74. ASSERT_INVARIANT( SEEK_SET == FILESYSTEM_SEEK_HEAD );
  75. ASSERT_INVARIANT( SEEK_END == FILESYSTEM_SEEK_TAIL );
  76. #ifdef _PS3
  77. /// A bunch of little subroutines to handle all the ickyness necessary
  78. /// in emulating the FindFirstFile() function (use of which is a WTF
  79. /// in itself).
  80. namespace // unnamed namespaces are a convenient way to mark a whole bunch of stuff as "static" ie internal linkage
  81. {
  82. int scandir(const char *dir, struct dirent ***namelist,
  83. int (*select)(const struct dirent *),
  84. int (*compar)(const struct dirent **, const struct dirent **))
  85. {
  86. DIR *d;
  87. struct dirent *entry;
  88. register int i=0;
  89. size_t entrysize;
  90. if ((d=opendir(dir)) == NULL)
  91. return(-1);
  92. *namelist=NULL;
  93. while ((entry=readdir(d)) != NULL)
  94. {
  95. if (select == NULL || (select != NULL && (*select)(entry)))
  96. {
  97. *namelist=(struct dirent **)realloc((void *)(*namelist),
  98. (size_t)((i+1)*sizeof(struct dirent *)));
  99. if (*namelist == NULL) return(-1);
  100. entrysize=sizeof(struct dirent)-sizeof(entry->d_name)+strlen(entry->d_name)+1;
  101. (*namelist)[i]=(struct dirent *)malloc(entrysize);
  102. if ((*namelist)[i] == NULL) return(-1);
  103. memcpy((*namelist)[i], entry, entrysize);
  104. i++;
  105. }
  106. }
  107. if (closedir(d)) return(-1);
  108. if (i == 0) return(-1);
  109. // if (compar != NULL)
  110. // qsort((void *)(*namelist), (size_t)i, sizeof(struct dirent *), compar);
  111. return(i);
  112. }
  113. int alphasort(const struct dirent **a, const struct dirent **b)
  114. {
  115. return(strcmp((*a)->d_name, (*b)->d_name));
  116. }
  117. char selectBuf[PATH_MAX];
  118. int FileSelect(const struct dirent *ent)
  119. {
  120. const char *mask=selectBuf;
  121. const char *name=ent->d_name;
  122. //DEBUG_PRINTF("Test:%s %s\n",mask,name);
  123. if(!strcmp(name,".") || !strcmp(name,"..") ) return 0;
  124. if(!strcmp(selectBuf,"*.*")) return 1;
  125. while( *mask && *name )
  126. {
  127. if(*mask=='*')
  128. {
  129. mask++; // move to the next char in the mask
  130. if(!*mask) // if this is the end of the mask its a match
  131. {
  132. return 1;
  133. }
  134. while(*name && toupper(*name)!=toupper(*mask))
  135. { // while the two don't meet up again
  136. name++;
  137. }
  138. if(!*name)
  139. { // end of the name
  140. break;
  141. }
  142. }
  143. else if (*mask!='?')
  144. {
  145. if( toupper(*mask) != toupper(*name) )
  146. { // mismatched!
  147. return 0;
  148. }
  149. else
  150. {
  151. mask++;
  152. name++;
  153. if( !*mask && !*name)
  154. { // if its at the end of the buffer
  155. return 1;
  156. }
  157. }
  158. }
  159. else /* mask is "?", we don't care*/
  160. {
  161. mask++;
  162. name++;
  163. }
  164. }
  165. return( !*mask && !*name ); // both of the strings are at the end
  166. }
  167. int FillDataStruct(FIND_DATA *dat)
  168. {
  169. struct stat fileStat;
  170. if(dat->numMatches<0)
  171. return -1;
  172. Q_strncpy(dat->cFileName,dat->namelist[dat->numMatches]->d_name, sizeof( dat->cFileName ) );
  173. if(!stat(dat->cFileName,&fileStat))
  174. {
  175. dat->dwFileAttributes=fileStat.st_mode;
  176. }
  177. else
  178. {
  179. dat->dwFileAttributes=0;
  180. }
  181. //DEBUG_PRINTF("%s\n", dat->namelist[dat->numMatches]->d_name);
  182. free(dat->namelist[dat->numMatches]);
  183. dat->numMatches--;
  184. return 1;
  185. }
  186. const char *GetSonyFSErrorString( int errorcode )
  187. {
  188. switch( errorcode )
  189. {
  190. case CELL_FS_SUCCEEDED:
  191. return "Normal termination";
  192. case CELL_FS_ENOTMOUNTED:
  193. return "File system corresponding to pathis not mounted";
  194. case CELL_FS_ENOENT:
  195. return "File specified by path does not exist";
  196. case CELL_FS_EIO:
  197. return "I/O error has occurred";
  198. case CELL_FS_ENOMEM:
  199. return "Memory is insufficient ";
  200. case CELL_FS_ENOTDIR:
  201. return "Components in path contain something other than a directory";
  202. case CELL_FS_ENAMETOOLONG:
  203. return "path or components in the path exceed the maximum length ";
  204. case CELL_FS_EFSSPECIFIC:
  205. return "File system specific internal error has occurred";
  206. case CELL_FS_EFAULT:
  207. return "pathor sb is NULL";
  208. case CELL_FS_EACCES:
  209. return "Search permission is denied for a component of path. ";
  210. default:
  211. return "Unknown error code";
  212. }
  213. }
  214. }
  215. HANDLE FindFirstFile(char *fileName, FIND_DATA *dat)
  216. {
  217. char nameStore[PATH_MAX];
  218. char *dir=NULL;
  219. int n,iret=-1;
  220. Q_strncpy(nameStore,fileName, sizeof( nameStore ) );
  221. FixUpPathCaseForPS3(nameStore);
  222. if(strrchr(nameStore,'/') )
  223. {
  224. dir=nameStore;
  225. while(strrchr(dir,'/') )
  226. {
  227. struct stat dirChk;
  228. // zero this with the dir name
  229. dir=strrchr(nameStore,'/');
  230. *dir='\0';
  231. dir=nameStore;
  232. stat(dir,&dirChk);
  233. if( dirChk.st_mode & _S_IFDIR )
  234. {
  235. break;
  236. }
  237. }
  238. }
  239. else
  240. {
  241. // couldn't find a dir seperator...
  242. return ( void * ) INVALID_HANDLE_VALUE;
  243. }
  244. if( strlen(dir)>0 )
  245. {
  246. Q_strncpy(selectBuf,fileName+strlen(dir)+1, sizeof( selectBuf ) );
  247. n = scandir(dir, &dat->namelist, FileSelect, alphasort);
  248. if (n < 0)
  249. {
  250. // silently return, nothing interesting
  251. }
  252. else
  253. {
  254. dat->numMatches=n-1; // n is the number of matches
  255. iret=FillDataStruct(dat);
  256. if(iret<0)
  257. {
  258. free(dat->namelist);
  259. }
  260. }
  261. }
  262. return reinterpret_cast<void*>(iret);
  263. }
  264. bool FindNextFile(HANDLE handle, FIND_DATA *dat)
  265. {
  266. AssertMsg( false, "WARNING: untested\n" );
  267. if(dat->numMatches<0)
  268. {
  269. free(dat->namelist);
  270. return false; // no matches left
  271. }
  272. FillDataStruct(dat);
  273. return true;
  274. }
  275. bool FindClose(HANDLE handle)
  276. {
  277. AssertMsg( false, "WARNING: untested\n" );
  278. return true;
  279. }
  280. #endif
  281. #if 0 // defined(_PS3)
  282. #define DebugPrint(fmt, ...) Msg( fmt, ## __VA_ARGS__ )
  283. static bool ThreadInIoThread()
  284. {
  285. return( ThreadGetCurrentId() == g_ioThreadId );
  286. }
  287. #endif //_PS3
  288. #if __DARWIN_64_BIT_INO_T
  289. #error badness
  290. #endif
  291. #if _DARWIN_FEATURE_64_BIT_INODE
  292. #error additional badness
  293. #endif
  294. //-----------------------------------------------------------------------------
  295. class CFileSystem_Stdio : public CBaseFileSystem
  296. {
  297. public:
  298. CFileSystem_Stdio();
  299. ~CFileSystem_Stdio();
  300. // Used to get at older versions
  301. void *QueryInterface( const char *pInterfaceName );
  302. // Higher level filesystem methods requiring specific behavior
  303. virtual void GetLocalCopy( const char *pFileName );
  304. virtual int HintResourceNeed( const char *hintlist, int forgetEverything );
  305. virtual bool IsFileImmediatelyAvailable(const char *pFileName);
  306. virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
  307. virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
  308. virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
  309. virtual bool IsSteam() const { return false; }
  310. virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 ) { return FILESYSTEM_MOUNT_OK; }
  311. bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign );
  312. void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset );
  313. void FreeOptimalReadBuffer( void *p );
  314. protected:
  315. // implementation of CBaseFileSystem virtual functions
  316. virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo );
  317. virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
  318. virtual void FS_fclose( FILE *fp );
  319. virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
  320. virtual long FS_ftell( FILE *fp );
  321. virtual int FS_feof( FILE *fp );
  322. virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
  323. virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
  324. virtual bool FS_setmode( FILE *fp, FileMode_t mode );
  325. virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
  326. virtual int FS_ferror( FILE *fp );
  327. virtual int FS_fflush( FILE *fp );
  328. virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
  329. virtual int FS_stat( const char *path, struct _stat *buf );
  330. virtual int FS_chmod( const char *path, int pmode );
  331. virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
  332. virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
  333. virtual bool FS_FindClose(HANDLE handle);
  334. virtual int FS_GetSectorSize( FILE * );
  335. private:
  336. bool CanAsync() const
  337. {
  338. return m_bCanAsync;
  339. }
  340. bool m_bMounted;
  341. bool m_bCanAsync;
  342. };
  343. //-----------------------------------------------------------------------------
  344. // Per-file worker classes
  345. //-----------------------------------------------------------------------------
  346. abstract_class CStdFilesystemFile
  347. {
  348. public:
  349. virtual ~CStdFilesystemFile() {}
  350. virtual void FS_setbufsize( unsigned nBytes ) = 0;
  351. virtual void FS_fclose() = 0;
  352. virtual void FS_fseek( int64 pos, int seekType ) = 0;
  353. virtual long FS_ftell() = 0;
  354. virtual int FS_feof() = 0;
  355. virtual size_t FS_fread( void *dest, size_t destSize, size_t size ) = 0;
  356. virtual size_t FS_fwrite( const void *src, size_t size ) = 0;
  357. virtual bool FS_setmode( FileMode_t mode ) = 0;
  358. virtual size_t FS_vfprintf( const char *fmt, va_list list ) = 0;
  359. virtual int FS_ferror() = 0;
  360. virtual int FS_fflush() = 0;
  361. virtual char *FS_fgets( char *dest, int destSize ) = 0;
  362. virtual int FS_GetSectorSize() { return 1; }
  363. };
  364. //---------------------------------------------------------
  365. class CStdioFile : public CStdFilesystemFile
  366. {
  367. public:
  368. static CStdioFile *FS_fopen( const char *filename, const char *options, int64 *size );
  369. virtual void FS_setbufsize( unsigned nBytes );
  370. virtual void FS_fclose();
  371. virtual void FS_fseek( int64 pos, int seekType );
  372. virtual long FS_ftell();
  373. virtual int FS_feof();
  374. virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
  375. virtual size_t FS_fwrite( const void *src, size_t size );
  376. virtual bool FS_setmode( FileMode_t mode );
  377. virtual size_t FS_vfprintf( const char *fmt, va_list list );
  378. virtual int FS_ferror();
  379. virtual int FS_fflush();
  380. virtual char *FS_fgets( char *dest, int destSize );
  381. #if defined( POSIX ) && !defined( _PS3 )
  382. static CUtlMap< int, CInterlockedInt > m_LockedFDMap;
  383. #endif
  384. private:
  385. CStdioFile( FILE *pFile, bool bWriteable )
  386. : m_pFile( pFile ), m_bWriteable( bWriteable )
  387. {
  388. }
  389. FILE *m_pFile;
  390. bool m_bWriteable;
  391. };
  392. #if defined( POSIX ) && !defined( _PS3 )
  393. CUtlMap< int, CInterlockedInt > CStdioFile::m_LockedFDMap;
  394. #endif
  395. //-----------------------------------------------------------------------------
  396. #ifdef _WIN32
  397. class CWin32ReadOnlyFile : public CStdFilesystemFile
  398. {
  399. public:
  400. static bool CanOpen( const char *filename, const char *options );
  401. static CWin32ReadOnlyFile *FS_fopen( const char *filename, const char *options, int64 *size );
  402. virtual void FS_setbufsize( unsigned nBytes ) {}
  403. virtual void FS_fclose();
  404. virtual void FS_fseek( int64 pos, int seekType );
  405. virtual long FS_ftell();
  406. virtual int FS_feof();
  407. virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
  408. virtual size_t FS_fwrite( const void *src, size_t size ) { return 0; }
  409. virtual bool FS_setmode( FileMode_t mode ) { Error( "Can't set mode, open a second file in right mode\n" ); return false; }
  410. virtual size_t FS_vfprintf( const char *fmt, va_list list ) { return 0; }
  411. virtual int FS_ferror() { return 0; }
  412. virtual int FS_fflush() { return 0; }
  413. virtual char *FS_fgets( char *dest, int destSize );
  414. virtual int FS_GetSectorSize() { return m_SectorSize; }
  415. private:
  416. CWin32ReadOnlyFile( HANDLE hFileUnbuffered, HANDLE hFileBuffered, int sectorSize, int64 fileSize, bool bOverlapped )
  417. : m_hFileUnbuffered( hFileUnbuffered ),
  418. m_hFileBuffered( hFileBuffered ),
  419. m_ReadPos( 0 ),
  420. m_Size( fileSize ),
  421. m_SectorSize( sectorSize ),
  422. m_bOverlapped( bOverlapped )
  423. {
  424. }
  425. int64 m_ReadPos;
  426. int64 m_Size;
  427. HANDLE m_hFileUnbuffered;
  428. HANDLE m_hFileBuffered;
  429. CThreadFastMutex m_Mutex;
  430. int m_SectorSize;
  431. bool m_bOverlapped;
  432. };
  433. #endif
  434. #if IsPlatformPS3()
  435. class CFiosReadOnlyFile : public CStdFilesystemFile
  436. {
  437. public:
  438. static bool CanOpen( const char *filename, const char *options );
  439. static CFiosReadOnlyFile *FS_fopen( const char *filename, const char *options, int64 *size );
  440. virtual void FS_setbufsize( unsigned nBytes ) {}
  441. virtual void FS_fclose();
  442. virtual void FS_fseek( int64 pos, int seekType );
  443. virtual long FS_ftell();
  444. virtual int FS_feof();
  445. virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
  446. virtual size_t FS_fwrite( const void *src, size_t size ) { return 0; }
  447. virtual bool FS_setmode( FileMode_t mode ) { Error( "Can't set mode, open a second file in right mode\n" ); return false; }
  448. virtual size_t FS_vfprintf( const char *fmt, va_list list ) { return 0; }
  449. virtual int FS_ferror() { return 0; }
  450. virtual int FS_fflush() { return 0; }
  451. virtual char *FS_fgets( char *dest, int destSize );
  452. virtual int FS_GetSectorSize() { return 2048; }
  453. private:
  454. CFiosReadOnlyFile( cell::fios::filehandle * pFileHandle, int64 nFileSize )
  455. :
  456. m_pHandle( pFileHandle ),
  457. m_nSize( nFileSize ),
  458. m_nReadPos( 0 )
  459. {
  460. // Do nothing...
  461. }
  462. cell::fios::filehandle * m_pHandle;
  463. int64 m_nSize;
  464. int64 m_nReadPos;
  465. };
  466. #endif
  467. //-----------------------------------------------------------------------------
  468. // singleton
  469. //-----------------------------------------------------------------------------
  470. CFileSystem_Stdio g_FileSystem_Stdio;
  471. #ifndef _PS3
  472. CAsyncFileSystem g_FileSystem_Async;
  473. #endif
  474. #if defined(_WIN32) && defined(DEDICATED)
  475. CBaseFileSystem *BaseFileSystem_Stdio( void )
  476. {
  477. return &g_FileSystem_Stdio;
  478. }
  479. #endif
  480. #if defined( DEDICATED ) && defined( LAUNCHERONLY ) // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere
  481. IFileSystem *g_pFileSystem = &g_FileSystem_Stdio;
  482. IBaseFileSystem *g_pBaseFileSystem = &g_FileSystem_Stdio;
  483. #else
  484. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
  485. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
  486. #endif
  487. #ifndef _PS3
  488. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CAsyncFileSystem, IAsyncFileSystem, ASYNCFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Async );
  489. #endif // _PS3
  490. //-----------------------------------------------------------------------------
  491. bool UseOptimalBufferAllocation()
  492. {
  493. static bool bUseOptimalBufferAllocation = ( IsX360() || ( !IsPosix() && Q_stristr( Plat_GetCommandLine(), "-unbuffered_io" ) != NULL ) );
  494. return bUseOptimalBufferAllocation;
  495. }
  496. ConVar filesystem_unbuffered_io( "filesystem_unbuffered_io", "1", 0, "" );
  497. #define UseUnbufferedIO() ( UseOptimalBufferAllocation() && filesystem_unbuffered_io.GetBool() )
  498. ConVar filesystem_native( "filesystem_native", "1", 0, "Use native FS or STDIO" );
  499. ConVar filesystem_max_stdio_read( "filesystem_max_stdio_read", IsX360() ? "64" : "16", 0, "" );
  500. ConVar filesystem_report_buffered_io( "filesystem_report_buffered_io", "0" );
  501. #if IsPlatformPS3()
  502. extern bool g_bUseFiosHddCache;
  503. ConVar fs_fios_enable_hdd_cache( "fs_fios_enable_hdd_cache", "0", 0, "Use fios HDD cache, disable this to have normal BluRay speed. 1 to enable it, 0 to disable it." );
  504. #endif
  505. //-----------------------------------------------------------------------------
  506. // constructor
  507. //-----------------------------------------------------------------------------
  508. CFileSystem_Stdio::CFileSystem_Stdio()
  509. {
  510. m_bMounted = false;
  511. m_bCanAsync = true;
  512. #if defined( POSIX ) && !defined( _PS3 )
  513. SetDefLessFunc( CStdioFile::m_LockedFDMap );
  514. #endif
  515. }
  516. //-----------------------------------------------------------------------------
  517. // Purpose:
  518. //-----------------------------------------------------------------------------
  519. CFileSystem_Stdio::~CFileSystem_Stdio()
  520. {
  521. Assert(!m_bMounted);
  522. }
  523. //-----------------------------------------------------------------------------
  524. // QueryInterface:
  525. //-----------------------------------------------------------------------------
  526. void *CFileSystem_Stdio::QueryInterface( const char *pInterfaceName )
  527. {
  528. // We also implement the IMatSystemSurface interface
  529. if (!Q_strncmp( pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
  530. return (IFileSystem*)this;
  531. return CBaseFileSystem::QueryInterface( pInterfaceName );
  532. }
  533. //-----------------------------------------------------------------------------
  534. //-----------------------------------------------------------------------------
  535. bool CFileSystem_Stdio::GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign )
  536. {
  537. unsigned sectorSize;
  538. CFileHandle *fh = ( CFileHandle *)hFile;
  539. #ifdef SUPPORT_VPK
  540. if ( fh && fh->m_VPKHandle )
  541. {
  542. return false;
  543. }
  544. #endif
  545. if ( hFile && UseOptimalBufferAllocation() )
  546. {
  547. sectorSize = fh->GetSectorSize();
  548. if ( !sectorSize || ( fh->m_pPackFileHandle && ( fh->m_pPackFileHandle->AbsoluteBaseOffset() % sectorSize ) ) )
  549. {
  550. sectorSize = 1;
  551. }
  552. }
  553. else
  554. {
  555. sectorSize = 1;
  556. }
  557. if ( pOffsetAlign )
  558. {
  559. *pOffsetAlign = sectorSize;
  560. }
  561. if ( pSizeAlign )
  562. {
  563. *pSizeAlign = sectorSize;
  564. }
  565. if ( pBufferAlign )
  566. {
  567. if ( IsX360() )
  568. {
  569. *pBufferAlign = 4;
  570. }
  571. else
  572. {
  573. *pBufferAlign = sectorSize;
  574. }
  575. }
  576. return ( sectorSize > 1 );
  577. }
  578. // was from launcher.cpp in EA PS3 port, but can't do that in a PRX
  579. const char* GetGameMode()
  580. {
  581. return "portal2";
  582. }
  583. //-----------------------------------------------------------------------------
  584. //-----------------------------------------------------------------------------
  585. void *CFileSystem_Stdio::AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset )
  586. {
  587. if ( !UseOptimalBufferAllocation() )
  588. {
  589. return CBaseFileSystem::AllocOptimalReadBuffer( hFile, nSize, nOffset );
  590. }
  591. unsigned sectorSize;
  592. if ( hFile != FILESYSTEM_INVALID_HANDLE )
  593. {
  594. CFileHandle *fh = ( CFileHandle *)hFile;
  595. sectorSize = fh->GetSectorSize();
  596. if ( !nSize )
  597. {
  598. nSize = fh->Size();
  599. }
  600. if ( fh->m_pPackFileHandle )
  601. {
  602. nOffset += fh->m_pPackFileHandle->AbsoluteBaseOffset();
  603. }
  604. }
  605. else
  606. {
  607. // an invalid handle gets a fake "optimal" but valid buffer
  608. // this path is for a caller that isn't doing i/o,
  609. // but needs an "optimal" buffer that can end up passed to FreeOptimalReadBuffer()
  610. sectorSize = 4;
  611. }
  612. bool bOffsetIsAligned = ( nOffset % sectorSize == 0 );
  613. unsigned nAllocSize = ( bOffsetIsAligned ) ? AlignValue( nSize, sectorSize ) : nSize;
  614. if ( IsX360() )
  615. {
  616. return malloc( nAllocSize );
  617. }
  618. else
  619. {
  620. unsigned nAllocAlignment = ( bOffsetIsAligned ) ? sectorSize : 4;
  621. return _aligned_malloc( nAllocSize, nAllocAlignment );
  622. }
  623. }
  624. //-----------------------------------------------------------------------------
  625. //-----------------------------------------------------------------------------
  626. void CFileSystem_Stdio::FreeOptimalReadBuffer( void *p )
  627. {
  628. if ( !UseOptimalBufferAllocation() )
  629. {
  630. CBaseFileSystem::FreeOptimalReadBuffer( p );
  631. return;
  632. }
  633. if ( p )
  634. {
  635. if ( IsX360() )
  636. {
  637. free( p );
  638. }
  639. else
  640. {
  641. _aligned_free( p );
  642. }
  643. }
  644. }
  645. //-----------------------------------------------------------------------------
  646. // Purpose: low-level filesystem wrapper
  647. //-----------------------------------------------------------------------------
  648. FILE *CFileSystem_Stdio::FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo )
  649. {
  650. if( ShouldFailIo() )
  651. return NULL;
  652. CStdFilesystemFile *pFile = NULL;
  653. if ( pInfo )
  654. pInfo->m_bLoadedFromSteamCache = false;
  655. #ifdef _WIN32
  656. if ( CWin32ReadOnlyFile::CanOpen( filename, options ) )
  657. {
  658. pFile = CWin32ReadOnlyFile::FS_fopen( filename, options, size );
  659. return (FILE *)pFile;
  660. }
  661. #endif
  662. #if IsPlatformPS3()
  663. if ( CFiosReadOnlyFile::CanOpen( filename, options ) )
  664. {
  665. pFile = CFiosReadOnlyFile::FS_fopen( filename, options, size );
  666. return (FILE *)pFile;
  667. }
  668. #endif
  669. pFile = CStdioFile::FS_fopen( filename, options, size );
  670. return (FILE *)pFile;
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Purpose: low-level filesystem wrapper
  674. //-----------------------------------------------------------------------------
  675. void CFileSystem_Stdio::FS_setbufsize( FILE *fp, unsigned nBytes )
  676. {
  677. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  678. pFile->FS_setbufsize( nBytes );
  679. }
  680. //-----------------------------------------------------------------------------
  681. // Purpose: low-level filesystem wrapper
  682. //-----------------------------------------------------------------------------
  683. void CFileSystem_Stdio::FS_fclose( FILE *fp )
  684. {
  685. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  686. if ( m_WhitelistFileTrackingEnabled )
  687. m_FileTracker2.RecordFileClose( fp );
  688. pFile->FS_fclose();
  689. delete pFile;
  690. }
  691. //-----------------------------------------------------------------------------
  692. // Purpose: low-level filesystem wrapper
  693. //-----------------------------------------------------------------------------
  694. void CFileSystem_Stdio::FS_fseek( FILE *fp, int64 pos, int seekType )
  695. {
  696. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  697. if ( m_WhitelistFileTrackingEnabled )
  698. m_FileTracker2.RecordFileSeek( fp, pos, seekType );
  699. pFile->FS_fseek( pos, seekType );
  700. }
  701. //-----------------------------------------------------------------------------
  702. // Purpose: low-level filesystem wrapper
  703. //-----------------------------------------------------------------------------
  704. long CFileSystem_Stdio::FS_ftell( FILE *fp )
  705. {
  706. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  707. return pFile->FS_ftell();
  708. }
  709. //-----------------------------------------------------------------------------
  710. // Purpose: low-level filesystem wrapper
  711. //-----------------------------------------------------------------------------
  712. int CFileSystem_Stdio::FS_feof( FILE *fp )
  713. {
  714. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  715. return pFile->FS_feof();
  716. }
  717. //-----------------------------------------------------------------------------
  718. // Purpose: low-level filesystem wrapper
  719. //-----------------------------------------------------------------------------
  720. size_t CFileSystem_Stdio::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
  721. {
  722. if( ShouldFailIo() )
  723. return 0;
  724. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  725. size_t nBytesRead = pFile->FS_fread( dest, destSize, size);
  726. Trace_FRead( nBytesRead, fp );
  727. if ( m_WhitelistFileTrackingEnabled )
  728. m_FileTracker2.RecordFileRead( dest, nBytesRead, size, fp );
  729. return nBytesRead;
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose: low-level filesystem wrapper
  733. //-----------------------------------------------------------------------------
  734. size_t CFileSystem_Stdio::FS_fwrite( const void *src, size_t size, FILE *fp )
  735. {
  736. if( ShouldFailIo() )
  737. return 0;
  738. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  739. size_t nBytesWritten = pFile->FS_fwrite(src, size);
  740. return nBytesWritten;
  741. }
  742. //-----------------------------------------------------------------------------
  743. // Purpose: low-level filesystem wrapper
  744. //-----------------------------------------------------------------------------
  745. bool CFileSystem_Stdio::FS_setmode( FILE *fp, FileMode_t mode )
  746. {
  747. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  748. return pFile->FS_setmode( mode );
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Purpose: low-level filesystem wrapper
  752. //-----------------------------------------------------------------------------
  753. size_t CFileSystem_Stdio::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
  754. {
  755. if( ShouldFailIo() )
  756. return 0;
  757. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  758. return pFile->FS_vfprintf(fmt, list);
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Purpose: low-level filesystem wrapper
  762. //-----------------------------------------------------------------------------
  763. int CFileSystem_Stdio::FS_ferror( FILE *fp )
  764. {
  765. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  766. return pFile->FS_ferror();
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose: low-level filesystem wrapper
  770. //-----------------------------------------------------------------------------
  771. int CFileSystem_Stdio::FS_fflush( FILE *fp )
  772. {
  773. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  774. return pFile->FS_fflush();
  775. }
  776. //-----------------------------------------------------------------------------
  777. // Purpose: low-level filesystem wrapper
  778. //-----------------------------------------------------------------------------
  779. char *CFileSystem_Stdio::FS_fgets( char *dest, int destSize, FILE *fp )
  780. {
  781. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  782. return pFile->FS_fgets(dest, destSize);
  783. }
  784. //-----------------------------------------------------------------------------
  785. // Purpose:
  786. // Input : *path -
  787. // pmode -
  788. // Output : int
  789. //-----------------------------------------------------------------------------
  790. int CFileSystem_Stdio::FS_chmod( const char *path, int pmode )
  791. {
  792. if ( !path )
  793. return -1;
  794. int rt = _chmod( path, pmode );
  795. #if defined( LINUX )
  796. if (rt==-1)
  797. {
  798. char file[MAX_PATH];
  799. if ( findFileInDirCaseInsensitive(path, file) )
  800. {
  801. rt=_chmod(file,pmode);
  802. }
  803. }
  804. #endif
  805. return rt;
  806. }
  807. //-----------------------------------------------------------------------------
  808. // Purpose: low-level filesystem wrapper
  809. //-----------------------------------------------------------------------------
  810. int CFileSystem_Stdio::FS_stat( const char *path, struct _stat *buf )
  811. {
  812. if ( !path )
  813. {
  814. return -1;
  815. }
  816. int rt;
  817. #ifdef _PS3
  818. CellFsStat cellBuf;
  819. CellFsErrno retFs = cellFsStat(path, &cellBuf);
  820. if(retFs == CELL_FS_SUCCEEDED)
  821. {
  822. buf->st_atime = cellBuf.st_atime;
  823. buf->st_blksize = cellBuf.st_blksize;
  824. buf->st_ctime = cellBuf.st_ctime;
  825. buf->st_gid = cellBuf.st_gid;
  826. buf->st_mode = cellBuf.st_mode;
  827. buf->st_mtime = cellBuf.st_mtime;
  828. buf->st_size = cellBuf.st_size;
  829. buf->st_uid = cellBuf.st_uid;
  830. buf->st_dev = 0;
  831. buf->st_ino = 0;
  832. buf->st_nlink = 0;
  833. buf->st_rdev = 0;
  834. buf->st_blocks = 0;
  835. rt = 0;
  836. }
  837. else
  838. {
  839. rt = -1;
  840. //TBD: SET ERRNO
  841. }
  842. #else
  843. rt = _stat( path, buf );
  844. #endif
  845. #if defined(LINUX)
  846. if ( rt == -1 )
  847. {
  848. char file[MAX_PATH];
  849. if ( findFileInDirCaseInsensitive( path, file ) )
  850. {
  851. rt = _stat( file, buf );
  852. }
  853. }
  854. #endif
  855. return rt;
  856. }
  857. //-----------------------------------------------------------------------------
  858. // Purpose: low-level filesystem wrapper
  859. //-----------------------------------------------------------------------------
  860. HANDLE CFileSystem_Stdio::FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat)
  861. {
  862. return ::FindFirstFile(const_cast<char *>(findname), dat);
  863. }
  864. //-----------------------------------------------------------------------------
  865. // Purpose: low-level filesystem wrapper
  866. //-----------------------------------------------------------------------------
  867. bool CFileSystem_Stdio::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
  868. {
  869. return (::FindNextFile(handle, dat) != 0);
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Purpose: low-level filesystem wrapper
  873. //-----------------------------------------------------------------------------
  874. bool CFileSystem_Stdio::FS_FindClose(HANDLE handle)
  875. {
  876. return (::FindClose(handle) != 0);
  877. }
  878. //-----------------------------------------------------------------------------
  879. //
  880. //-----------------------------------------------------------------------------
  881. int CFileSystem_Stdio::FS_GetSectorSize( FILE *fp )
  882. {
  883. CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
  884. return pFile->FS_GetSectorSize();
  885. }
  886. //-----------------------------------------------------------------------------
  887. // Purpose: files are always immediately available on disk
  888. //-----------------------------------------------------------------------------
  889. bool CFileSystem_Stdio::IsFileImmediatelyAvailable(const char *pFileName)
  890. {
  891. return true;
  892. }
  893. // enable this if you want the stdio filesystem to pretend it's steam, and make people wait for resources
  894. //#define DEBUG_WAIT_FOR_RESOURCES_API
  895. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  896. static float g_flDebugProgress = 0.0f;
  897. #endif
  898. //-----------------------------------------------------------------------------
  899. // Purpose: steam call, unnecessary in stdio
  900. //-----------------------------------------------------------------------------
  901. WaitForResourcesHandle_t CFileSystem_Stdio::WaitForResources( const char *resourcelist )
  902. {
  903. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  904. g_flDebugProgress = 0.0f;
  905. #endif
  906. return 1;
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Purpose: steam call, unnecessary in stdio
  910. //-----------------------------------------------------------------------------
  911. bool CFileSystem_Stdio::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
  912. {
  913. VPROF_BUDGET( "GetWaitForResourcesProgress (stdio)", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  914. #if defined(DEBUG_WAIT_FOR_RESOURCES_API)
  915. g_flDebugProgress += 0.002f;
  916. if (g_flDebugProgress < 1.0f)
  917. {
  918. *progress = g_flDebugProgress;
  919. *complete = false;
  920. return true;
  921. }
  922. #endif
  923. // always return that we're complete
  924. *progress = 0.0f;
  925. *complete = true;
  926. return true;
  927. }
  928. //-----------------------------------------------------------------------------
  929. // Purpose: steam call, unnecessary in stdio
  930. //-----------------------------------------------------------------------------
  931. void CFileSystem_Stdio::CancelWaitForResources( WaitForResourcesHandle_t handle )
  932. {
  933. }
  934. //-----------------------------------------------------------------------------
  935. // Purpose:
  936. //-----------------------------------------------------------------------------
  937. void CFileSystem_Stdio::GetLocalCopy( const char *pFileName )
  938. {
  939. // do nothing. . everything is local.
  940. }
  941. //-----------------------------------------------------------------------------
  942. // Purpose:
  943. //-----------------------------------------------------------------------------
  944. int CFileSystem_Stdio::HintResourceNeed( const char *hintlist, int forgetEverything )
  945. {
  946. // do nothing. . everything is local.
  947. return 0;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: low-level filesystem wrapper
  951. //-----------------------------------------------------------------------------
  952. CStdioFile *CStdioFile::FS_fopen( const char *filename, const char *options, int64 *size )
  953. {
  954. MEM_ALLOC_CREDIT();
  955. FILE *pFile = NULL;
  956. // stop newline characters at end of filename
  957. Assert(!strchr(filename, '\n') && !strchr(filename, '\r'));
  958. pFile = fopen(filename, options);
  959. if (pFile && size)
  960. {
  961. // todo: replace with filelength()?
  962. struct _stat buf;
  963. int rt = _stat( filename, &buf );
  964. if (rt == 0)
  965. {
  966. *size = buf.st_size;
  967. }
  968. }
  969. #if defined( LINUX )
  970. if(!pFile && !strchr(options,'w') && !strchr(options,'+') ) // try opening the lower cased version
  971. {
  972. char file[MAX_PATH];
  973. if ( findFileInDirCaseInsensitive(filename, file ) )
  974. {
  975. pFile = fopen( file, options );
  976. if (pFile && size)
  977. {
  978. // todo: replace with filelength()?
  979. struct _stat buf;
  980. int rt = _stat( file, &buf );
  981. if (rt == 0)
  982. {
  983. *size = buf.st_size;
  984. }
  985. }
  986. }
  987. }
  988. #endif
  989. if ( pFile )
  990. {
  991. bool bWriteable = false;
  992. if ( strchr(options,'w') || strchr(options,'a') )
  993. bWriteable = true;
  994. #if defined( POSIX ) && !defined( _PS3 )
  995. if ( bWriteable )
  996. {
  997. // 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)
  998. // so use flock here to mimic that behavior
  999. ThreadId_t curThread = ThreadGetCurrentId();
  1000. int fd = fileno_unlocked( pFile );
  1001. int iLockID = m_LockedFDMap.Find( fd );
  1002. int ret = flock( fd, LOCK_EX | LOCK_NB );
  1003. if ( ret < 0 )
  1004. {
  1005. if ( errno == EWOULDBLOCK )
  1006. {
  1007. if ( iLockID != m_LockedFDMap.InvalidIndex() &&
  1008. m_LockedFDMap[iLockID] != -1 &&
  1009. curThread != m_LockedFDMap[iLockID] )
  1010. {
  1011. ret = flock( fd, LOCK_EX );
  1012. if ( ret < 0 )
  1013. {
  1014. fclose( pFile );
  1015. return NULL;
  1016. }
  1017. }
  1018. }
  1019. else
  1020. {
  1021. fclose( pFile );
  1022. return NULL;
  1023. }
  1024. }
  1025. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  1026. m_LockedFDMap[iLockID] = curThread;
  1027. else
  1028. m_LockedFDMap.Insert( fd, curThread );
  1029. rewind( pFile );
  1030. }
  1031. #endif
  1032. return new CStdioFile( pFile, bWriteable );
  1033. }
  1034. return NULL;
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // Purpose: low-level filesystem wrapper
  1038. //-----------------------------------------------------------------------------
  1039. void CStdioFile::FS_setbufsize( unsigned nBytes )
  1040. {
  1041. #ifdef _PS3
  1042. if ( nBytes )
  1043. {
  1044. setvbuf( m_pFile, NULL, _IOFBF, nBytes );
  1045. }
  1046. #elif defined _WIN32
  1047. if ( nBytes )
  1048. {
  1049. setvbuf( m_pFile, NULL, _IOFBF, 32768 );
  1050. }
  1051. else
  1052. {
  1053. setvbuf( m_pFile, NULL, _IONBF, 0 );
  1054. // hack to make microsoft stdio not always read one stray byte on odd sized files
  1055. // hopefully this isn't needed on vs2015??
  1056. #if (defined(_MSC_VER) && (_MSC_VER < 1900))
  1057. m_pFile->_bufsiz = 1;
  1058. #endif
  1059. }
  1060. #endif
  1061. }
  1062. //-----------------------------------------------------------------------------
  1063. // Purpose: low-level filesystem wrapper
  1064. //-----------------------------------------------------------------------------
  1065. void CStdioFile::FS_fclose()
  1066. {
  1067. #if defined( POSIX ) && !defined( _PS3 )
  1068. if ( m_bWriteable )
  1069. {
  1070. fflush( m_pFile );
  1071. int fd = fileno_unlocked( m_pFile );
  1072. flock( fd, LOCK_UN );
  1073. int iLockID = m_LockedFDMap.Find( fd );
  1074. if ( iLockID != m_LockedFDMap.InvalidIndex() )
  1075. m_LockedFDMap[ iLockID ] = -1;
  1076. }
  1077. #endif
  1078. fclose(m_pFile);
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Purpose: low-level filesystem wrapper
  1082. //-----------------------------------------------------------------------------
  1083. void CStdioFile::FS_fseek( int64 pos, int seekType )
  1084. {
  1085. int nNewSeekType = seekType;
  1086. // Handle values outside the 32-bit signed range.
  1087. // Only 0 or 1 of the while loops will execute inthe same call.
  1088. while ( pos > INT_MAX )
  1089. {
  1090. fseek( m_pFile, INT_MAX, nNewSeekType );
  1091. pos -= INT_MAX;
  1092. nNewSeekType = SEEK_CUR; // Now all seeks need to be relative
  1093. }
  1094. while ( pos < INT_MIN )
  1095. {
  1096. fseek( m_pFile, INT_MIN, nNewSeekType );
  1097. pos -= INT_MIN;
  1098. nNewSeekType = SEEK_CUR; // Now all seeks need to be relative
  1099. }
  1100. fseek( m_pFile, pos, nNewSeekType );
  1101. }
  1102. //-----------------------------------------------------------------------------
  1103. // Purpose: low-level filesystem wrapper
  1104. //-----------------------------------------------------------------------------
  1105. long CStdioFile::FS_ftell()
  1106. {
  1107. return ftell(m_pFile);
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Purpose: low-level filesystem wrapper
  1111. //-----------------------------------------------------------------------------
  1112. int CStdioFile::FS_feof()
  1113. {
  1114. return feof(m_pFile);
  1115. }
  1116. //-----------------------------------------------------------------------------
  1117. // Purpose: low-level filesystem wrapper
  1118. //-----------------------------------------------------------------------------
  1119. size_t CStdioFile::FS_fread( void *dest, size_t destSize, size_t size )
  1120. {
  1121. // read (size) of bytes to ensure truncated reads returns bytes read and not 0
  1122. return fread( dest, 1, size, m_pFile );
  1123. }
  1124. #define WRITE_CHUNK (256 * 1024)
  1125. //-----------------------------------------------------------------------------
  1126. // Purpose: low-level filesystem wrapper
  1127. //
  1128. // This routine breaks data into chunks if the amount to be written is beyond WRITE_CHUNK (256kb)
  1129. // Windows can fail on monolithic writes of ~12MB or more, so we work around that here
  1130. //-----------------------------------------------------------------------------
  1131. size_t CStdioFile::FS_fwrite( const void *src, size_t size )
  1132. {
  1133. if ( size > WRITE_CHUNK )
  1134. {
  1135. size_t remaining = size;
  1136. const byte* current = (const byte *) src;
  1137. size_t total = 0;
  1138. while ( remaining > 0 )
  1139. {
  1140. size_t bytesToCopy = MIN(remaining, WRITE_CHUNK);
  1141. total += fwrite(current, 1, bytesToCopy, m_pFile);
  1142. remaining -= bytesToCopy;
  1143. current += bytesToCopy;
  1144. }
  1145. Assert( total == size );
  1146. return total;
  1147. }
  1148. return fwrite(src, 1, size, m_pFile);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)
  1149. }
  1150. //-----------------------------------------------------------------------------
  1151. // Purpose: low-level filesystem wrapper
  1152. //-----------------------------------------------------------------------------
  1153. bool CStdioFile::FS_setmode( FileMode_t mode )
  1154. {
  1155. #ifdef _WIN32
  1156. int fd = _fileno( m_pFile );
  1157. int newMode = ( mode == FM_BINARY ) ? _O_BINARY : _O_TEXT;
  1158. return ( _setmode( fd, newMode) != -1 );
  1159. #else
  1160. return false;
  1161. #endif
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. // Purpose: low-level filesystem wrapper
  1165. //-----------------------------------------------------------------------------
  1166. size_t CStdioFile::FS_vfprintf( const char *fmt, va_list list )
  1167. {
  1168. return vfprintf(m_pFile, fmt, list);
  1169. }
  1170. //-----------------------------------------------------------------------------
  1171. // Purpose: low-level filesystem wrapper
  1172. //-----------------------------------------------------------------------------
  1173. int CStdioFile::FS_ferror()
  1174. {
  1175. return ferror(m_pFile);
  1176. }
  1177. //-----------------------------------------------------------------------------
  1178. // Purpose: low-level filesystem wrapper
  1179. //-----------------------------------------------------------------------------
  1180. int CStdioFile::FS_fflush()
  1181. {
  1182. return fflush(m_pFile);
  1183. }
  1184. //-----------------------------------------------------------------------------
  1185. // Purpose: low-level filesystem wrapper
  1186. //-----------------------------------------------------------------------------
  1187. char *CStdioFile::FS_fgets( char *dest, int destSize )
  1188. {
  1189. return fgets(dest, destSize, m_pFile);
  1190. }
  1191. //-----------------------------------------------------------------------------
  1192. //
  1193. //-----------------------------------------------------------------------------
  1194. #ifdef _WIN32
  1195. ConVar filesystem_use_overlapped_io( "filesystem_use_overlapped_io", "1", 0, "" );
  1196. #define UseOverlappedIO() filesystem_use_overlapped_io.GetBool()
  1197. //-----------------------------------------------------------------------------
  1198. //
  1199. //-----------------------------------------------------------------------------
  1200. int GetSectorSize( const char *pszFilename )
  1201. {
  1202. if ( ( !pszFilename[0] || !pszFilename[1] ) ||
  1203. ( pszFilename[0] == '\\' && pszFilename[1] == '\\' ) ||
  1204. ( pszFilename[0] == '/' && pszFilename[1] == '/' ) )
  1205. {
  1206. // Cannot determine sector size with a UNC path (need volume identifier)
  1207. return 0;
  1208. }
  1209. if ( IsX360() )
  1210. {
  1211. // purposely dvd centric, which is also the worst case
  1212. return XBOX_DVD_SECTORSIZE;
  1213. }
  1214. #if defined( _WIN32 ) && !defined( FILESYSTEM_STEAM ) && !defined( _X360 )
  1215. char szAbsoluteFilename[MAX_FILEPATH];
  1216. if ( pszFilename[1] != ':' )
  1217. {
  1218. Q_MakeAbsolutePath( szAbsoluteFilename, sizeof(szAbsoluteFilename), pszFilename );
  1219. pszFilename = szAbsoluteFilename;
  1220. }
  1221. DWORD sectorSize = 1;
  1222. struct DriveSectorSize_t
  1223. {
  1224. char volume;
  1225. DWORD sectorSize;
  1226. };
  1227. static DriveSectorSize_t cachedSizes[4];
  1228. char volume = tolower( *pszFilename );
  1229. int i;
  1230. for ( i = 0; i < ARRAYSIZE(cachedSizes) && cachedSizes[i].volume; i++ )
  1231. {
  1232. if ( cachedSizes[i].volume == volume )
  1233. {
  1234. sectorSize = cachedSizes[i].sectorSize;
  1235. break;
  1236. }
  1237. }
  1238. if ( sectorSize == 1 )
  1239. {
  1240. char root[4] = "X:\\";
  1241. root[0] = *pszFilename;
  1242. DWORD ignored;
  1243. if ( !GetDiskFreeSpace( root, &ignored, &sectorSize, &ignored, &ignored ) )
  1244. {
  1245. sectorSize = 0;
  1246. }
  1247. if ( i < ARRAYSIZE(cachedSizes) )
  1248. {
  1249. cachedSizes[i].volume = volume;
  1250. cachedSizes[i].sectorSize = sectorSize;
  1251. }
  1252. }
  1253. return sectorSize;
  1254. #else
  1255. return 0;
  1256. #endif
  1257. }
  1258. //-----------------------------------------------------------------------------
  1259. //
  1260. //-----------------------------------------------------------------------------
  1261. class CThreadIOEventPool
  1262. {
  1263. public:
  1264. ~CThreadIOEventPool()
  1265. {
  1266. }
  1267. CThreadEvent *GetEvent()
  1268. {
  1269. return m_Events.GetObject();
  1270. }
  1271. void ReleaseEvent( CThreadEvent *pEvent )
  1272. {
  1273. m_Events.PutObject( pEvent );
  1274. }
  1275. private:
  1276. CTSPool<CThreadEvent> m_Events;
  1277. };
  1278. CThreadIOEventPool g_ThreadIOEvents;
  1279. //-----------------------------------------------------------------------------
  1280. //
  1281. //-----------------------------------------------------------------------------
  1282. bool CWin32ReadOnlyFile::CanOpen( const char *filename, const char *options )
  1283. {
  1284. return ( options[0] == 'r' && options[1] == 'b' && options[2] == 0 && filesystem_native.GetBool() );
  1285. }
  1286. //-----------------------------------------------------------------------------
  1287. //
  1288. //-----------------------------------------------------------------------------
  1289. static HANDLE OpenWin32File( const char *filename, bool bOverlapped, bool bUnbuffered, int64 *pFileSize )
  1290. {
  1291. HANDLE hFile;
  1292. DWORD createFlags = FILE_ATTRIBUTE_NORMAL;
  1293. if ( bOverlapped )
  1294. {
  1295. createFlags |= FILE_FLAG_OVERLAPPED;
  1296. }
  1297. if ( bUnbuffered )
  1298. {
  1299. createFlags |= FILE_FLAG_NO_BUFFERING;
  1300. }
  1301. hFile = ::CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, createFlags, NULL );
  1302. if ( hFile != INVALID_HANDLE_VALUE && !*pFileSize )
  1303. {
  1304. LARGE_INTEGER fileSize;
  1305. if ( !GetFileSizeEx( hFile, &fileSize ) )
  1306. {
  1307. CloseHandle( hFile );
  1308. hFile = INVALID_HANDLE_VALUE;
  1309. }
  1310. *pFileSize = fileSize.QuadPart;
  1311. }
  1312. return hFile;
  1313. }
  1314. CWin32ReadOnlyFile *CWin32ReadOnlyFile::FS_fopen( const char *filename, const char *options, int64 *size )
  1315. {
  1316. Assert( CanOpen( filename, options ) );
  1317. int sectorSize = 0;
  1318. bool bTryUnbuffered = ( UseUnbufferedIO() && ( sectorSize = GetSectorSize( filename ) ) != 0 );
  1319. bool bOverlapped = UseOverlappedIO();
  1320. HANDLE hFileUnbuffered = INVALID_HANDLE_VALUE;
  1321. int64 fileSize = 0;
  1322. if ( bTryUnbuffered )
  1323. {
  1324. hFileUnbuffered = OpenWin32File( filename, bOverlapped, true, &fileSize );
  1325. if ( hFileUnbuffered == INVALID_HANDLE_VALUE )
  1326. {
  1327. return NULL;
  1328. }
  1329. }
  1330. HANDLE hFileBuffered = OpenWin32File( filename, bOverlapped, false, &fileSize );
  1331. if ( hFileBuffered == INVALID_HANDLE_VALUE )
  1332. {
  1333. if ( hFileUnbuffered != INVALID_HANDLE_VALUE )
  1334. {
  1335. CloseHandle( hFileUnbuffered );
  1336. }
  1337. return NULL;
  1338. }
  1339. if ( size )
  1340. {
  1341. *size = fileSize;
  1342. }
  1343. return new CWin32ReadOnlyFile( hFileUnbuffered, hFileBuffered, ( sectorSize ) ? sectorSize : 1, fileSize, bOverlapped );
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Purpose: low-level filesystem wrapper
  1347. //-----------------------------------------------------------------------------
  1348. void CWin32ReadOnlyFile::FS_fclose()
  1349. {
  1350. if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
  1351. {
  1352. CloseHandle( m_hFileUnbuffered );
  1353. }
  1354. if ( m_hFileBuffered != INVALID_HANDLE_VALUE )
  1355. {
  1356. CloseHandle( m_hFileBuffered );
  1357. }
  1358. }
  1359. //-----------------------------------------------------------------------------
  1360. // Purpose: low-level filesystem wrapper
  1361. //-----------------------------------------------------------------------------
  1362. void CWin32ReadOnlyFile::FS_fseek( int64 pos, int seekType )
  1363. {
  1364. switch ( seekType )
  1365. {
  1366. case SEEK_SET:
  1367. m_ReadPos = pos;
  1368. break;
  1369. case SEEK_CUR:
  1370. m_ReadPos += pos;
  1371. break;
  1372. case SEEK_END:
  1373. m_ReadPos = m_Size - pos;
  1374. break;
  1375. }
  1376. }
  1377. //-----------------------------------------------------------------------------
  1378. // Purpose: low-level filesystem wrapper
  1379. //-----------------------------------------------------------------------------
  1380. long CWin32ReadOnlyFile::FS_ftell()
  1381. {
  1382. return m_ReadPos;
  1383. }
  1384. //-----------------------------------------------------------------------------
  1385. // Purpose: low-level filesystem wrapper
  1386. //-----------------------------------------------------------------------------
  1387. int CWin32ReadOnlyFile::FS_feof()
  1388. {
  1389. return ( m_ReadPos >= m_Size );
  1390. }
  1391. // ends up on a thread's stack, don't blindly increase without awareness of that implication
  1392. // 360 threads have small stacks, using small buffer of the worst case quantum sector size
  1393. #if !defined( _X360 )
  1394. #define READ_TEMP_BUFFER ( 32*1024 )
  1395. #else
  1396. #define READ_TEMP_BUFFER ( 2*XBOX_DVD_SECTORSIZE )
  1397. #endif
  1398. //-----------------------------------------------------------------------------
  1399. // Purpose: low-level filesystem wrapper
  1400. //-----------------------------------------------------------------------------
  1401. size_t CWin32ReadOnlyFile::FS_fread( void *dest, size_t destSize, size_t size )
  1402. {
  1403. VPROF_BUDGET( "CWin32ReadOnlyFile::FS_fread", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  1404. if ( !size || ( m_hFileUnbuffered == INVALID_HANDLE_VALUE && m_hFileBuffered == INVALID_HANDLE_VALUE ) )
  1405. {
  1406. return 0;
  1407. }
  1408. CThreadEvent *pEvent = NULL;
  1409. if ( destSize == (size_t)-1 )
  1410. {
  1411. destSize = size;
  1412. }
  1413. byte tempBuffer[READ_TEMP_BUFFER];
  1414. HANDLE hReadFile = m_hFileBuffered;
  1415. int nBytesToRead = size;
  1416. byte *pDest = (byte *)dest;
  1417. int64 offset = m_ReadPos;
  1418. if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
  1419. {
  1420. const int destBaseAlign = ( IsX360() ) ? 4 : m_SectorSize;
  1421. bool bDestBaseIsAligned = ( (DWORD)dest % destBaseAlign == 0 );
  1422. bool bCanReadUnbufferedDirect = ( bDestBaseIsAligned && ( destSize % m_SectorSize == 0 ) && ( m_ReadPos % m_SectorSize == 0 ) );
  1423. if ( bCanReadUnbufferedDirect )
  1424. {
  1425. // fastest path, unbuffered
  1426. nBytesToRead = AlignValue( size, m_SectorSize );
  1427. hReadFile = m_hFileUnbuffered;
  1428. }
  1429. else
  1430. {
  1431. // not properly aligned, snap to alignments
  1432. // attempt to perform single unbuffered operation using stack buffer
  1433. int64 alignedOffset = AlignValue( ( m_ReadPos - m_SectorSize ) + 1, m_SectorSize );
  1434. unsigned int alignedBytesToRead = AlignValue( ( m_ReadPos - alignedOffset ) + size, m_SectorSize );
  1435. if ( alignedBytesToRead <= sizeof( tempBuffer ) - destBaseAlign )
  1436. {
  1437. // read operation can be performed as unbuffered follwed by a post fixup
  1438. nBytesToRead = alignedBytesToRead;
  1439. offset = alignedOffset;
  1440. pDest = AlignValue( tempBuffer, destBaseAlign );
  1441. hReadFile = m_hFileUnbuffered;
  1442. }
  1443. }
  1444. }
  1445. OVERLAPPED overlapped = { 0 };
  1446. if ( m_bOverlapped )
  1447. {
  1448. pEvent = g_ThreadIOEvents.GetEvent();
  1449. overlapped.hEvent = *pEvent;
  1450. }
  1451. #ifdef REPORT_BUFFERED_IO
  1452. if ( hReadFile == m_hFileBuffered && filesystem_report_buffered_io.GetBool() )
  1453. {
  1454. Msg( "Buffered Operation :(\n" );
  1455. }
  1456. #endif
  1457. // some disk drivers will fail if read is too large
  1458. static int MAX_READ = filesystem_max_stdio_read.GetInt()*1024*1024;
  1459. const int MIN_READ = 64*1024;
  1460. bool bReadOk = true;
  1461. DWORD nBytesRead = 0;
  1462. size_t result = 0;
  1463. int64 currentOffset = offset;
  1464. while ( bReadOk && nBytesToRead > 0 )
  1465. {
  1466. int nCurBytesToRead = min( nBytesToRead, MAX_READ );
  1467. DWORD nCurBytesRead = 0;
  1468. overlapped.Offset = currentOffset & 0xFFFFFFFF;
  1469. overlapped.OffsetHigh = ( currentOffset >> 32 ) & 0xFFFFFFFF;
  1470. bReadOk = ( ::ReadFile( hReadFile, pDest + nBytesRead, nCurBytesToRead, &nCurBytesRead, &overlapped ) != 0 );
  1471. if ( !bReadOk )
  1472. {
  1473. if ( m_bOverlapped && GetLastError() == ERROR_IO_PENDING )
  1474. {
  1475. // Read is pending, we should block until the OS is finished. Otherwise this loop is just a evil spinloop.
  1476. // (Why are we even using asynchronous I/O in this loop?)
  1477. if ( GetOverlappedResult( hReadFile, &overlapped, &nCurBytesRead, TRUE ) )
  1478. {
  1479. bReadOk = true;
  1480. }
  1481. }
  1482. }
  1483. if ( bReadOk )
  1484. {
  1485. nBytesRead += nCurBytesRead;
  1486. nBytesToRead -= nCurBytesToRead;
  1487. currentOffset += nCurBytesRead;
  1488. }
  1489. if ( !bReadOk )
  1490. {
  1491. DWORD dwError = GetLastError();
  1492. if ( IsX360() )
  1493. {
  1494. if ( dwError == ERROR_DISK_CORRUPT || dwError == ERROR_FILE_CORRUPT )
  1495. {
  1496. FSDirtyDiskReportFunc_t func = g_FileSystem_Stdio.GetDirtyDiskReportFunc();
  1497. if ( func )
  1498. {
  1499. func();
  1500. result = 0;
  1501. }
  1502. }
  1503. }
  1504. if ( dwError == ERROR_NO_SYSTEM_RESOURCES && MAX_READ > MIN_READ )
  1505. {
  1506. MAX_READ /= 2;
  1507. bReadOk = true;
  1508. DevMsg( "ERROR_NO_SYSTEM_RESOURCES: Reducing max read to %d bytes\n", MAX_READ );
  1509. }
  1510. else
  1511. {
  1512. DevMsg( "Unknown read error %d\n", dwError );
  1513. }
  1514. }
  1515. }
  1516. if ( bReadOk )
  1517. {
  1518. if ( nBytesRead && hReadFile == m_hFileUnbuffered && pDest != dest )
  1519. {
  1520. int nBytesExtra = ( m_ReadPos - offset );
  1521. nBytesRead -= nBytesExtra;
  1522. if ( nBytesRead )
  1523. {
  1524. memcpy( dest, (byte *)pDest + nBytesExtra, size );
  1525. }
  1526. }
  1527. result = min( nBytesRead, size );
  1528. }
  1529. if ( m_bOverlapped )
  1530. {
  1531. pEvent->Reset();
  1532. g_ThreadIOEvents.ReleaseEvent( pEvent );
  1533. }
  1534. m_ReadPos += result;
  1535. return result;
  1536. }
  1537. //-----------------------------------------------------------------------------
  1538. // Purpose: low-level filesystem wrapper
  1539. //-----------------------------------------------------------------------------
  1540. char *CWin32ReadOnlyFile::FS_fgets( char *dest, int destSize )
  1541. {
  1542. if ( FS_feof() )
  1543. {
  1544. return NULL;
  1545. }
  1546. int nStartPos = m_ReadPos;
  1547. int nBytesRead = FS_fread( dest, destSize, destSize );
  1548. if ( !nBytesRead )
  1549. {
  1550. return NULL;
  1551. }
  1552. dest[min( nBytesRead, destSize - 1)] = 0;
  1553. char *pNewline = strchr( dest, '\n' );
  1554. if ( pNewline )
  1555. {
  1556. // advance past, leave \n
  1557. pNewline++;
  1558. *pNewline = 0;
  1559. }
  1560. else
  1561. {
  1562. pNewline = &dest[min( nBytesRead, destSize - 1)];
  1563. }
  1564. m_ReadPos = nStartPos + ( pNewline - dest ) + 1;
  1565. return dest;
  1566. }
  1567. #endif
  1568. #if IsPlatformPS3()
  1569. const char * GetSupportedPrefix()
  1570. {
  1571. return g_pPS3PathInfo->GameImagePath();
  1572. }
  1573. int GetSupportedPrefixLength()
  1574. {
  1575. return strlen( GetSupportedPrefix() );
  1576. }
  1577. bool CFiosReadOnlyFile::CanOpen( const char *filename, const char *options )
  1578. {
  1579. if( ShouldFailIo() )
  1580. return false;
  1581. extern ConVar fs_fios_enabled;
  1582. if ( fs_fios_enabled.GetBool() )
  1583. {
  1584. bool bSupported = ( options[0] == 'r' && options[1] == 'b' && options[2] == 0 && filesystem_native.GetBool() );
  1585. bSupported &= ( memcmp( filename, GetSupportedPrefix(), GetSupportedPrefixLength() ) == 0 );
  1586. return bSupported;
  1587. }
  1588. else
  1589. {
  1590. return false;
  1591. }
  1592. }
  1593. //-----------------------------------------------------------------------------
  1594. //
  1595. //-----------------------------------------------------------------------------
  1596. static cell::fios::filehandle * OpenFiosFile( const char *filename, int64 *pFileSize )
  1597. {
  1598. if( ShouldFailIo() )
  1599. return NULL;
  1600. cell::fios::scheduler * pScheduler = cell::fios::scheduler::getDefaultScheduler();
  1601. cell::fios::filehandle * pFileHandle;
  1602. cell::fios::err_t err;
  1603. Assert( memcmp( filename, GetSupportedPrefix(), GetSupportedPrefixLength() ) == 0);
  1604. filename += GetSupportedPrefixLength(); // Skip the prefix, FIOS already takes it in account
  1605. err = pScheduler->getFileSizeSync( NULL, filename, pFileSize );
  1606. if ( err != cell::fios::CELL_FIOS_NOERROR )
  1607. {
  1608. Warning( "[FIOS] Failed to get size of file '%s'.\n", filename );
  1609. return NULL;
  1610. }
  1611. err = pScheduler->openFileSync( NULL, filename, cell::fios::kO_RDONLY, &pFileHandle );
  1612. if ( err != cell::fios::CELL_FIOS_NOERROR )
  1613. {
  1614. Warning( "[FIOS] Failed to open file '%s'.\n", filename );
  1615. return NULL;
  1616. }
  1617. return pFileHandle;
  1618. }
  1619. CFiosReadOnlyFile * CFiosReadOnlyFile::FS_fopen( const char *filename, const char *options, int64 *size )
  1620. {
  1621. if( ShouldFailIo() )
  1622. return NULL;
  1623. Assert( CanOpen( filename, options ) );
  1624. int64 nFileSize;
  1625. cell::fios::filehandle * pFileHandle = OpenFiosFile( filename, &nFileSize );
  1626. if ( pFileHandle == NULL )
  1627. {
  1628. return NULL;
  1629. }
  1630. if ( size )
  1631. {
  1632. *size = nFileSize;
  1633. }
  1634. return new CFiosReadOnlyFile( pFileHandle, nFileSize );
  1635. }
  1636. //-----------------------------------------------------------------------------
  1637. // Purpose: low-level filesystem wrapper
  1638. //-----------------------------------------------------------------------------
  1639. void CFiosReadOnlyFile::FS_fclose()
  1640. {
  1641. if ( m_pHandle != NULL )
  1642. {
  1643. cell::fios::err_t err = cell::fios::scheduler::getDefaultScheduler()->closeFileSync( NULL, m_pHandle );
  1644. if ( err != cell::fios::CELL_FIOS_NOERROR )
  1645. {
  1646. Warning( "[FIOS] Failed to close file.\n" );
  1647. }
  1648. }
  1649. }
  1650. //-----------------------------------------------------------------------------
  1651. // Purpose: low-level filesystem wrapper
  1652. //-----------------------------------------------------------------------------
  1653. void CFiosReadOnlyFile::FS_fseek( int64 pos, int seekType )
  1654. {
  1655. switch ( seekType )
  1656. {
  1657. case SEEK_SET:
  1658. m_nReadPos = pos;
  1659. break;
  1660. case SEEK_CUR:
  1661. m_nReadPos += pos;
  1662. break;
  1663. case SEEK_END:
  1664. m_nReadPos = m_nSize - pos;
  1665. break;
  1666. }
  1667. }
  1668. //-----------------------------------------------------------------------------
  1669. // Purpose: low-level filesystem wrapper
  1670. //-----------------------------------------------------------------------------
  1671. long CFiosReadOnlyFile::FS_ftell()
  1672. {
  1673. return m_nReadPos;
  1674. }
  1675. //-----------------------------------------------------------------------------
  1676. // Purpose: low-level filesystem wrapper
  1677. //-----------------------------------------------------------------------------
  1678. int CFiosReadOnlyFile::FS_feof()
  1679. {
  1680. return ( m_nReadPos >= m_nSize );
  1681. }
  1682. //-----------------------------------------------------------------------------
  1683. // Purpose: low-level filesystem wrapper
  1684. //-----------------------------------------------------------------------------
  1685. // Set the flag to true if any of the op is processing.
  1686. void IsAnyOpProcessing( void *pContext, cell::fios::op *pOp )
  1687. {
  1688. bool * pBool = ( bool * )pContext;
  1689. bool bFinished = pOp->isDone() || pOp->isCancelled();
  1690. *pBool |= ( bFinished == false ); // Mark the ops that are still working
  1691. }
  1692. size_t CFiosReadOnlyFile::FS_fread( void *dest, size_t destSize, size_t size )
  1693. {
  1694. VPROF_BUDGET( "CFiosReadOnlyFile::FS_fread", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  1695. if( ShouldFailIo() )
  1696. return 0;
  1697. if ( size == 0 )
  1698. {
  1699. return 0;
  1700. }
  1701. cell::fios::opattr_t opattr = FIOS_OPATTR_INITIALIZER;
  1702. // The user can disable usage of HDD cache with the ConVar fs_fios_enable_hdd_cache.
  1703. // However in some case, the game will disable its usage temporarily too (with g_bUseFiosHddCache).
  1704. // This can happen if the game is saving. We want to avoid the save system and FIOS to compete for the HDD usage.
  1705. // In that case, IO accesses will be done on the BluRay. It is only temporary (few seconds), and most data should be in memory anyway.
  1706. // We just want to avoid cases where a single data takes several seconds to load.
  1707. if ( fs_fios_enable_hdd_cache.GetBool() && ( g_bUseFiosHddCache == false ) )
  1708. {
  1709. // Display a message so we can detect prolonged incorrect state.
  1710. static uint32 nLastSpew = 0;
  1711. const int SPEW_EVERY_N_MILLISECONDS = 5 * 1000; // Don't need to spew too much. Every 5 seconds is enough for us to detect potential issue.
  1712. uint32 nCurrentTime = Plat_MSTime();
  1713. if ( nCurrentTime > nLastSpew + SPEW_EVERY_N_MILLISECONDS )
  1714. {
  1715. Msg( "Fios HDD accesses disabled as a save is occurring.\n" );
  1716. nLastSpew = nCurrentTime;
  1717. }
  1718. }
  1719. cell::fios::scheduler *pScheduler = cell::fios::scheduler::getDefaultScheduler();
  1720. uint32_t opFlags = cell::fios::kOPF_DONTFILLDISKCACHE; // By default, full cache usage
  1721. // Again another FIOS function "pScheduler->isIdle()" does not work as expected. Implement another work around.
  1722. bool bWorkingOps = false;
  1723. pScheduler->iterateOps( &IsAnyOpProcessing, &bWorkingOps );
  1724. if ( bWorkingOps )
  1725. {
  1726. // It is not idle, it is probably prefetching or doing something else. Let's reduce the HDD usage (read but don't write).
  1727. // If the data is really important, it will be cached later when the scheduler is idle.
  1728. opFlags = cell::fios::kOPF_DONTFILLCACHE;
  1729. }
  1730. opattr.opflags = ( fs_fios_enable_hdd_cache.GetBool() && g_bUseFiosHddCache ) ? opFlags : cell::fios::kOPF_NOCACHE;
  1731. opattr.deadline = kDEADLINE_NOW; // Consider using kDEADLINE_ASAP
  1732. // By using ASAP, the hope is that FIOS will schedule the read in the best manner to reduce seeks
  1733. // NOW could help serve this read better but could reduce overall performance.
  1734. // We use NOW, as the non-persistent prefetches are ASAP
  1735. // And persistent prefetches are LATER (the priority doesn't really apply between prefetches otherwise).
  1736. opattr.priority = cell::fios::kPRIO_DEFAULT;
  1737. opattr.pCallback = 0;
  1738. opattr.opflags = 0;
  1739. opattr.pLicense = 0;
  1740. cell::fios::err_t err = pScheduler->readFileSync( &opattr, m_pHandle, dest, size, m_nReadPos );
  1741. if ( err != cell::fios::CELL_FIOS_NOERROR )
  1742. {
  1743. return 0;
  1744. }
  1745. m_nReadPos += size;
  1746. return size;
  1747. }
  1748. //-----------------------------------------------------------------------------
  1749. // Purpose: low-level filesystem wrapper
  1750. //-----------------------------------------------------------------------------
  1751. char *CFiosReadOnlyFile::FS_fgets( char *dest, int destSize )
  1752. {
  1753. if( ShouldFailIo() )
  1754. return NULL;
  1755. if ( FS_feof() )
  1756. {
  1757. return NULL;
  1758. }
  1759. int nStartPos = m_nReadPos;
  1760. int nBytesRead = FS_fread( dest, destSize, destSize );
  1761. if ( !nBytesRead )
  1762. {
  1763. return NULL;
  1764. }
  1765. dest[imin( nBytesRead, destSize - 1)] = 0;
  1766. char *pNewline = strchr( dest, '\n' );
  1767. if ( pNewline )
  1768. {
  1769. // advance past, leave \n
  1770. pNewline++;
  1771. *pNewline = 0;
  1772. }
  1773. else
  1774. {
  1775. pNewline = &dest[imin( nBytesRead, destSize - 1)];
  1776. }
  1777. m_nReadPos = nStartPos + ( pNewline - dest ) + 1;
  1778. return dest;
  1779. }
  1780. #endif