Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1001 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 "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 long 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, long 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 );
long 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 );
long 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