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.
1003 lines
35 KiB
1003 lines
35 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
|
|
#ifndef BASEFILESYSTEM_H |
|
#define BASEFILESYSTEM_H |
|
|
|
#ifdef _WIN32 |
|
#pragma once |
|
#endif |
|
|
|
#if defined( _WIN32 ) |
|
|
|
#if !defined( _X360 ) |
|
#include <io.h> |
|
#include <direct.h> |
|
#define WIN32_LEAN_AND_MEAN |
|
#include <windows.h> |
|
#endif |
|
#undef GetCurrentDirectory |
|
#undef GetJob |
|
#undef AddJob |
|
|
|
#include "tier0/threadtools.h" |
|
#include <stdio.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <malloc.h> |
|
#include <string.h> |
|
#include "tier1/utldict.h" |
|
|
|
#elif defined(POSIX) |
|
#include <unistd.h> // unlink |
|
#include "linux_support.h" |
|
#define INVALID_HANDLE_VALUE (void *)-1 |
|
|
|
// undo the prepended "_" 's |
|
#define _chmod chmod |
|
#define _stat stat |
|
#define _alloca alloca |
|
#define _S_IFDIR S_IFDIR |
|
#endif |
|
|
|
#include <time.h> |
|
#include "refcount.h" |
|
#include "filesystem.h" |
|
#include "tier1/utlvector.h" |
|
#include <stdarg.h> |
|
#include "tier1/utlhashtable.h" |
|
#include "tier1/utlrbtree.h" |
|
#include "tier1/utlsymbol.h" |
|
#include "tier1/utllinkedlist.h" |
|
#include "tier1/utlstring.h" |
|
#include "tier1/UtlSortVector.h" |
|
#include "bspfile.h" |
|
#include "tier1/utldict.h" |
|
#include "tier1/tier1.h" |
|
#include "byteswap.h" |
|
#include "threadsaferefcountedobject.h" |
|
#include "filetracker.h" |
|
// #include "filesystem_init.h" |
|
|
|
#if defined( SUPPORT_PACKED_STORE ) |
|
#include "vpklib/packedstore.h" |
|
#endif |
|
|
|
#include <time.h> |
|
|
|
#include "tier0/memdbgon.h" |
|
|
|
#ifdef _WIN32 |
|
#define CORRECT_PATH_SEPARATOR '\\' |
|
#define INCORRECT_PATH_SEPARATOR '/' |
|
#elif defined(POSIX) |
|
#define CORRECT_PATH_SEPARATOR '/' |
|
#define INCORRECT_PATH_SEPARATOR '\\' |
|
#endif |
|
|
|
#ifdef _WIN32 |
|
#define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/') |
|
#elif defined(POSIX) |
|
#define PATHSEPARATOR(c) ((c) == '/') |
|
#endif //_WIN32 |
|
|
|
#define MAX_FILEPATH 512 |
|
|
|
extern CUtlSymbolTableMT g_PathIDTable; |
|
|
|
enum FileMode_t |
|
{ |
|
FM_BINARY, |
|
FM_TEXT |
|
}; |
|
|
|
enum FileType_t |
|
{ |
|
FT_NORMAL, |
|
FT_PACK_BINARY, |
|
FT_PACK_TEXT, |
|
FT_MEMORY_BINARY, |
|
FT_MEMORY_TEXT |
|
}; |
|
|
|
class IThreadPool; |
|
class CBlockingFileItemList; |
|
class KeyValues; |
|
class CCompiledKeyValuesReader; |
|
class CBaseFileSystem; |
|
class CPackFileHandle; |
|
class CPackFile; |
|
class IFileList; |
|
class CFileOpenInfo; |
|
class CFileAsyncReadJob; |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
class CFileHandle |
|
{ |
|
public: |
|
CFileHandle( CBaseFileSystem* fs ); |
|
virtual ~CFileHandle(); |
|
|
|
void Init( CBaseFileSystem* fs ); |
|
|
|
int GetSectorSize(); |
|
bool IsOK(); |
|
void Flush(); |
|
void SetBufferSize( int nBytes ); |
|
|
|
int Read( void* pBuffer, int nLength ); |
|
int Read( void* pBuffer, int nDestSize, int nLength ); |
|
|
|
int Write( const void* pBuffer, int nLength ); |
|
int Seek( int64 nOffset, int nWhence ); |
|
int Tell(); |
|
int Size(); |
|
|
|
int64 AbsoluteBaseOffset(); |
|
bool EndOfFile(); |
|
|
|
#if !defined( _RETAIL ) |
|
char *m_pszTrueFileName; |
|
char const *Name() const { return m_pszTrueFileName ? m_pszTrueFileName : ""; } |
|
|
|
void SetName( char const *pName ) |
|
{ |
|
Assert( pName ); |
|
Assert( !m_pszTrueFileName ); |
|
int len = Q_strlen( pName ); |
|
m_pszTrueFileName = new char[len + 1]; |
|
memcpy( m_pszTrueFileName, pName, len + 1 ); |
|
} |
|
#endif |
|
|
|
CPackFileHandle *m_pPackFileHandle; |
|
#if defined( SUPPORT_PACKED_STORE ) |
|
CPackedStoreFileHandle m_VPKHandle; |
|
#endif |
|
int64 m_nLength; |
|
FileType_t m_type; |
|
FILE *m_pFile; |
|
|
|
protected: |
|
CBaseFileSystem *m_fs; |
|
|
|
enum |
|
{ |
|
MAGIC = 0x43464861, // 'CFHa', |
|
FREE_MAGIC = 0x4672654d // 'FreM' |
|
}; |
|
unsigned int m_nMagic; |
|
|
|
bool IsValid(); |
|
}; |
|
|
|
class CMemoryFileHandle : public CFileHandle |
|
{ |
|
public: |
|
CMemoryFileHandle( CBaseFileSystem* pFS, CMemoryFileBacking* pBacking ) |
|
: CFileHandle( pFS ), m_pBacking( pBacking ), m_nPosition( 0 ) { m_nLength = pBacking->m_nLength; } |
|
|
|
~CMemoryFileHandle() { m_pBacking->Release(); } |
|
|
|
int Read( void* pBuffer, int nDestSize, int nLength ); |
|
int Seek( int64 nOffset, int nWhence ); |
|
int Tell() { return m_nPosition; } |
|
int Size() { return (int) m_nLength; } |
|
|
|
CMemoryFileBacking *m_pBacking; |
|
int m_nPosition; |
|
|
|
private: |
|
CMemoryFileHandle( const CMemoryFileHandle& ); // not defined |
|
CMemoryFileHandle& operator=( const CMemoryFileHandle& ); // not defined |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
#ifdef AsyncRead |
|
#undef AsyncRead |
|
#undef AsyncReadMutiple |
|
#endif |
|
|
|
#ifdef SUPPORT_PACKED_STORE |
|
class CPackedStoreRefCount : public CPackedStore, public CRefCounted<CRefCountServiceMT> |
|
{ |
|
public: |
|
CPackedStoreRefCount( char const *pFileBasename, char *pszFName, IBaseFileSystem *pFS ); |
|
|
|
bool m_bSignatureValid; |
|
}; |
|
#else |
|
class CPackedStoreRefCount : public CRefCounted<CRefCountServiceMT> |
|
{ |
|
}; |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
abstract_class CBaseFileSystem : public CTier1AppSystem< IFileSystem > |
|
{ |
|
friend class CPackFileHandle; |
|
friend class CZipPackFileHandle; |
|
friend class CPackFile; |
|
friend class CZipPackFile; |
|
friend class CFileHandle; |
|
friend class CFileTracker; |
|
friend class CFileTracker2; |
|
friend class CFileOpenInfo; |
|
|
|
typedef CTier1AppSystem< IFileSystem > BaseClass; |
|
|
|
public: |
|
CBaseFileSystem(); |
|
~CBaseFileSystem(); |
|
|
|
// Methods of IAppSystem |
|
virtual void *QueryInterface( const char *pInterfaceName ); |
|
virtual InitReturnVal_t Init(); |
|
virtual void Shutdown(); |
|
|
|
void InitAsync(); |
|
void ShutdownAsync(); |
|
|
|
void ParsePathID( const char* &pFilename, const char* &pPathID, char tempPathID[MAX_PATH] ); |
|
|
|
// file handling |
|
virtual FileHandle_t Open( const char *pFileName, const char *pOptions, const char *pathID ); |
|
virtual FileHandle_t OpenEx( const char *pFileName, const char *pOptions, unsigned flags = 0, const char *pathID = 0, char **ppszResolvedFilename = NULL ); |
|
virtual void Close( FileHandle_t ); |
|
virtual void Seek( FileHandle_t file, int pos, FileSystemSeek_t method ); |
|
virtual unsigned int Tell( FileHandle_t file ); |
|
virtual unsigned int Size( FileHandle_t file ); |
|
virtual unsigned int Size( const char *pFileName, const char *pPathID ); |
|
|
|
virtual void SetBufferSize( FileHandle_t file, unsigned nBytes ); |
|
virtual bool IsOk( FileHandle_t file ); |
|
virtual void Flush( FileHandle_t file ); |
|
virtual bool Precache( const char *pFileName, const char *pPathID ); |
|
virtual bool EndOfFile( FileHandle_t file ); |
|
|
|
virtual int Read( void *pOutput, int size, FileHandle_t file ); |
|
virtual int ReadEx( void* pOutput, int sizeDest, int size, FileHandle_t file ); |
|
virtual int Write( void const* pInput, int size, FileHandle_t file ); |
|
virtual char *ReadLine( char *pOutput, int maxChars, FileHandle_t file ); |
|
virtual int FPrintf( FileHandle_t file, PRINTF_FORMAT_STRING const char *pFormat, ... ) FMTFUNCTION( 3, 4 ); |
|
|
|
// Reads/writes files to utlbuffers |
|
virtual bool ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes, int nStartingByte, FSAllocFunc_t pfnAlloc = NULL ); |
|
virtual bool WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf ); |
|
virtual bool UnzipFile( const char *pFileName, const char *pPath, const char *pDestination ); |
|
virtual int ReadFileEx( const char *pFileName, const char *pPath, void **ppBuf, bool bNullTerminate, bool bOptimalAlloc, int nMaxBytes = 0, int nStartingByte = 0, FSAllocFunc_t pfnAlloc = NULL ); |
|
virtual bool ReadToBuffer( FileHandle_t hFile, CUtlBuffer &buf, int nMaxBytes = 0, FSAllocFunc_t pfnAlloc = NULL ); |
|
|
|
// Optimal buffer |
|
bool GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign ); |
|
void *AllocOptimalReadBuffer( FileHandle_t hFile, unsigned nSize, unsigned nOffset ) { return malloc( nSize ); } |
|
void FreeOptimalReadBuffer( void *p ) { free( p ); } |
|
|
|
// Gets the current working directory |
|
virtual bool GetCurrentDirectory( char* pDirectory, int maxlen ); |
|
|
|
// this isn't implementable on STEAM as is. |
|
virtual void CreateDirHierarchy( const char *path, const char *pathID ); |
|
|
|
// returns true if the file is a directory |
|
virtual bool IsDirectory( const char *pFileName, const char *pathID ); |
|
|
|
// path info |
|
virtual const char *GetLocalPath( const char *pFileName, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars ); |
|
virtual bool FullPathToRelativePath( const char *pFullpath, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars ); |
|
virtual bool GetCaseCorrectFullPath_Ptr( const char *pFullPath, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars ); |
|
|
|
// removes a file from disk |
|
virtual void RemoveFile( char const* pRelativePath, const char *pathID ); |
|
|
|
// Remove all search paths (including write path?) |
|
virtual void RemoveAllSearchPaths( void ); |
|
|
|
// Purpose: Removes all search paths for a given pathID, such as all "GAME" paths. |
|
virtual void RemoveSearchPaths( const char *pathID ); |
|
|
|
// STUFF FROM IFileSystem |
|
// Add paths in priority order (mod dir, game dir, ....) |
|
// Can also add pak files (errr, NOT YET!) |
|
virtual void AddSearchPath( const char *pPath, const char *pathID, SearchPathAdd_t addType ); |
|
virtual bool RemoveSearchPath( const char *pPath, const char *pathID ); |
|
virtual void PrintSearchPaths( void ); |
|
|
|
virtual void MarkPathIDByRequestOnly( const char *pPathID, bool bRequestOnly ); |
|
|
|
virtual bool FileExists( const char *pFileName, const char *pPathID = NULL ); |
|
virtual time_t GetFileTime( const char *pFileName, const char *pPathID = NULL ); |
|
virtual bool IsFileWritable( char const *pFileName, const char *pPathID = NULL ); |
|
virtual bool SetFileWritable( char const *pFileName, bool writable, const char *pPathID = 0 ); |
|
virtual void FileTimeToString( char *pString, int maxChars, time_t fileTime ); |
|
|
|
virtual const char *FindFirst( const char *pWildCard, FileFindHandle_t *pHandle ); |
|
virtual const char *FindFirstEx( const char *pWildCard, const char *pPathID, FileFindHandle_t *pHandle ); |
|
virtual const char *FindNext( FileFindHandle_t handle ); |
|
virtual bool FindIsDirectory( FileFindHandle_t handle ); |
|
virtual void FindClose( FileFindHandle_t handle ); |
|
|
|
virtual void PrintOpenedFiles( void ); |
|
virtual void SetWarningFunc( void (*pfnWarning)( PRINTF_FORMAT_STRING const char *fmt, ... ) ); |
|
virtual void SetWarningLevel( FileWarningLevel_t level ); |
|
virtual void AddLoggingFunc( FileSystemLoggingFunc_t logFunc ); |
|
virtual void RemoveLoggingFunc( FileSystemLoggingFunc_t logFunc ); |
|
virtual bool RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID ); |
|
|
|
virtual void GetLocalCopy( const char *pFileName ); |
|
|
|
virtual bool FixUpPath( const char *pFileName, char *pFixedUpFileName, int sizeFixedUpFileName ); |
|
|
|
virtual FileNameHandle_t FindOrAddFileName( char const *pFileName ); |
|
virtual FileNameHandle_t FindFileName( char const *pFileName ); |
|
virtual bool String( const FileNameHandle_t& handle, char *buf, int buflen ); |
|
virtual int GetPathIndex( const FileNameHandle_t &handle ); |
|
time_t GetPathTime( const char *pFileName, const char *pPathID ); |
|
|
|
virtual void EnableWhitelistFileTracking( bool bEnable, bool bCacheAllVPKHashes, bool bRecalculateAndCheckHashes ); |
|
virtual void RegisterFileWhitelist( IPureServerWhitelist *pWhiteList, IFileList **ppFilesToReload ) OVERRIDE; |
|
virtual void MarkAllCRCsUnverified(); |
|
virtual void CacheFileCRCs( const char *pPathname, ECacheCRCType eType, IFileList *pFilter ); |
|
//void CacheFileCRCs_R( const char *pPathname, ECacheCRCType eType, IFileList *pFilter, CUtlDict<int,int> &searchPathNames ); |
|
virtual EFileCRCStatus CheckCachedFileHash( const char *pPathID, const char *pRelativeFilename, int nFileFraction, FileHash_t *pFileHash ); |
|
virtual int GetUnverifiedFileHashes( CUnverifiedFileHash *pFiles, int nMaxFiles ); |
|
virtual int GetWhitelistSpewFlags(); |
|
virtual void SetWhitelistSpewFlags( int flags ); |
|
virtual void InstallDirtyDiskReportFunc( FSDirtyDiskReportFunc_t func ); |
|
|
|
// Low-level file caching |
|
virtual FileCacheHandle_t CreateFileCache(); |
|
virtual void AddFilesToFileCache( FileCacheHandle_t cacheId, const char **ppFileNames, int nFileNames, const char *pPathID ); |
|
virtual bool IsFileCacheFileLoaded( FileCacheHandle_t cacheId, const char* pFileName ); |
|
virtual bool IsFileCacheLoaded( FileCacheHandle_t cacheId ); |
|
virtual void DestroyFileCache( FileCacheHandle_t cacheId ); |
|
|
|
virtual void CacheAllVPKFileHashes( bool bCacheAllVPKHashes, bool bRecalculateAndCheckHashes ); |
|
virtual bool CheckVPKFileHash( int PackFileID, int nPackFileNumber, int nFileFraction, MD5Value_t &md5Value ); |
|
virtual void NotifyFileUnloaded( const char *pszFilename, const char *pPathId ) OVERRIDE; |
|
|
|
// Returns the file system statistics retreived by the implementation. Returns NULL if not supported. |
|
virtual const FileSystemStatistics *GetFilesystemStatistics(); |
|
|
|
// Load dlls |
|
virtual CSysModule *LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly ); |
|
virtual void UnloadModule( CSysModule *pModule ); |
|
|
|
//-------------------------------------------------------- |
|
// asynchronous file loading |
|
//-------------------------------------------------------- |
|
virtual FSAsyncStatus_t AsyncReadMultiple( const FileAsyncRequest_t *pRequests, int nRequests, FSAsyncControl_t *pControls ); |
|
virtual FSAsyncStatus_t AsyncReadMultipleCreditAlloc( const FileAsyncRequest_t *pRequests, int nRequests, const char *pszFile, int line, FSAsyncControl_t *phControls = NULL ); |
|
virtual FSAsyncStatus_t AsyncFinish( FSAsyncControl_t hControl, bool wait ); |
|
virtual FSAsyncStatus_t AsyncGetResult( FSAsyncControl_t hControl, void **ppData, int *pSize ); |
|
virtual FSAsyncStatus_t AsyncAbort( FSAsyncControl_t hControl ); |
|
virtual FSAsyncStatus_t AsyncStatus( FSAsyncControl_t hControl ); |
|
virtual FSAsyncStatus_t AsyncSetPriority(FSAsyncControl_t hControl, int newPriority); |
|
virtual FSAsyncStatus_t AsyncFlush(); |
|
virtual FSAsyncStatus_t AsyncAppend(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, FSAsyncControl_t *pControl) { return AsyncWrite( pFileName, pSrc, nSrcBytes, bFreeMemory, true, pControl); } |
|
virtual FSAsyncStatus_t AsyncWrite(const char *pFileName, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl); |
|
virtual FSAsyncStatus_t AsyncWriteFile(const char *pFileName, const CUtlBuffer *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend, FSAsyncControl_t *pControl); |
|
virtual FSAsyncStatus_t AsyncAppendFile(const char *pDestFileName, const char *pSrcFileName, FSAsyncControl_t *pControl); |
|
virtual void AsyncFinishAll( int iToPriority = INT_MIN ); |
|
virtual void AsyncFinishAllWrites(); |
|
virtual bool AsyncSuspend(); |
|
virtual bool AsyncResume(); |
|
|
|
virtual void AsyncAddRef( FSAsyncControl_t hControl ); |
|
virtual void AsyncRelease( FSAsyncControl_t hControl ); |
|
virtual FSAsyncStatus_t AsyncBeginRead( const char *pszFile, FSAsyncFile_t *phFile ); |
|
virtual FSAsyncStatus_t AsyncEndRead( FSAsyncFile_t hFile ); |
|
virtual void AsyncAddFetcher( IAsyncFileFetch *pFetcher ); |
|
virtual void AsyncRemoveFetcher( IAsyncFileFetch *pFetcher ); |
|
|
|
//-------------------------------------------------------- |
|
// pack files |
|
//-------------------------------------------------------- |
|
bool AddPackFile( const char *pFileName, const char *pathID ); |
|
bool AddPackFileFromPath( const char *pPath, const char *pakfile, bool bCheckForAppendedPack, const char *pathID ); |
|
|
|
// converts a partial path into a full path |
|
// can be filtered to restrict path types and can provide info about resolved path |
|
virtual const char *RelativePathToFullPath( const char *pFileName, const char *pPathID, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars, PathTypeFilter_t pathFilter = FILTER_NONE, PathTypeQuery_t *pPathType = NULL ); |
|
|
|
// Returns the search path, each path is separated by ;s. Returns the length of the string returned |
|
virtual int GetSearchPath( const char *pathID, bool bGetPackFiles, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars ); |
|
|
|
#if defined( TRACK_BLOCKING_IO ) |
|
virtual void EnableBlockingFileAccessTracking( bool state ); |
|
virtual bool IsBlockingFileAccessEnabled() const; |
|
virtual IBlockingFileItemList *RetrieveBlockingFileAccessInfo(); |
|
|
|
virtual void RecordBlockingFileAccess( bool synchronous, const FileBlockingItem& item ); |
|
|
|
virtual bool SetAllowSynchronousLogging( bool state ); |
|
#endif |
|
|
|
virtual bool GetFileTypeForFullPath( char const *pFullPath, wchar_t *buf, size_t bufSizeInBytes ); |
|
|
|
virtual void BeginMapAccess(); |
|
virtual void EndMapAccess(); |
|
virtual bool FullPathToRelativePathEx( const char *pFullpath, const char *pPathId, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars ); |
|
|
|
FSAsyncStatus_t SyncRead( const FileAsyncRequest_t &request ); |
|
FSAsyncStatus_t SyncWrite(const char *pszFilename, const void *pSrc, int nSrcBytes, bool bFreeMemory, bool bAppend ); |
|
FSAsyncStatus_t SyncAppendFile(const char *pAppendToFileName, const char *pAppendFromFileName ); |
|
FSAsyncStatus_t SyncGetFileSize( const FileAsyncRequest_t &request ); |
|
void DoAsyncCallback( const FileAsyncRequest_t &request, void *pData, int nBytesRead, FSAsyncStatus_t result ); |
|
|
|
void SetupPreloadData(); |
|
void DiscardPreloadData(); |
|
|
|
virtual void LoadCompiledKeyValues( KeyValuesPreloadType_t type, char const *archiveFile ); |
|
|
|
// If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of compiled keyvalues loaded during startup. |
|
// Otherwise, it'll just fall through to the regular KeyValues loading routines |
|
virtual KeyValues *LoadKeyValues( KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ); |
|
virtual bool LoadKeyValues( KeyValues& head, KeyValuesPreloadType_t type, char const *filename, char const *pPathID = 0 ); |
|
virtual bool ExtractRootKeyName( KeyValuesPreloadType_t type, char *outbuf, size_t bufsize, char const *filename, char const *pPathID = 0 ); |
|
|
|
virtual DVDMode_t GetDVDMode() { return m_DVDMode; } |
|
|
|
FSDirtyDiskReportFunc_t GetDirtyDiskReportFunc() { return m_DirtyDiskReportFunc; } |
|
|
|
//----------------------------------------------------------------------------- |
|
// MemoryFile cache implementation |
|
//----------------------------------------------------------------------------- |
|
class CFileCacheObject; |
|
|
|
// XXX For now, we assume that all path IDs are "GAME", never cache files |
|
// outside of the game search path, and preferentially return those files |
|
// whenever anyone searches for a match even if an on-disk file in another |
|
// folder would have been found first in a traditional search. extending |
|
// the memory cache to cover non-game files isn't necessary right now, but |
|
// should just be a matter of defining a more complex key type. (henryg) |
|
|
|
// Register a CMemoryFileBacking; must balance with UnregisterMemoryFile. |
|
// Returns false and outputs an ref-bumped pointer to the existing entry |
|
// if the same file has already been registered by someone else; this must |
|
// be Unregistered to maintain the balance. |
|
virtual bool RegisterMemoryFile( CMemoryFileBacking *pFile, CMemoryFileBacking **ppExistingFileWithRef ); |
|
|
|
// Unregister a CMemoryFileBacking; must balance with RegisterMemoryFile. |
|
virtual void UnregisterMemoryFile( CMemoryFileBacking *pFile ); |
|
|
|
//------------------------------------ |
|
// Synchronous path for file operations |
|
//------------------------------------ |
|
class CPathIDInfo |
|
{ |
|
public: |
|
const CUtlSymbol& GetPathID() const; |
|
const char* GetPathIDString() const; |
|
void SetPathID( CUtlSymbol id ); |
|
|
|
public: |
|
// See MarkPathIDByRequestOnly. |
|
bool m_bByRequestOnly; |
|
|
|
private: |
|
CUtlSymbol m_PathID; |
|
const char *m_pDebugPathID; |
|
}; |
|
|
|
//////////////////////////////////////////////// |
|
// IMPLEMENTATION DETAILS FOR CBaseFileSystem // |
|
//////////////////////////////////////////////// |
|
|
|
class CSearchPath |
|
{ |
|
public: |
|
CSearchPath( void ); |
|
~CSearchPath( void ); |
|
|
|
const char* GetPathString() const; |
|
const char* GetDebugString() const; |
|
|
|
// Path ID ("game", "mod", "gamebin") accessors. |
|
const CUtlSymbol& GetPathID() const; |
|
const char* GetPathIDString() const; |
|
|
|
// Search path (c:\hl2\hl2) accessors. |
|
void SetPath( CUtlSymbol id ); |
|
const CUtlSymbol& GetPath() const; |
|
|
|
void SetPackFile(CPackFile *pPackFile) { m_pPackFile = pPackFile; } |
|
CPackFile *GetPackFile() const { return m_pPackFile; } |
|
|
|
#ifdef SUPPORT_PACKED_STORE |
|
void SetPackedStore( CPackedStoreRefCount *pPackedStore ) { m_pPackedStore = pPackedStore; } |
|
#endif |
|
CPackedStoreRefCount *GetPackedStore() const { return m_pPackedStore; } |
|
|
|
bool IsMapPath() const; |
|
|
|
int m_storeId; |
|
|
|
// Used to track if its search |
|
CPathIDInfo *m_pPathIDInfo; |
|
|
|
bool m_bIsRemotePath; |
|
|
|
bool m_bIsTrustedForPureServer; |
|
|
|
private: |
|
CUtlSymbol m_Path; |
|
const char *m_pDebugPath; |
|
CPackFile *m_pPackFile; |
|
CPackedStoreRefCount *m_pPackedStore; |
|
}; |
|
|
|
class CSearchPathsVisits |
|
{ |
|
public: |
|
void Reset() |
|
{ |
|
m_Visits.RemoveAll(); |
|
} |
|
|
|
bool MarkVisit( const CSearchPath &searchPath ) |
|
{ |
|
if ( m_Visits.Find( searchPath.m_storeId ) == m_Visits.InvalidIndex() ) |
|
{ |
|
MEM_ALLOC_CREDIT(); |
|
m_Visits.AddToTail( searchPath.m_storeId ); |
|
return false; |
|
} |
|
return true; |
|
} |
|
|
|
private: |
|
CUtlVector<int> m_Visits; // This is a copy of IDs for the search paths we've visited, so |
|
}; |
|
|
|
class CSearchPathsIterator |
|
{ |
|
public: |
|
CSearchPathsIterator( CBaseFileSystem *pFileSystem, const char **ppszFilename, const char *pszPathID, PathTypeFilter_t pathTypeFilter = FILTER_NONE ) |
|
: m_iCurrent( -1 ), |
|
m_PathTypeFilter( pathTypeFilter ) |
|
{ |
|
char tempPathID[MAX_PATH]; |
|
if ( *ppszFilename && (*ppszFilename)[0] == '/' && (*ppszFilename)[1] == '/' ) // ONLY '//' (and not '\\') for our special format |
|
{ |
|
// Allow for UNC-type syntax to specify the path ID. |
|
pFileSystem->ParsePathID( *ppszFilename, pszPathID, tempPathID ); |
|
} |
|
if ( pszPathID ) |
|
{ |
|
m_pathID = g_PathIDTable.AddString( pszPathID ); |
|
} |
|
else |
|
{ |
|
m_pathID = UTL_INVAL_SYMBOL; |
|
} |
|
|
|
if ( *ppszFilename && !Q_IsAbsolutePath( *ppszFilename ) ) |
|
{ |
|
// Copy paths to minimize mutex lock time |
|
pFileSystem->m_SearchPathsMutex.Lock(); |
|
CopySearchPaths( pFileSystem->m_SearchPaths ); |
|
pFileSystem->m_SearchPathsMutex.Unlock(); |
|
|
|
pFileSystem->FixUpPath ( *ppszFilename, m_Filename, sizeof( m_Filename ) ); |
|
} |
|
else |
|
{ |
|
// If it's an absolute path, it isn't worth using the paths at all. Simplify |
|
// client logic by pretending there's a search path of 1 |
|
m_EmptyPathIDInfo.m_bByRequestOnly = false; |
|
m_EmptySearchPath.m_pPathIDInfo = &m_EmptyPathIDInfo; |
|
m_EmptySearchPath.SetPath( m_pathID ); |
|
m_EmptySearchPath.m_storeId = -1; |
|
m_Filename[0] = '\0'; |
|
} |
|
} |
|
|
|
CSearchPathsIterator( CBaseFileSystem *pFileSystem, const char *pszPathID, PathTypeFilter_t pathTypeFilter = FILTER_NONE ) |
|
: m_iCurrent( -1 ), |
|
m_PathTypeFilter( pathTypeFilter ) |
|
{ |
|
if ( pszPathID ) |
|
{ |
|
m_pathID = g_PathIDTable.AddString( pszPathID ); |
|
} |
|
else |
|
{ |
|
m_pathID = UTL_INVAL_SYMBOL; |
|
} |
|
// Copy paths to minimize mutex lock time |
|
pFileSystem->m_SearchPathsMutex.Lock(); |
|
CopySearchPaths( pFileSystem->m_SearchPaths ); |
|
pFileSystem->m_SearchPathsMutex.Unlock(); |
|
m_Filename[0] = '\0'; |
|
} |
|
|
|
CSearchPath *GetFirst(); |
|
CSearchPath *GetNext(); |
|
|
|
private: |
|
CSearchPathsIterator( const CSearchPathsIterator & ); |
|
void operator=(const CSearchPathsIterator &); |
|
void CopySearchPaths( const CUtlVector<CSearchPath> &searchPaths ); |
|
|
|
int m_iCurrent; |
|
CUtlSymbol m_pathID; |
|
CUtlVector<CSearchPath> m_SearchPaths; |
|
CSearchPathsVisits m_visits; |
|
CSearchPath m_EmptySearchPath; |
|
CPathIDInfo m_EmptyPathIDInfo; |
|
PathTypeFilter_t m_PathTypeFilter; |
|
char m_Filename[MAX_PATH]; // set for relative names only |
|
}; |
|
|
|
friend class CSearchPathsIterator; |
|
|
|
struct FindData_t |
|
{ |
|
WIN32_FIND_DATA findData; |
|
int currentSearchPathID; |
|
CUtlVector<char> wildCardString; |
|
HANDLE findHandle; |
|
CSearchPathsVisits m_VisitedSearchPaths; // This is a copy of IDs for the search paths we've visited, so avoids searching duplicate paths. |
|
int m_CurrentStoreID; // CSearchPath::m_storeId of the current search path. |
|
|
|
CUtlSymbol m_FilterPathID; // What path ID are we looking at? Ignore all others. (Only set by FindFirstEx). |
|
|
|
CUtlDict<int,int> m_VisitedFiles; // We go through the search paths in priority order, and we use this to make sure |
|
// that we don't return the same file more than once. |
|
CUtlStringList m_fileMatchesFromVPKOrPak; |
|
CUtlStringList m_dirMatchesFromVPKOrPak; |
|
}; |
|
|
|
friend class CSearchPath; |
|
|
|
IPureServerWhitelist *m_pPureServerWhitelist; |
|
int m_WhitelistSpewFlags; // Combination of WHITELIST_SPEW_ flags. |
|
|
|
// logging functions |
|
CUtlVector< FileSystemLoggingFunc_t > m_LogFuncs; |
|
|
|
CThreadMutex m_SearchPathsMutex; |
|
CUtlVector< CSearchPath > m_SearchPaths; |
|
CUtlVector<CPathIDInfo*> m_PathIDInfos; |
|
CUtlLinkedList<FindData_t> m_FindData; |
|
|
|
CSearchPath *FindSearchPathByStoreId( int storeId ); |
|
|
|
int m_iMapLoad; |
|
|
|
// Global list of pack file handles |
|
CUtlVector<CPackFile *> m_ZipFiles; |
|
|
|
FILE *m_pLogFile; |
|
bool m_bOutputDebugString; |
|
|
|
IThreadPool * m_pThreadPool; |
|
CThreadFastMutex m_AsyncCallbackMutex; |
|
|
|
// Statistics: |
|
FileSystemStatistics m_Stats; |
|
|
|
#if defined( TRACK_BLOCKING_IO ) |
|
CBlockingFileItemList *m_pBlockingItems; |
|
bool m_bBlockingFileAccessReportingEnabled; |
|
bool m_bAllowSynchronousLogging; |
|
|
|
friend class CBlockingFileItemList; |
|
friend class CAutoBlockReporter; |
|
#endif |
|
|
|
CFileTracker2 m_FileTracker2; |
|
|
|
protected: |
|
//---------------------------------------------------------------------------- |
|
// Purpose: Functions implementing basic file system behavior. |
|
//---------------------------------------------------------------------------- |
|
virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size ) = 0; |
|
virtual void FS_setbufsize( FILE *fp, unsigned nBytes ) = 0; |
|
virtual void FS_fclose( FILE *fp ) = 0; |
|
virtual void FS_fseek( FILE *fp, int64 pos, int seekType ) = 0; |
|
virtual long FS_ftell( FILE *fp ) = 0; |
|
virtual int FS_feof( FILE *fp ) = 0; |
|
size_t FS_fread( void *dest, size_t size, FILE *fp ) { return FS_fread( dest, (size_t)-1, size, fp ); } |
|
virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp ) = 0; |
|
virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp ) = 0; |
|
virtual bool FS_setmode( FILE *fp, FileMode_t mode ) { return false; } |
|
virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list ) = 0; |
|
virtual int FS_ferror( FILE *fp ) = 0; |
|
virtual int FS_fflush( FILE *fp ) = 0; |
|
virtual char *FS_fgets( char *dest, int destSize, FILE *fp ) = 0; |
|
virtual int FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache=NULL ) = 0; |
|
virtual int FS_chmod( const char *path, int pmode ) = 0; |
|
virtual HANDLE FS_FindFirstFile( const char *findname, WIN32_FIND_DATA *dat) = 0; |
|
virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat) = 0; |
|
virtual bool FS_FindClose(HANDLE handle) = 0; |
|
virtual int FS_GetSectorSize( FILE * ) { return 1; } |
|
|
|
#if defined( TRACK_BLOCKING_IO ) |
|
void BlockingFileAccess_EnterCriticalSection(); |
|
void BlockingFileAccess_LeaveCriticalSection(); |
|
|
|
CThreadMutex m_BlockingFileMutex; |
|
|
|
#endif |
|
|
|
void GetFileNameForHandle( FileHandle_t handle, char *buf, size_t buflen ); |
|
|
|
protected: |
|
//----------------------------------------------------------------------------- |
|
// Purpose: For tracking unclosed files |
|
// NOTE: The symbol table could take up memory that we don't want to eat here. |
|
// In that case, we shouldn't store them in a table, or we should store them as locally allocates stings |
|
// so we can control the size |
|
//----------------------------------------------------------------------------- |
|
class COpenedFile |
|
{ |
|
public: |
|
COpenedFile( void ); |
|
~COpenedFile( void ); |
|
|
|
COpenedFile( const COpenedFile& src ); |
|
|
|
bool operator==( const COpenedFile& src ) const; |
|
|
|
void SetName( char const *name ); |
|
char const *GetName( void ); |
|
|
|
FILE *m_pFile; |
|
char *m_pName; |
|
}; |
|
|
|
CThreadFastMutex m_MemoryFileMutex; |
|
CUtlHashtable< const char*, CMemoryFileBacking* > m_MemoryFileHash; |
|
|
|
|
|
//CUtlRBTree< COpenedFile, int > m_OpenedFiles; |
|
CThreadMutex m_OpenedFilesMutex; |
|
CUtlVector <COpenedFile> m_OpenedFiles; |
|
|
|
static bool OpenedFileLessFunc( COpenedFile const& src1, COpenedFile const& src2 ); |
|
|
|
FileWarningLevel_t m_fwLevel; |
|
void (*m_pfnWarning)( PRINTF_FORMAT_STRING const char *fmt, ... ); |
|
|
|
FILE *Trace_FOpen( const char *filename, const char *options, unsigned flags, int64 *size ); |
|
void Trace_FClose( FILE *fp ); |
|
void Trace_FRead( int size, FILE* file ); |
|
void Trace_FWrite( int size, FILE* file ); |
|
|
|
void Trace_DumpUnclosedFiles( void ); |
|
|
|
public: |
|
void LogAccessToFile( char const *accesstype, char const *fullpath, char const *options ); |
|
void Warning( FileWarningLevel_t level, PRINTF_FORMAT_STRING const char *fmt, ... ); |
|
|
|
protected: |
|
// Note: if pFoundStoreID is passed in, then it will set that to the CSearchPath::m_storeId value of the search path it found the file in. |
|
const char* FindFirstHelper( const char *pWildCard, const char *pPathID, FileFindHandle_t *pHandle, int *pFoundStoreID ); |
|
bool FindNextFileHelper( FindData_t *pFindData, int *pFoundStoreID ); |
|
bool FindNextFileInVPKOrPakHelper( FindData_t *pFindData ); |
|
|
|
void RemoveAllMapSearchPaths( void ); |
|
void AddMapPackFile( const char *pPath, const char *pPathID, SearchPathAdd_t addType ); |
|
void AddPackFiles( const char *pPath, const CUtlSymbol &pathID, SearchPathAdd_t addType ); |
|
bool PreparePackFile( CPackFile &packfile, int offsetofpackinmetafile, int64 filelen ); |
|
void AddVPKFile( const char *pPath, const char *pPathID, SearchPathAdd_t addType ); |
|
bool RemoveVPKFile( const char *pPath, const char *pPathID ); |
|
|
|
void HandleOpenRegularFile( CFileOpenInfo &openInfo, bool bIsAbsolutePath ); |
|
|
|
FileHandle_t FindFileInSearchPath( CFileOpenInfo &openInfo ); |
|
time_t FastFileTime( const CSearchPath *path, const char *pFileName ); |
|
|
|
const char *GetWritePath( const char *pFilename, const char *pathID ); |
|
|
|
// Computes a full write path |
|
void ComputeFullWritePath( char* pDest, int maxlen, const char *pWritePathID, char const *pRelativePath ); |
|
|
|
void AddSearchPathInternal( const char *pPath, const char *pathID, SearchPathAdd_t addType, bool bAddPackFiles ); |
|
|
|
// Opens a file for read or write |
|
FileHandle_t OpenForRead( const char *pFileName, const char *pOptions, unsigned flags, const char *pathID, char **ppszResolvedFilename = NULL ); |
|
FileHandle_t OpenForWrite( const char *pFileName, const char *pOptions, const char *pathID ); |
|
CSearchPath *FindWritePath( const char *pFilename, const char *pathID ); |
|
|
|
// Helper function for fs_log file logging |
|
void LogFileAccess( const char *pFullFileName ); |
|
bool LookupKeyValuesRootKeyName( char const *filename, char const *pPathID, char *rootName, size_t bufsize ); |
|
void UnloadCompiledKeyValues(); |
|
|
|
// If bByRequestOnly is -1, then it will default to false if it doesn't already exist, and it |
|
// won't change it if it does already exist. Otherwise, it will be set to the value of bByRequestOnly. |
|
CPathIDInfo* FindOrAddPathIDInfo( const CUtlSymbol &id, int bByRequestOnly ); |
|
static bool FilterByPathID( const CSearchPath *pSearchPath, const CUtlSymbol &pathID ); |
|
|
|
// Global/shared filename/path table |
|
CUtlFilenameSymbolTable m_FileNames; |
|
|
|
int m_WhitelistFileTrackingEnabled; // -1 if unset, 0 if disabled (single player), 1 if enabled (multiplayer). |
|
FSDirtyDiskReportFunc_t m_DirtyDiskReportFunc; |
|
|
|
void SetSearchPathIsTrustedSource( CSearchPath *pPath ); |
|
|
|
struct CompiledKeyValuesPreloaders_t |
|
{ |
|
CompiledKeyValuesPreloaders_t() : |
|
m_CacheFile( 0 ), |
|
m_pReader( 0 ) |
|
{ |
|
} |
|
FileNameHandle_t m_CacheFile; |
|
CCompiledKeyValuesReader *m_pReader; |
|
}; |
|
|
|
CompiledKeyValuesPreloaders_t m_PreloadData[ NUM_PRELOAD_TYPES ]; |
|
|
|
static CUtlSymbol m_GamePathID; |
|
static CUtlSymbol m_BSPPathID; |
|
|
|
static DVDMode_t m_DVDMode; |
|
|
|
// Pack exclude paths are strictly for 360 to allow holes in search paths and pack files |
|
// which fall through to support new or dynamic data on the host pc. |
|
static CUtlVector< FileNameHandle_t > m_ExcludePaths; |
|
|
|
/// List of installed hooks to intercept async file operations |
|
CUtlVector< IAsyncFileFetch * > m_vecAsyncFetchers; |
|
|
|
/// List of active async jobs being serviced by customer fetchers |
|
CUtlVector< CFileAsyncReadJob * > m_vecAsyncCustomFetchJobs; |
|
|
|
/// Remove a custom fetch job from the list (and release our reference) |
|
friend class CFileAsyncReadJob; |
|
void RemoveAsyncCustomFetchJob( CFileAsyncReadJob *pJob ); |
|
}; |
|
|
|
inline const CUtlSymbol& CBaseFileSystem::CPathIDInfo::GetPathID() const |
|
{ |
|
return m_PathID; |
|
} |
|
|
|
|
|
inline const char* CBaseFileSystem::CPathIDInfo::GetPathIDString() const |
|
{ |
|
return g_PathIDTable.String( m_PathID ); |
|
} |
|
|
|
|
|
inline const char* CBaseFileSystem::CSearchPath::GetPathString() const |
|
{ |
|
return g_PathIDTable.String( m_Path ); |
|
} |
|
|
|
|
|
inline void CBaseFileSystem::CPathIDInfo::SetPathID( CUtlSymbol sym ) |
|
{ |
|
m_PathID = sym; |
|
m_pDebugPathID = GetPathIDString(); |
|
} |
|
|
|
|
|
inline const CUtlSymbol& CBaseFileSystem::CSearchPath::GetPathID() const |
|
{ |
|
return m_pPathIDInfo->GetPathID(); |
|
} |
|
|
|
|
|
inline const char* CBaseFileSystem::CSearchPath::GetPathIDString() const |
|
{ |
|
return m_pPathIDInfo->GetPathIDString(); |
|
} |
|
|
|
|
|
inline void CBaseFileSystem::CSearchPath::SetPath( CUtlSymbol id ) |
|
{ |
|
m_Path = id; |
|
m_pDebugPath = g_PathIDTable.String( m_Path ); |
|
} |
|
|
|
|
|
inline const CUtlSymbol& CBaseFileSystem::CSearchPath::GetPath() const |
|
{ |
|
return m_Path; |
|
} |
|
|
|
|
|
inline bool CBaseFileSystem::FilterByPathID( const CSearchPath *pSearchPath, const CUtlSymbol &pathID ) |
|
{ |
|
if ( (UtlSymId_t)pathID == UTL_INVAL_SYMBOL ) |
|
{ |
|
// They didn't specify a specific search path, so if this search path's path ID is by |
|
// request only, then ignore it. |
|
return pSearchPath->m_pPathIDInfo->m_bByRequestOnly; |
|
} |
|
else |
|
{ |
|
// Bit of a hack, but specifying "BSP" as the search path will search in "GAME" for only the map/.bsp pack file path |
|
if ( pathID == m_BSPPathID ) |
|
{ |
|
if ( pSearchPath->GetPathID() != m_GamePathID ) |
|
return true; |
|
|
|
if ( !pSearchPath->GetPackFile() ) |
|
return true; |
|
|
|
if ( !pSearchPath->IsMapPath() ) |
|
return true; |
|
|
|
return false; |
|
} |
|
else |
|
{ |
|
return (pSearchPath->GetPathID() != pathID); |
|
} |
|
} |
|
} |
|
|
|
#if defined( TRACK_BLOCKING_IO ) |
|
|
|
class CAutoBlockReporter |
|
{ |
|
public: |
|
|
|
CAutoBlockReporter( CBaseFileSystem *fs, bool synchronous, char const *filename, int eBlockType, int nTypeOfAccess ) : |
|
m_pFS( fs ), |
|
m_Item( eBlockType, filename, 0.0f, nTypeOfAccess ), |
|
m_bSynchronous( synchronous ) |
|
{ |
|
Assert( m_pFS ); |
|
m_Timer.Start(); |
|
} |
|
|
|
CAutoBlockReporter( CBaseFileSystem *fs, bool synchronous, FileHandle_t handle, int eBlockType, int nTypeOfAccess ) : |
|
m_pFS( fs ), |
|
m_Item( eBlockType, NULL, 0.0f, nTypeOfAccess ), |
|
m_bSynchronous( synchronous ) |
|
{ |
|
Assert( m_pFS ); |
|
char name[ 512 ]; |
|
m_pFS->GetFileNameForHandle( handle, name, sizeof( name ) ); |
|
m_Item.SetFileName( name ); |
|
m_Timer.Start(); |
|
} |
|
|
|
~CAutoBlockReporter() |
|
{ |
|
m_Timer.End(); |
|
m_Item.m_flElapsed = m_Timer.GetDuration().GetSeconds(); |
|
m_pFS->RecordBlockingFileAccess( m_bSynchronous, m_Item ); |
|
} |
|
|
|
private: |
|
|
|
CBaseFileSystem *m_pFS; |
|
|
|
CFastTimer m_Timer; |
|
FileBlockingItem m_Item; |
|
bool m_bSynchronous; |
|
}; |
|
|
|
#define AUTOBLOCKREPORTER_FN( name, fs, sync, filename, blockType, accessType ) CAutoBlockReporter block##name( fs, sync, filename, blockType, accessType ); |
|
#define AUTOBLOCKREPORTER_FH( name, fs, sync, handle, blockType, accessType ) CAutoBlockReporter block##name( fs, sync, handle, blockType, accessType ); |
|
|
|
#else |
|
|
|
#define AUTOBLOCKREPORTER_FN( name, fs, sync, filename, blockType, accessType ) // Nothing |
|
#define AUTOBLOCKREPORTER_FH( name, fs, sync, handle , blockType, accessType ) // Nothing |
|
|
|
#endif |
|
|
|
// singleton accessor |
|
CBaseFileSystem *BaseFileSystem(); |
|
|
|
#include "tier0/memdbgoff.h" |
|
|
|
#endif // BASEFILESYSTEM_H
|
|
|