summaryrefslogtreecommitdiff
path: root/filesystem/filesystem_stdio.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /filesystem/filesystem_stdio.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'filesystem/filesystem_stdio.cpp')
-rw-r--r--filesystem/filesystem_stdio.cpp1606
1 files changed, 1606 insertions, 0 deletions
diff --git a/filesystem/filesystem_stdio.cpp b/filesystem/filesystem_stdio.cpp
new file mode 100644
index 0000000..43916fe
--- /dev/null
+++ b/filesystem/filesystem_stdio.cpp
@@ -0,0 +1,1606 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#include "basefilesystem.h"
+#include "packfile.h"
+#include "tier0/dbg.h"
+#include "tier0/threadtools.h"
+#ifdef _WIN32
+#include "tier0/tslist.h"
+#elif defined(POSIX)
+#include <fcntl.h>
+#ifdef LINUX
+#include <sys/file.h>
+#endif
+#endif
+#include "tier1/convar.h"
+#include "tier0/vcrmode.h"
+#include "tier0/vprof.h"
+#include "tier1/fmtstr.h"
+#include "tier1/utlrbtree.h"
+#include "vstdlib/osversion.h"
+
+#ifdef _X360
+#undef WaitForSingleObject
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ASSERT_INVARIANT( SEEK_CUR == FILESYSTEM_SEEK_CURRENT );
+ASSERT_INVARIANT( SEEK_SET == FILESYSTEM_SEEK_HEAD );
+ASSERT_INVARIANT( SEEK_END == FILESYSTEM_SEEK_TAIL );
+
+//-----------------------------------------------------------------------------
+
+class CFileSystem_Stdio : public CBaseFileSystem
+{
+public:
+ CFileSystem_Stdio();
+ ~CFileSystem_Stdio();
+
+ // Used to get at older versions
+ void *QueryInterface( const char *pInterfaceName );
+
+ // Higher level filesystem methods requiring specific behavior
+ virtual void GetLocalCopy( const char *pFileName );
+ virtual int HintResourceNeed( const char *hintlist, int forgetEverything );
+ virtual bool IsFileImmediatelyAvailable(const char *pFileName);
+ virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
+ virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
+ virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
+ virtual bool IsSteam() const { return false; }
+ virtual FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 ) { return FILESYSTEM_MOUNT_OK; }
+
+ bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign );
+ void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset );
+ void FreeOptimalReadBuffer( void *p );
+
+protected:
+ // implementation of CBaseFileSystem virtual functions
+ virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size );
+ virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
+ virtual void FS_fclose( FILE *fp );
+ virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
+ virtual long FS_ftell( FILE *fp );
+ virtual int FS_feof( FILE *fp );
+ virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
+ virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
+ virtual bool FS_setmode( FILE *fp, FileMode_t mode );
+ virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
+ virtual int FS_ferror( FILE *fp );
+ virtual int FS_fflush( FILE *fp );
+ virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
+ virtual int FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache=NULL );
+ virtual int FS_chmod( const char *path, int pmode );
+ virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
+ virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
+ virtual bool FS_FindClose(HANDLE handle);
+ virtual int FS_GetSectorSize( FILE * );
+
+private:
+ bool CanAsync() const
+ {
+ return m_bCanAsync;
+ }
+
+ bool m_bMounted;
+ bool m_bCanAsync;
+};
+
+
+//-----------------------------------------------------------------------------
+// Per-file worker classes
+//-----------------------------------------------------------------------------
+abstract_class CStdFilesystemFile
+{
+public:
+ virtual ~CStdFilesystemFile() {}
+ virtual void FS_setbufsize( unsigned nBytes ) = 0;
+ virtual void FS_fclose() = 0;
+ virtual void FS_fseek( int64 pos, int seekType ) = 0;
+ virtual long FS_ftell() = 0;
+ virtual int FS_feof() = 0;
+ virtual size_t FS_fread( void *dest, size_t destSize, size_t size ) = 0;
+ virtual size_t FS_fwrite( const void *src, size_t size ) = 0;
+ virtual bool FS_setmode( FileMode_t mode ) = 0;
+ virtual size_t FS_vfprintf( const char *fmt, va_list list ) = 0;
+ virtual int FS_ferror() = 0;
+ virtual int FS_fflush() = 0;
+ virtual char *FS_fgets( char *dest, int destSize ) = 0;
+ virtual int FS_GetSectorSize() { return 1; }
+};
+
+//---------------------------------------------------------
+
+class CStdioFile : public CStdFilesystemFile
+{
+public:
+ static CStdioFile *FS_fopen( const char *filename, const char *options, int64 *size );
+
+ virtual void FS_setbufsize( unsigned nBytes );
+ virtual void FS_fclose();
+ virtual void FS_fseek( int64 pos, int seekType );
+ virtual long FS_ftell();
+ virtual int FS_feof();
+ virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
+ virtual size_t FS_fwrite( const void *src, size_t size );
+ virtual bool FS_setmode( FileMode_t mode );
+ virtual size_t FS_vfprintf( const char *fmt, va_list list );
+ virtual int FS_ferror();
+ virtual int FS_fflush();
+ virtual char *FS_fgets( char *dest, int destSize );
+
+#ifdef POSIX
+ static CUtlMap< ino_t, CThreadMutex * > m_LockedFDMap;
+ static CThreadMutex m_MutexLockedFD;
+#endif
+private:
+ CStdioFile( FILE *pFile, bool bWriteable )
+ : m_pFile( pFile ), m_bWriteable( bWriteable )
+ {
+ }
+
+ FILE *m_pFile;
+ bool m_bWriteable;
+};
+
+#ifdef POSIX
+CUtlMap< ino_t, CThreadMutex * > CStdioFile::m_LockedFDMap;
+CThreadMutex CStdioFile::m_MutexLockedFD;
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifdef _WIN32
+class CWin32ReadOnlyFile : public CStdFilesystemFile
+{
+public:
+ static bool CanOpen( const char *filename, const char *options );
+ static CWin32ReadOnlyFile *FS_fopen( const char *filename, const char *options, int64 *size );
+
+ virtual void FS_setbufsize( unsigned nBytes ) {}
+ virtual void FS_fclose();
+ virtual void FS_fseek( int64 pos, int seekType );
+ virtual long FS_ftell();
+ virtual int FS_feof();
+ virtual size_t FS_fread( void *dest, size_t destSize, size_t size);
+ virtual size_t FS_fwrite( const void *src, size_t size ) { return 0; }
+ virtual bool FS_setmode( FileMode_t mode ) { Error( "Can't set mode, open a second file in right mode\n" ); return false; }
+ virtual size_t FS_vfprintf( const char *fmt, va_list list ) { return 0; }
+ virtual int FS_ferror() { return 0; }
+ virtual int FS_fflush() { return 0; }
+ virtual char *FS_fgets( char *dest, int destSize );
+ virtual int FS_GetSectorSize() { return m_SectorSize; }
+
+private:
+ CWin32ReadOnlyFile( HANDLE hFileUnbuffered, HANDLE hFileBuffered, int sectorSize, int64 fileSize, bool bOverlapped )
+ : m_hFileUnbuffered( hFileUnbuffered ),
+ m_hFileBuffered( hFileBuffered ),
+ m_ReadPos( 0 ),
+ m_Size( fileSize ),
+ m_SectorSize( sectorSize ),
+ m_bOverlapped( bOverlapped )
+ {
+ }
+
+ int64 m_ReadPos;
+ int64 m_Size;
+ HANDLE m_hFileUnbuffered;
+ HANDLE m_hFileBuffered;
+ CThreadFastMutex m_Mutex;
+ int m_SectorSize;
+ bool m_bOverlapped;
+};
+
+#endif
+
+//-----------------------------------------------------------------------------
+// singleton
+//-----------------------------------------------------------------------------
+CFileSystem_Stdio g_FileSystem_Stdio;
+#if defined(_WIN32) && defined(DEDICATED)
+CBaseFileSystem *BaseFileSystem_Stdio( void )
+{
+ return &g_FileSystem_Stdio;
+}
+#endif
+
+#ifdef DEDICATED // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere
+
+IFileSystem *g_pFileSystem = &g_FileSystem_Stdio;
+IBaseFileSystem *g_pBaseFileSystem = &g_FileSystem_Stdio;
+
+#else
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );
+
+#endif
+
+//-----------------------------------------------------------------------------
+
+#ifndef _RETAIL
+bool UseOptimalBufferAllocation()
+{
+ static bool bUseOptimalBufferAllocation = ( IsX360() || ( !IsLinux() && Q_stristr( Plat_GetCommandLine(), "-unbuffered_io" ) != NULL ) );
+ return bUseOptimalBufferAllocation;
+}
+ConVar filesystem_unbuffered_io( "filesystem_unbuffered_io", "1", 0, "" );
+#define UseUnbufferedIO() ( UseOptimalBufferAllocation() && filesystem_unbuffered_io.GetBool() )
+#else
+#define UseUnbufferedIO() true
+#endif
+
+ConVar filesystem_native( "filesystem_native", "1", 0, "Use native FS or STDIO" );
+ConVar filesystem_max_stdio_read( "filesystem_max_stdio_read", IsX360() ? "64" : "16", 0, "" );
+ConVar filesystem_report_buffered_io( "filesystem_report_buffered_io", "0" );
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CFileSystem_Stdio::CFileSystem_Stdio()
+{
+ m_bMounted = false;
+ m_bCanAsync = true;
+#ifdef POSIX
+ SetDefLessFunc( CStdioFile::m_LockedFDMap );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CFileSystem_Stdio::~CFileSystem_Stdio()
+{
+#ifdef POSIX
+ FOR_EACH_MAP_FAST( CStdioFile::m_LockedFDMap, i )
+ {
+ Assert( CStdioFile::m_LockedFDMap[ i ] );
+ delete CStdioFile::m_LockedFDMap[ i ];
+ }
+ CStdioFile::m_LockedFDMap.RemoveAll();
+#endif
+
+ Assert(!m_bMounted);
+}
+
+
+//-----------------------------------------------------------------------------
+// QueryInterface:
+//-----------------------------------------------------------------------------
+void *CFileSystem_Stdio::QueryInterface( const char *pInterfaceName )
+{
+ // We also implement the IMatSystemSurface interface
+ if (!Q_strncmp( pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
+ return (IFileSystem*)this;
+
+ return CBaseFileSystem::QueryInterface( pInterfaceName );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign )
+{
+ unsigned sectorSize;
+
+ if ( hFile && UseOptimalBufferAllocation() )
+ {
+ CFileHandle *fh = ( CFileHandle *)hFile;
+ sectorSize = fh->GetSectorSize();
+
+ if ( !sectorSize || ( fh->m_pPackFileHandle && ( fh->m_pPackFileHandle->AbsoluteBaseOffset() % sectorSize ) ) )
+ {
+ sectorSize = 1;
+ }
+ }
+ else
+ {
+ sectorSize = 1;
+ }
+
+ if ( pOffsetAlign )
+ {
+ *pOffsetAlign = sectorSize;
+ }
+
+ if ( pSizeAlign )
+ {
+ *pSizeAlign = sectorSize;
+ }
+
+ if ( pBufferAlign )
+ {
+ if ( IsX360() )
+ {
+ *pBufferAlign = 4;
+ }
+ else
+ {
+ *pBufferAlign = sectorSize;
+ }
+ }
+
+ return ( sectorSize > 1 );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void *CFileSystem_Stdio::AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset )
+{
+ if ( !UseOptimalBufferAllocation() )
+ {
+ return CBaseFileSystem::AllocOptimalReadBuffer( hFile, nSize, nOffset );
+ }
+
+ unsigned sectorSize;
+ if ( hFile != FILESYSTEM_INVALID_HANDLE )
+ {
+ CFileHandle *fh = ( CFileHandle *)hFile;
+ sectorSize = fh->GetSectorSize();
+
+ if ( !nSize )
+ {
+ nSize = fh->Size();
+ }
+
+ if ( fh->m_pPackFileHandle )
+ {
+ nOffset += fh->m_pPackFileHandle->AbsoluteBaseOffset();
+ }
+ }
+ else
+ {
+ // an invalid handle gets a fake "optimal" but valid buffer
+ // this path is for a caller that isn't doing i/o,
+ // but needs an "optimal" buffer that can end up passed to FreeOptimalReadBuffer()
+ sectorSize = 4;
+ }
+
+ bool bOffsetIsAligned = ( nOffset % sectorSize == 0 );
+ unsigned nAllocSize = ( bOffsetIsAligned ) ? AlignValue( nSize, sectorSize ) : nSize;
+
+ if ( IsX360() )
+ {
+ return malloc( nAllocSize );
+ }
+ else
+ {
+ unsigned nAllocAlignment = ( bOffsetIsAligned ) ? sectorSize : 4;
+ return _aligned_malloc( nAllocSize, nAllocAlignment );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::FreeOptimalReadBuffer( void *p )
+{
+ if ( !UseOptimalBufferAllocation() )
+ {
+ CBaseFileSystem::FreeOptimalReadBuffer( p );
+ return;
+ }
+
+ if ( p )
+ {
+ if ( IsX360() )
+ {
+ free( p );
+ }
+ else
+ {
+ _aligned_free( p );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+FILE *CFileSystem_Stdio::FS_fopen( const char *filenameT, const char *options, unsigned flags, int64 *size )
+{
+ CStdFilesystemFile *pFile = NULL;
+
+ char filename[ MAX_PATH ];
+
+ CBaseFileSystem::FixUpPath ( filenameT, filename, sizeof( filename ) );
+
+#ifdef _WIN32
+ if ( CWin32ReadOnlyFile::CanOpen( filename, options ) )
+ {
+ pFile = CWin32ReadOnlyFile::FS_fopen( filename, options, size );
+ if ( pFile )
+ {
+ return (FILE *)pFile;
+ }
+ }
+#endif
+
+ pFile = CStdioFile::FS_fopen( filename, options, size );
+
+ return (FILE *)pFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::FS_setbufsize( FILE *fp, unsigned nBytes )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ pFile->FS_setbufsize( nBytes );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::FS_fclose( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+
+ pFile->FS_fclose();
+ delete pFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::FS_fseek( FILE *fp, int64 pos, int seekType )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+
+ pFile->FS_fseek( pos, seekType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+long CFileSystem_Stdio::FS_ftell( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_ftell();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_feof( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_feof();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Stdio::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
+ if( ThreadInMainThread() )
+ {
+ tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
+ }
+
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ size_t nBytesRead = pFile->FS_fread( dest, destSize, size);
+
+ Trace_FRead( nBytesRead, fp );
+
+ return nBytesRead;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Stdio::FS_fwrite( const void *src, size_t size, FILE *fp )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
+ if( ThreadInMainThread() )
+ {
+ tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesWrite" );
+ }
+
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+
+ size_t nBytesWritten = pFile->FS_fwrite(src, size);
+
+ return nBytesWritten;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::FS_setmode( FILE *fp, FileMode_t mode )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_setmode( mode );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CFileSystem_Stdio::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_vfprintf(fmt, list);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_ferror( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_ferror();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_fflush( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_fflush();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+char *CFileSystem_Stdio::FS_fgets( char *dest, int destSize, FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_fgets(dest, destSize);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *path -
+// pmode -
+// Output : int
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_chmod( const char *pathT, int pmode )
+{
+ if ( !pathT )
+ return -1;
+
+ char path[ MAX_PATH ];
+
+ CBaseFileSystem::FixUpPath ( pathT, path, sizeof( path ) );
+
+ int rt = _chmod( path, pmode );
+#if defined(LINUX)
+ if (rt==-1)
+ {
+ char caseFixedName[ MAX_PATH ];
+ const bool found = findFileInDirCaseInsensitive_safe( path, caseFixedName );
+ if ( found )
+ {
+ rt=_chmod( caseFixedName, pmode );
+ }
+ }
+#endif
+ return rt;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: A replacement for _stat() backed by GetFileAttributesEx for XP users
+//
+// Workaround for:
+// https://connect.microsoft.com/VisualStudio/feedback/details/1600505/stat-not-working-on-windows-xp-using-v14-xp-platform-toolset-vs2015
+//
+// This is not well tested or meant to be a proper implementation of stat(), but rather a band-aid for XP users only
+// until microsoft pushes a runtime update to fix above issue :-/
+//-----------------------------------------------------------------------------
+#if defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
+static int WindowsXPStatShim( const char *pathT, struct _stat *buf )
+{
+ WIN32_FILE_ATTRIBUTE_DATA fileAttributes;
+ if ( !GetFileAttributesEx(pathT, GetFileExInfoStandard, &fileAttributes) )
+ {
+ *_errno() = ENOENT;
+ return -1;
+ }
+
+ memset( buf, 0, sizeof(struct _stat) );
+
+ // Mode
+ unsigned short permBits = _S_IREAD; // If GetFileAttributes let us see it, we can read it. I think.
+ if ( fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ buf->st_mode |= _S_IFDIR;
+ permBits |= _S_IEXEC;
+ }
+ else
+ {
+ buf->st_mode |= _S_IFREG;
+
+ const char *pExt = V_GetFileExtension( pathT );
+ if ( V_strcasecmp( pExt, "exe" ) == 0 ||
+ V_strcasecmp( pExt, "bat" ) == 0 ||
+ V_strcasecmp( pExt, "com" ) == 0 ||
+ V_strcasecmp( pExt, "cmd" ) == 0 )
+ {
+ // Windows stat seems to set this flag for these extensions
+ permBits |= _S_IEXEC;
+ }
+ }
+
+ if ( fileAttributes.dwFileAttributes & FILE_ATTRIBUTE_READONLY )
+ {
+ permBits |= S_IWRITE;
+ }
+
+ // Duplicate permission bits to user/group/world (windows stat doesn't care)
+ buf->st_mode |= permBits | permBits >> 3 | permBits >> 6;
+
+ // Device is just drive-letter-index according to msdn
+ char driveLetter = tolower( pathT[ 0 ] );
+ if ( driveLetter >= 'a' && driveLetter <= 'z' && pathT[1] == ':' )
+ {
+ unsigned char driveIdx = driveLetter - 'a';
+ buf->st_dev = driveIdx;
+ buf->st_rdev = driveIdx;
+ }
+ else
+ {
+ buf->st_dev = _getdrive();
+ buf->st_rdev = _getdrive();
+ }
+
+ buf->st_nlink = 1;
+ buf->st_size = (uint64_t)fileAttributes.nFileSizeHigh << 32 | fileAttributes.nFileSizeLow;
+ // The 90s was a hell of a time I guess.
+ uint64_t actualAccessTime = (uint64_t)fileAttributes.ftLastAccessTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftLastAccessTime.dwLowDateTime;
+ uint64_t actualModTime = (uint64_t)fileAttributes.ftLastWriteTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftLastWriteTime.dwLowDateTime;
+ uint64_t actualCreationTime = (uint64_t)fileAttributes.ftCreationTime.dwHighDateTime << 32 | (uint64_t)fileAttributes.ftCreationTime.dwLowDateTime;
+ uint64_t ullMSUniverseToEveryoneElseUniverse = (369 * 365 + 89) * 86400ull; // okay
+ buf->st_atime = actualAccessTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
+ buf->st_mtime = actualModTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
+ buf->st_ctime = actualCreationTime / 10000000ull - ullMSUniverseToEveryoneElseUniverse;
+
+ // st_uid/st_gid always 0 on windows
+
+ return 0;
+}
+#endif // defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_stat( const char *pathT, struct _stat *buf, bool *pbLoadedFromSteamCache )
+{
+ if ( pbLoadedFromSteamCache )
+ *pbLoadedFromSteamCache = false;
+
+ if ( !pathT )
+ {
+ return -1;
+ }
+
+ char path[ MAX_PATH ];
+
+ CBaseFileSystem::FixUpPath ( pathT, path, sizeof( path ) );
+
+ int rt = _stat( path, buf );
+
+ // Workaround bug wherein stat() randomly fails on Windows XP. See comment on function.
+#if defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
+ if ( rt == -1 )
+ {
+ EOSType eOSType = GetOSType();
+ if ( eOSType == k_eWin2000 || eOSType == k_eWinXP || eOSType == k_eWin2003 )
+ {
+ rt = WindowsXPStatShim( path, buf );
+ }
+ }
+#endif // defined(_WIN32) && defined(FILESYSTEM_MSVC2015_STAT_BUG_WORKAROUND)
+
+#if defined(LINUX)
+ if ( rt == -1 )
+ {
+ char caseFixedName[ MAX_PATH ];
+ bool found = findFileInDirCaseInsensitive_safe( path, caseFixedName );
+ if ( found )
+ {
+ rt = _stat( caseFixedName, buf );
+ }
+ }
+#endif
+ return rt;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+HANDLE CFileSystem_Stdio::FS_FindFirstFile(const char *findnameT, WIN32_FIND_DATA *dat)
+{
+ char findname[ MAX_PATH ];
+
+ CBaseFileSystem::FixUpPath ( findnameT, findname, sizeof( findname ) );
+
+ return ::FindFirstFile(findname, dat);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
+{
+
+ if (INVALID_HANDLE_VALUE == handle) // invalid handle should return false
+ return false;
+
+ return (::FindNextFile(handle, dat) != 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::FS_FindClose(HANDLE handle)
+{
+ return (::FindClose(handle) != 0);
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::FS_GetSectorSize( FILE *fp )
+{
+ CStdFilesystemFile *pFile = ((CStdFilesystemFile *)fp);
+ return pFile->FS_GetSectorSize();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: files are always immediately available on disk
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::IsFileImmediatelyAvailable(const char *pFileName)
+{
+ return true;
+}
+
+// enable this if you want the stdio filesystem to pretend it's steam, and make people wait for resources
+//#define DEBUG_WAIT_FOR_RESOURCES_API
+
+#if defined(DEBUG_WAIT_FOR_RESOURCES_API)
+static float g_flDebugProgress = 0.0f;
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+WaitForResourcesHandle_t CFileSystem_Stdio::WaitForResources( const char *resourcelist )
+{
+#if defined(DEBUG_WAIT_FOR_RESOURCES_API)
+ g_flDebugProgress = 0.0f;
+#endif
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+bool CFileSystem_Stdio::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
+{
+#if defined(DEBUG_WAIT_FOR_RESOURCES_API)
+ g_flDebugProgress += 0.002f;
+ if (g_flDebugProgress < 1.0f)
+ {
+ *progress = g_flDebugProgress;
+ *complete = false;
+ return true;
+ }
+#endif
+
+ // always return that we're complete
+ *progress = 0.0f;
+ *complete = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: steam call, unnecessary in stdio
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::CancelWaitForResources( WaitForResourcesHandle_t handle )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CFileSystem_Stdio::GetLocalCopy( const char *pFileName )
+{
+ // do nothing. . everything is local.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CFileSystem_Stdio::HintResourceNeed( const char *hintlist, int forgetEverything )
+{
+ // do nothing. . everything is local.
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+CStdioFile *CStdioFile::FS_fopen( const char *filenameT, const char *options, int64 *size )
+{
+ FILE *pFile = NULL;
+ char *p = NULL;
+ char filename[MAX_PATH];
+ struct _stat buf;
+
+ V_strncpy( filename, filenameT, sizeof(filename) );
+
+ // stop newline characters at end of filename
+ p = strchr( filename, '\n' );
+ if ( p )
+ *p = '\0';
+ p = strchr( filename, '\r' );
+ if ( p )
+ *p = '\0';
+
+
+ pFile = fopen(filename, options);
+ if (pFile && size)
+ {
+ // todo: replace with filelength()?
+ int rt = _stat( filename, &buf );
+ if (rt == 0)
+ {
+ *size = buf.st_size;
+ }
+ }
+
+#if defined(LINUX)
+ if(!pFile && !strchr(options,'w') && !strchr(options,'+') ) // try opening the lower cased version
+ {
+ char caseFixedName[ MAX_PATH ];
+ bool found = findFileInDirCaseInsensitive_safe( filename, caseFixedName );
+ if ( found )
+ {
+ pFile = fopen( caseFixedName, options );
+
+ if (pFile && size)
+ {
+ // todo: replace with filelength()?
+ struct _stat buf;
+ int rt = _stat( caseFixedName, &buf );
+ if (rt == 0)
+ {
+ *size = buf.st_size;
+ }
+ }
+ }
+ }
+#endif
+
+ if ( pFile )
+ {
+ bool bWriteable = false;
+ if ( strchr(options,'w') || strchr(options,'a') )
+ bWriteable = true;
+
+#if defined POSIX
+ if ( bWriteable )
+ {
+ CThreadMutex *pMutex = NULL;
+
+ {
+ AUTO_LOCK( m_MutexLockedFD );
+ // Win32 has an undocumented feature that is serialized ALL writes to a file across threads (i.e only 1 thread can open a file at a time)
+ // so add a lock here to mimic that behavior
+
+ int iLockID = m_LockedFDMap.Find( buf.st_ino );
+ if ( iLockID != m_LockedFDMap.InvalidIndex() )
+ {
+ pMutex = m_LockedFDMap[iLockID];
+ }
+ else
+ {
+ CThreadMutex *newMutex = new CThreadMutex;
+ pMutex = m_LockedFDMap[m_LockedFDMap.Insert( buf.st_ino, newMutex )];
+ }
+ }
+ // grab the lock once we have UNLOCKED m_MutexLockedFD so we don't deadlock on a close
+ pMutex->Lock();
+
+ rewind( pFile );
+
+ // we need to get the file size again after the lock returns
+ if (pFile && size)
+ {
+ int rt = _stat( filename, &buf );
+ if (rt == 0)
+ {
+ *size = buf.st_size;
+ }
+ }
+
+ }
+#endif
+ return new CStdioFile( pFile, bWriteable );
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CStdioFile::FS_setbufsize( unsigned nBytes )
+{
+#ifdef _WIN32
+ if ( nBytes )
+ {
+ setvbuf( m_pFile, NULL, _IOFBF, 32768 );
+ }
+ else
+ {
+ setvbuf( m_pFile, NULL, _IONBF, 0 );
+#if defined(_MSC_VER) && ( _MSC_VER < 1900 )
+ // hack to make microsoft stdio not always read one stray byte on odd sized files
+ m_pFile->_bufsiz = 1;
+#endif
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CStdioFile::FS_fclose()
+{
+#ifdef POSIX
+ if ( m_bWriteable )
+ {
+ AUTO_LOCK( m_MutexLockedFD );
+
+ struct _stat buf;
+ int fd = fileno_unlocked( m_pFile );
+ fstat( fd, &buf );
+
+ fflush( m_pFile );
+ int iLockID = m_LockedFDMap.Find( buf.st_ino );
+ if ( iLockID != m_LockedFDMap.InvalidIndex() )
+ {
+ m_LockedFDMap[iLockID]->Unlock();
+ }
+ }
+#endif
+ fclose(m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CStdioFile::FS_fseek( int64 pos, int seekType )
+{
+ fseek( m_pFile, pos, seekType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+long CStdioFile::FS_ftell()
+{
+ return ftell(m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CStdioFile::FS_feof()
+{
+ return feof(m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CStdioFile::FS_fread( void *dest, size_t destSize, size_t size )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
+ if( ThreadInMainThread() )
+ {
+ tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
+ }
+
+ // read (size) of bytes to ensure truncated reads returns bytes read and not 0
+ return fread( dest, 1, size, m_pFile );
+}
+
+
+#define WRITE_CHUNK (256 * 1024)
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//
+// This routine breaks data into chunks if the amount to be written is beyond WRITE_CHUNK (256kb)
+// Windows can fail on monolithic writes of ~12MB or more, so we work around that here
+//-----------------------------------------------------------------------------
+size_t CStdioFile::FS_fwrite( const void *src, size_t size )
+{
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
+ if( ThreadInMainThread() )
+ {
+ tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesWrite" );
+ }
+
+ if ( size > WRITE_CHUNK )
+ {
+ size_t remaining = size;
+ const byte* current = (const byte *) src;
+ size_t total = 0;
+
+ while ( remaining > 0 )
+ {
+ size_t bytesToCopy = min(remaining, (size_t)WRITE_CHUNK);
+
+ total += fwrite(current, 1, bytesToCopy, m_pFile);
+
+ remaining -= bytesToCopy;
+ current += bytesToCopy;
+ }
+
+ Assert( total == size );
+ return total;
+ }
+
+ return fwrite(src, 1, size, m_pFile);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+bool CStdioFile::FS_setmode( FileMode_t mode )
+{
+#ifdef _WIN32
+ int fd = _fileno( m_pFile );
+ int newMode = ( mode == FM_BINARY ) ? _O_BINARY : _O_TEXT;
+ return ( _setmode( fd, newMode) != -1 );
+#else
+ return false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CStdioFile::FS_vfprintf( const char *fmt, va_list list )
+{
+ return vfprintf(m_pFile, fmt, list);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CStdioFile::FS_ferror()
+{
+ return ferror(m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CStdioFile::FS_fflush()
+{
+ return fflush(m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+char *CStdioFile::FS_fgets( char *dest, int destSize )
+{
+ return fgets(dest, destSize, m_pFile);
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+#ifdef _WIN32
+
+ConVar filesystem_use_overlapped_io( "filesystem_use_overlapped_io", "1", 0, "" );
+#define UseOverlappedIO() filesystem_use_overlapped_io.GetBool()
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+int GetSectorSize( const char *pszFilename )
+{
+ if ( ( !pszFilename[0] || !pszFilename[1] ) ||
+ ( pszFilename[0] == '\\' && pszFilename[1] == '\\' ) ||
+ ( pszFilename[0] == '/' && pszFilename[1] == '/' ) )
+ {
+ // Cannot determine sector size with a UNC path (need volume identifier)
+ return 0;
+ }
+
+ if ( IsX360() )
+ {
+ // purposely dvd centric, which is also the worst case
+ return XBOX_DVD_SECTORSIZE;
+ }
+
+#if defined( _WIN32 ) && !defined( FILESYSTEM_STEAM ) && !defined( _X360 )
+ char szAbsoluteFilename[MAX_FILEPATH];
+ if ( pszFilename[1] != ':' )
+ {
+ Q_MakeAbsolutePath( szAbsoluteFilename, sizeof(szAbsoluteFilename), pszFilename );
+ pszFilename = szAbsoluteFilename;
+ }
+
+ DWORD sectorSize = 1;
+
+ struct DriveSectorSize_t
+ {
+ char volume;
+ DWORD sectorSize;
+ };
+
+ static DriveSectorSize_t cachedSizes[4];
+
+ char volume = tolower( *pszFilename );
+
+ int i;
+ for ( i = 0; i < ARRAYSIZE(cachedSizes) && cachedSizes[i].volume; i++ )
+ {
+ if ( cachedSizes[i].volume == volume )
+ {
+ sectorSize = cachedSizes[i].sectorSize;
+ break;
+ }
+ }
+
+ if ( sectorSize == 1 )
+ {
+ char root[4] = "X:\\";
+ root[0] = *pszFilename;
+
+ DWORD ignored;
+ if ( !GetDiskFreeSpace( root, &ignored, &sectorSize, &ignored, &ignored ) )
+ {
+ sectorSize = 0;
+ }
+
+ if ( i < ARRAYSIZE(cachedSizes) )
+ {
+ cachedSizes[i].volume = volume;
+ cachedSizes[i].sectorSize = sectorSize;
+ }
+ }
+
+ return sectorSize;
+#else
+ return 0;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+class CThreadIOEventPool
+{
+public:
+ ~CThreadIOEventPool()
+ {
+ CThreadEvent *pEvent;
+
+ while ( m_Events.PopItem( &pEvent ) )
+ {
+ delete pEvent;
+ }
+ }
+
+ CThreadEvent *GetEvent()
+ {
+ CThreadEvent *pEvent;
+
+ if ( m_Events.PopItem( &pEvent ) )
+ {
+ return pEvent;
+ }
+
+ return new CThreadEvent;
+ }
+
+ void ReleaseEvent( CThreadEvent *pEvent )
+ {
+ m_Events.PushItem( pEvent );
+ }
+
+private:
+ CTSList<CThreadEvent *> m_Events;
+};
+
+
+CThreadIOEventPool g_ThreadIOEvents;
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CWin32ReadOnlyFile::CanOpen( const char *filename, const char *options )
+{
+ return ( options[0] == 'r' && options[1] == 'b' && options[2] == 0 && filesystem_native.GetBool() );
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+
+static HANDLE OpenWin32File( const char *filename, bool bOverlapped, bool bUnbuffered, int64 *pFileSize )
+{
+ HANDLE hFile;
+
+ DWORD createFlags = FILE_ATTRIBUTE_NORMAL;
+
+ if ( bOverlapped )
+ {
+ createFlags |= FILE_FLAG_OVERLAPPED;
+ }
+
+ if ( bUnbuffered )
+ {
+ createFlags |= FILE_FLAG_NO_BUFFERING;
+ }
+
+ hFile = ::CreateFile( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, createFlags, NULL );
+ if ( hFile != INVALID_HANDLE_VALUE && !*pFileSize )
+ {
+ LARGE_INTEGER fileSize;
+ if ( !GetFileSizeEx( hFile, &fileSize ) )
+ {
+ CloseHandle( hFile );
+ hFile = INVALID_HANDLE_VALUE;
+ }
+ *pFileSize = fileSize.QuadPart;
+ }
+ return hFile;
+}
+
+CWin32ReadOnlyFile *CWin32ReadOnlyFile::FS_fopen( const char *filename, const char *options, int64 *size )
+{
+ Assert( CanOpen( filename, options ) );
+
+ int sectorSize = 0;
+ bool bTryUnbuffered = ( UseUnbufferedIO() && ( sectorSize = GetSectorSize( filename ) ) != 0 );
+ bool bOverlapped = UseOverlappedIO();
+
+ HANDLE hFileUnbuffered = INVALID_HANDLE_VALUE;
+ int64 fileSize = 0;
+
+ if ( bTryUnbuffered )
+ {
+ hFileUnbuffered = OpenWin32File( filename, bOverlapped, true, &fileSize );
+ if ( hFileUnbuffered == INVALID_HANDLE_VALUE )
+ {
+ return NULL;
+ }
+ }
+
+ HANDLE hFileBuffered = OpenWin32File( filename, bOverlapped, false, &fileSize );
+ if ( hFileBuffered == INVALID_HANDLE_VALUE )
+ {
+ if ( hFileUnbuffered != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( hFileUnbuffered );
+ }
+ return NULL;
+ }
+
+ if ( size )
+ {
+ *size = fileSize;
+ }
+
+ return new CWin32ReadOnlyFile( hFileUnbuffered, hFileBuffered, ( sectorSize ) ? sectorSize : 1, fileSize, bOverlapped );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CWin32ReadOnlyFile::FS_fclose()
+{
+ if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( m_hFileUnbuffered );
+ }
+
+ if ( m_hFileBuffered != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle( m_hFileBuffered );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+void CWin32ReadOnlyFile::FS_fseek( int64 pos, int seekType )
+{
+ switch ( seekType )
+ {
+ case SEEK_SET:
+ m_ReadPos = pos;
+ break;
+
+ case SEEK_CUR:
+ m_ReadPos += pos;
+ break;
+
+ case SEEK_END:
+ m_ReadPos = m_Size - pos;
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+long CWin32ReadOnlyFile::FS_ftell()
+{
+ return m_ReadPos;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+int CWin32ReadOnlyFile::FS_feof()
+{
+ return ( m_ReadPos >= m_Size );
+}
+
+// ends up on a thread's stack, don't blindly increase without awareness of that implication
+// 360 threads have small stacks, using small buffer of the worst case quantum sector size
+#if !defined( _X360 )
+#define READ_TEMP_BUFFER ( 32*1024 )
+#else
+#define READ_TEMP_BUFFER ( 2*XBOX_DVD_SECTORSIZE )
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+size_t CWin32ReadOnlyFile::FS_fread( void *dest, size_t destSize, size_t size )
+{
+ VPROF_BUDGET( "CWin32ReadOnlyFile::FS_fread", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
+ tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s %t", __FUNCTION__, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) );
+ if( ThreadInMainThread() )
+ {
+ tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, size, "FileBytesRead" );
+ }
+
+ if ( !size || ( m_hFileUnbuffered == INVALID_HANDLE_VALUE && m_hFileBuffered == INVALID_HANDLE_VALUE ) )
+ {
+ return 0;
+ }
+
+ CThreadEvent *pEvent = NULL;
+
+ if ( destSize == (size_t)-1 )
+ {
+ destSize = size;
+ }
+
+ byte tempBuffer[READ_TEMP_BUFFER];
+ HANDLE hReadFile = m_hFileBuffered;
+ int nBytesToRead = size;
+ byte *pDest = (byte *)dest;
+ int64 offset = m_ReadPos;
+
+ if ( m_hFileUnbuffered != INVALID_HANDLE_VALUE )
+ {
+ const int destBaseAlign = ( IsX360() ) ? 4 : m_SectorSize;
+ bool bDestBaseIsAligned = ( (DWORD)dest % destBaseAlign == 0 );
+ bool bCanReadUnbufferedDirect = ( bDestBaseIsAligned && ( destSize % m_SectorSize == 0 ) && ( m_ReadPos % m_SectorSize == 0 ) );
+
+ if ( bCanReadUnbufferedDirect )
+ {
+ // fastest path, unbuffered
+ nBytesToRead = AlignValue( size, m_SectorSize );
+ hReadFile = m_hFileUnbuffered;
+ }
+ else
+ {
+ // not properly aligned, snap to alignments
+ // attempt to perform single unbuffered operation using stack buffer
+ int64 alignedOffset = AlignValue( ( m_ReadPos - m_SectorSize ) + 1, m_SectorSize );
+ unsigned int alignedBytesToRead = AlignValue( ( m_ReadPos - alignedOffset ) + size, m_SectorSize );
+ if ( alignedBytesToRead <= sizeof( tempBuffer ) - destBaseAlign )
+ {
+ // read operation can be performed as unbuffered follwed by a post fixup
+ nBytesToRead = alignedBytesToRead;
+ offset = alignedOffset;
+ pDest = AlignValue( tempBuffer, destBaseAlign );
+ hReadFile = m_hFileUnbuffered;
+ }
+ }
+ }
+
+ OVERLAPPED overlapped = { 0 };
+ if ( m_bOverlapped )
+ {
+ pEvent = g_ThreadIOEvents.GetEvent();
+ overlapped.hEvent = *pEvent;
+ }
+
+#ifdef REPORT_BUFFERED_IO
+ if ( hReadFile == m_hFileBuffered && filesystem_report_buffered_io.GetBool() )
+ {
+ Msg( "Buffered Operation :(\n" );
+ }
+#endif
+
+ // some disk drivers will fail if read is too large
+ static int MAX_READ = filesystem_max_stdio_read.GetInt()*1024*1024;
+ const int MIN_READ = 64*1024;
+ bool bReadOk = true;
+ DWORD nBytesRead = 0;
+ size_t result = 0;
+ int64 currentOffset = offset;
+
+ while ( bReadOk && nBytesToRead > 0 )
+ {
+ int nCurBytesToRead = min( nBytesToRead, MAX_READ );
+ DWORD nCurBytesRead = 0;
+
+ overlapped.Offset = currentOffset & 0xFFFFFFFF;
+ overlapped.OffsetHigh = ( currentOffset >> 32 ) & 0xFFFFFFFF;
+
+ bReadOk = ( ::ReadFile( hReadFile, pDest + nBytesRead, nCurBytesToRead, &nCurBytesRead, &overlapped ) != 0 );
+ if ( !bReadOk )
+ {
+ if ( m_bOverlapped && GetLastError() == ERROR_IO_PENDING )
+ {
+ bReadOk = true;
+ }
+ }
+
+ if ( bReadOk )
+ {
+ if ( !m_bOverlapped || GetOverlappedResult( hReadFile, &overlapped, &nCurBytesRead, true ) )
+ {
+ nBytesRead += nCurBytesRead;
+ nBytesToRead -= nCurBytesRead;
+ currentOffset += nCurBytesRead;
+ }
+ else
+ {
+ if ( m_bOverlapped )
+ {
+ if ( GetLastError() == ERROR_HANDLE_EOF )
+ {
+ nBytesToRead = 0; // we have hit the end of the file
+ }
+ else
+ {
+ bReadOk = false;
+ }
+ }
+ else
+ {
+ bReadOk = false;
+ }
+ }
+
+ if ( !m_bOverlapped && nCurBytesRead == 0 )
+ {
+ nBytesToRead = 0; // we have hit the end of the file
+ }
+
+ }
+
+ if ( nBytesToRead > 0 && nCurBytesRead == 0 ) // if you failed to ready anything this time then bail the loop
+ {
+ DevMsg( "Got zero length read" );
+ bReadOk = false;
+ }
+ else if ( !bReadOk )
+ {
+ DWORD dwError = GetLastError();
+
+ if ( IsX360() )
+ {
+ if ( dwError == ERROR_DISK_CORRUPT || dwError == ERROR_FILE_CORRUPT )
+ {
+ FSDirtyDiskReportFunc_t func = g_FileSystem_Stdio.GetDirtyDiskReportFunc();
+ if ( func )
+ {
+ func();
+ result = 0;
+ }
+ }
+ }
+
+ if ( dwError == ERROR_NO_SYSTEM_RESOURCES && MAX_READ > MIN_READ )
+ {
+ MAX_READ /= 2;
+ bReadOk = true;
+ DevMsg( "ERROR_NO_SYSTEM_RESOURCES: Reducing max read to %d bytes\n", MAX_READ );
+ }
+ else
+ {
+ DevMsg( "Unknown read error %d\n", dwError );
+ }
+ }
+ }
+
+ if ( bReadOk )
+ {
+ if ( nBytesRead && hReadFile == m_hFileUnbuffered && pDest != dest )
+ {
+ int nBytesExtra = ( m_ReadPos - offset );
+ nBytesRead -= nBytesExtra;
+ if ( nBytesRead )
+ {
+ memcpy( dest, (byte *)pDest + nBytesExtra, size );
+ }
+ }
+
+ result = min( (size_t)nBytesRead, size );
+ }
+
+ if ( m_bOverlapped )
+ {
+ pEvent->Reset();
+ g_ThreadIOEvents.ReleaseEvent( pEvent );
+ }
+
+ m_ReadPos += result;
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: low-level filesystem wrapper
+//-----------------------------------------------------------------------------
+char *CWin32ReadOnlyFile::FS_fgets( char *dest, int destSize )
+{
+ if ( FS_feof() )
+ {
+ return NULL;
+ }
+ int nStartPos = m_ReadPos;
+ int nBytesRead = FS_fread( dest, destSize, destSize );
+ if ( !nBytesRead )
+ {
+ return NULL;
+ }
+
+ dest[min( nBytesRead, destSize - 1)] = 0;
+ char *pNewline = strchr( dest, '\n' );
+ if ( pNewline )
+ {
+ // advance past, leave \n
+ pNewline++;
+ *pNewline = 0;
+ }
+ else
+ {
+ pNewline = &dest[min( nBytesRead, destSize - 1)];
+ }
+ m_ReadPos = nStartPos + ( pNewline - dest ) + 1;
+
+ return dest;
+}
+
+
+#endif // _WIN32