//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//===========================================================================//
# ifdef POSIX
# define _snwprintf swprintf
# endif
# include "basefilesystem.h"
# include "tier0/vprof.h"
# include "tier1/characterset.h"
# include "tier1/utlbuffer.h"
# include "tier1/convar.h"
# include "tier1/KeyValues.h"
# include "tier0/icommandline.h"
# include "generichash.h"
# include "tier1/utllinkedlist.h"
# include "filesystem/IQueuedLoader.h"
# include "tier2/tier2.h"
# include "zip_utils.h"
# include "packfile.h"
# ifdef _X360
# include "xbox/xbox_launch.h"
# endif
# ifndef DEDICATED
# include "keyvaluescompiler.h"
# endif
# include "ifilelist.h"
# ifdef IS_WINDOWS_PC
// Needed for getting file type string
# define WIN32_LEAN_AND_MEAN
# include <shellapi.h>
# endif
# if defined( _X360 )
# include "xbox\xbox_win32stubs.h"
# undef GetCurrentDirectory
# endif
# include <time.h>
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
ConVar fs_report_sync_opens ( " fs_report_sync_opens " , " 0 " , 0 , " 0:Off, 1:Blocking only, 2:All " ) ;
ConVar fs_warning_mode ( " fs_warning_mode " , " 0 " , 0 , " 0:Off, 1:Warn main thread, 2:Warn other threads " ) ;
# define BSPOUTPUT 0 // bsp output flag -- determines type of fs_log output to generate
static void AddSeperatorAndFixPath ( char * str ) ;
// Case-insensitive symbol table for path IDs.
CUtlSymbolTableMT g_PathIDTable ( 0 , 32 , true ) ;
int g_iNextSearchPathID = 1 ;
// This can be used to easily fix a filename on the stack.
# ifdef DBGFLAG_ASSERT
// Look for cases like materials\\blah.vmt.
static bool V_CheckDoubleSlashes ( const char * pStr )
{
int len = V_strlen ( pStr ) ;
for ( int i = 1 ; i < len - 1 ; i + + )
{
if ( ( pStr [ i ] = = ' / ' | | pStr [ i ] = = ' \\ ' ) & & ( pStr [ i + 1 ] = = ' / ' | | pStr [ i + 1 ] = = ' \\ ' ) )
{
Msg ( " Double slashes found in path '%s' \n " , pStr ) ;
return true ;
}
}
return false ;
}
# define CHECK_DOUBLE_SLASHES( x ) V_CheckDoubleSlashes(x)
# else
# define CHECK_DOUBLE_SLASHES( x )
# endif
static void LogFileOpen ( const char * vpk , const char * pFilename , const char * pAbsPath )
{
static const char * mode = NULL ;
// Figure out if we should be doing this at all, the first time we are acalled
if ( mode = = NULL )
{
if ( CommandLine ( ) - > FindParm ( " -log_opened_files " ) )
mode = " wt " ;
else
mode = " " ;
}
if ( * mode = = ' \0 ' )
return ;
// Open file for write or append
FILE * f = fopen ( " opened_files.txt " , mode ) ;
Assert ( f ) ;
if ( f )
{
fprintf ( f , " %s, %s, %s \n " , vpk , pFilename , pAbsPath ) ;
//fprintf( f, "%s\n", pFilename );
fclose ( f ) ;
// If this was the first time, switch from write to append for further writes
mode = " at " ;
}
}
static CBaseFileSystem * g_pBaseFileSystem ;
CBaseFileSystem * BaseFileSystem ( )
{
return g_pBaseFileSystem ;
}
ConVar filesystem_buffer_size ( " filesystem_buffer_size " , " 0 " , 0 , " Size of per file buffers. 0 for none " ) ;
# if defined( TRACK_BLOCKING_IO )
// If we hit more than 100 items in a frame, we're probably doing a level load...
# define MAX_ITEMS 100
class CBlockingFileItemList : public IBlockingFileItemList
{
public :
CBlockingFileItemList ( CBaseFileSystem * fs )
:
m_pFS ( fs ) ,
m_bLocked ( false )
{
}
// You can't call any of the below calls without calling these methods!!!!
virtual void LockMutex ( )
{
Assert ( ! m_bLocked ) ;
if ( m_bLocked )
return ;
m_bLocked = true ;
m_pFS - > BlockingFileAccess_EnterCriticalSection ( ) ;
}
virtual void UnlockMutex ( )
{
Assert ( m_bLocked ) ;
if ( ! m_bLocked )
return ;
m_pFS - > BlockingFileAccess_LeaveCriticalSection ( ) ;
m_bLocked = false ;
}
virtual int First ( ) const
{
if ( ! m_bLocked )
{
Error ( " CBlockingFileItemList::First() w/o calling EnterCriticalSectionFirst! " ) ;
}
return m_Items . Head ( ) ;
}
virtual int Next ( int i ) const
{
if ( ! m_bLocked )
{
Error ( " CBlockingFileItemList::Next() w/o calling EnterCriticalSectionFirst! " ) ;
}
return m_Items . Next ( i ) ;
}
virtual int InvalidIndex ( ) const
{
return m_Items . InvalidIndex ( ) ;
}
virtual const FileBlockingItem & Get ( int index ) const
{
if ( ! m_bLocked )
{
Error ( " CBlockingFileItemList::Get( %d ) w/o calling EnterCriticalSectionFirst! " , index ) ;
}
return m_Items [ index ] ;
}
virtual void Reset ( )
{
if ( ! m_bLocked )
{
Error ( " CBlockingFileItemList::Reset() w/o calling EnterCriticalSectionFirst! " ) ;
}
m_Items . RemoveAll ( ) ;
}
void Add ( const FileBlockingItem & item )
{
// Ack, should use a linked list probably...
while ( m_Items . Count ( ) > MAX_ITEMS )
{
m_Items . Remove ( m_Items . Head ( ) ) ;
}
m_Items . AddToTail ( item ) ;
}
private :
CUtlLinkedList < FileBlockingItem , unsigned short > m_Items ;
CBaseFileSystem * m_pFS ;
bool m_bLocked ;
} ;
# endif
CUtlSymbol CBaseFileSystem : : m_GamePathID ;
CUtlSymbol CBaseFileSystem : : m_BSPPathID ;
DVDMode_t CBaseFileSystem : : m_DVDMode ;
CUtlVector < FileNameHandle_t > CBaseFileSystem : : m_ExcludePaths ;
class CStoreIDEntry
{
public :
CStoreIDEntry ( ) { }
CStoreIDEntry ( const char * pPathIDStr , int storeID )
{
m_PathIDString = pPathIDStr ;
m_StoreID = storeID ;
}
public :
CUtlSymbol m_PathIDString ;
int m_StoreID ;
} ;
#if 0
static CStoreIDEntry * FindPrevFileByStoreID ( CUtlDict < CUtlVector < CStoreIDEntry > * , int > & filesByStoreID , const char * pFilename , const char * pPathIDStr , int foundStoreID )
{
int iEntry = filesByStoreID . Find ( pFilename ) ;
if ( iEntry = = filesByStoreID . InvalidIndex ( ) )
{
CUtlVector < CStoreIDEntry > * pList = new CUtlVector < CStoreIDEntry > ;
pList - > AddToTail ( CStoreIDEntry ( pPathIDStr , foundStoreID ) ) ;
filesByStoreID . Insert ( pFilename , pList ) ;
return NULL ;
}
else
{
// Now is there a previous entry with a different path ID string and the same store ID?
CUtlVector < CStoreIDEntry > * pList = filesByStoreID [ iEntry ] ;
for ( int i = 0 ; i < pList - > Count ( ) ; i + + )
{
CStoreIDEntry & entry = pList - > Element ( i ) ;
if ( entry . m_StoreID = = foundStoreID & & V_stricmp ( entry . m_PathIDString . String ( ) , pPathIDStr ) ! = 0 )
return & entry ;
}
return NULL ;
}
}
# endif
//-----------------------------------------------------------------------------
class CBaseFileSystem : : CFileCacheObject
{
public :
CFileCacheObject ( CBaseFileSystem * pFS ) ;
~ CFileCacheObject ( ) ;
void AddFiles ( const char * * ppFileNames , int nFileNames ) ;
bool IsReady ( ) const { return m_nPending = = 0 ; }
static void IOCallback ( const FileAsyncRequest_t & request , int nBytesRead , FSAsyncStatus_t err ) ;
struct Info_t
{
const char * pFileName ;
FSAsyncControl_t hIOAsync ;
CMemoryFileBacking * pBacking ;
CFileCacheObject * pOwner ;
} ;
CBaseFileSystem * m_pFS ;
CInterlockedInt m_nPending ;
CThreadFastMutex m_InfosMutex ;
CUtlVector < Info_t * > m_Infos ;
private :
void ProcessNewEntries ( int start ) ;
CFileCacheObject ( const CFileCacheObject & ) ; // not implemented
CFileCacheObject & operator = ( const CFileCacheObject & ) ; // not implemented
} ;
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CBaseFileSystem : : CBaseFileSystem ( )
: m_FileTracker2 ( this )
{
g_pBaseFileSystem = this ;
g_pFullFileSystem = this ;
m_WhitelistFileTrackingEnabled = - 1 ;
// If this changes then FileNameHandleInternal_t/FileNameHandle_t needs to be fixed!!!
Assert ( sizeof ( CUtlSymbol ) = = sizeof ( short ) ) ;
// Clear out statistics
memset ( & m_Stats , 0 , sizeof ( m_Stats ) ) ;
m_fwLevel = FILESYSTEM_WARNING_REPORTUNCLOSED ;
m_pfnWarning = NULL ;
m_pLogFile = NULL ;
m_bOutputDebugString = false ;
m_WhitelistSpewFlags = 0 ;
m_DirtyDiskReportFunc = NULL ;
m_pPureServerWhitelist = NULL ;
m_pThreadPool = NULL ;
# if defined( TRACK_BLOCKING_IO )
m_pBlockingItems = new CBlockingFileItemList ( this ) ;
m_bBlockingFileAccessReportingEnabled = false ;
m_bAllowSynchronousLogging = true ;
# endif
m_iMapLoad = 0 ;
Q_memset ( m_PreloadData , 0 , sizeof ( m_PreloadData ) ) ;
// allows very specifc constrained behavior
m_DVDMode = DVDMODE_OFF ;
if ( IsX360 ( ) )
{
if ( CommandLine ( ) - > FindParm ( " -dvd " ) )
{
m_DVDMode = DVDMODE_STRICT ;
}
else if ( CommandLine ( ) - > FindParm ( " -dvddev " ) )
{
m_DVDMode = DVDMODE_DEV ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : ~ CBaseFileSystem ( )
{
m_PathIDInfos . PurgeAndDeleteElements ( ) ;
# if defined( TRACK_BLOCKING_IO )
delete m_pBlockingItems ;
# endif
// Free the whitelist.
RegisterFileWhitelist ( NULL , NULL ) ;
}
//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
void * CBaseFileSystem : : QueryInterface ( const char * pInterfaceName )
{
// We also implement the IMatSystemSurface interface
if ( ! Q_strncmp ( pInterfaceName , BASEFILESYSTEM_INTERFACE_VERSION , Q_strlen ( BASEFILESYSTEM_INTERFACE_VERSION ) + 1 ) )
return ( IBaseFileSystem * ) this ;
return NULL ;
}
InitReturnVal_t CBaseFileSystem : : Init ( )
{
InitReturnVal_t nRetVal = BaseClass : : Init ( ) ;
if ( nRetVal ! = INIT_OK )
return nRetVal ;
// This is a special tag to allow iterating just the BSP file, it doesn't show up in the list per se, but gets converted to "GAME" in the filter function
m_BSPPathID = g_PathIDTable . AddString ( " BSP " ) ;
m_GamePathID = g_PathIDTable . AddString ( " GAME " ) ;
if ( getenv ( " fs_debug " ) )
{
m_bOutputDebugString = true ;
}
const char * logFileName = CommandLine ( ) - > ParmValue ( " -fs_log " ) ;
if ( logFileName )
{
m_pLogFile = fopen ( logFileName , " w " ) ; // STEAM OK
if ( ! m_pLogFile )
return INIT_FAILED ;
fprintf ( m_pLogFile , " @echo off \n " ) ;
fprintf ( m_pLogFile , " setlocal \n " ) ;
const char * fs_target = CommandLine ( ) - > ParmValue ( " -fs_target " ) ;
if ( fs_target )
{
fprintf ( m_pLogFile , " set fs_target= \" %s \" \n " , fs_target ) ;
}
fprintf ( m_pLogFile , " if \" %%fs_target%% \" == \" \" goto error \n " ) ;
fprintf ( m_pLogFile , " @echo on \n " ) ;
}
InitAsync ( ) ;
if ( IsX360 ( ) & & m_DVDMode = = DVDMODE_DEV )
{
// exclude paths are valid ony in dvddev mode
char szExcludeFile [ MAX_PATH ] ;
const char * pRemotePath = CommandLine ( ) - > ParmValue ( " -remote " ) ;
const char * pBasePath = CommandLine ( ) - > ParmValue ( " -basedir " ) ;
if ( pRemotePath & & pBasePath )
{
// the optional exclude path file only exists at the remote path
V_ComposeFileName ( pRemotePath , " xbox_exclude_paths.txt " , szExcludeFile , sizeof ( szExcludeFile ) ) ;
// populate the exclusion list
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
if ( ReadFile ( szExcludeFile , NULL , buf , 0 , 0 ) )
{
characterset_t breakSet ;
CharacterSetBuild ( & breakSet , " " ) ;
char szPath [ MAX_PATH ] ;
char szToken [ MAX_PATH ] ;
for ( ; ; )
{
int nTokenSize = buf . ParseToken ( & breakSet , szToken , sizeof ( szToken ) ) ;
if ( nTokenSize < = 0 )
{
break ;
}
char * pToken = szToken ;
if ( pToken [ 0 ] = = ' \\ ' )
{
// skip past possible initial seperator
pToken + + ;
}
V_ComposeFileName ( pBasePath , pToken , szPath , sizeof ( szPath ) ) ;
V_AppendSlash ( szPath , sizeof ( szPath ) ) ;
FileNameHandle_t hFileName = FindOrAddFileName ( szPath ) ;
if ( m_ExcludePaths . Find ( hFileName ) = = - 1 )
{
m_ExcludePaths . AddToTail ( hFileName ) ;
}
}
}
}
}
return INIT_OK ;
}
void CBaseFileSystem : : Shutdown ( )
{
ShutdownAsync ( ) ;
m_FileTracker2 . ShutdownAsync ( ) ;
# ifndef _X360
if ( m_pLogFile )
{
if ( CommandLine ( ) - > FindParm ( " -fs_logbins " ) > = 0 )
{
char cwd [ MAX_FILEPATH ] ;
getcwd ( cwd , MAX_FILEPATH - 1 ) ;
fprintf ( m_pLogFile , " set binsrc= \" %s \" \n " , cwd ) ;
fprintf ( m_pLogFile , " mkdir \" %%fs_target%% \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ hl2.exe \" \" %%fs_target%% \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ hl2.dat \" \" %%fs_target%% \" \n " ) ;
fprintf ( m_pLogFile , " mkdir \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ *.asi \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ materialsystem.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ shaderapidx9.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ filesystem_stdio.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ soundemittersystem.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ stdshader*.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ shader_nv*.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ launcher.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ engine.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ mss32.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ tier0.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ vgui2.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ vguimatsurface.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ voice_miles.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ vphysics.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ vstdlib.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ studiorender.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ bin \\ vaudio_miles.dll \" \" %%fs_target%% \\ bin \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ hl2 \\ resource \\ *.ttf \" \" %%fs_target%% \\ hl2 \\ resource \" \n " ) ;
fprintf ( m_pLogFile , " copy \" %%binsrc%% \\ hl2 \\ bin \\ gameui.dll \" \" %%fs_target%% \\ hl2 \\ bin \" \n " ) ;
}
fprintf ( m_pLogFile , " goto done \n " ) ;
fprintf ( m_pLogFile , " :error \n " ) ;
fprintf ( m_pLogFile , " echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \" \n " ) ;
fprintf ( m_pLogFile , " echo ERROR: must set fs_target=targetpath (ie. \" set fs_target=u: \\ destdir \" )! \n " ) ;
fprintf ( m_pLogFile , " echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \" \n " ) ;
fprintf ( m_pLogFile , " :done \n " ) ;
fclose ( m_pLogFile ) ; // STEAM OK
}
# endif
UnloadCompiledKeyValues ( ) ;
RemoveAllSearchPaths ( ) ;
Trace_DumpUnclosedFiles ( ) ;
BaseClass : : Shutdown ( ) ;
}
//-----------------------------------------------------------------------------
// Computes a full write path
//-----------------------------------------------------------------------------
inline void CBaseFileSystem : : ComputeFullWritePath ( char * pDest , int maxlen , const char * pRelativePath , const char * pWritePathID )
{
Q_strncpy ( pDest , GetWritePath ( pRelativePath , pWritePathID ) , maxlen ) ;
Q_strncat ( pDest , pRelativePath , maxlen , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( pDest ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : src1 -
// src2 -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : OpenedFileLessFunc ( COpenedFile const & src1 , COpenedFile const & src2 )
{
return src1 . m_pFile < src2 . m_pFile ;
}
void CBaseFileSystem : : InstallDirtyDiskReportFunc ( FSDirtyDiskReportFunc_t func )
{
m_DirtyDiskReportFunc = func ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fullpath -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : LogAccessToFile ( char const * accesstype , char const * fullpath , char const * options )
{
LOCAL_THREAD_LOCK ( ) ;
if ( m_fwLevel > = FILESYSTEM_WARNING_REPORTALLACCESSES & & ! V_stristr ( fullpath , " console.log " ) )
{
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES , " ---FS%s: %s %s (%.3f) \n " , ThreadInMainThread ( ) ? " " : " [a] " , accesstype , fullpath , Plat_FloatTime ( ) ) ;
}
int c = m_LogFuncs . Count ( ) ;
if ( ! c )
return ;
for ( int i = 0 ; i < c ; + + i )
{
( m_LogFuncs [ i ] ) ( fullpath , options ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *filename -
// *options -
// Output : FILE
//-----------------------------------------------------------------------------
FILE * CBaseFileSystem : : Trace_FOpen ( const char * filenameT , const char * options , unsigned flags , int64 * size )
{
AUTOBLOCKREPORTER_FN ( Trace_FOpen , this , true , filenameT , FILESYSTEM_BLOCKING_SYNCHRONOUS , FileBlockingItem : : FB_ACCESS_OPEN ) ;
char filename [ MAX_PATH ] ;
FixUpPath ( filenameT , filename , sizeof ( filename ) ) ;
FILE * fp = FS_fopen ( filename , options , flags , size ) ;
if ( fp )
{
if ( options [ 0 ] = = ' r ' )
{
FS_setbufsize ( fp , filesystem_buffer_size . GetInt ( ) ) ;
}
else
{
FS_setbufsize ( fp , 32 * 1024 ) ;
}
AUTO_LOCK ( m_OpenedFilesMutex ) ;
COpenedFile file ;
file . SetName ( filename ) ;
file . m_pFile = fp ;
m_OpenedFiles . AddToTail ( file ) ;
LogAccessToFile ( " open " , filename , options ) ;
}
return fp ;
}
void CBaseFileSystem : : GetFileNameForHandle ( FileHandle_t handle , char * buf , size_t buflen )
{
V_strncpy ( buf , " Unknown " , buflen ) ;
/*
CFileHandle * fh = ( CFileHandle * ) handle ;
if ( ! fh )
{
buf [ 0 ] = 0 ;
return ;
}
# if !defined( _RETAIL )
// Pack file filehandles store the underlying name for convenience
if ( fh - > IsPack ( ) )
{
Q_strncpy ( buf , fh - > Name ( ) , buflen ) ;
return ;
}
# endif
AUTO_LOCK ( m_OpenedFilesMutex ) ;
COpenedFile file ;
file . m_pFile = fh - > GetFileHandle ( ) ;
int result = m_OpenedFiles . Find ( file ) ;
if ( result ! = - 1 )
{
COpenedFile found = m_OpenedFiles [ result ] ;
Q_strncpy ( buf , found . GetName ( ) , buflen ) ;
}
else
{
buf [ 0 ] = 0 ;
}
*/
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fp -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Trace_FClose ( FILE * fp )
{
if ( fp )
{
m_OpenedFilesMutex . Lock ( ) ;
COpenedFile file ;
file . m_pFile = fp ;
int result = m_OpenedFiles . Find ( file ) ;
if ( result ! = - 1 /*m_OpenedFiles.InvalidIdx()*/ )
{
COpenedFile found = m_OpenedFiles [ result ] ;
if ( m_fwLevel > = FILESYSTEM_WARNING_REPORTALLACCESSES & & ! V_stristr ( found . GetName ( ) , " console.log " ) )
{
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES , " ---FS%s: close %s %p %i (%.3f) \n " , ThreadInMainThread ( ) ? " " : " [a] " , found . GetName ( ) , fp , m_OpenedFiles . Count ( ) , Plat_FloatTime ( ) ) ;
}
m_OpenedFiles . Remove ( result ) ;
}
else
{
Assert ( 0 ) ;
if ( m_fwLevel > = FILESYSTEM_WARNING_REPORTALLACCESSES )
{
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES , " Tried to close unknown file pointer %p \n " , fp ) ;
}
}
m_OpenedFilesMutex . Unlock ( ) ;
FS_fclose ( fp ) ;
}
}
void CBaseFileSystem : : Trace_FRead ( int size , FILE * fp )
{
if ( ! fp | | m_fwLevel < FILESYSTEM_WARNING_REPORTALLACCESSES_READ )
return ;
AUTO_LOCK ( m_OpenedFilesMutex ) ;
COpenedFile file ;
file . m_pFile = fp ;
int result = m_OpenedFiles . Find ( file ) ;
if ( result ! = - 1 )
{
COpenedFile found = m_OpenedFiles [ result ] ;
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES_READ , " ---FS%s: read %s %i %p (%.3f) \n " , ThreadInMainThread ( ) ? " " : " [a] " , found . GetName ( ) , size , fp , Plat_FloatTime ( ) ) ;
}
else
{
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES_READ , " Tried to read %i bytes from unknown file pointer %p \n " , size , fp ) ;
}
}
void CBaseFileSystem : : Trace_FWrite ( int size , FILE * fp )
{
if ( ! fp | | m_fwLevel < FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE )
return ;
COpenedFile file ;
file . m_pFile = fp ;
AUTO_LOCK ( m_OpenedFilesMutex ) ;
int result = m_OpenedFiles . Find ( file ) ;
if ( result ! = - 1 )
{
COpenedFile found = m_OpenedFiles [ result ] ;
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE , " ---FS%s: write %s %i %p \n " , ThreadInMainThread ( ) ? " " : " [a] " , found . GetName ( ) , size , fp ) ;
}
else
{
Warning ( FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE , " Tried to write %i bytes from unknown file pointer %p \n " , size , fp ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Trace_DumpUnclosedFiles ( void )
{
/*
AUTO_LOCK ( m_OpenedFilesMutex ) ;
for ( int i = 0 ; i < m_OpenedFiles . Count ( ) ; i + + )
{
COpenedFile * found = & m_OpenedFiles [ i ] ;
if ( m_fwLevel > = FILESYSTEM_WARNING_REPORTUNCLOSED )
{
// This warning can legitimately trigger because the log file is, by design, not
// closed. It is not closed at shutdown in order to avoid race conditions. It is
// not closed at each call to log information because the performance costs,
// especially if Microsoft Security Essentials is running, are extreme.
// In addition, when this warning triggers it can, due to the state of the process,
// actually cause a crash.
Warning ( FILESYSTEM_WARNING_REPORTUNCLOSED , " File %s was never closed \n " , found - > GetName ( ) ) ;
}
}
*/
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : PrintOpenedFiles ( void )
{
FileWarningLevel_t saveLevel = m_fwLevel ;
m_fwLevel = FILESYSTEM_WARNING_REPORTUNCLOSED ;
Trace_DumpUnclosedFiles ( ) ;
m_fwLevel = saveLevel ;
}
# if defined( SUPPORT_PACKED_STORE )
CPackedStoreRefCount : : CPackedStoreRefCount ( char const * pFileBasename , char * pszFName , IBaseFileSystem * pFS )
: CPackedStore ( pFileBasename , pszFName , pFS , false )
{
// If the VPK is signed, check the signature
//
// !FIXME! This code is simple a linchpin that a hacker could detour to bypass sv_pure
# ifdef VPK_ENABLE_SIGNING
CPackedStore : : ESignatureCheckResult eSigResult = CPackedStore : : CheckSignature ( 0 , NULL ) ;
m_bSignatureValid = ( eSigResult = = CPackedStore : : eSignatureCheckResult_ValidSignature ) ;
# else
m_bSignatureValid = false ;
# endif
}
# endif
void CBaseFileSystem : : AddVPKFile ( char const * pPath , const char * pPathID , SearchPathAdd_t addType )
{
# if defined( SUPPORT_PACKED_STORE )
char nameBuf [ MAX_PATH ] ;
Q_MakeAbsolutePath ( nameBuf , sizeof ( nameBuf ) , pPath ) ;
Q_FixSlashes ( nameBuf ) ;
CUtlSymbol pathIDSym = g_PathIDTable . AddString ( pPathID ) ;
// See if we already have this vpk file as a search path
CPackedStoreRefCount * pVPK = NULL ;
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackedStoreRefCount * p = m_SearchPaths [ i ] . GetPackedStore ( ) ;
if ( p & & V_stricmp ( p - > FullPathName ( ) , nameBuf ) = = 0 )
{
// Already present
if ( m_SearchPaths [ i ] . GetPath ( ) = = pathIDSym )
return ;
// Present, but for a different path
pVPK = p ;
}
}
// Create a new VPK if we didn't don't already have it opened
if ( pVPK = = NULL )
{
char pszFName [ MAX_PATH ] ;
pVPK = new CPackedStoreRefCount ( nameBuf , pszFName , this ) ;
if ( pVPK - > IsEmpty ( ) )
{
delete pVPK ;
return ;
}
pVPK - > RegisterFileTracker ( ( IThreadedFileMD5Processor * ) & m_FileTracker2 ) ;
pVPK - > m_PackFileID = m_FileTracker2 . NotePackFileOpened ( pVPK - > FullPathName ( ) , pPathID , 0 ) ;
}
else
{
pVPK - > AddRef ( ) ;
}
// Crete a search path for this
CSearchPath * sp = & m_SearchPaths [ ( addType = = PATH_ADD_TO_TAIL ) ? m_SearchPaths . AddToTail ( ) : m_SearchPaths . AddToHead ( ) ] ;
sp - > SetPackedStore ( pVPK ) ;
sp - > m_storeId = g_iNextSearchPathID + + ;
sp - > SetPath ( pathIDSym ) ;
sp - > m_pPathIDInfo = FindOrAddPathIDInfo ( g_PathIDTable . AddString ( pPathID ) , - 1 ) ;
// Check if we're trusted or not
SetSearchPathIsTrustedSource ( sp ) ;
# endif // SUPPORT_PACKED_STORE
}
bool CBaseFileSystem : : RemoveVPKFile ( const char * pPath , const char * pPathID )
{
# if defined( SUPPORT_PACKED_STORE )
char nameBuf [ MAX_PATH ] ;
Q_MakeAbsolutePath ( nameBuf , sizeof ( nameBuf ) , pPath ) ;
Q_FixSlashes ( nameBuf ) ;
CUtlSymbol pathIDSym = g_PathIDTable . AddString ( pPathID ) ;
// See if we already have this vpk file as a search path
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackedStoreRefCount * p = m_SearchPaths [ i ] . GetPackedStore ( ) ;
if ( p & & V_stricmp ( p - > FullPathName ( ) , nameBuf ) = = 0 )
{
// remove if we find one
if ( m_SearchPaths [ i ] . GetPath ( ) = = pathIDSym )
{
m_SearchPaths . Remove ( i ) ;
return true ;
}
}
}
# endif // SUPPORT_PACKED_STORE
return false ;
}
//-----------------------------------------------------------------------------
// Purpose: Adds the specified pack file to the list
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : AddPackFile ( const char * pFileName , const char * pathID )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
AsyncFinishAll ( ) ;
return AddPackFileFromPath ( " " , pFileName , true , pathID ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Adds a pack file from the specified path
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : AddPackFileFromPath ( const char * pPath , const char * pakfile , bool bCheckForAppendedPack , const char * pathID )
{
char fullpath [ MAX_PATH ] ;
_snprintf ( fullpath , sizeof ( fullpath ) , " %s%s " , pPath , pakfile ) ;
Q_FixSlashes ( fullpath ) ;
struct _stat buf ;
if ( FS_stat ( fullpath , & buf ) = = - 1 )
return false ;
CPackFile * pf = new CZipPackFile ( this ) ;
pf - > m_hPackFileHandleFS = Trace_FOpen ( fullpath , " rb " , 0 , NULL ) ;
if ( ! pf - > m_hPackFileHandleFS )
{
delete pf ;
return false ;
}
// NOTE: Opening .bsp fiels inside of VPK files is not supported
// Get the length of the pack file:
FS_fseek ( ( FILE * ) pf - > m_hPackFileHandleFS , 0 , FILESYSTEM_SEEK_TAIL ) ;
int64 len = FS_ftell ( ( FILE * ) pf - > m_hPackFileHandleFS ) ;
FS_fseek ( ( FILE * ) pf - > m_hPackFileHandleFS , 0 , FILESYSTEM_SEEK_HEAD ) ;
if ( ! pf - > Prepare ( len ) )
{
// Failed for some reason, ignore it
Trace_FClose ( pf - > m_hPackFileHandleFS ) ;
pf - > m_hPackFileHandleFS = NULL ;
delete pf ;
return false ;
}
// Add this pack file to the search path:
CSearchPath * sp = & m_SearchPaths [ m_SearchPaths . AddToTail ( ) ] ;
pf - > SetPath ( sp - > GetPath ( ) ) ;
pf - > m_lPackFileTime = GetFileTime ( pakfile ) ;
sp - > SetPath ( pPath ) ;
sp - > m_pPathIDInfo - > SetPathID ( pathID ) ;
sp - > SetPackFile ( pf ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Search pPath for pak?.pak files and add to search path if found
// Input : *pPath -
//-----------------------------------------------------------------------------
# if !defined( _X360 )
# define PACK_NAME_FORMAT "zip%i.zip"
# else
# define PACK_NAME_FORMAT "zip%i.360.zip"
# define PACK_LOCALIZED_NAME_FORMAT "zip%i_%s.360.zip"
# endif
void CBaseFileSystem : : AddPackFiles ( const char * pPath , const CUtlSymbol & pathID , SearchPathAdd_t addType )
{
Assert ( ThreadInMainThread ( ) ) ;
DISK_INTENSIVE ( ) ;
CUtlVector < CUtlString > pakNames ;
CUtlVector < int64 > pakSizes ;
// determine pak files, [zip0..zipN]
for ( int i = 0 ; ; i + + )
{
char pakfile [ MAX_PATH ] ;
char fullpath [ MAX_PATH ] ;
V_snprintf ( pakfile , sizeof ( pakfile ) , PACK_NAME_FORMAT , i ) ;
V_ComposeFileName ( pPath , pakfile , fullpath , sizeof ( fullpath ) ) ;
struct _stat buf ;
if ( FS_stat ( fullpath , & buf ) = = - 1 )
break ;
MEM_ALLOC_CREDIT ( ) ;
pakNames . AddToTail ( pakfile ) ;
pakSizes . AddToTail ( ( int64 ) ( ( unsigned int ) buf . st_size ) ) ;
}
# if defined( _X360 )
// localized zips are added last to ensure they appear first in the search path construction
// localized zips can only appear in the game or mod directories
bool bUseEnglishAudio = XboxLaunch ( ) - > GetForceEnglish ( ) ;
if ( XBX_IsLocalized ( ) & & ( bUseEnglishAudio = = false ) & &
( V_stricmp ( g_PathIDTable . String ( pathID ) , " game " ) = = 0 | | V_stricmp ( g_PathIDTable . String ( pathID ) , " mod " ) = = 0 ) )
{
// determine localized pak files, [zip0_language..zipN_language]
for ( int i = 0 ; ; i + + )
{
char pakfile [ MAX_PATH ] ;
char fullpath [ MAX_PATH ] ;
V_snprintf ( pakfile , sizeof ( pakfile ) , PACK_LOCALIZED_NAME_FORMAT , i , XBX_GetLanguageString ( ) ) ;
V_ComposeFileName ( pPath , pakfile , fullpath , sizeof ( fullpath ) ) ;
struct _stat buf ;
if ( FS_stat ( fullpath , & buf ) = = - 1 )
break ;
pakNames . AddToTail ( pakfile ) ;
pakSizes . AddToTail ( ( int64 ) ( ( unsigned int ) buf . st_size ) ) ;
}
}
# endif
// Add any zip files in the format zip1.zip ... zip0.zip
// Add them backwards so zip(N) is higher priority than zip(N-1), etc.
int pakcount = pakSizes . Count ( ) ;
int nCount = 0 ;
for ( int i = pakcount - 1 ; i > = 0 ; i - - )
{
char fullpath [ MAX_PATH ] ;
V_ComposeFileName ( pPath , pakNames [ i ] . Get ( ) , fullpath , sizeof ( fullpath ) ) ;
int nIndex ;
if ( addType = = PATH_ADD_TO_TAIL )
{
nIndex = m_SearchPaths . AddToTail ( ) ;
}
else
{
nIndex = m_SearchPaths . InsertBefore ( nCount ) ;
+ + nCount ;
}
CSearchPath * sp = & m_SearchPaths [ nIndex ] ;
sp - > m_pPathIDInfo = FindOrAddPathIDInfo ( pathID , - 1 ) ;
sp - > m_storeId = g_iNextSearchPathID + + ;
sp - > SetPath ( g_PathIDTable . AddString ( pPath ) ) ;
CPackFile * pf = NULL ;
for ( int iPackFile = 0 ; iPackFile < m_ZipFiles . Count ( ) ; iPackFile + + )
{
if ( ! Q_stricmp ( m_ZipFiles [ iPackFile ] - > m_ZipName . Get ( ) , fullpath ) )
{
pf = m_ZipFiles [ iPackFile ] ;
sp - > SetPackFile ( pf ) ;
pf - > AddRef ( ) ;
}
}
if ( ! pf )
{
MEM_ALLOC_CREDIT ( ) ;
pf = new CZipPackFile ( this ) ;
pf - > SetPath ( sp - > GetPath ( ) ) ;
pf - > m_ZipName = fullpath ;
m_ZipFiles . AddToTail ( pf ) ;
sp - > SetPackFile ( pf ) ;
pf - > m_lPackFileTime = GetFileTime ( fullpath ) ;
pf - > m_hPackFileHandleFS = Trace_FOpen ( fullpath , " rb " , 0 , NULL ) ;
if ( pf - > m_hPackFileHandleFS )
{
FS_setbufsize ( pf - > m_hPackFileHandleFS , 32 * 1024 ) ; // 32k buffer.
if ( pf - > Prepare ( pakSizes [ i ] ) )
{
FS_setbufsize ( pf - > m_hPackFileHandleFS , filesystem_buffer_size . GetInt ( ) ) ;
}
else
{
// Failed for some reason, ignore it
if ( pf - > m_hPackFileHandleFS )
{
Trace_FClose ( pf - > m_hPackFileHandleFS ) ;
pf - > m_hPackFileHandleFS = NULL ;
}
m_SearchPaths . Remove ( nIndex ) ;
}
}
else
{
// !NOTE! Pack files inside of VPK not supported
//pf->m_hPackFileHandleVPK = FindFileInVPKs( fullpath );
//if ( !pf->m_hPackFileHandleVPK || !pf->Prepare( pakSizes[i] ) )
{
m_SearchPaths . Remove ( nIndex ) ;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Wipe all map (.bsp) pak file search paths
//-----------------------------------------------------------------------------
void CBaseFileSystem : : RemoveAllMapSearchPaths ( void )
{
AsyncFinishAll ( ) ;
int c = m_SearchPaths . Count ( ) ;
for ( int i = c - 1 ; i > = 0 ; i - - )
{
if ( ! ( m_SearchPaths [ i ] . GetPackFile ( ) & & m_SearchPaths [ i ] . GetPackFile ( ) - > m_bIsMapPath ) )
{
continue ;
}
m_SearchPaths . Remove ( i ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : AddMapPackFile ( const char * pPath , const char * pPathID , SearchPathAdd_t addType )
{
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pPath , pPathID , tempPathID ) ;
// Security nightmares already, should not let things explicitly loading from e.g. "MOD" get surprise untrusted
// files unless you really really know what you're doing.
AssertMsg ( V_strcasecmp ( pPathID , " GAME " ) = = 0 ,
" Mounting map files anywhere besides GAME is asking for pain " ) ;
char newPath [ MAX_FILEPATH ] ;
// +2 for '\0' and potential slash added at end.
V_strncpy ( newPath , pPath , sizeof ( newPath ) ) ;
# ifdef _WIN32 // don't do this on linux!
V_strlower ( newPath ) ;
# endif
V_FixSlashes ( newPath ) ;
// Open the .bsp and find the map lump
char fullpath [ MAX_FILEPATH ] ;
if ( V_IsAbsolutePath ( newPath ) ) // If it's an absolute path, just use that.
{
V_strncpy ( fullpath , newPath , sizeof ( fullpath ) ) ;
}
else
{
if ( ! GetLocalPath ( newPath , fullpath , sizeof ( fullpath ) ) )
{
// Couldn't find that .bsp file!!!
return ;
}
}
int c = m_SearchPaths . Count ( ) ;
for ( int i = c - 1 ; i > = 0 ; i - - )
{
if ( ! ( m_SearchPaths [ i ] . GetPackFile ( ) & & m_SearchPaths [ i ] . GetPackFile ( ) - > m_bIsMapPath ) )
continue ;
if ( V_stricmp ( m_SearchPaths [ i ] . GetPackFile ( ) - > m_ZipName . Get ( ) , fullpath ) = = 0 )
{
// Already set as map path
return ;
}
}
RemoveAllMapSearchPaths ( ) ;
CUtlSymbol pathSymbol = g_PathIDTable . AddString ( newPath ) ;
int iStoreId = CRC32_ProcessSingleBuffer ( fullpath , V_strlen ( fullpath ) ) | 0x80000000 ; // set store ID based on .bsp filename, so if we remount the same map file, it will have the same storeid
// Look through already-open packfiles in case we intentionally
// preserved this ZIP across a map reload via refcount holding
FOR_EACH_VEC ( m_ZipFiles , i )
{
CPackFile * pf = m_ZipFiles [ i ] ;
if ( pf & & pf - > m_bIsMapPath & & pf - > GetPath ( ) = = pathSymbol & & V_stricmp ( pf - > m_ZipName . Get ( ) , fullpath ) = = 0 )
{
CSearchPath * sp = & m_SearchPaths [ ( addType = = PATH_ADD_TO_TAIL ) ? m_SearchPaths . AddToTail ( ) : m_SearchPaths . AddToHead ( ) ] ;
pf - > AddRef ( ) ;
sp - > SetPackFile ( pf ) ;
sp - > m_storeId = iStoreId ;
sp - > SetPath ( pathSymbol ) ;
sp - > m_pPathIDInfo = FindOrAddPathIDInfo ( g_PathIDTable . AddString ( pPathID ) , - 1 ) ;
if ( IsX360 ( ) & & ! V_strnicmp ( newPath , " net: " , 4 ) )
{
sp - > m_bIsRemotePath = true ;
}
SetSearchPathIsTrustedSource ( sp ) ;
return ;
}
}
{
FILE * fp = Trace_FOpen ( fullpath , " rb " , 0 , NULL ) ;
if ( ! fp )
{
// Couldn't open it
Warning ( FILESYSTEM_WARNING , " Couldn't open .bsp %s for embedded pack file check \n " , fullpath ) ;
return ;
}
// Get the .bsp file header
dheader_t header ;
memset ( & header , 0 , sizeof ( dheader_t ) ) ;
m_Stats . nBytesRead + = FS_fread ( & header , sizeof ( header ) , fp ) ;
m_Stats . nReads + + ;
if ( header . ident ! = IDBSPHEADER | | header . version < MINBSPVERSION | | header . version > BSPVERSION )
{
Trace_FClose ( fp ) ;
return ;
}
// Find the LUMP_PAKFILE offset
lump_t * packfile = & header . lumps [ LUMP_PAKFILE ] ;
if ( packfile - > filelen < = sizeof ( lump_t ) )
{
// It's empty or only contains a file header ( so there are no entries ), so don't add to search paths
Trace_FClose ( fp ) ;
return ;
}
// Seek to correct position
FS_fseek ( fp , packfile - > fileofs , FILESYSTEM_SEEK_HEAD ) ;
CPackFile * pf = new CZipPackFile ( this ) ;
pf - > m_bIsMapPath = true ;
pf - > m_hPackFileHandleFS = fp ;
MEM_ALLOC_CREDIT ( ) ;
pf - > m_ZipName = fullpath ;
if ( pf - > Prepare ( packfile - > filelen , packfile - > fileofs ) )
{
int nIndex ;
if ( addType = = PATH_ADD_TO_TAIL )
{
nIndex = m_SearchPaths . AddToTail ( ) ;
}
else
{
nIndex = m_SearchPaths . AddToHead ( ) ;
}
CSearchPath * sp = & m_SearchPaths [ nIndex ] ;
sp - > SetPackFile ( pf ) ;
sp - > m_storeId = iStoreId ;
sp - > SetPath ( pathSymbol ) ;
sp - > m_pPathIDInfo = FindOrAddPathIDInfo ( g_PathIDTable . AddString ( pPathID ) , - 1 ) ;
if ( IsX360 ( ) & & ! V_strnicmp ( newPath , " net: " , 4 ) )
{
sp - > m_bIsRemotePath = true ;
}
pf - > SetPath ( pathSymbol ) ;
pf - > m_lPackFileTime = GetFileTime ( newPath ) ;
Trace_FClose ( pf - > m_hPackFileHandleFS ) ;
pf - > m_hPackFileHandleFS = NULL ;
m_ZipFiles . AddToTail ( pf ) ;
SetSearchPathIsTrustedSource ( sp ) ;
}
else
{
delete pf ;
}
}
}
void CBaseFileSystem : : BeginMapAccess ( )
{
if ( m_iMapLoad + + = = 0 )
{
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
CSearchPath * pSearchPath = & m_SearchPaths [ i ] ;
CPackFile * pPackFile = pSearchPath - > GetPackFile ( ) ;
if ( pPackFile & & pPackFile - > m_bIsMapPath )
{
pPackFile - > AddRef ( ) ;
pPackFile - > m_mutex . Lock ( ) ;
# if defined( SUPPORT_PACKED_STORE )
if ( pPackFile - > m_nOpenFiles = = 0 & & pPackFile - > m_hPackFileHandleFS = = NULL & & ! pPackFile - > m_hPackFileHandleVPK )
# else
if ( pPackFile - > m_nOpenFiles = = 0 & & pPackFile - > m_hPackFileHandleFS = = NULL )
# endif
{
// Try opening the file as a regular file
pPackFile - > m_hPackFileHandleFS = Trace_FOpen ( pPackFile - > m_ZipName , " rb " , 0 , NULL ) ;
// !NOTE! Pack files inside of VPK not supported
//#if defined( SUPPORT_PACKED_STORE )
// if ( !pPackFile->m_hPackFileHandleFS )
// {
// pPackFile->m_hPackFileHandleVPK = FindFileInVPKs( pPackFile->m_ZipName );
// }
//#endif
}
pPackFile - > m_nOpenFiles + + ;
pPackFile - > m_mutex . Unlock ( ) ;
}
}
}
}
void CBaseFileSystem : : EndMapAccess ( )
{
if ( m_iMapLoad - - = = 1 )
{
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
CSearchPath * pSearchPath = & m_SearchPaths [ i ] ;
CPackFile * pPackFile = pSearchPath - > GetPackFile ( ) ;
if ( pPackFile & & pPackFile - > m_bIsMapPath )
{
pPackFile - > m_mutex . Lock ( ) ;
pPackFile - > m_nOpenFiles - - ;
if ( pPackFile - > m_nOpenFiles = = 0 )
{
if ( pPackFile - > m_hPackFileHandleFS )
{
Trace_FClose ( pPackFile - > m_hPackFileHandleFS ) ;
pPackFile - > m_hPackFileHandleFS = NULL ;
}
}
pPackFile - > m_mutex . Unlock ( ) ;
pPackFile - > Release ( ) ;
}
}
}
}
void CBaseFileSystem : : PrintSearchPaths ( void )
{
Msg ( " --------------- \n " ) ;
Msg ( " Paths: \n " ) ;
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
CSearchPath * pSearchPath = & m_SearchPaths [ i ] ;
const char * pszPack = " " ;
const char * pszType = " " ;
if ( pSearchPath - > GetPackFile ( ) & & pSearchPath - > GetPackFile ( ) - > m_bIsMapPath )
{
pszType = " (map) " ;
}
else if ( pSearchPath - > GetPackFile ( ) )
{
pszType = " (pack) " ;
pszPack = pSearchPath - > GetPackFile ( ) - > m_ZipName ;
}
# ifdef SUPPORT_PACKED_STORE
else if ( pSearchPath - > GetPackedStore ( ) )
{
pszType = " (VPK) " ;
pszPack = pSearchPath - > GetPackedStore ( ) - > FullPathName ( ) ;
}
# endif
Msg ( " \" %s \" \" %s \" %s%s \n " , pSearchPath - > GetPathString ( ) , ( const char * ) pSearchPath - > GetPathIDString ( ) , pszType , pszPack ) ;
}
if ( IsX360 ( ) & & m_ExcludePaths . Count ( ) )
{
// dump current list
Msg ( " \n Exclude: \n " ) ;
char szPath [ MAX_PATH ] ;
for ( int i = 0 ; i < m_ExcludePaths . Count ( ) ; i + + )
{
if ( String ( m_ExcludePaths [ i ] , szPath , sizeof ( szPath ) ) )
{
Msg ( " \" %s \" \n " , szPath ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: This is where search paths are created. map files are created at head of list (they occur after
// file system paths have already been set ) so they get highest priority. Otherwise, we add the disk (non-packfile)
// path and then the paks if they exist for the path
// Input : *pPath -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : AddSearchPathInternal ( const char * pPath , const char * pathID , SearchPathAdd_t addType , bool bAddPackFiles )
{
AsyncFinishAll ( ) ;
Assert ( ThreadInMainThread ( ) ) ;
// Map pak files have their own handler
if ( V_stristr ( pPath , " .bsp " ) )
{
AddMapPackFile ( pPath , pathID , addType ) ;
return ;
}
// So do VPK files
if ( V_stristr ( pPath , " .vpk " ) )
{
AddVPKFile ( pPath , pathID , addType ) ;
return ;
}
// Clean up the name
char newPath [ MAX_FILEPATH ] ;
if ( pPath [ 0 ] = = 0 )
{
newPath [ 0 ] = newPath [ 1 ] = 0 ;
}
else
{
if ( IsX360 ( ) | | Q_IsAbsolutePath ( pPath ) )
{
Q_strncpy ( newPath , pPath , sizeof ( newPath ) ) ;
}
else
{
Q_MakeAbsolutePath ( newPath , sizeof ( newPath ) , pPath ) ;
}
# ifdef _WIN32
Q_strlower ( newPath ) ;
# endif
AddSeperatorAndFixPath ( newPath ) ;
}
// Make sure that it doesn't already exist
CUtlSymbol pathSym , pathIDSym ;
pathSym = g_PathIDTable . AddString ( newPath ) ;
pathIDSym = g_PathIDTable . AddString ( pathID ) ;
int i ;
int c = m_SearchPaths . Count ( ) ;
int id = 0 ;
for ( i = 0 ; i < c ; i + + )
{
CSearchPath * pSearchPath = & m_SearchPaths [ i ] ;
if ( pSearchPath - > GetPath ( ) = = pathSym & & pSearchPath - > GetPathID ( ) = = pathIDSym )
{
if ( ( addType = = PATH_ADD_TO_HEAD & & i = = 0 ) | | ( addType = = PATH_ADD_TO_TAIL ) )
{
return ; // this entry is already at the head
}
else
{
m_SearchPaths . Remove ( i ) ; // remove it from its current position so we can add it back to the head
i - - ;
c - - ;
}
}
if ( ! id & & pSearchPath - > GetPath ( ) = = pathSym )
{
// get first found - all reference the same store
id = pSearchPath - > m_storeId ;
}
}
if ( ! id )
{
id = g_iNextSearchPathID + + ;
}
if ( IsX360 ( ) & & bAddPackFiles & & ( ! Q_stricmp ( pathID , " DEFAULT_WRITE_PATH " ) | | ! Q_stricmp ( pathID , " LOGDIR " ) ) )
{
// xbox can be assured that no zips would ever be loaded on its write path
// otherwise xbox reloads zips because of mirrored drive mappings
bAddPackFiles = false ;
}
// Add to list
bool bAdded = false ;
int nIndex = m_SearchPaths . Count ( ) ;
if ( bAddPackFiles )
{
// Add pack files for this path next
AddPackFiles ( newPath , pathIDSym , addType ) ;
bAdded = m_SearchPaths . Count ( ) ! = nIndex ;
}
if ( addType = = PATH_ADD_TO_HEAD )
{
nIndex = m_SearchPaths . Count ( ) - nIndex ;
Assert ( nIndex > = 0 ) ;
}
if ( IsPC ( ) | | ! bAddPackFiles | | ! bAdded )
{
// Grab last entry and set the path
m_SearchPaths . InsertBefore ( nIndex ) ;
}
else if ( IsX360 ( ) & & bAddPackFiles & & bAdded )
{
// 360 needs to find files (for the preload hit) in the zip first for fast loading
// 360 always adds the non-pack search path *after* the pack file but respects the overall list ordering
if ( addType = = PATH_ADD_TO_HEAD )
{
m_SearchPaths . InsertBefore ( nIndex ) ;
}
else
{
nIndex = m_SearchPaths . Count ( ) - 1 ;
m_SearchPaths . InsertAfter ( nIndex ) ;
nIndex + + ;
}
}
CSearchPath * sp = & m_SearchPaths [ nIndex ] ;
sp - > SetPath ( pathSym ) ;
sp - > m_pPathIDInfo = FindOrAddPathIDInfo ( pathIDSym , - 1 ) ;
// all matching paths have a reference to the same store
sp - > m_storeId = id ;
if ( IsX360 ( ) & & ! V_strnicmp ( newPath , " net: " , 4 ) )
{
sp - > m_bIsRemotePath = true ;
}
}
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath * CBaseFileSystem : : FindSearchPathByStoreId ( int storeId )
{
FOR_EACH_VEC ( m_SearchPaths , i )
{
if ( m_SearchPaths [ i ] . m_storeId = = storeId )
return & m_SearchPaths [ i ] ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Create the search path.
//-----------------------------------------------------------------------------
void CBaseFileSystem : : AddSearchPath ( const char * pPath , const char * pathID , SearchPathAdd_t addType )
{
int currCount = m_SearchPaths . Count ( ) ;
AddSearchPathInternal ( pPath , pathID , addType , true ) ;
if ( IsX360 ( ) & & m_DVDMode = = DVDMODE_DEV )
{
// dvd development mode clones a search path based on the remote path for fall through
const char * pRemotePath = CommandLine ( ) - > ParmValue ( " -remote " ) ;
const char * pBasePath = CommandLine ( ) - > ParmValue ( " -basedir " ) ;
if ( pRemotePath & & pBasePath & & ! V_stristr ( pPath , " .bsp " ) )
{
// isolate the search path from the base path
if ( ! V_strnicmp ( pPath , pBasePath , strlen ( pBasePath ) ) )
{
// substitue the remote path
char szRemotePath [ MAX_PATH ] ;
V_strncpy ( szRemotePath , pRemotePath , sizeof ( szRemotePath ) ) ;
V_strncat ( szRemotePath , pPath + strlen ( pBasePath ) , sizeof ( szRemotePath ) ) ;
// no pack files are allowed on the fall through remote path
AddSearchPathInternal ( szRemotePath , pathID , addType , false ) ;
}
}
}
if ( currCount ! = m_SearchPaths . Count ( ) )
{
# if !defined( DEDICATED )
if ( IsDebug ( ) )
{
// spew updated search paths
// PrintSearchPaths();
}
# endif
}
}
//-----------------------------------------------------------------------------
// Returns the search path, each path is separated by ;s. Returns the length of the string returned
// Pack search paths include the pack name, so that callers can still form absolute paths
// and that absolute path can be sent to the filesystem, and mounted as a file inside a pack.
//-----------------------------------------------------------------------------
int CBaseFileSystem : : GetSearchPath ( const char * pathID , bool bGetPackFiles , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars )
{
AUTO_LOCK ( m_SearchPathsMutex ) ;
if ( maxLenInChars )
{
pDest [ 0 ] = 0 ;
}
// Build up result into string object
CUtlString sResult ;
CSearchPathsIterator iter ( this , pathID , bGetPackFiles ? FILTER_NONE : FILTER_CULLPACK ) ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
if ( ! sResult . IsEmpty ( ) )
sResult + = " ; " ;
CUtlString sName ;
if ( pSearchPath - > GetPackFile ( ) )
{
sResult + = pSearchPath - > GetPackFile ( ) - > m_ZipName . String ( ) ;
sResult + = CORRECT_PATH_SEPARATOR_S ;
}
# ifdef SUPPORT_PACKED_STORE
else if ( pSearchPath - > GetPackedStore ( ) )
{
sResult + = pSearchPath - > GetPackedStore ( ) - > FullPathName ( ) ;
}
# endif
else
{
sResult + = pSearchPath - > GetPathString ( ) ;
}
}
// Copy into user's buffer, possibly truncating
if ( maxLenInChars )
{
V_strncpy ( pDest , sResult . String ( ) , maxLenInChars ) ;
}
// Return 1 extra for the NULL terminator
return sResult . Length ( ) + 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pPath -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : RemoveSearchPath ( const char * pPath , const char * pathID )
{
AsyncFinishAll ( ) ;
char newPath [ MAX_FILEPATH ] ;
newPath [ 0 ] = 0 ;
if ( pPath )
{
// +2 for '\0' and potential slash added at end.
Q_strncpy ( newPath , pPath , sizeof ( newPath ) ) ;
# ifdef _WIN32 // don't do this on linux!
Q_strlower ( newPath ) ;
# endif
if ( V_stristr ( newPath , " .bsp " ) )
{
Q_FixSlashes ( newPath ) ;
}
else if ( V_stristr ( newPath , " .vpk " ) )
{
return RemoveVPKFile ( newPath , pathID ) ;
}
else
{
AddSeperatorAndFixPath ( newPath ) ;
}
}
CUtlSymbol lookup = g_PathIDTable . AddString ( newPath ) ;
CUtlSymbol id = g_PathIDTable . AddString ( pathID ) ;
bool bret = false ;
// Count backward since we're possibly deleting one or more pack files, too
int i ;
int c = m_SearchPaths . Count ( ) ;
for ( i = c - 1 ; i > = 0 ; i - - )
{
if ( newPath [ 0 ] & & m_SearchPaths [ i ] . GetPath ( ) ! = lookup )
continue ;
if ( FilterByPathID ( & m_SearchPaths [ i ] , id ) )
continue ;
m_SearchPaths . Remove ( i ) ;
bret = true ;
}
return bret ;
}
//-----------------------------------------------------------------------------
// Purpose: Removes all search paths for a given pathID, such as all "GAME" paths.
// Input : pathID -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : RemoveSearchPaths ( const char * pathID )
{
AsyncFinishAll ( ) ;
int nCount = m_SearchPaths . Count ( ) ;
for ( int i = nCount - 1 ; i > = 0 ; i - - )
{
if ( ! Q_stricmp ( m_SearchPaths . Element ( i ) . GetPathIDString ( ) , pathID ) )
{
m_SearchPaths . FastRemove ( i ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath * CBaseFileSystem : : FindWritePath ( const char * pFilename , const char * pathID )
{
CUtlSymbol lookup = g_PathIDTable . AddString ( pathID ) ;
AUTO_LOCK ( m_SearchPathsMutex ) ;
// a pathID has been specified, find the first match in the path list
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
// pak files are not allowed to be written to...
CSearchPath * pSearchPath = & m_SearchPaths [ i ] ;
if ( pSearchPath - > GetPackFile ( ) | | pSearchPath - > GetPackedStore ( ) )
{
continue ;
}
if ( IsX360 ( ) & & ( m_DVDMode = = DVDMODE_DEV ) & & pFilename & & ! pSearchPath - > m_bIsRemotePath )
{
bool bIgnorePath = false ;
char szExcludePath [ MAX_PATH ] ;
char szFilename [ MAX_PATH ] ;
V_ComposeFileName ( pSearchPath - > GetPathString ( ) , pFilename , szFilename , sizeof ( szFilename ) ) ;
for ( int j = 0 ; j < m_ExcludePaths . Count ( ) ; j + + )
{
if ( g_pFullFileSystem - > String ( m_ExcludePaths [ j ] , szExcludePath , sizeof ( szExcludePath ) ) )
{
if ( ! V_strnicmp ( szFilename , szExcludePath , strlen ( szExcludePath ) ) )
{
bIgnorePath = true ;
break ;
}
}
}
if ( bIgnorePath )
{
// filename matches exclusion path, skip it
// favoring the next path which should be the path fall through hit
continue ;
}
}
if ( ! pathID | | ( pSearchPath - > GetPathID ( ) = = lookup ) )
{
return pSearchPath ;
}
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds a search path that should be used for writing to, given a pathID.
//-----------------------------------------------------------------------------
const char * CBaseFileSystem : : GetWritePath ( const char * pFilename , const char * pathID )
{
CSearchPath * pSearchPath = NULL ;
if ( pathID & & pathID [ 0 ] ! = ' \0 ' )
{
// Check for "game_write" and "mod_write"
if ( V_stricmp ( pathID , " game " ) = = 0 )
pSearchPath = FindWritePath ( pFilename , " game_write " ) ;
else if ( V_stricmp ( pathID , " mod " ) = = 0 )
pSearchPath = FindWritePath ( pFilename , " mod_write " ) ;
if ( pSearchPath = = NULL )
pSearchPath = FindWritePath ( pFilename , pathID ) ;
if ( pSearchPath )
{
return pSearchPath - > GetPathString ( ) ;
}
Warning ( FILESYSTEM_WARNING , " Requested non-existent write path %s! \n " , pathID ) ;
}
pSearchPath = FindWritePath ( pFilename , " DEFAULT_WRITE_PATH " ) ;
if ( pSearchPath )
{
return pSearchPath - > GetPathString ( ) ;
}
pSearchPath = FindWritePath ( pFilename , NULL ) ; // okay, just return the first search path added!
if ( pSearchPath )
{
return pSearchPath - > GetPathString ( ) ;
}
// Hope this is reasonable!!
return " . \\ " ;
}
//-----------------------------------------------------------------------------
// Reads/writes files to utlbuffers. Attempts alignment fixups for optimal read
//-----------------------------------------------------------------------------
CTHREADLOCAL ( char * ) g_pszReadFilename ;
bool CBaseFileSystem : : ReadToBuffer ( FileHandle_t fp , CUtlBuffer & buf , int nMaxBytes , FSAllocFunc_t pfnAlloc )
{
SetBufferSize ( fp , 0 ) ; // TODO: what if it's a pack file? restore buffer size?
int nBytesToRead = Size ( fp ) ;
if ( nBytesToRead = = 0 )
{
// no data in file
return true ;
}
if ( nMaxBytes > 0 )
{
// can't read more than file has
nBytesToRead = min ( nMaxBytes , nBytesToRead ) ;
}
int nBytesRead = 0 ;
int nBytesOffset = 0 ;
int iStartPos = Tell ( fp ) ;
if ( nBytesToRead ! = 0 )
{
int nBytesDestBuffer = nBytesToRead ;
unsigned nSizeAlign = 0 , nBufferAlign = 0 , nOffsetAlign = 0 ;
bool bBinary = ! ( buf . IsText ( ) & & ! buf . ContainsCRLF ( ) ) ;
if ( bBinary & & ! IsLinux ( ) & & ! buf . IsExternallyAllocated ( ) & & ! pfnAlloc & &
( buf . TellPut ( ) = = 0 ) & & ( buf . TellGet ( ) = = 0 ) & & ( iStartPos % 4 = = 0 ) & &
GetOptimalIOConstraints ( fp , & nOffsetAlign , & nSizeAlign , & nBufferAlign ) )
{
// correct conditions to allow an optimal read
if ( iStartPos % nOffsetAlign ! = 0 )
{
// move starting position back to nearest alignment
nBytesOffset = ( iStartPos % nOffsetAlign ) ;
Assert ( ( iStartPos - nBytesOffset ) % nOffsetAlign = = 0 ) ;
Seek ( fp , - nBytesOffset , FILESYSTEM_SEEK_CURRENT ) ;
// going to read from aligned start, increase target buffer size by offset alignment
nBytesDestBuffer + = nBytesOffset ;
}
// snap target buffer size to its size alignment
// add additional alignment slop for target pointer adjustment
nBytesDestBuffer = AlignValue ( nBytesDestBuffer , nSizeAlign ) + nBufferAlign ;
}
if ( ! pfnAlloc )
{
buf . EnsureCapacity ( nBytesDestBuffer + buf . TellPut ( ) ) ;
}
else
{
// caller provided allocator
void * pMemory = ( * pfnAlloc ) ( g_pszReadFilename . Get ( ) , nBytesDestBuffer ) ;
buf . SetExternalBuffer ( pMemory , nBytesDestBuffer , 0 , buf . GetFlags ( ) & ~ CUtlBuffer : : EXTERNAL_GROWABLE ) ;
}
int seekGet = - 1 ;
if ( nBytesDestBuffer ! = nBytesToRead )
{
// doing optimal read, align target pointer
int nAlignedBase = AlignValue ( ( byte * ) buf . Base ( ) , nBufferAlign ) - ( byte * ) buf . Base ( ) ;
buf . SeekPut ( CUtlBuffer : : SEEK_HEAD , nAlignedBase ) ;
// the buffer read position is slid forward to ignore the addtional
// starting offset alignment
seekGet = nAlignedBase + nBytesOffset ;
}
nBytesRead = ReadEx ( buf . PeekPut ( ) , buf . Size ( ) - buf . TellPut ( ) , nBytesToRead + nBytesOffset , fp ) ;
buf . SeekPut ( CUtlBuffer : : SEEK_CURRENT , nBytesRead ) ;
if ( seekGet ! = - 1 )
{
// can only seek the get after data has been put, otherwise buffer sets overflow error
buf . SeekGet ( CUtlBuffer : : SEEK_HEAD , seekGet ) ;
}
Seek ( fp , iStartPos + ( nBytesRead - nBytesOffset ) , FILESYSTEM_SEEK_HEAD ) ;
}
return ( nBytesRead ! = 0 ) ;
}
//-----------------------------------------------------------------------------
// Reads/writes files to utlbuffers
// NOTE NOTE!!
// If you change this implementation, copy it into CBaseVMPIFileSystem::ReadFile
// in vmpi_filesystem.cpp
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : ReadFile ( const char * pFileName , const char * pPath , CUtlBuffer & buf , int nMaxBytes , int nStartingByte , FSAllocFunc_t pfnAlloc )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
bool bBinary = ! ( buf . IsText ( ) & & ! buf . ContainsCRLF ( ) ) ;
FileHandle_t fp = Open ( pFileName , ( bBinary ) ? " rb " : " rt " , pPath ) ;
if ( ! fp )
return false ;
if ( nStartingByte ! = 0 )
{
Seek ( fp , nStartingByte , FILESYSTEM_SEEK_HEAD ) ;
}
if ( pfnAlloc )
{
g_pszReadFilename . Set ( ( char * ) pFileName ) ;
}
bool bSuccess = ReadToBuffer ( fp , buf , nMaxBytes , pfnAlloc ) ;
Close ( fp ) ;
return bSuccess ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CBaseFileSystem : : ReadFileEx ( const char * pFileName , const char * pPath , void * * ppBuf , bool bNullTerminate , bool bOptimalAlloc , int nMaxBytes , int nStartingByte , FSAllocFunc_t pfnAlloc )
{
FileHandle_t fp = Open ( pFileName , " rb " , pPath ) ;
if ( ! fp )
{
return 0 ;
}
if ( IsX360 ( ) )
{
// callers are sloppy, always want optimal
bOptimalAlloc = true ;
}
SetBufferSize ( fp , 0 ) ; // TODO: what if it's a pack file? restore buffer size?
int nBytesToRead = Size ( fp ) ;
int nBytesRead = 0 ;
if ( nMaxBytes > 0 )
{
nBytesToRead = min ( nMaxBytes , nBytesToRead ) ;
if ( bNullTerminate )
{
nBytesToRead - - ;
}
}
if ( nBytesToRead ! = 0 )
{
int nBytesBuf ;
if ( ! * ppBuf )
{
nBytesBuf = nBytesToRead + ( ( bNullTerminate ) ? 1 : 0 ) ;
if ( ! pfnAlloc & & ! bOptimalAlloc )
{
// AllocOptimalReadBuffer does malloc...
* ppBuf = malloc ( nBytesBuf ) ;
}
else if ( ! pfnAlloc )
{
* ppBuf = AllocOptimalReadBuffer ( fp , nBytesBuf , 0 ) ;
nBytesBuf = GetOptimalReadSize ( fp , nBytesBuf ) ;
}
else
{
* ppBuf = ( * pfnAlloc ) ( pFileName , nBytesBuf ) ;
}
}
else
{
nBytesBuf = nMaxBytes ;
}
if ( nStartingByte ! = 0 )
{
Seek ( fp , nStartingByte , FILESYSTEM_SEEK_HEAD ) ;
}
nBytesRead = ReadEx ( * ppBuf , nBytesBuf , nBytesToRead , fp ) ;
if ( bNullTerminate )
{
( ( byte * ) ( * ppBuf ) ) [ nBytesToRead ] = 0 ;
}
}
Close ( fp ) ;
return nBytesRead ;
}
//-----------------------------------------------------------------------------
// NOTE NOTE!!
// If you change this implementation, copy it into CBaseVMPIFileSystem::WriteFile
// in vmpi_filesystem.cpp
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : WriteFile ( const char * pFileName , const char * pPath , CUtlBuffer & buf )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
const char * pWriteFlags = " wb " ;
if ( buf . IsText ( ) & & ! buf . ContainsCRLF ( ) )
{
pWriteFlags = " wt " ;
}
FileHandle_t fp = Open ( pFileName , pWriteFlags , pPath ) ;
if ( ! fp )
return false ;
int nBytesWritten = Write ( buf . Base ( ) , buf . TellPut ( ) , fp ) ;
Close ( fp ) ;
return ( nBytesWritten = = buf . TellPut ( ) ) ;
}
bool CBaseFileSystem : : UnzipFile ( const char * pFileName , const char * pPath , const char * pDestination )
{
IZip * pZip = IZip : : CreateZip ( NULL , true ) ;
HANDLE hZipFile = pZip - > ParseFromDisk ( pFileName ) ;
if ( ! hZipFile )
{
Msg ( " Bad or missing zip file, failed to open '%s' \n " , pFileName ) ;
return false ;
}
int iZipIndex = - 1 ;
int iFileSize ;
char szFileName [ MAX_PATH ] ;
// Create Directories
CreateDirHierarchy ( pDestination , pPath ) ;
while ( 1 )
{
// Get the next file in the zip
szFileName [ 0 ] = ' \0 ' ;
iFileSize = 0 ;
iZipIndex = pZip - > GetNextFilename ( iZipIndex , szFileName , sizeof ( szFileName ) , iFileSize ) ;
// If there aren't any more files then break out of this while
if ( iZipIndex = = - 1 )
break ;
int iFileNameLength = Q_strlen ( szFileName ) ;
if ( szFileName [ iFileNameLength - 1 ] = = ' / ' )
{
// Its a directory, so create it
szFileName [ iFileNameLength - 1 ] = ' \0 ' ;
char szFinalName [ MAX_PATH ] ;
Q_snprintf ( szFinalName , sizeof ( szFinalName ) , " %s%c%s " , pDestination , CORRECT_PATH_SEPARATOR , szFileName ) ;
CreateDirHierarchy ( szFinalName , pPath ) ;
}
}
// Write Files
while ( 1 )
{
szFileName [ 0 ] = ' \0 ' ;
iFileSize = 0 ;
iZipIndex = pZip - > GetNextFilename ( iZipIndex , szFileName , sizeof ( szFileName ) , iFileSize ) ;
// If there aren't any more files then break out of this while
if ( iZipIndex = = - 1 )
break ;
int iFileNameLength = Q_strlen ( szFileName ) ;
if ( szFileName [ iFileNameLength - 1 ] ! = ' / ' )
{
// It's not a directory, so write the file
CUtlBuffer fileBuffer ;
fileBuffer . Purge ( ) ;
if ( pZip - > ReadFileFromZip ( hZipFile , szFileName , false , fileBuffer ) )
{
char szFinalName [ MAX_PATH ] ;
Q_snprintf ( szFinalName , sizeof ( szFinalName ) , " %s%c%s " , pDestination , CORRECT_PATH_SEPARATOR , szFileName ) ;
// Make sure the directory actually exists in case the ZIP doesn't list it (our zip utils create zips like this)
char szFilePath [ MAX_PATH ] ;
Q_strncpy ( szFilePath , szFinalName , sizeof ( szFilePath ) ) ;
Q_StripFilename ( szFilePath ) ;
CreateDirHierarchy ( szFilePath , pPath ) ;
WriteFile ( szFinalName , pPath , fileBuffer ) ;
}
}
}
# ifdef WIN32
: : CloseHandle ( hZipFile ) ;
# else
fclose ( ( FILE * ) hZipFile ) ;
# endif
IZip : : ReleaseZip ( pZip ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : RemoveAllSearchPaths ( void )
{
AUTO_LOCK ( m_SearchPathsMutex ) ;
m_SearchPaths . Purge ( ) ;
//m_PackFileHandles.Purge();
}
void CBaseFileSystem : : LogFileAccess ( const char * pFullFileName )
{
if ( ! m_pLogFile )
{
return ;
}
char buf [ 1024 ] ;
# if BSPOUTPUT
Q_snprintf ( buf , sizeof ( buf ) , " %s \n %s \n " , pShortFileName , pFullFileName ) ;
fprintf ( m_pLogFile , " %s " , buf ) ; // STEAM OK
# else
char cwd [ MAX_FILEPATH ] ;
getcwd ( cwd , MAX_FILEPATH - 1 ) ;
Q_strcat ( cwd , " \\ " , sizeof ( cwd ) ) ;
if ( Q_strnicmp ( cwd , pFullFileName , strlen ( cwd ) ) = = 0 )
{
const char * pFileNameWithoutExeDir = pFullFileName + strlen ( cwd ) ;
char targetPath [ MAX_FILEPATH ] ;
strcpy ( targetPath , " %fs_target% \\ " ) ;
strcat ( targetPath , pFileNameWithoutExeDir ) ;
Q_snprintf ( buf , sizeof ( buf ) , " copy \" %s \" \" %s \" \n " , pFullFileName , targetPath ) ;
char tmp [ MAX_FILEPATH ] ;
Q_strncpy ( tmp , targetPath , sizeof ( tmp ) ) ;
Q_StripFilename ( tmp ) ;
fprintf ( m_pLogFile , " mkdir \" %s \" \n " , tmp ) ; // STEAM OK
fprintf ( m_pLogFile , " %s " , buf ) ; // STEAM OK
}
else
{
Assert ( 0 ) ;
}
# endif
}
class CPackedStore ;
class CFileOpenInfo
{
public :
CFileOpenInfo ( CBaseFileSystem * pFileSystem , const char * pFileName , const CBaseFileSystem : : CSearchPath * path , const char * pOptions , int flags , char * * ppszResolvedFilename ) :
m_pFileSystem ( pFileSystem ) , m_pFileName ( pFileName ) , m_pSearchPath ( path ) , m_pOptions ( pOptions ) , m_Flags ( flags ) , m_ppszResolvedFilename ( ppszResolvedFilename )
{
m_pFileHandle = NULL ;
m_ePureFileClass = ePureServerFileClass_Any ;
if ( m_pFileSystem - > m_pPureServerWhitelist )
{
m_ePureFileClass = m_pFileSystem - > m_pPureServerWhitelist - > GetFileClass ( pFileName ) ;
}
if ( m_ppszResolvedFilename )
* m_ppszResolvedFilename = NULL ;
m_pPackFile = NULL ;
m_pVPKFile = NULL ;
m_AbsolutePath [ 0 ] = ' \0 ' ;
}
~ CFileOpenInfo ( )
{
if ( IsX360 ( ) )
{
return ;
}
}
void SetAbsolutePath ( const char * pFormat , . . . )
{
va_list marker ;
va_start ( marker , pFormat ) ;
V_vsnprintf ( m_AbsolutePath , sizeof ( m_AbsolutePath ) , pFormat , marker ) ;
va_end ( marker ) ;
V_FixSlashes ( m_AbsolutePath ) ;
}
void SetResolvedFilename ( const char * pStr )
{
if ( m_ppszResolvedFilename )
{
Assert ( ! ( * m_ppszResolvedFilename ) ) ;
* m_ppszResolvedFilename = strdup ( pStr ) ;
}
}
// Handles telling CFileTracker about the file we just opened so it can remember
// where the file came from, and possibly calculate a CRC if necessary.
void HandleFileCRCTracking ( const char * pRelativeFileName )
{
if ( IsX360 ( ) )
{
return ;
}
if ( m_pFileSystem - > m_WhitelistFileTrackingEnabled = = 0 )
return ;
if ( m_pFileHandle & & m_pSearchPath )
{
FILE * fp = m_pFileHandle - > m_pFile ;
int64 nLength = m_pFileHandle - > m_nLength ;
# ifdef SUPPORT_PACKED_STORE
if ( m_pVPKFile )
{
m_pFileSystem - > m_FileTracker2 . NotePackFileAccess ( pRelativeFileName , m_pSearchPath - > GetPathIDString ( ) , m_pSearchPath - > m_storeId , m_pFileHandle - > m_VPKHandle ) ;
return ;
}
# endif
// we always record hashes of everything we load. we may filter later.
m_pFileSystem - > m_FileTracker2 . NoteFileLoadedFromDisk ( pRelativeFileName , m_pSearchPath - > GetPathIDString ( ) , m_pSearchPath - > m_storeId , fp , nLength ) ;
}
}
# ifdef SUPPORT_PACKED_STORE
void SetFromPackedStoredFileHandle ( const CPackedStoreFileHandle & fHandle , CBaseFileSystem * pFileSystem )
{
Assert ( fHandle ) ;
Assert ( m_pFileHandle = = NULL ) ;
m_pFileHandle = new CFileHandle ( pFileSystem ) ;
m_pFileHandle - > m_VPKHandle = fHandle ;
m_pFileHandle - > m_type = FT_NORMAL ;
m_pFileHandle - > m_nLength = fHandle . m_nFileSize ;
}
# endif
public :
CBaseFileSystem * m_pFileSystem ;
// These are output parameters.
CFileHandle * m_pFileHandle ;
char * * m_ppszResolvedFilename ;
CPackFile * m_pPackFile ;
CPackedStore * m_pVPKFile ;
const char * m_pFileName ;
const CBaseFileSystem : : CSearchPath * m_pSearchPath ;
const char * m_pOptions ;
int m_Flags ;
EPureServerFileClass m_ePureFileClass ;
char m_AbsolutePath [ MAX_FILEPATH ] ; // This is set
} ;
void CBaseFileSystem : : HandleOpenRegularFile ( CFileOpenInfo & openInfo , bool bIsAbsolutePath )
{
openInfo . m_pFileHandle = NULL ;
int64 size ;
FILE * fp = Trace_FOpen ( openInfo . m_AbsolutePath , openInfo . m_pOptions , openInfo . m_Flags , & size ) ;
if ( fp )
{
if ( m_pLogFile )
{
LogFileAccess ( openInfo . m_AbsolutePath ) ;
}
if ( m_bOutputDebugString )
{
# ifdef _WIN32
Plat_DebugString ( " fs_debug: " ) ;
Plat_DebugString ( openInfo . m_AbsolutePath ) ;
Plat_DebugString ( " \n " ) ;
# elif POSIX
fprintf ( stderr , " fs_debug: %s \n " , openInfo . m_AbsolutePath ) ;
# endif
}
openInfo . m_pFileHandle = new CFileHandle ( this ) ;
openInfo . m_pFileHandle - > m_pFile = fp ;
openInfo . m_pFileHandle - > m_type = FT_NORMAL ;
openInfo . m_pFileHandle - > m_nLength = size ;
openInfo . SetResolvedFilename ( openInfo . m_AbsolutePath ) ;
LogFileOpen ( " Loose " , openInfo . m_pFileName , openInfo . m_AbsolutePath ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: The base file search goes through here
// Input : *path -
// *pFileName -
// *pOptions -
// packfile -
// *filetime -
// Output : FileHandle_t
//-----------------------------------------------------------------------------
FileHandle_t CBaseFileSystem : : FindFileInSearchPath ( CFileOpenInfo & openInfo )
{
VPROF ( " CBaseFileSystem::FindFile " ) ;
Assert ( openInfo . m_pSearchPath ) ;
openInfo . m_pFileHandle = NULL ;
// Loading from pack file?
CPackFile * pPackFile = openInfo . m_pSearchPath - > GetPackFile ( ) ;
if ( pPackFile )
{
openInfo . m_pFileHandle = pPackFile - > OpenFile ( openInfo . m_pFileName , openInfo . m_pOptions ) ;
openInfo . m_pPackFile = pPackFile ;
if ( openInfo . m_pFileHandle )
{
char tempStr [ MAX_PATH * 2 + 2 ] ;
V_snprintf ( tempStr , sizeof ( tempStr ) , " %s%c%s " , pPackFile - > m_ZipName . String ( ) , CORRECT_PATH_SEPARATOR , openInfo . m_pFileName ) ;
openInfo . SetResolvedFilename ( tempStr ) ;
}
// If it's a BSP file, then the BSP file got CRC'd elsewhere so no need to verify stuff in there.
return ( FileHandle_t ) openInfo . m_pFileHandle ;
}
// Loading from VPK?
# ifdef SUPPORT_PACKED_STORE
CPackedStore * pVPK = openInfo . m_pSearchPath - > GetPackedStore ( ) ;
if ( pVPK )
{
CPackedStoreFileHandle fHandle = pVPK - > OpenFile ( openInfo . m_pFileName ) ;
if ( fHandle )
{
openInfo . SetFromPackedStoredFileHandle ( fHandle , this ) ;
openInfo . SetResolvedFilename ( openInfo . m_pFileName ) ;
openInfo . m_pVPKFile = pVPK ;
LogFileOpen ( " VPK " , openInfo . m_pFileName , pVPK - > BaseName ( ) ) ;
openInfo . HandleFileCRCTracking ( openInfo . m_pFileName ) ;
return ( FileHandle_t ) openInfo . m_pFileHandle ;
}
return NULL ;
}
# endif
// Load loose file specified as relative filename
// Convert filename to lowercase. All files in the
// game logical filesystem must be accessed by lowercase name
char szLowercaseFilename [ MAX_PATH ] ;
V_strcpy_safe ( szLowercaseFilename , openInfo . m_pFileName ) ;
V_strlower ( szLowercaseFilename ) ;
openInfo . SetAbsolutePath ( " %s%s " , openInfo . m_pSearchPath - > GetPathString ( ) , szLowercaseFilename ) ;
// now have an absolute name
HandleOpenRegularFile ( openInfo , false ) ;
return ( FileHandle_t ) openInfo . m_pFileHandle ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
FileHandle_t CBaseFileSystem : : OpenForRead ( const char * pFileNameT , const char * pOptions , unsigned flags , const char * pathID , char * * ppszResolvedFilename )
{
VPROF ( " CBaseFileSystem::OpenForRead " ) ;
char pFileNameBuff [ MAX_PATH ] ;
const char * pFileName = pFileNameBuff ;
FixUpPath ( pFileNameT , pFileNameBuff , sizeof ( pFileNameBuff ) ) ;
// Try the memory cache for un-restricted searches or "GAME" items.
if ( ! pathID | | Q_stricmp ( pathID , " GAME " ) = = 0 )
{
CMemoryFileBacking * pBacking = NULL ;
{
AUTO_LOCK ( m_MemoryFileMutex ) ;
CUtlHashtable < const char * , CMemoryFileBacking * > & table = m_MemoryFileHash ;
UtlHashHandle_t idx = table . Find ( pFileName ) ;
if ( idx ! = table . InvalidHandle ( ) )
{
pBacking = table [ idx ] ;
pBacking - > AddRef ( ) ;
}
}
if ( pBacking )
{
if ( pBacking - > m_nLength ! = - 1 )
{
CFileHandle * pFile = new CMemoryFileHandle ( this , pBacking ) ;
pFile - > m_type = strstr ( pOptions , " b " ) ? FT_MEMORY_BINARY : FT_MEMORY_TEXT ;
return ( FileHandle_t ) pFile ;
}
else
{
// length -1 == cached failure to read
return ( FileHandle_t ) NULL ;
}
}
else if ( ThreadInMainThread ( ) & & fs_report_sync_opens . GetInt ( ) > 0 )
{
DevWarning ( " blocking load %s \n " , pFileName ) ;
}
}
CFileOpenInfo openInfo ( this , pFileName , NULL , pOptions , flags , ppszResolvedFilename ) ;
// Already have an absolute path?
// If so, don't bother iterating search paths.
if ( V_IsAbsolutePath ( pFileName ) )
{
openInfo . SetAbsolutePath ( " %s " , pFileName ) ;
// Check if it's of the form C:/a/b/c/blah.zip/materials/blah.vtf
// an absolute path can encode a zip pack file (i.e. caller wants to open the file from within the pack file)
// format must strictly be ????.zip\????
// assuming a reasonable restriction that the zip must be a pre-existing search path zip
char * pZipExt = V_stristr ( openInfo . m_AbsolutePath , " .zip " CORRECT_PATH_SEPARATOR_S ) ;
if ( ! pZipExt )
pZipExt = V_stristr ( openInfo . m_AbsolutePath , " .bsp " CORRECT_PATH_SEPARATOR_S ) ;
# if defined( SUPPORT_PACKED_STORE )
if ( ! pZipExt )
pZipExt = V_stristr ( openInfo . m_AbsolutePath , " .vpk " CORRECT_PATH_SEPARATOR_S ) ;
# endif
if ( pZipExt & & pZipExt [ 5 ] )
{
// Cut string at the slash
char * pSlash = pZipExt + 4 ;
Assert ( * pSlash = = CORRECT_PATH_SEPARATOR ) ;
* pSlash = ' \0 ' ;
// want relative portion only, everything after the slash
char * pRelativeFileName = pSlash + 1 ;
// Find the zip or VPK in the search paths
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
// In VPK?
# if defined( SUPPORT_PACKED_STORE )
CPackedStore * pVPK = m_SearchPaths [ i ] . GetPackedStore ( ) ;
if ( pVPK )
{
if ( V_stricmp ( pVPK - > FullPathName ( ) , openInfo . m_AbsolutePath ) = = 0 )
{
CPackedStoreFileHandle fHandle = pVPK - > OpenFile ( pRelativeFileName ) ;
if ( fHandle )
{
openInfo . m_pSearchPath = & m_SearchPaths [ i ] ;
openInfo . SetFromPackedStoredFileHandle ( fHandle , this ) ;
}
break ;
}
continue ;
}
# endif
// In .zip?
CPackFile * pPackFile = m_SearchPaths [ i ] . GetPackFile ( ) ;
if ( pPackFile )
{
if ( Q_stricmp ( pPackFile - > m_ZipName . Get ( ) , openInfo . m_AbsolutePath ) = = 0 )
{
openInfo . m_pSearchPath = & m_SearchPaths [ i ] ;
openInfo . m_pFileHandle = pPackFile - > OpenFile ( pRelativeFileName , openInfo . m_pOptions ) ;
openInfo . m_pPackFile = pPackFile ;
break ;
}
}
}
if ( openInfo . m_pFileHandle )
{
openInfo . SetResolvedFilename ( openInfo . m_pFileName ) ;
openInfo . HandleFileCRCTracking ( pRelativeFileName ) ;
}
return ( FileHandle_t ) openInfo . m_pFileHandle ;
}
// Otherwise, it must be a regular file, specified by absolute filename
HandleOpenRegularFile ( openInfo , true ) ;
// !FIXME! We probably need to deal with CRC tracking, right?
return ( FileHandle_t ) openInfo . m_pFileHandle ;
}
// Run through all the search paths.
PathTypeFilter_t pathFilter = FILTER_NONE ;
if ( IsX360 ( ) )
{
if ( flags & FSOPEN_NEVERINPACK )
{
pathFilter = FILTER_CULLPACK ;
}
else if ( m_DVDMode = = DVDMODE_STRICT )
{
// most all files on the dvd are expected to be in the pack
// don't allow disk paths to be searched, which is very expensive on the dvd
pathFilter = FILTER_CULLNONPACK ;
}
}
CSearchPathsIterator iter ( this , & pFileName , pathID , pathFilter ) ;
for ( openInfo . m_pSearchPath = iter . GetFirst ( ) ; openInfo . m_pSearchPath ! = NULL ; openInfo . m_pSearchPath = iter . GetNext ( ) )
{
FileHandle_t filehandle = FindFileInSearchPath ( openInfo ) ;
if ( filehandle )
{
// Check if search path is excluded due to pure server white list,
// then we should make a note of this fact, and keep searching
if ( ! openInfo . m_pSearchPath - > m_bIsTrustedForPureServer & & openInfo . m_ePureFileClass = = ePureServerFileClass_AnyTrusted )
{
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Ignoring %s from %s for pure server operation \n " , openInfo . m_pFileName , openInfo . m_pSearchPath - > GetDebugString ( ) ) ;
# endif
m_FileTracker2 . NoteFileIgnoredForPureServer ( openInfo . m_pFileName , pathID , openInfo . m_pSearchPath - > m_storeId ) ;
Close ( filehandle ) ;
openInfo . m_pFileHandle = NULL ;
if ( ppszResolvedFilename & & * ppszResolvedFilename )
{
free ( * ppszResolvedFilename ) ;
* ppszResolvedFilename = NULL ;
}
continue ;
}
//
openInfo . HandleFileCRCTracking ( openInfo . m_pFileName ) ;
return filehandle ;
}
}
LogFileOpen ( " [Failed] " , pFileName , " " ) ;
return ( FileHandle_t ) 0 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
FileHandle_t CBaseFileSystem : : OpenForWrite ( const char * pFileName , const char * pOptions , const char * pathID )
{
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pathID , tempPathID ) ;
if ( ThreadInMainThread ( ) & & fs_report_sync_opens . GetInt ( ) )
{
DevWarning ( " blocking write %s \n " , pFileName ) ;
}
// Opening for write or append uses the write path
// Unless an absolute path is specified...
const char * pTmpFileName ;
char szScratchFileName [ MAX_PATH ] ;
if ( Q_IsAbsolutePath ( pFileName ) )
{
pTmpFileName = pFileName ;
}
else
{
ComputeFullWritePath ( szScratchFileName , sizeof ( szScratchFileName ) , pFileName , pathID ) ;
pTmpFileName = szScratchFileName ;
}
int64 size ;
FILE * fp = Trace_FOpen ( pTmpFileName , pOptions , 0 , & size ) ;
if ( ! fp )
{
return ( FileHandle_t ) 0 ;
}
CFileHandle * fh = new CFileHandle ( this ) ;
fh - > m_nLength = size ;
fh - > m_type = FT_NORMAL ;
fh - > m_pFile = fp ;
return ( FileHandle_t ) fh ;
}
// This looks for UNC-type filename specifiers, which should be used instead of
// passing in path ID. So if it finds //mod/cfg/config.cfg, it translates
// pFilename to "cfg/config.cfg" and pPathID to "mod" (mod is placed in tempPathID).
void CBaseFileSystem : : ParsePathID ( const char * & pFilename , const char * & pPathID , char tempPathID [ MAX_PATH ] )
{
tempPathID [ 0 ] = 0 ;
if ( ! pFilename | | pFilename [ 0 ] = = 0 )
return ;
// FIXME: Pain! Backslashes are used to denote network drives, forward to denote path ids
// HOORAY! We call FixSlashes everywhere. That will definitely not work
// I'm not changing it yet though because I don't know how painful the bugs would be that would be generated
bool bIsForwardSlash = ( pFilename [ 0 ] = = ' / ' & & pFilename [ 1 ] = = ' / ' ) ;
// bool bIsBackwardSlash = ( pFilename[0] == '\\' && pFilename[1] == '\\' );
if ( ! bIsForwardSlash ) //&& !bIsBackwardSlash )
return ;
// They're specifying two path IDs. Ignore the one passed-in.
if ( pPathID )
{
Warning ( FILESYSTEM_WARNING , " FS: Specified two path IDs (%s, %s). \n " , pFilename , pPathID ) ;
}
// Parse out the path ID.
const char * pIn = & pFilename [ 2 ] ;
char * pOut = tempPathID ;
while ( * pIn & & ! PATHSEPARATOR ( * pIn ) & & ( pOut - tempPathID ) < ( MAX_PATH - 1 ) )
{
* pOut + + = * pIn + + ;
}
* pOut = 0 ;
if ( tempPathID [ 0 ] = = ' * ' )
{
// * means NULL.
pPathID = NULL ;
}
else
{
pPathID = tempPathID ;
}
// Move pFilename up past the part with the path ID.
if ( * pIn = = 0 )
pFilename = pIn ;
else
pFilename = pIn + 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
FileHandle_t CBaseFileSystem : : Open ( const char * pFileName , const char * pOptions , const char * pathID )
{
return OpenEx ( pFileName , pOptions , 0 , pathID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
FileHandle_t CBaseFileSystem : : OpenEx ( const char * pFileName , const char * pOptions , unsigned flags , const char * pathID , char * * ppszResolvedFilename )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s(%s, %s, %u %s ) " , __FUNCTION__ , tmDynamicString ( TELEMETRY_LEVEL0 , pFileName ) , tmDynamicString ( TELEMETRY_LEVEL0 , pOptions ) , flags , tmDynamicString ( TELEMETRY_LEVEL0 , pathID ) ) ;
VPROF_BUDGET ( " CBaseFileSystem::Open " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
if ( ! pFileName )
return ( FileHandle_t ) 0 ;
CHECK_DOUBLE_SLASHES ( pFileName ) ;
if ( ThreadInMainThread ( ) & & fs_report_sync_opens . GetInt ( ) > 1 )
{
: : Warning ( " Open( %s ) \n " , pFileName ) ;
}
// Allow for UNC-type syntax to specify the path ID.
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pathID , tempPathID ) ;
// Try each of the search paths in succession
// FIXME: call createdirhierarchy upon opening for write.
if ( strstr ( pOptions , " r " ) & & ! strstr ( pOptions , " + " ) )
{
return OpenForRead ( pFileName , pOptions , flags , pathID , ppszResolvedFilename ) ;
}
return OpenForWrite ( pFileName , pOptions , pathID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Close ( FileHandle_t file )
{
VPROF_BUDGET ( " CBaseFileSystem::Close " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
if ( ! file )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Close NULL file handle! \n " ) ;
return ;
}
delete ( CFileHandle * ) file ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Seek ( FileHandle_t file , int pos , FileSystemSeek_t whence )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s (pos=%d, whence=%d) " , __FUNCTION__ , pos , whence ) ;
VPROF_BUDGET ( " CBaseFileSystem::Seek " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " Tried to Seek NULL file handle! \n " ) ;
return ;
}
fh - > Seek ( pos , whence ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : file -
// Output : unsigned int
//-----------------------------------------------------------------------------
unsigned int CBaseFileSystem : : Tell ( FileHandle_t file )
{
VPROF_BUDGET ( " CBaseFileSystem::Tell " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
if ( ! file )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Tell NULL file handle! \n " ) ;
return 0 ;
}
// Pack files are relative
return ( ( CFileHandle * ) file ) - > Tell ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : file -
// Output : unsigned int
//-----------------------------------------------------------------------------
unsigned int CBaseFileSystem : : Size ( FileHandle_t file )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
VPROF_BUDGET ( " CBaseFileSystem::Size " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
if ( ! file )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Size NULL file handle! \n " ) ;
return 0 ;
}
return ( ( CFileHandle * ) file ) - > Size ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : file -
// Output : unsigned int
//-----------------------------------------------------------------------------
unsigned int CBaseFileSystem : : Size ( const char * pFileName , const char * pPathID )
{
VPROF_BUDGET ( " CBaseFileSystem::Size " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CHECK_DOUBLE_SLASHES ( pFileName ) ;
// handle the case where no name passed...
if ( ! pFileName | | ! pFileName [ 0 ] )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Size NULL filename! \n " ) ;
return 0 ;
}
// Ok, fall through to the fast path.
unsigned result = 0 ;
FileHandle_t h = Open ( pFileName , " rb " , pPathID ) ;
if ( h )
{
result = Size ( h ) ;
Close ( h ) ;
}
return result ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
// *pFileName -
// Output : long
//-----------------------------------------------------------------------------
time_t CBaseFileSystem : : FastFileTime ( const CSearchPath * path , const char * pFileName )
{
struct _stat buf ;
if ( path - > GetPackFile ( ) )
{
// If we found the file:
if ( path - > GetPackFile ( ) - > ContainsFile ( pFileName ) )
{
return ( path - > GetPackFile ( ) - > m_lPackFileTime ) ;
}
}
# ifdef SUPPORT_PACKED_STORE
else if ( path - > GetPackedStore ( ) )
{
// Hm, should we support this in some way?
return 0L ;
}
# endif
else
{
// Is it an absolute path?
char pTmpFileName [ MAX_FILEPATH ] ;
if ( Q_IsAbsolutePath ( pFileName ) )
{
Q_strncpy ( pTmpFileName , pFileName , sizeof ( pTmpFileName ) ) ;
}
else
{
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , path - > GetPathString ( ) , pFileName ) ;
}
Q_FixSlashes ( pTmpFileName ) ;
if ( FS_stat ( pTmpFileName , & buf ) ! = - 1 )
{
return buf . st_mtime ;
}
# ifdef LINUX
char caseFixedName [ MAX_PATH ] ;
bool found = findFileInDirCaseInsensitive_safe ( pTmpFileName , caseFixedName ) ;
if ( found & & FS_stat ( caseFixedName , & buf ) ! = - 1 )
{
return buf . st_mtime ;
}
# endif
}
return ( 0L ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : EndOfFile ( FileHandle_t file )
{
if ( ! file )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to EndOfFile NULL file handle! \n " ) ;
return true ;
}
return ( ( CFileHandle * ) file ) - > EndOfFile ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseFileSystem : : Read ( void * pOutput , int size , FileHandle_t file )
{
return ReadEx ( pOutput , size , size , file ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseFileSystem : : ReadEx ( void * pOutput , int destSize , int size , FileHandle_t file )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s (%d bytes) " , __FUNCTION__ , size ) ;
VPROF_BUDGET ( " CBaseFileSystem::Read " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
if ( ! file )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Read NULL file handle! \n " ) ;
return 0 ;
}
if ( size < 0 )
{
return 0 ;
}
return ( ( CFileHandle * ) file ) - > Read ( pOutput , destSize , size ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Blow away current readers
// Input : -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : UnloadCompiledKeyValues ( )
{
# ifndef DEDICATED
for ( int i = 0 ; i < IFileSystem : : NUM_PRELOAD_TYPES ; + + i )
{
delete m_PreloadData [ i ] . m_pReader ;
m_PreloadData [ i ] . m_pReader = NULL ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: Put data file into list of at specific slot, will be loaded when ::SetupPreloadData() gets called
// Input : type -
// *archiveFile -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : LoadCompiledKeyValues ( KeyValuesPreloadType_t type , char const * archiveFile )
{
// Just add to list for appropriate loader
Assert ( type > = 0 & & type < IFileSystem : : NUM_PRELOAD_TYPES ) ;
CompiledKeyValuesPreloaders_t & loader = m_PreloadData [ type ] ;
Assert ( loader . m_CacheFile = = 0 ) ;
loader . m_CacheFile = FindOrAddFileName ( archiveFile ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Takes a passed in KeyValues& head and fills in the precompiled keyvalues data into it.
// Input : head -
// type -
// *filename -
// *pPathID -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : LoadKeyValues ( KeyValues & head , KeyValuesPreloadType_t type , char const * filename , char const * pPathID /*= 0*/ )
{
bool bret = true ;
# ifndef DEDICATED
char tempPathID [ MAX_PATH ] ;
ParsePathID ( filename , pPathID , tempPathID ) ;
// FIXME: THIS STUFF DOESN'T TRACK pPathID AT ALL RIGHT NOW!!!!!
if ( ! m_PreloadData [ type ] . m_pReader | | ! m_PreloadData [ type ] . m_pReader - > InstanceInPlace ( head , filename ) )
{
bret = head . LoadFromFile ( this , filename , pPathID ) ;
}
return bret ;
# else
bret = head . LoadFromFile ( this , filename , pPathID ) ;
return bret ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose: 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
// Input : type -
// *filename -
// *pPathID -
// Output : KeyValues
//-----------------------------------------------------------------------------
KeyValues * CBaseFileSystem : : LoadKeyValues ( KeyValuesPreloadType_t type , char const * filename , char const * pPathID /*= 0*/ )
{
KeyValues * kv = NULL ;
if ( ! m_PreloadData [ type ] . m_pReader )
{
kv = new KeyValues ( filename ) ;
if ( kv )
{
kv - > LoadFromFile ( this , filename , pPathID ) ;
}
}
else
{
# ifndef DEDICATED
// FIXME: THIS STUFF DOESN'T TRACK pPathID AT ALL RIGHT NOW!!!!!
kv = m_PreloadData [ type ] . m_pReader - > Instance ( filename ) ;
if ( ! kv )
{
kv = new KeyValues ( filename ) ;
if ( kv )
{
kv - > LoadFromFile ( this , filename , pPathID ) ;
}
}
# endif
}
return kv ;
}
//-----------------------------------------------------------------------------
// Purpose: This is the fallback method of reading the name of the first key in the file
// Input : *filename -
// *pPathID -
// *rootName -
// bufsize -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : LookupKeyValuesRootKeyName ( char const * filename , char const * pPathID , char * rootName , size_t bufsize )
{
if ( FileExists ( filename , pPathID ) )
{
// open file and get shader name
FileHandle_t hFile = Open ( filename , " r " , pPathID ) ;
if ( hFile = = FILESYSTEM_INVALID_HANDLE )
{
return false ;
}
char buf [ 128 ] ;
ReadLine ( buf , sizeof ( buf ) , hFile ) ;
Close ( hFile ) ;
// The name will possibly come in as "foo"\n
// So we need to strip the starting " character
char * pStart = buf ;
if ( * pStart = = ' \" ' )
{
+ + pStart ;
}
// Then copy the rest of the string
Q_strncpy ( rootName , pStart , bufsize ) ;
// And then strip off the \n and the " character at the end, in that order
int len = Q_strlen ( pStart ) ;
while ( len > 0 & & rootName [ len - 1 ] = = ' \n ' )
{
rootName [ len - 1 ] = 0 ;
- - len ;
}
while ( len > 0 & & rootName [ len - 1 ] = = ' \" ' )
{
rootName [ len - 1 ] = 0 ;
- - len ;
}
}
else
{
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: Tries to look up the name of the first key in the file from the compiled data
// Input : type -
// *outbuf -
// bufsize -
// *filename -
// *pPathID -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : ExtractRootKeyName ( KeyValuesPreloadType_t type , char * outbuf , size_t bufsize , char const * filename , char const * pPathID /*= 0*/ )
{
char tempPathID [ MAX_PATH ] ;
ParsePathID ( filename , pPathID , tempPathID ) ;
bool bret = true ;
if ( ! m_PreloadData [ type ] . m_pReader )
{
// Use fallback
bret = LookupKeyValuesRootKeyName ( filename , pPathID , outbuf , bufsize ) ;
}
else
{
# ifndef DEDICATED
// Try to use cache
bret = m_PreloadData [ type ] . m_pReader - > LookupKeyValuesRootKeyName ( filename , outbuf , bufsize ) ;
if ( ! bret )
{
// Not in cache, use fallback
bret = LookupKeyValuesRootKeyName ( filename , pPathID , outbuf , bufsize ) ;
}
# endif
}
return bret ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : SetupPreloadData ( )
{
int i ;
for ( i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackFile * pf = m_SearchPaths [ i ] . GetPackFile ( ) ;
if ( pf )
{
pf - > SetupPreloadData ( ) ;
}
}
# ifndef DEDICATED
if ( ! CommandLine ( ) - > FindParm ( " -fs_nopreloaddata " ) )
{
// Loads in the precompiled keyvalues data for each type
for ( i = 0 ; i < NUM_PRELOAD_TYPES ; + + i )
{
CompiledKeyValuesPreloaders_t & preloader = m_PreloadData [ i ] ;
Assert ( ! preloader . m_pReader ) ;
char fn [ MAX_PATH ] ;
if ( preloader . m_CacheFile ! = 0 & &
String ( preloader . m_CacheFile , fn , sizeof ( fn ) ) )
{
preloader . m_pReader = new CCompiledKeyValuesReader ( ) ;
preloader . m_pReader - > LoadFile ( fn ) ;
}
}
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : DiscardPreloadData ( )
{
int i ;
for ( i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackFile * pf = m_SearchPaths [ i ] . GetPackFile ( ) ;
if ( pf )
{
pf - > DiscardPreloadData ( ) ;
}
}
UnloadCompiledKeyValues ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseFileSystem : : Write ( void const * pInput , int size , FileHandle_t file )
{
VPROF_BUDGET ( " CBaseFileSystem::Write " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
AUTOBLOCKREPORTER_FH ( Write , this , true , file , FILESYSTEM_BLOCKING_SYNCHRONOUS , FileBlockingItem : : FB_ACCESS_WRITE ) ;
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Write NULL file handle! \n " ) ;
return 0 ;
}
return fh - > Write ( pInput , size ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CBaseFileSystem : : FPrintf ( FileHandle_t file , const char * pFormat , . . . )
{
va_list args ;
va_start ( args , pFormat ) ;
VPROF_BUDGET ( " CBaseFileSystem::FPrintf " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to FPrintf NULL file handle! \n " ) ;
return 0 ;
}
/*
if ( ! fh - > GetFileHandle ( ) )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to FPrintf NULL file pointer inside valid file handle! \n " ) ;
return 0 ;
}
*/
char buffer [ 65535 ] ;
int len = vsnprintf ( buffer , sizeof ( buffer ) , pFormat , args ) ;
len = fh - > Write ( buffer , len ) ;
//int len = FS_vfprintf( fh->GetFileHandle() , pFormat, args );
va_end ( args ) ;
return len ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : SetBufferSize ( FileHandle_t file , unsigned nBytes )
{
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to SetBufferSize NULL file handle! \n " ) ;
return ;
}
fh - > SetBufferSize ( nBytes ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : IsOk ( FileHandle_t file )
{
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to IsOk NULL file handle! \n " ) ;
return false ;
}
return fh - > IsOK ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Flush ( FileHandle_t file )
{
VPROF_BUDGET ( " CBaseFileSystem::Flush " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to Flush NULL file handle! \n " ) ;
return ;
}
fh - > Flush ( ) ;
}
bool CBaseFileSystem : : Precache ( const char * pFileName , const char * pPathID )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
// Allow for UNC-type syntax to specify the path ID.
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pPathID , tempPathID ) ;
Assert ( pPathID ) ;
// Really simple, just open, the file, read it all in and close it.
// We probably want to use file mapping to do this eventually.
FileHandle_t f = Open ( pFileName , " rb " , pPathID ) ;
if ( ! f )
return false ;
// not for consoles, the read discard is a negative benefit, slow and clobbers small drive caches
if ( IsPC ( ) )
{
char buffer [ 16384 ] ;
while ( sizeof ( buffer ) = = Read ( buffer , sizeof ( buffer ) , f ) ) ;
}
Close ( f ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
char * CBaseFileSystem : : ReadLine ( char * pOutput , int maxChars , FileHandle_t file )
{
VPROF_BUDGET ( " CBaseFileSystem::ReadLine " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CFileHandle * fh = ( CFileHandle * ) file ;
if ( ! fh )
{
Warning ( FILESYSTEM_WARNING , " FS: Tried to ReadLine NULL file handle! \n " ) ;
return NULL ;
}
m_Stats . nReads + + ;
int nRead = 0 ;
// Read up to maxchars:
while ( nRead < ( maxChars - 1 ) )
{
// Are we at the end of the file?
if ( 1 ! = fh - > Read ( pOutput + nRead , 1 ) )
break ;
// Translate for text mode files:
if ( ( fh - > m_type = = FT_PACK_TEXT | | fh - > m_type = = FT_MEMORY_TEXT ) & & pOutput [ nRead ] = = ' \r ' )
{
// Ignore \r
continue ;
}
// We're done when we hit a '\n'
if ( pOutput [ nRead ] = = ' \n ' )
{
nRead + + ;
break ;
}
// Get outta here if we find a NULL.
if ( pOutput [ nRead ] = = ' \0 ' )
{
pOutput [ nRead ] = ' \n ' ;
nRead + + ;
break ;
}
nRead + + ;
}
if ( nRead < maxChars )
pOutput [ nRead ] = ' \0 ' ;
m_Stats . nBytesRead + = nRead ;
return ( nRead ) ? pOutput : NULL ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
// Output : long
//-----------------------------------------------------------------------------
time_t CBaseFileSystem : : GetFileTime ( const char * pFileName , const char * pPathID )
{
VPROF_BUDGET ( " CBaseFileSystem::GetFileTime " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CHECK_DOUBLE_SLASHES ( pFileName ) ;
CSearchPathsIterator iter ( this , & pFileName , pPathID ) ;
char tempFileName [ MAX_PATH ] ;
Q_strncpy ( tempFileName , pFileName , sizeof ( tempFileName ) ) ;
Q_FixSlashes ( tempFileName ) ;
# ifdef _WIN32
Q_strlower ( tempFileName ) ;
# endif
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
time_t ft = FastFileTime ( pSearchPath , tempFileName ) ;
if ( ft ! = 0L )
{
if ( ! pSearchPath - > GetPackFile ( ) & & m_LogFuncs . Count ( ) )
{
char pTmpFileName [ MAX_FILEPATH ] ;
if ( strchr ( tempFileName , ' : ' ) )
{
Q_strncpy ( pTmpFileName , tempFileName , sizeof ( pTmpFileName ) ) ;
}
else
{
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , tempFileName ) ;
}
Q_FixSlashes ( tempFileName ) ;
LogAccessToFile ( " filetime " , pTmpFileName , " " ) ;
}
return ft ;
}
}
return ( time_t ) 0L ;
}
time_t CBaseFileSystem : : GetPathTime ( const char * pFileName , const char * pPathID )
{
VPROF_BUDGET ( " CBaseFileSystem::GetPathTime " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CSearchPathsIterator iter ( this , & pFileName , pPathID ) ;
char tempFileName [ MAX_PATH ] ;
Q_strncpy ( tempFileName , pFileName , sizeof ( tempFileName ) ) ;
Q_FixSlashes ( tempFileName ) ;
# ifdef _WIN32
Q_strlower ( tempFileName ) ;
# endif
time_t pathTime = 0L ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
time_t ft = FastFileTime ( pSearchPath , tempFileName ) ;
if ( ft > pathTime )
pathTime = ft ;
if ( ft ! = 0L )
{
if ( ! pSearchPath - > GetPackFile ( ) & & m_LogFuncs . Count ( ) )
{
char pTmpFileName [ MAX_FILEPATH ] ;
if ( strchr ( tempFileName , ' : ' ) )
{
Q_strncpy ( pTmpFileName , tempFileName , sizeof ( pTmpFileName ) ) ;
}
else
{
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , tempFileName ) ;
}
Q_FixSlashes ( tempFileName ) ;
LogAccessToFile ( " filetime " , pTmpFileName , " " ) ;
}
}
}
return pathTime ;
}
void CBaseFileSystem : : MarkAllCRCsUnverified ( )
{
if ( IsX360 ( ) )
{
return ;
}
m_FileTracker2 . MarkAllCRCsUnverified ( ) ;
}
void CBaseFileSystem : : CacheFileCRCs ( const char * pPathname , ECacheCRCType eType , IFileList * pFilter )
{
if ( IsX360 ( ) )
{
return ;
}
}
EFileCRCStatus CBaseFileSystem : : CheckCachedFileHash ( const char * pPathID , const char * pRelativeFilename , int nFileFraction , FileHash_t * pFileHash )
{
return m_FileTracker2 . CheckCachedFileHash ( pPathID , pRelativeFilename , nFileFraction , pFileHash ) ;
}
void CBaseFileSystem : : EnableWhitelistFileTracking ( bool bEnable , bool bCacheAllVPKHashes , bool bRecalculateAndCheckHashes )
{
if ( IsX360 ( ) )
{
m_WhitelistFileTrackingEnabled = false ;
return ;
}
if ( m_WhitelistFileTrackingEnabled ! = - 1 )
{
Error ( " CBaseFileSystem::EnableWhitelistFileTracking called more than once. " ) ;
}
m_WhitelistFileTrackingEnabled = bEnable ;
if ( m_WhitelistFileTrackingEnabled & & bCacheAllVPKHashes )
{
CacheAllVPKFileHashes ( bCacheAllVPKHashes , bRecalculateAndCheckHashes ) ;
}
}
void CBaseFileSystem : : CacheAllVPKFileHashes ( bool bCacheAllVPKHashes , bool bRecalculateAndCheckHashes )
{
# ifdef SUPPORT_PACKED_STORE
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackedStore * pVPK = m_SearchPaths [ i ] . GetPackedStore ( ) ;
if ( pVPK = = NULL )
continue ;
if ( ! pVPK - > BTestDirectoryHash ( ) )
{
Msg ( " VPK dir file hash does not match. File corrupted or modified. \n " ) ;
}
if ( ! pVPK - > BTestMasterChunkHash ( ) )
{
Msg ( " VPK chunk hash hash does not match. File corrupted or modified. \n " ) ;
}
CUtlVector < ChunkHashFraction_t > & vecChunkHash = pVPK - > AccessPackFileHashes ( ) ;
CPackedStoreFileHandle fhandle = pVPK - > GetHandleForHashingFiles ( ) ;
CUtlVector < ChunkHashFraction_t > vecChunkHashFractionCopy ;
if ( bRecalculateAndCheckHashes )
{
CUtlString sPackFileErrors ;
pVPK - > GetPackFileLoadErrorSummary ( sPackFileErrors ) ;
if ( sPackFileErrors . Length ( ) )
{
Msg ( " Errors occured loading files. \n " ) ;
Msg ( " %s " , sPackFileErrors . String ( ) ) ;
Msg ( " Verify integrity of your game files, perform memory and disk diagnostics on your system. \n " ) ;
}
else
Msg ( " No VPK Errors occured loading files. \n " ) ;
Msg ( " Recomputing all VPK file hashes. \n " ) ;
vecChunkHashFractionCopy . Swap ( vecChunkHash ) ;
}
int cFailures = 0 ;
if ( vecChunkHash . Count ( ) = = 0 )
{
if ( vecChunkHashFractionCopy . Count ( ) = = 0 )
Msg ( " File hash information not found: Hashing all VPK files for pure server operation. \n " ) ;
pVPK - > HashAllChunkFiles ( ) ;
if ( vecChunkHashFractionCopy . Count ( ) ! = 0 )
{
if ( vecChunkHash . Count ( ) ! = vecChunkHashFractionCopy . Count ( ) )
{
Msg ( " VPK hash count does not match. VPK content may be corrupt. \n " ) ;
}
else if ( Q_memcmp ( vecChunkHash . Base ( ) , vecChunkHashFractionCopy . Base ( ) , vecChunkHash . Count ( ) * sizeof ( vecChunkHash [ 0 ] ) ) ! = 0 )
{
Msg ( " VPK hashes do not match. VPK content may be corrupt. \n " ) ;
// find the actual mismatch
FOR_EACH_VEC ( vecChunkHashFractionCopy , iHash )
{
if ( Q_memcmp ( vecChunkHashFractionCopy [ iHash ] . m_md5contents . bits , vecChunkHash [ iHash ] . m_md5contents . bits , sizeof ( vecChunkHashFractionCopy [ iHash ] . m_md5contents . bits ) ) ! = 0 )
{
Msg ( " VPK hash for file %d failure at offset %x. \n " , vecChunkHashFractionCopy [ iHash ] . m_nPackFileNumber , vecChunkHashFractionCopy [ iHash ] . m_nFileFraction ) ;
cFailures + + ;
}
}
}
}
}
if ( bCacheAllVPKHashes )
{
Msg ( " Loaded %d VPK file hashes from %s for pure server operation. \n " , vecChunkHash . Count ( ) , pVPK - > FullPathName ( ) ) ;
FOR_EACH_VEC ( vecChunkHash , i )
{
m_FileTracker2 . AddFileHashForVPKFile ( vecChunkHash [ i ] . m_nPackFileNumber , vecChunkHash [ i ] . m_nFileFraction , vecChunkHash [ i ] . m_cbChunkLen , vecChunkHash [ i ] . m_md5contents , fhandle ) ;
}
}
else
{
if ( cFailures = = 0 & & vecChunkHash . Count ( ) = = vecChunkHashFractionCopy . Count ( ) )
Msg ( " File hashes checked. %d matches. no failures. \n " , vecChunkHash . Count ( ) ) ;
else
Msg ( " File hashes checked. %d matches. %d failures. \n " , vecChunkHash . Count ( ) , cFailures ) ;
}
}
# endif
}
bool CBaseFileSystem : : CheckVPKFileHash ( int PackFileID , int nPackFileNumber , int nFileFraction , MD5Value_t & md5Value )
{
# ifdef SUPPORT_PACKED_STORE
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
CPackedStore * pVPK = m_SearchPaths [ i ] . GetPackedStore ( ) ;
if ( pVPK = = NULL | | pVPK - > m_PackFileID ! = PackFileID )
continue ;
ChunkHashFraction_t fileHashFraction ;
if ( pVPK - > FindFileHashFraction ( nPackFileNumber , nFileFraction , fileHashFraction ) )
{
CPackedStoreFileHandle fhandle = pVPK - > GetHandleForHashingFiles ( ) ;
fhandle . m_nFileNumber = nPackFileNumber ;
char szFileName [ MAX_PATH ] ;
pVPK - > GetPackFileName ( fhandle , szFileName , sizeof ( szFileName ) ) ;
char hex [ 34 ] ;
Q_memset ( hex , 0 , sizeof ( hex ) ) ;
Q_binarytohex ( ( const byte * ) md5Value . bits , sizeof ( md5Value . bits ) , hex , sizeof ( hex ) ) ;
char hex2 [ 34 ] ;
Q_memset ( hex2 , 0 , sizeof ( hex2 ) ) ;
Q_binarytohex ( ( const byte * ) fileHashFraction . m_md5contents . bits , sizeof ( fileHashFraction . m_md5contents . bits ) , hex2 , sizeof ( hex2 ) ) ;
if ( Q_memcmp ( fileHashFraction . m_md5contents . bits , md5Value . bits , sizeof ( md5Value . bits ) ) ! = 0 )
{
Msg ( " File %s offset %x hash %s does not match ( should be %s ) \n " , szFileName , nFileFraction , hex , hex2 ) ;
return false ;
}
else
{
return true ;
}
}
}
# else
Error ( " CBaseFileSystem::CheckVPKFileHash should not be called, SUPPORT_PACKED_STORE not defined " ) ;
# endif
return false ;
}
void CBaseFileSystem : : RegisterFileWhitelist ( IPureServerWhitelist * pWhiteList , IFileList * * pFilesToReload )
{
if ( pFilesToReload )
* pFilesToReload = NULL ;
if ( IsX360 ( ) )
{
return ;
}
if ( m_pPureServerWhitelist )
{
m_pPureServerWhitelist - > Release ( ) ;
m_pPureServerWhitelist = NULL ;
}
if ( pWhiteList )
{
pWhiteList - > AddRef ( ) ;
m_pPureServerWhitelist = pWhiteList ;
}
// update which search paths are considered trusted
FOR_EACH_VEC ( m_SearchPaths , i )
{
SetSearchPathIsTrustedSource ( & m_SearchPaths [ i ] ) ;
}
// See if we need to reload any files
if ( pFilesToReload )
* pFilesToReload = m_FileTracker2 . GetFilesToUnloadForWhitelistChange ( pWhiteList ) ;
}
void CBaseFileSystem : : NotifyFileUnloaded ( const char * pszFilename , const char * pPathId )
{
m_FileTracker2 . NoteFileUnloaded ( pszFilename , pPathId ) ;
}
void CBaseFileSystem : : SetSearchPathIsTrustedSource ( CSearchPath * pSearchPath )
{
# if 1
pSearchPath - > m_bIsTrustedForPureServer = true ;
# else // Broken, I am lazy to fix this
// Most paths are not considered trusted
pSearchPath - > m_bIsTrustedForPureServer = false ;
// If we don't have a pure server whitelist, we cannot say that any
// particular files are trusted. (But then again, all files will be
// accepted, so it won't really matter.)
if ( m_pPureServerWhitelist = = NULL )
return ;
// Treat map packs as trusted, because we will send the CRC of the map pack to the server
if ( pSearchPath - > GetPackFile ( ) & & pSearchPath - > GetPackFile ( ) - > m_bIsMapPath )
{
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Setting map pack search path %s as trusted \n " , pSearchPath - > GetDebugString ( ) ) ;
# endif
pSearchPath - > m_bIsTrustedForPureServer = true ;
return ;
}
# ifdef SUPPORT_PACKED_STORE
// Only signed VPK's can be trusted
CPackedStoreRefCount * pVPK = pSearchPath - > GetPackedStore ( ) ;
if ( pVPK = = NULL )
{
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Setting %s as untrusted (loose files) \n " , pSearchPath - > GetDebugString ( ) ) ;
# endif
return ;
}
if ( ! pVPK - > m_bSignatureValid )
{
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Setting %s as untrusted (unsigned VPK) \n " , pSearchPath - > GetDebugString ( ) ) ;
# endif
return ;
}
const CUtlVector < uint8 > & key = pVPK - > GetSignaturePublicKey ( ) ;
for ( int iKeyIndex = 0 ; iKeyIndex < m_pPureServerWhitelist - > GetTrustedKeyCount ( ) ; + + iKeyIndex )
{
int nKeySz = 0 ;
const byte * pbKey = m_pPureServerWhitelist - > GetTrustedKey ( iKeyIndex , & nKeySz ) ;
Assert ( pbKey ! = NULL & & nKeySz > 0 ) ;
if ( key . Count ( ) = = nKeySz & & V_memcmp ( pbKey , key . Base ( ) , nKeySz ) = = 0 )
{
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Setting %s as untrusted \n " , pSearchPath - > GetDebugString ( ) ) ;
# endif
pSearchPath - > m_bIsTrustedForPureServer = true ;
return ;
}
}
# ifdef PURE_SERVER_DEBUG_SPEW
Msg ( " Setting %s as untrusted. (Key not in trusted key list) \n " , pSearchPath - > GetDebugString ( ) ) ;
# endif
# endif
# endif
}
int CBaseFileSystem : : GetUnverifiedFileHashes ( CUnverifiedFileHash * pFiles , int nMaxFiles )
{
return m_FileTracker2 . GetUnverifiedFileHashes ( pFiles , nMaxFiles ) ;
}
int CBaseFileSystem : : GetWhitelistSpewFlags ( )
{
return m_WhitelistSpewFlags ;
}
void CBaseFileSystem : : SetWhitelistSpewFlags ( int flags )
{
m_WhitelistSpewFlags = flags ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pString -
// maxCharsIncludingTerminator -
// fileTime -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : FileTimeToString ( char * pString , int maxCharsIncludingTerminator , time_t fileTime )
{
if ( IsX360 ( ) )
{
char szTemp [ 256 ] ;
time_t time = fileTime ;
V_strncpy ( szTemp , ctime ( & time ) , sizeof ( szTemp ) ) ;
char * pFinalColon = Q_strrchr ( szTemp , ' : ' ) ;
if ( pFinalColon )
* pFinalColon = ' \0 ' ;
// Clip off the day of the week
V_strncpy ( pString , szTemp + 4 , maxCharsIncludingTerminator ) ;
}
else
{
time_t time = fileTime ;
V_strncpy ( pString , ctime ( & time ) , maxCharsIncludingTerminator ) ;
// We see a linefeed at the end of these strings...if there is one, gobble it up
int len = V_strlen ( pString ) ;
if ( pString [ len - 1 ] = = ' \n ' )
{
pString [ len - 1 ] = ' \0 ' ;
}
pString [ maxCharsIncludingTerminator - 1 ] = ' \0 ' ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : FileExists ( const char * pFileName , const char * pPathID )
{
VPROF_BUDGET ( " CBaseFileSystem::FileExists " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
CHECK_DOUBLE_SLASHES ( pFileName ) ;
FileHandle_t h = Open ( pFileName , " rb " , pPathID ) ;
if ( h )
{
Close ( h ) ;
return true ;
}
return false ;
}
bool CBaseFileSystem : : IsFileWritable ( char const * pFileName , char const * pPathID /*=0*/ )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
struct _stat buf ;
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pPathID , tempPathID ) ;
if ( Q_IsAbsolutePath ( pFileName ) )
{
if ( FS_stat ( pFileName , & buf ) ! = - 1 )
{
# ifdef WIN32
if ( buf . st_mode & _S_IWRITE )
# elif defined (LINUX) && !defined (ANDROID)
if ( buf . st_mode & S_IWRITE )
# elif ANDROID
if ( buf . st_mode & S_IWUSR )
# else
if ( buf . st_mode & S_IWRITE )
# endif
{
return true ;
}
}
return false ;
}
CSearchPathsIterator iter ( this , & pFileName , pPathID , FILTER_CULLPACK ) ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
char pTmpFileName [ MAX_FILEPATH ] ;
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , pFileName ) ;
Q_FixSlashes ( pTmpFileName ) ;
if ( FS_stat ( pTmpFileName , & buf ) ! = - 1 )
{
# ifdef WIN32
if ( buf . st_mode & _S_IWRITE )
# elif defined (LINUX) && !defined (ANDROID)
if ( buf . st_mode & S_IWRITE )
# elif ANDROID
if ( buf . st_mode & S_IWUSR )
# else
if ( buf . st_mode & S_IWRITE )
# endif
{
return true ;
}
}
}
return false ;
}
bool CBaseFileSystem : : SetFileWritable ( char const * pFileName , bool writable , const char * pPathID /*= 0*/ )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
# ifdef _WIN32
int pmode = writable ? ( _S_IWRITE | _S_IREAD ) : ( _S_IREAD ) ;
# elif ANDROID
int pmode = writable ? ( S_IWUSR | S_IRUSR ) : ( S_IRUSR ) ;
# else
int pmode = writable ? ( S_IWRITE | S_IREAD ) : ( S_IREAD ) ;
# endif
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pPathID , tempPathID ) ;
if ( Q_IsAbsolutePath ( pFileName ) )
{
return ( FS_chmod ( pFileName , pmode ) = = 0 ) ;
}
CSearchPathsIterator iter ( this , & pFileName , pPathID , FILTER_CULLPACK ) ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
char pTmpFileName [ MAX_FILEPATH ] ;
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , pFileName ) ;
Q_FixSlashes ( pTmpFileName ) ;
if ( FS_chmod ( pTmpFileName , pmode ) = = 0 )
{
return true ;
}
}
// Failure
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : IsDirectory ( const char * pFileName , const char * pathID )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
// Allow for UNC-type syntax to specify the path ID.
struct _stat buf ;
char pTempBuf [ MAX_PATH ] ;
Q_strncpy ( pTempBuf , pFileName , sizeof ( pTempBuf ) ) ;
Q_StripTrailingSlash ( pTempBuf ) ;
pFileName = pTempBuf ;
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pathID , tempPathID ) ;
if ( Q_IsAbsolutePath ( pFileName ) )
{
if ( FS_stat ( pFileName , & buf ) ! = - 1 )
{
if ( buf . st_mode & _S_IFDIR )
return true ;
}
return false ;
}
CSearchPathsIterator iter ( this , & pFileName , pathID , FILTER_CULLPACK ) ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
# ifdef SUPPORT_PACKED_STORE
if ( pSearchPath - > GetPackedStore ( ) )
{
CUtlStringList outDir , outFile ;
pSearchPath - > GetPackedStore ( ) - > GetFileAndDirLists ( outDir , outFile , false ) ;
FOR_EACH_VEC ( outDir , i )
{
if ( ! Q_stricmp ( outDir [ i ] , pFileName ) )
return true ;
}
}
else
# endif // SUPPORT_PACKED_STORE
{
char pTmpFileName [ MAX_FILEPATH ] ;
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , pFileName ) ;
Q_FixSlashes ( pTmpFileName ) ;
if ( FS_stat ( pTmpFileName , & buf ) ! = - 1 )
{
if ( buf . st_mode & _S_IFDIR )
return true ;
}
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *path -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : CreateDirHierarchy ( const char * pRelativePathT , const char * pathID )
{
// Allow for UNC-type syntax to specify the path ID.
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pRelativePathT , pathID , tempPathID ) ; // use the original path param to preserve "//"
char pRelativePathBuff [ MAX_PATH ] ;
const char * pRelativePath = pRelativePathBuff ;
FixUpPath ( pRelativePathT , pRelativePathBuff , sizeof ( pRelativePathBuff ) ) ;
CHECK_DOUBLE_SLASHES ( pRelativePath ) ;
char szScratchFileName [ MAX_PATH ] ;
if ( ! Q_IsAbsolutePath ( pRelativePath ) )
{
Assert ( pathID ) ;
ComputeFullWritePath ( szScratchFileName , sizeof ( szScratchFileName ) , pRelativePath , pathID ) ;
}
else
{
Q_strncpy ( szScratchFileName , pRelativePath , sizeof ( szScratchFileName ) ) ;
}
int len = strlen ( szScratchFileName ) + 1 ;
char * end = szScratchFileName + len ;
char * s = szScratchFileName ;
while ( s < end )
{
if ( * s = = CORRECT_PATH_SEPARATOR & & s ! = szScratchFileName & & ( IsLinux ( ) | | * ( s - 1 ) ! = ' : ' ) )
{
* s = ' \0 ' ;
# if defined( _WIN32 )
_mkdir ( szScratchFileName ) ;
# elif defined( POSIX )
mkdir ( szScratchFileName , S_IRWXU | S_IRGRP | S_IROTH ) ; // owner has rwx, rest have r
# endif
* s = CORRECT_PATH_SEPARATOR ;
}
s + + ;
}
# if defined( _WIN32 )
_mkdir ( szScratchFileName ) ;
# elif defined( POSIX )
mkdir ( szScratchFileName , S_IRWXU | S_IRGRP | S_IROTH ) ;
# endif
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pWildCard -
// *pHandle -
// Output : const char
//-----------------------------------------------------------------------------
const char * CBaseFileSystem : : FindFirstEx ( const char * pWildCard , const char * pPathID , FileFindHandle_t * pHandle )
{
CHECK_DOUBLE_SLASHES ( pWildCard ) ;
return FindFirstHelper ( pWildCard , pPathID , pHandle , NULL ) ;
}
const char * CBaseFileSystem : : FindFirstHelper ( const char * pWildCardT , const char * pPathID , FileFindHandle_t * pHandle , int * pFoundStoreID )
{
VPROF_BUDGET ( " CBaseFileSystem::FindFirst " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
Assert ( pWildCardT ) ;
Assert ( pHandle ) ;
FileFindHandle_t hTmpHandle = m_FindData . AddToTail ( ) ;
FindData_t * pFindData = & m_FindData [ hTmpHandle ] ;
Assert ( pFindData ) ;
if ( pPathID )
{
pFindData - > m_FilterPathID = g_PathIDTable . AddString ( pPathID ) ;
}
char pWildCard [ MAX_PATH ] ;
FixUpPath ( pWildCardT , pWildCard , sizeof ( pWildCard ) ) ;
int maxlen = strlen ( pWildCard ) + 1 ;
pFindData - > wildCardString . AddMultipleToTail ( maxlen ) ;
Q_strncpy ( pFindData - > wildCardString . Base ( ) , pWildCard , maxlen ) ;
pFindData - > findHandle = INVALID_HANDLE_VALUE ;
if ( Q_IsAbsolutePath ( pWildCard ) )
{
// Absolute path, cannot be VPK or Pak
pFindData - > findHandle = FS_FindFirstFile ( pWildCard , & pFindData - > findData ) ;
pFindData - > currentSearchPathID = - 1 ;
}
else
{
int c = m_SearchPaths . Count ( ) ;
for ( pFindData - > currentSearchPathID = 0 ;
pFindData - > currentSearchPathID < c ;
pFindData - > currentSearchPathID + + )
{
CSearchPath * pSearchPath = & m_SearchPaths [ pFindData - > currentSearchPathID ] ;
if ( FilterByPathID ( pSearchPath , pFindData - > m_FilterPathID ) )
continue ;
// already visited this path?
if ( pFindData - > m_VisitedSearchPaths . MarkVisit ( * pSearchPath ) )
continue ;
// If this is a VPK or Pak file, build list of matches now and use helper
bool bIsVPKOrPak = false ;
if ( pSearchPath - > GetPackFile ( ) )
{
// XXX(johns) This support didn't exist for a long time, and I'm now worried about various things
// looking for misc files suddenly finding them in the (untrusted) BSP and causing security
// nightmares. For now, restricting FindFirst() support to BSPs only when the BSP search path
// is explicitly requested, but this would otherwise work fine.
if ( ! pPathID | | V_strcmp ( pPathID , " BSP " ) ! = 0 )
{
continue ;
}
Assert ( pFindData - > m_dirMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
Assert ( pFindData - > m_fileMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
pSearchPath - > GetPackFile ( ) - > GetFileAndDirLists ( pWildCard , pFindData - > m_dirMatchesFromVPKOrPak , pFindData - > m_fileMatchesFromVPKOrPak , true ) ;
bIsVPKOrPak = true ;
}
# ifdef SUPPORT_PACKED_STORE
if ( pSearchPath - > GetPackedStore ( ) )
{
Assert ( pFindData - > m_dirMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
Assert ( pFindData - > m_fileMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
pSearchPath - > GetPackedStore ( ) - > GetFileAndDirLists ( pWildCard , pFindData - > m_dirMatchesFromVPKOrPak , pFindData - > m_fileMatchesFromVPKOrPak , true ) ;
bIsVPKOrPak = true ;
}
# endif
if ( bIsVPKOrPak )
{
if ( FindNextFileInVPKOrPakHelper ( pFindData ) )
{
// Remember that we visited this file already.
pFindData - > m_VisitedFiles . Insert ( pFindData - > findData . cFileName , 0 ) ;
* pHandle = hTmpHandle ;
return pFindData - > findData . cFileName ;
}
continue ;
}
// Otherwise, raw FS find for relative path
char pTmpFileName [ MAX_FILEPATH ] ;
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , pFindData - > wildCardString . Base ( ) ) ;
Q_FixSlashes ( pTmpFileName ) ;
pFindData - > findHandle = FS_FindFirstFile ( pTmpFileName , & pFindData - > findData ) ;
pFindData - > m_CurrentStoreID = pSearchPath - > m_storeId ;
if ( pFindData - > findHandle ! = INVALID_HANDLE_VALUE )
break ;
}
}
// We have a result from the filesystem
if ( pFindData - > findHandle ! = INVALID_HANDLE_VALUE )
{
// Remember that we visited this file already.
pFindData - > m_VisitedFiles . Insert ( pFindData - > findData . cFileName , 0 ) ;
if ( pFoundStoreID )
* pFoundStoreID = pFindData - > m_CurrentStoreID ;
* pHandle = hTmpHandle ;
return pFindData - > findData . cFileName ;
}
// Handle failure here
pFindData = 0 ;
m_FindData . Remove ( hTmpHandle ) ;
* pHandle = - 1 ;
return NULL ;
}
const char * CBaseFileSystem : : FindFirst ( const char * pWildCard , FileFindHandle_t * pHandle )
{
return FindFirstEx ( pWildCard , NULL , pHandle ) ;
}
// Get the next file, trucking through the path. . don't check for duplicates.
bool CBaseFileSystem : : FindNextFileHelper ( FindData_t * pFindData , int * pFoundStoreID )
{
// Try the same search path that we were already searching on.
if ( FS_FindNextFile ( pFindData - > findHandle , & pFindData - > findData ) )
{
if ( pFoundStoreID )
* pFoundStoreID = pFindData - > m_CurrentStoreID ;
return true ;
}
if ( FindNextFileInVPKOrPakHelper ( pFindData ) )
return true ;
// This happens when we searched a full path
if ( pFindData - > currentSearchPathID < 0 )
return false ;
pFindData - > currentSearchPathID + + ;
if ( pFindData - > findHandle ! = INVALID_HANDLE_VALUE )
{
FS_FindClose ( pFindData - > findHandle ) ;
}
pFindData - > findHandle = INVALID_HANDLE_VALUE ;
int c = m_SearchPaths . Count ( ) ;
for ( ; pFindData - > currentSearchPathID < c ; + + pFindData - > currentSearchPathID )
{
CSearchPath * pSearchPath = & m_SearchPaths [ pFindData - > currentSearchPathID ] ;
if ( FilterByPathID ( pSearchPath , pFindData - > m_FilterPathID ) )
continue ;
// already visited this path
if ( pFindData - > m_VisitedSearchPaths . MarkVisit ( * pSearchPath ) )
continue ;
if ( pSearchPath - > GetPackFile ( ) )
{
// XXX(johns) This support didn't exist for a long time, and I'm now worried about various things
// looking for misc files suddenly finding them in the (untrusted) BSP and causing security
// nightmares. For now, restricting FindFirst() support to BSPs only when the BSP search path
// is explicitly requested, but this would otherwise work fine.
if ( ! pFindData - > m_FilterPathID | | V_strcmp ( g_PathIDTable . String ( pFindData - > m_FilterPathID ) , " BSP " ) ! = 0 )
{
continue ;
}
Assert ( pFindData - > m_dirMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
Assert ( pFindData - > m_fileMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
pSearchPath - > GetPackFile ( ) - > GetFileAndDirLists ( pFindData - > wildCardString . Base ( ) , pFindData - > m_dirMatchesFromVPKOrPak , pFindData - > m_fileMatchesFromVPKOrPak , true ) ;
if ( FindNextFileInVPKOrPakHelper ( pFindData ) )
return true ;
continue ;
}
# ifdef SUPPORT_PACKED_STORE
if ( pSearchPath - > GetPackedStore ( ) )
{
Assert ( pFindData - > m_dirMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
Assert ( pFindData - > m_fileMatchesFromVPKOrPak . Count ( ) = = 0 ) ;
pSearchPath - > GetPackedStore ( ) - > GetFileAndDirLists ( pFindData - > wildCardString . Base ( ) , pFindData - > m_dirMatchesFromVPKOrPak , pFindData - > m_fileMatchesFromVPKOrPak , true ) ;
if ( FindNextFileInVPKOrPakHelper ( pFindData ) )
return true ;
continue ;
}
# endif
char pTmpFileName [ MAX_FILEPATH ] ;
Q_snprintf ( pTmpFileName , sizeof ( pTmpFileName ) , " %s%s " , pSearchPath - > GetPathString ( ) , pFindData - > wildCardString . Base ( ) ) ;
Q_FixSlashes ( pTmpFileName ) ;
pFindData - > findHandle = FS_FindFirstFile ( pTmpFileName , & pFindData - > findData ) ;
pFindData - > m_CurrentStoreID = pSearchPath - > m_storeId ;
if ( pFindData - > findHandle ! = INVALID_HANDLE_VALUE )
{
if ( pFoundStoreID )
* pFoundStoreID = pFindData - > m_CurrentStoreID ;
return true ;
}
}
return false ;
}
bool CBaseFileSystem : : FindNextFileInVPKOrPakHelper ( FindData_t * pFindData )
{
// Return the next one from the list of VPK matches if there is one
if ( pFindData - > m_fileMatchesFromVPKOrPak . Count ( ) > 0 )
{
V_strncpy ( pFindData - > findData . cFileName , V_UnqualifiedFileName ( pFindData - > m_fileMatchesFromVPKOrPak [ 0 ] ) , sizeof ( pFindData - > findData . cFileName ) ) ;
pFindData - > findData . dwFileAttributes = 0 ;
delete pFindData - > m_fileMatchesFromVPKOrPak . Head ( ) ;
pFindData - > m_fileMatchesFromVPKOrPak . RemoveMultipleFromHead ( 1 ) ;
return true ;
}
// Return the next one from the list of VPK matches if there is one
if ( pFindData - > m_dirMatchesFromVPKOrPak . Count ( ) > 0 )
{
V_strncpy ( pFindData - > findData . cFileName , V_UnqualifiedFileName ( pFindData - > m_dirMatchesFromVPKOrPak [ 0 ] ) , sizeof ( pFindData - > findData . cFileName ) ) ;
pFindData - > findData . dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY ;
delete pFindData - > m_dirMatchesFromVPKOrPak . Head ( ) ;
pFindData - > m_dirMatchesFromVPKOrPak . RemoveMultipleFromHead ( 1 ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handle -
// Output : const char
//-----------------------------------------------------------------------------
const char * CBaseFileSystem : : FindNext ( FileFindHandle_t handle )
{
VPROF_BUDGET ( " CBaseFileSystem::FindNext " , VPROF_BUDGETGROUP_OTHER_FILESYSTEM ) ;
FindData_t * pFindData = & m_FindData [ handle ] ;
while ( 1 )
{
if ( FindNextFileHelper ( pFindData , NULL ) )
{
if ( pFindData - > m_VisitedFiles . Find ( pFindData - > findData . cFileName ) = = - 1 )
{
pFindData - > m_VisitedFiles . Insert ( pFindData - > findData . cFileName , 0 ) ;
return pFindData - > findData . cFileName ;
}
}
else
{
return NULL ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handle -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : FindIsDirectory ( FileFindHandle_t handle )
{
FindData_t * pFindData = & m_FindData [ handle ] ;
return ! ! ( pFindData - > findData . dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handle -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : FindClose ( FileFindHandle_t handle )
{
if ( ( handle < 0 ) | | ( ! m_FindData . IsInList ( handle ) ) )
return ;
FindData_t * pFindData = & m_FindData [ handle ] ;
Assert ( pFindData ) ;
if ( pFindData - > findHandle ! = INVALID_HANDLE_VALUE )
{
FS_FindClose ( pFindData - > findHandle ) ;
}
pFindData - > findHandle = INVALID_HANDLE_VALUE ;
pFindData - > wildCardString . Purge ( ) ;
pFindData - > m_fileMatchesFromVPKOrPak . PurgeAndDeleteElements ( ) ;
pFindData - > m_dirMatchesFromVPKOrPak . PurgeAndDeleteElements ( ) ;
m_FindData . Remove ( handle ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : GetLocalCopy ( const char * pFileName )
{
// do nothing. . everything is local.
}
//-----------------------------------------------------------------------------
// Purpose: Fixes up Path names. Will fix up platform specific slashes, remove
// any ../ or ./, fix //s, and lowercase anything under the directory
// that the game is installed to. We expect all files to be lower cased
// there - especially on Linux (where case sensitivity is the norm).
//
// Input : *pFileName - Original name to convert
// *pFixedUpFileName - a buffer to put the converted filename into
// sizeFixedUpFileName - the size of the above buffer in chars
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : FixUpPath ( const char * pFileName , char * pFixedUpFileName , int sizeFixedUpFileName )
{
// If appropriate fixes up the filename to ensure that it's handled properly by the system.
//
V_strncpy ( pFixedUpFileName , pFileName , sizeFixedUpFileName ) ;
V_FixSlashes ( pFixedUpFileName , CORRECT_PATH_SEPARATOR ) ;
// V_RemoveDotSlashes( pFixedUpFileName, CORRECT_PATH_SEPARATOR, true );
V_FixDoubleSlashes ( pFixedUpFileName ) ;
if ( ! V_IsAbsolutePath ( pFixedUpFileName ) )
{
V_strlower ( pFixedUpFileName ) ;
}
else
{
// Get the BASE_PATH, skip past - if necessary, and lowercase the rest
// Not just yet...
int iBaseLength = 0 ;
char pBaseDir [ MAX_PATH ] ;
// Need to get "BASE_PATH" from the filesystem paths, and then check this name against it.
//
iBaseLength = GetSearchPath ( " BASE_PATH " , true , pBaseDir , sizeof ( pBaseDir ) ) ;
if ( iBaseLength )
{
// If the first part of the pFixedUpFilename is pBaseDir
// then lowercase the part after that.
if ( * pBaseDir & & ( iBaseLength + 1 < V_strlen ( pFixedUpFileName ) ) & & ( 0 ! = V_strncmp ( pBaseDir , pFixedUpFileName , iBaseLength ) ) )
{
V_strlower ( & pFixedUpFileName [ iBaseLength - 1 ] ) ;
}
}
}
// Msg("CBaseFileSystem::FixUpPath: Converted %s to %s\n", pFileName, pFixedUpFileName); // too noisy
# ifdef NEVER // Useful if you're trying to see why your file may not be found (if you have a mixed case file)
if ( strncmp ( pFixedUpFileName , pFileName , 256 ) )
{
printf ( " FixUpPath->Converting %s to %s \n " , pFileName , pFixedUpFileName ) ;
}
# endif // NEVER
return true ;
}
//-----------------------------------------------------------------------------
// Converts a partial path into a full path
// Relative paths that are pack based are returned as an absolute path .../zip?.zip/foo
// A pack absolute path can be sent back in for opening, and the file will be properly
// detected as pack based and mounted inside the pack.
//-----------------------------------------------------------------------------
const char * CBaseFileSystem : : RelativePathToFullPath ( const char * pFileName , const char * pPathID , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars , PathTypeFilter_t pathFilter , PathTypeQuery_t * pPathType )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
struct _stat buf ;
if ( pPathType )
{
* pPathType = PATH_IS_NORMAL ;
}
// Convert filename to lowercase. All files in the
// game logical filesystem must be accessed by lowercase name
char szLowercaseFilename [ MAX_PATH ] ;
FixUpPath ( pFileName , szLowercaseFilename , sizeof ( szLowercaseFilename ) ) ;
pFileName = szLowercaseFilename ;
// Fill in the default in case it's not found...
V_strncpy ( pDest , pFileName , maxLenInChars ) ;
// @FD This is arbitrary and seems broken. If the caller needs this filter, they should
// request it with the flag themselves. As it is, I cannot search all the file paths
// for a file using this function because there is no option that says, "No, really, I
// mean ALL SEARCH PATHS." The current problem I'm trying to fix is that sounds are not
// working if they are in the BSP. I wrote code that assumed that I could just ask for
// the absolute path of a file, since we are able to open files with these absolute
// filenames, and that each particular filesystem call wouldn't have its own individual
// quirks.
// if ( IsPC() && pathFilter == FILTER_NONE )
// {
// // X360TBD: PC legacy behavior never returned pack paths
// // do legacy behavior to ensure naive callers don't break
// pathFilter = FILTER_CULLPACK;
// }
CSearchPathsIterator iter ( this , & pFileName , pPathID , pathFilter ) ;
for ( CSearchPath * pSearchPath = iter . GetFirst ( ) ; pSearchPath ! = NULL ; pSearchPath = iter . GetNext ( ) )
{
CPackFile * pPack = pSearchPath - > GetPackFile ( ) ;
if ( pPack )
{
if ( pPack - > ContainsFile ( pFileName ) )
{
if ( pPathType )
{
if ( pPack - > m_bIsMapPath )
{
* pPathType | = PATH_IS_MAPPACKFILE ;
}
else
{
* pPathType | = PATH_IS_PACKFILE ;
}
if ( pSearchPath - > m_bIsRemotePath )
{
* pPathType | = PATH_IS_REMOTE ;
}
}
// form an encoded absolute path that can be decoded by our FS as pak based
const char * pszPackName = pPack - > m_ZipName . String ( ) ;
int len = V_strlen ( pszPackName ) ;
int nTotalLen = len + 1 + V_strlen ( pFileName ) ;
if ( nTotalLen > = maxLenInChars )
{
: : Warning ( " File %s was found in %s, but resulting abs filename won't fit in callers buffer of %d bytes \n " ,
pFileName , pszPackName , maxLenInChars ) ;
Assert ( false ) ;
return NULL ;
}
V_strncpy ( pDest , pszPackName , maxLenInChars ) ;
V_AppendSlash ( pDest , maxLenInChars ) ;
V_strncat ( pDest , pFileName , maxLenInChars ) ;
Assert ( V_strlen ( pDest ) = = nTotalLen ) ;
return pDest ;
}
continue ;
}
// Found in VPK?
# ifdef SUPPORT_PACKED_STORE
CPackedStore * pVPK = pSearchPath - > GetPackedStore ( ) ;
if ( pVPK )
{
CPackedStoreFileHandle vpkHandle = pVPK - > OpenFile ( pFileName ) ;
if ( vpkHandle )
{
const char * pszVpkName = vpkHandle . m_pOwner - > FullPathName ( ) ;
Assert ( V_GetFileExtension ( pszVpkName ) ! = NULL ) ;
int len = V_strlen ( pszVpkName ) ;
int nTotalLen = len + 1 + V_strlen ( pFileName ) ;
if ( nTotalLen > = maxLenInChars )
{
: : Warning ( " File %s was found in %s, but resulting abs filename won't fit in callers buffer of %d bytes \n " ,
pFileName , pszVpkName , maxLenInChars ) ;
Assert ( false ) ;
return NULL ;
}
V_strncpy ( pDest , pszVpkName , maxLenInChars ) ;
V_AppendSlash ( pDest , maxLenInChars ) ;
V_strncat ( pDest , pFileName , maxLenInChars ) ;
V_FixSlashes ( pDest ) ;
return pDest ;
}
continue ;
}
# endif
char pTmpFileName [ MAX_FILEPATH ] ;
V_sprintf_safe ( pTmpFileName , " %s%s " , pSearchPath - > GetPathString ( ) , pFileName ) ;
V_FixSlashes ( pTmpFileName ) ;
if ( FS_stat ( pTmpFileName , & buf ) ! = - 1 )
{
V_strncpy ( pDest , pTmpFileName , maxLenInChars ) ;
if ( pPathType & & pSearchPath - > m_bIsRemotePath )
{
* pPathType | = PATH_IS_REMOTE ;
}
return pDest ;
}
}
// not found
return NULL ;
}
const char * CBaseFileSystem : : GetLocalPath ( const char * pFileName , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
return RelativePathToFullPath ( pFileName , NULL , pDest , maxLenInChars ) ;
}
//-----------------------------------------------------------------------------
// Returns true on success, otherwise false if it can't be resolved
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : FullPathToRelativePathEx ( const char * pFullPath , const char * pPathId , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars )
{
CHECK_DOUBLE_SLASHES ( pFullPath ) ;
int nInlen = V_strlen ( pFullPath ) ;
if ( nInlen < = 0 )
{
pDest [ 0 ] = 0 ;
return false ;
}
V_strncpy ( pDest , pFullPath , maxLenInChars ) ;
char pInPath [ MAX_FILEPATH ] ;
V_strcpy_safe ( pInPath , pFullPath ) ;
# ifdef _WIN32
V_strlower ( pInPath ) ;
# endif
V_FixSlashes ( pInPath ) ;
CUtlSymbol lookup ;
if ( pPathId )
{
lookup = g_PathIDTable . AddString ( pPathId ) ;
}
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
// FIXME: Should this work with embedded pak files?
if ( m_SearchPaths [ i ] . GetPackFile ( ) & & m_SearchPaths [ i ] . GetPackFile ( ) - > m_bIsMapPath )
continue ;
// Skip paths that are not on the specified search path
if ( FilterByPathID ( & m_SearchPaths [ i ] , lookup ) )
continue ;
char pSearchBase [ MAX_FILEPATH ] ;
V_strncpy ( pSearchBase , m_SearchPaths [ i ] . GetPathString ( ) , sizeof ( pSearchBase ) ) ;
# ifdef _WIN32
V_strlower ( pSearchBase ) ;
# endif
V_FixSlashes ( pSearchBase ) ;
int nSearchLen = V_strlen ( pSearchBase ) ;
if ( V_strnicmp ( pSearchBase , pInPath , nSearchLen ) )
continue ;
V_strncpy ( pDest , & pInPath [ nSearchLen ] , maxLenInChars ) ;
return true ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Obsolete version
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : FullPathToRelativePath ( const char * pFullPath , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars )
{
return FullPathToRelativePathEx ( pFullPath , NULL , pDest , maxLenInChars ) ;
}
//-----------------------------------------------------------------------------
// Returns true on successfully retrieve case-sensitive full path, otherwise false
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : GetCaseCorrectFullPath_Ptr ( const char * pFullPath , OUT_Z_CAP ( maxLenInChars ) char * pDest , int maxLenInChars )
{
CHECK_DOUBLE_SLASHES ( pFullPath ) ;
# ifndef _WIN32
V_strncpy ( pDest , pFullPath , maxLenInChars ) ;
return true ;
# else
char szCurrentDir [ MAX_PATH ] ;
V_strcpy_safe ( szCurrentDir , pFullPath ) ;
V_StripLastDir ( szCurrentDir , sizeof ( szCurrentDir ) ) ;
CUtlString strSearchName = pFullPath ;
strSearchName . StripTrailingSlash ( ) ;
strSearchName = strSearchName . Slice ( V_strlen ( szCurrentDir ) , strSearchName . Length ( ) ) ;
CUtlString strSearchPath = szCurrentDir ;
strSearchPath + = " * " ;
CUtlString strFoundCaseCorrectName ;
FileFindHandle_t findHandle ;
const char * pszCaseCorrectName = FindFirst ( strSearchPath . Get ( ) , & findHandle ) ;
while ( pszCaseCorrectName )
{
if ( V_stricmp ( strSearchName . String ( ) , pszCaseCorrectName ) = = 0 )
{
strFoundCaseCorrectName = pszCaseCorrectName ;
break ;
}
pszCaseCorrectName = FindNext ( findHandle ) ;
}
FindClose ( findHandle ) ;
// Not found
if ( strFoundCaseCorrectName . IsEmpty ( ) )
{
V_strncpy ( pDest , pFullPath , maxLenInChars ) ;
return false ;
}
// If drive path, no need to recurse anymore.
bool bResult = false ;
char szDir [ MAX_PATH ] ;
if ( ! IsDirectory ( szCurrentDir , NULL ) )
{
V_strupr ( szCurrentDir ) ;
V_strncpy ( szDir , szCurrentDir , sizeof ( szDir ) ) ;
bResult = true ;
}
else
{
bResult = GetCaseCorrectFullPath ( szCurrentDir , szDir ) ;
}
// connect the current path with the case-correct dir/file name
V_MakeAbsolutePath ( pDest , maxLenInChars , strFoundCaseCorrectName . String ( ) , szDir ) ;
return bResult ;
# endif // _WIN32
}
//-----------------------------------------------------------------------------
// Deletes a file
//-----------------------------------------------------------------------------
void CBaseFileSystem : : RemoveFile ( char const * pRelativePath , const char * pathID )
{
CHECK_DOUBLE_SLASHES ( pRelativePath ) ;
// Allow for UNC-type syntax to specify the path ID.
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pRelativePath , pathID , tempPathID ) ;
Assert ( pathID | | ! IsX360 ( ) ) ;
// Opening for write or append uses Write Path
char szScratchFileName [ MAX_PATH ] ;
if ( Q_IsAbsolutePath ( pRelativePath ) )
{
Q_strncpy ( szScratchFileName , pRelativePath , sizeof ( szScratchFileName ) ) ;
}
else
{
ComputeFullWritePath ( szScratchFileName , sizeof ( szScratchFileName ) , pRelativePath , pathID ) ;
}
int fail = unlink ( szScratchFileName ) ;
if ( fail ! = 0 )
{
Warning ( FILESYSTEM_WARNING , " Unable to remove %s! \n " , szScratchFileName ) ;
}
}
//-----------------------------------------------------------------------------
// Renames a file
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : RenameFile ( char const * pOldPath , char const * pNewPath , const char * pathID )
{
Assert ( pOldPath & & pNewPath ) ;
CHECK_DOUBLE_SLASHES ( pOldPath ) ;
CHECK_DOUBLE_SLASHES ( pNewPath ) ;
// Allow for UNC-type syntax to specify the path ID.
char pPathIdCopy [ MAX_PATH ] ;
const char * pOldPathId = pathID ;
if ( pathID )
{
Q_strncpy ( pPathIdCopy , pathID , sizeof ( pPathIdCopy ) ) ;
pOldPathId = pPathIdCopy ;
}
char tempOldPathID [ MAX_PATH ] ;
ParsePathID ( pOldPath , pOldPathId , tempOldPathID ) ;
Assert ( pOldPathId ) ;
// Allow for UNC-type syntax to specify the path ID.
char tempNewPathID [ MAX_PATH ] ;
ParsePathID ( pNewPath , pathID , tempNewPathID ) ;
Assert ( pathID ) ;
char pNewFileName [ MAX_PATH ] ;
char szScratchFileName [ MAX_PATH ] ;
// The source file may be in a fallback directory, so just resolve the actual path, don't assume pathid...
RelativePathToFullPath ( pOldPath , pOldPathId , szScratchFileName , sizeof ( szScratchFileName ) ) ;
// Figure out the dest path
if ( ! Q_IsAbsolutePath ( pNewPath ) )
{
ComputeFullWritePath ( pNewFileName , sizeof ( pNewFileName ) , pNewPath , pathID ) ;
}
else
{
Q_strncpy ( pNewFileName , pNewPath , sizeof ( pNewFileName ) ) ;
}
// Make sure the directory exitsts, too
char pPathOnly [ MAX_PATH ] ;
Q_strncpy ( pPathOnly , pNewFileName , sizeof ( pPathOnly ) ) ;
Q_StripFilename ( pPathOnly ) ;
CreateDirHierarchy ( pPathOnly , pathID ) ;
// Now copy the file over
int fail = rename ( szScratchFileName , pNewFileName ) ;
if ( fail ! = 0 )
{
Warning ( FILESYSTEM_WARNING , " Unable to rename %s to %s! \n " , szScratchFileName , pNewFileName ) ;
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : **ppdir -
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : GetCurrentDirectory ( char * pDirectory , int maxlen )
{
# if defined( _WIN32 ) && !defined( _X360 )
if ( ! : : GetCurrentDirectoryA ( maxlen , pDirectory ) )
# elif defined( POSIX ) || defined( _X360 )
if ( ! getcwd ( pDirectory , maxlen ) )
# endif
return false ;
Q_FixSlashes ( pDirectory ) ;
// Strip the last slash
int len = strlen ( pDirectory ) ;
if ( pDirectory [ len - 1 ] = = CORRECT_PATH_SEPARATOR )
{
pDirectory [ len - 1 ] = 0 ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : pfnWarning - warning function callback
//-----------------------------------------------------------------------------
void CBaseFileSystem : : SetWarningFunc ( void ( * pfnWarning ) ( const char * fmt , . . . ) )
{
m_pfnWarning = pfnWarning ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : level -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : SetWarningLevel ( FileWarningLevel_t level )
{
m_fwLevel = level ;
}
const FileSystemStatistics * CBaseFileSystem : : GetFilesystemStatistics ( )
{
return & m_Stats ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : level -
// *fmt -
// ... -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : Warning ( FileWarningLevel_t level , const char * fmt , . . . )
{
if ( level > m_fwLevel )
return ;
if ( ( fs_warning_mode . GetInt ( ) = = 1 & & ! ThreadInMainThread ( ) ) | | ( fs_warning_mode . GetInt ( ) = = 2 & & ThreadInMainThread ( ) ) )
return ;
va_list argptr ;
char warningtext [ 4096 ] ;
va_start ( argptr , fmt ) ;
Q_vsnprintf ( warningtext , sizeof ( warningtext ) , fmt , argptr ) ;
va_end ( argptr ) ;
// Dump to stdio
printf ( " %s " , warningtext ) ;
if ( m_pfnWarning )
{
( * m_pfnWarning ) ( warningtext ) ;
}
else
{
# ifdef _WIN32
Plat_DebugString ( warningtext ) ;
# endif
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : COpenedFile : : COpenedFile ( void )
{
m_pFile = NULL ;
m_pName = NULL ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : COpenedFile : : ~ COpenedFile ( void )
{
delete [ ] m_pName ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : src -
//-----------------------------------------------------------------------------
CBaseFileSystem : : COpenedFile : : COpenedFile ( const COpenedFile & src )
{
m_pFile = src . m_pFile ;
if ( src . m_pName )
{
int len = strlen ( src . m_pName ) + 1 ;
m_pName = new char [ len ] ;
Q_strncpy ( m_pName , src . m_pName , len ) ;
}
else
{
m_pName = NULL ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : src -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : COpenedFile : : operator = = ( const CBaseFileSystem : : COpenedFile & src ) const
{
return src . m_pFile = = m_pFile ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *name -
//-----------------------------------------------------------------------------
void CBaseFileSystem : : COpenedFile : : SetName ( char const * name )
{
delete [ ] m_pName ;
int len = strlen ( name ) + 1 ;
m_pName = new char [ len ] ;
Q_strncpy ( m_pName , name , len ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Output : char
//-----------------------------------------------------------------------------
char const * CBaseFileSystem : : COpenedFile : : GetName ( void )
{
return m_pName ? m_pName : " ??? " ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath : : CSearchPath ( void )
{
m_Path = g_PathIDTable . AddString ( " " ) ;
m_pDebugPath = " " ;
m_storeId = 0 ;
m_pPackFile = NULL ;
m_pPathIDInfo = NULL ;
m_bIsRemotePath = false ;
m_pPackedStore = NULL ;
m_bIsTrustedForPureServer = false ;
}
const char * CBaseFileSystem : : CSearchPath : : GetDebugString ( ) const
{
if ( GetPackFile ( ) )
{
return GetPackFile ( ) - > m_ZipName ;
}
# ifdef SUPPORT_PACKED_STORE
if ( GetPackedStore ( ) )
{
return GetPackedStore ( ) - > FullPathName ( ) ;
}
# endif
return GetPathString ( ) ;
}
bool CBaseFileSystem : : CSearchPath : : IsMapPath ( ) const
{
return GetPackFile ( ) - > m_bIsMapPath ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath : : ~ CSearchPath ( void )
{
if ( m_pPackFile )
{
m_pPackFile - > Release ( ) ;
}
if ( m_pPackedStore )
{
m_pPackedStore - > Release ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath * CBaseFileSystem : : CSearchPathsIterator : : GetFirst ( )
{
if ( m_SearchPaths . Count ( ) )
{
m_visits . Reset ( ) ;
m_iCurrent = - 1 ;
return GetNext ( ) ;
}
return & m_EmptySearchPath ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CBaseFileSystem : : CSearchPath * CBaseFileSystem : : CSearchPathsIterator : : GetNext ( )
{
CSearchPath * pSearchPath = NULL ;
for ( m_iCurrent + + ; m_iCurrent < m_SearchPaths . Count ( ) ; m_iCurrent + + )
{
pSearchPath = & m_SearchPaths [ m_iCurrent ] ;
if ( m_PathTypeFilter = = FILTER_CULLPACK & & pSearchPath - > GetPackFile ( ) )
continue ;
if ( m_PathTypeFilter = = FILTER_CULLNONPACK & & ! pSearchPath - > GetPackFile ( ) )
continue ;
if ( CBaseFileSystem : : FilterByPathID ( pSearchPath , m_pathID ) )
continue ;
// 360 can optionally ignore a local search path in dvddev mode
// ignoring a local search path falls through to its cloned remote path
// map paths are exempt from this exclusion logic
if ( IsX360 ( ) & & ( m_DVDMode = = DVDMODE_DEV ) & & m_Filename [ 0 ] & & ! pSearchPath - > m_bIsRemotePath )
{
bool bIsMapPath = pSearchPath - > GetPackFile ( ) & & pSearchPath - > GetPackFile ( ) - > m_bIsMapPath ;
if ( ! bIsMapPath )
{
bool bIgnorePath = false ;
char szExcludePath [ MAX_PATH ] ;
char szFilename [ MAX_PATH ] ;
V_ComposeFileName ( pSearchPath - > GetPathString ( ) , m_Filename , szFilename , sizeof ( szFilename ) ) ;
for ( int i = 0 ; i < m_ExcludePaths . Count ( ) ; i + + )
{
if ( g_pFullFileSystem - > String ( m_ExcludePaths [ i ] , szExcludePath , sizeof ( szExcludePath ) ) )
{
if ( ! V_strnicmp ( szFilename , szExcludePath , strlen ( szExcludePath ) ) )
{
bIgnorePath = true ;
break ;
}
}
}
if ( bIgnorePath )
{
// filename matches exclusion path, skip it
continue ;
}
}
}
if ( ! m_visits . MarkVisit ( * pSearchPath ) )
break ;
}
if ( m_iCurrent < m_SearchPaths . Count ( ) )
{
return pSearchPath ;
}
return NULL ;
}
void CBaseFileSystem : : CSearchPathsIterator : : CopySearchPaths ( const CUtlVector < CSearchPath > & searchPaths )
{
m_SearchPaths = searchPaths ;
for ( int i = 0 ; i < m_SearchPaths . Count ( ) ; i + + )
{
if ( m_SearchPaths [ i ] . GetPackFile ( ) )
{
m_SearchPaths [ i ] . GetPackFile ( ) - > AddRef ( ) ;
}
else if ( m_SearchPaths [ i ] . GetPackedStore ( ) )
{
m_SearchPaths [ i ] . GetPackedStore ( ) - > AddRef ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Load/unload a DLL
//-----------------------------------------------------------------------------
CSysModule * CBaseFileSystem : : LoadModule ( const char * pFileName , const char * pPathID , bool bValidatedDllOnly )
{
CHECK_DOUBLE_SLASHES ( pFileName ) ;
CSysModule * pModule = NULL ;
LogFileAccess ( pFileName ) ;
if ( ! pPathID )
{
pPathID = " EXECUTABLE_PATH " ; // default to the bin dir
}
char tempPathID [ MAX_PATH ] ;
ParsePathID ( pFileName , pPathID , tempPathID ) ;
CUtlSymbol lookup = g_PathIDTable . AddString ( pPathID ) ;
// a pathID has been specified, find the first match in the path list
int c = m_SearchPaths . Count ( ) ;
for ( int i = 0 ; i < c ; i + + )
{
// pak files don't have modules
if ( m_SearchPaths [ i ] . GetPackFile ( ) )
continue ;
if ( FilterByPathID ( & m_SearchPaths [ i ] , lookup ) )
continue ;
Q_snprintf ( tempPathID , sizeof ( tempPathID ) , " %s%s " , m_SearchPaths [ i ] . GetPathString ( ) , pFileName ) ; // append the path to this dir.
pModule = Sys_LoadModule ( tempPathID ) ;
if ( pModule )
{
// we found the binary in one of our search paths
return pModule ;
}
# ifdef POSIX
Q_snprintf ( tempPathID , sizeof ( tempPathID ) , " %slib%s " , m_SearchPaths [ i ] . GetPathString ( ) , pFileName ) ; // append the path to this dir.
pModule = Sys_LoadModule ( tempPathID ) ;
if ( pModule )
return pModule ;
# endif
}
# ifdef POSIX
if ( ! pModule )
{
Q_snprintf ( tempPathID , sizeof ( tempPathID ) , " lib%s " , pFileName ) ;
pModule = Sys_LoadModule ( tempPathID ) ;
}
# endif
if ( ! pModule )
pModule = Sys_LoadModule ( pFileName ) ;
return pModule ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CBaseFileSystem : : UnloadModule ( CSysModule * pModule )
{
Sys_UnloadModule ( pModule ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Adds a filesystem logging function
//-----------------------------------------------------------------------------
void CBaseFileSystem : : AddLoggingFunc ( FileSystemLoggingFunc_t logFunc )
{
Assert ( ! m_LogFuncs . IsValidIndex ( m_LogFuncs . Find ( logFunc ) ) ) ;
m_LogFuncs . AddToTail ( logFunc ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Removes a filesystem logging function
//-----------------------------------------------------------------------------
void CBaseFileSystem : : RemoveLoggingFunc ( FileSystemLoggingFunc_t logFunc )
{
m_LogFuncs . FindAndRemove ( logFunc ) ;
}
//-----------------------------------------------------------------------------
// Make sure that slashes are of the right kind and that there is a slash at the
// end of the filename.
// WARNING!!: assumes that you have an extra byte allocated in the case that you need
// a slash at the end.
//-----------------------------------------------------------------------------
static void AddSeperatorAndFixPath ( char * str )
{
char * lastChar = & str [ strlen ( str ) - 1 ] ;
if ( * lastChar ! = CORRECT_PATH_SEPARATOR & & * lastChar ! = INCORRECT_PATH_SEPARATOR )
{
lastChar [ 1 ] = CORRECT_PATH_SEPARATOR ;
lastChar [ 2 ] = ' \0 ' ;
}
Q_FixSlashes ( str ) ;
if ( IsX360 ( ) )
{
// 360 FS won't resolve any path with ../
V_RemoveDotSlashes ( str ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pFileName -
// Output : FileNameHandle_t
//-----------------------------------------------------------------------------
FileNameHandle_t CBaseFileSystem : : FindOrAddFileName ( char const * pFileName )
{
return m_FileNames . FindOrAddFileName ( pFileName ) ;
}
FileNameHandle_t CBaseFileSystem : : FindFileName ( char const * pFileName )
{
return m_FileNames . FindFileName ( pFileName ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : handle -
// Output : char const
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : String ( const FileNameHandle_t & handle , char * buf , int buflen )
{
return m_FileNames . String ( handle , buf , buflen ) ;
}
int CBaseFileSystem : : GetPathIndex ( const FileNameHandle_t & handle )
{
return m_FileNames . PathIndex ( handle ) ;
}
CBaseFileSystem : : CPathIDInfo * CBaseFileSystem : : FindOrAddPathIDInfo ( const CUtlSymbol & id , int bByRequestOnly )
{
for ( int i = 0 ; i < m_PathIDInfos . Count ( ) ; i + + )
{
CBaseFileSystem : : CPathIDInfo * pInfo = m_PathIDInfos [ i ] ;
if ( pInfo - > GetPathID ( ) = = id )
{
if ( bByRequestOnly ! = - 1 )
{
pInfo - > m_bByRequestOnly = ( bByRequestOnly ! = 0 ) ;
}
return pInfo ;
}
}
// Add a new one.
CBaseFileSystem : : CPathIDInfo * pInfo = new CBaseFileSystem : : CPathIDInfo ;
m_PathIDInfos . AddToTail ( pInfo ) ;
pInfo - > SetPathID ( id ) ;
pInfo - > m_bByRequestOnly = ( bByRequestOnly = = 1 ) ;
return pInfo ;
}
void CBaseFileSystem : : MarkPathIDByRequestOnly ( const char * pPathID , bool bRequestOnly )
{
FindOrAddPathIDInfo ( g_PathIDTable . AddString ( pPathID ) , bRequestOnly ) ;
}
# if defined( TRACK_BLOCKING_IO )
void CBaseFileSystem : : EnableBlockingFileAccessTracking ( bool state )
{
m_bBlockingFileAccessReportingEnabled = state ;
}
bool CBaseFileSystem : : IsBlockingFileAccessEnabled ( ) const
{
return m_bBlockingFileAccessReportingEnabled ;
}
IBlockingFileItemList * CBaseFileSystem : : RetrieveBlockingFileAccessInfo ( )
{
Assert ( m_pBlockingItems ) ;
return m_pBlockingItems ;
}
void CBaseFileSystem : : RecordBlockingFileAccess ( bool synchronous , const FileBlockingItem & item )
{
AUTO_LOCK ( m_BlockingFileMutex ) ;
// Not tracking anything
if ( ! m_bBlockingFileAccessReportingEnabled )
return ;
if ( synchronous & & ! m_bAllowSynchronousLogging & & ( item . m_ItemType = = FILESYSTEM_BLOCKING_SYNCHRONOUS ) )
return ;
// Track it
m_pBlockingItems - > Add ( item ) ;
}
bool CBaseFileSystem : : SetAllowSynchronousLogging ( bool state )
{
bool oldState = m_bAllowSynchronousLogging ;
m_bAllowSynchronousLogging = state ;
return oldState ;
}
void CBaseFileSystem : : BlockingFileAccess_EnterCriticalSection ( )
{
m_BlockingFileMutex . Lock ( ) ;
}
void CBaseFileSystem : : BlockingFileAccess_LeaveCriticalSection ( )
{
m_BlockingFileMutex . Unlock ( ) ;
}
# endif // TRACK_BLOCKING_IO
bool CBaseFileSystem : : GetFileTypeForFullPath ( char const * pFullPath , wchar_t * buf , size_t bufSizeInBytes )
{
# if !defined( _X360 ) && !defined( POSIX )
wchar_t wcharpath [ 512 ] ;
: : MultiByteToWideChar ( CP_UTF8 , 0 , pFullPath , - 1 , wcharpath , sizeof ( wcharpath ) / sizeof ( wchar_t ) ) ;
wcharpath [ ( sizeof ( wcharpath ) / sizeof ( wchar_t ) ) - 1 ] = L ' \0 ' ;
SHFILEINFOW info = { 0 } ;
DWORD_PTR dwResult = SHGetFileInfoW (
wcharpath ,
0 ,
& info ,
sizeof ( info ) ,
SHGFI_TYPENAME
) ;
if ( dwResult )
{
wcsncpy ( buf , info . szTypeName , ( bufSizeInBytes / sizeof ( wchar_t ) ) ) ;
buf [ ( bufSizeInBytes / sizeof ( wchar_t ) ) - 1 ] = L ' \0 ' ;
return true ;
}
else
# endif
{
char ext [ 32 ] ;
Q_ExtractFileExtension ( pFullPath , ext , sizeof ( ext ) ) ;
# ifdef POSIX
_snwprintf ( buf , ( bufSizeInBytes / sizeof ( wchar_t ) ) - 1 , L " %s File " , V_strupr ( ext ) ) ; // Matches what Windows does
# else
_snwprintf ( buf , ( bufSizeInBytes / sizeof ( wchar_t ) ) - 1 , L " .%S " , ext ) ;
# endif
buf [ ( bufSizeInBytes / sizeof ( wchar_t ) ) - 1 ] = L ' \0 ' ;
}
return false ;
}
bool CBaseFileSystem : : GetOptimalIOConstraints ( FileHandle_t hFile , unsigned * pOffsetAlign , unsigned * pSizeAlign , unsigned * pBufferAlign )
{
if ( pOffsetAlign )
* pOffsetAlign = 1 ;
if ( pSizeAlign )
* pSizeAlign = 1 ;
if ( pBufferAlign )
* pBufferAlign = 1 ;
return false ;
}
//-----------------------------------------------------------------------------
FileCacheHandle_t CBaseFileSystem : : CreateFileCache ( )
{
return new CFileCacheObject ( this ) ;
}
//-----------------------------------------------------------------------------
void CBaseFileSystem : : AddFilesToFileCache ( FileCacheHandle_t cacheId , const char * * ppFileNames , int nFileNames , const char * pPathID )
{
// For now, assuming that we're only used with GAME.
Assert ( pPathID & & Q_strcasecmp ( pPathID , " GAME " ) = = 0 ) ;
return static_cast < CFileCacheObject * > ( cacheId ) - > AddFiles ( ppFileNames , nFileNames ) ;
}
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : IsFileCacheLoaded ( FileCacheHandle_t cacheId )
{
return static_cast < CFileCacheObject * > ( cacheId ) - > IsReady ( ) ;
}
//-----------------------------------------------------------------------------
void CBaseFileSystem : : DestroyFileCache ( FileCacheHandle_t cacheId )
{
delete static_cast < CFileCacheObject * > ( cacheId ) ;
}
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : IsFileCacheFileLoaded ( FileCacheHandle_t cacheId , const char * pFileName )
{
# ifdef _DEBUG
CFileCacheObject * pFileCache = static_cast < CFileCacheObject * > ( cacheId ) ;
bool bFileIsHeldByCache = false ;
{
AUTO_LOCK ( pFileCache - > m_InfosMutex ) ;
for ( int i = 0 ; i < pFileCache - > m_Infos . Count ( ) ; + + i )
{
if ( pFileCache - > m_Infos [ i ] - > pFileName & & Q_strcmp ( pFileCache - > m_Infos [ i ] - > pFileName , pFileName ) )
{
bFileIsHeldByCache = true ;
break ;
}
}
}
Assert ( bFileIsHeldByCache ) ;
# endif
AUTO_LOCK ( m_MemoryFileMutex ) ;
return m_MemoryFileHash . Find ( pFileName ) ! = m_MemoryFileHash . InvalidHandle ( ) ;
}
//-----------------------------------------------------------------------------
bool CBaseFileSystem : : RegisterMemoryFile ( CMemoryFileBacking * pFile , CMemoryFileBacking * * ppExistingFileWithRef )
{
Assert ( pFile - > m_pFS = = static_cast < IFileSystem * > ( this ) ) ;
Assert ( pFile - > m_pFileName & & pFile - > m_pFileName [ 0 ] ) ;
AUTO_LOCK ( m_MemoryFileMutex ) ;
CMemoryFileBacking * pInTable = m_MemoryFileHash [ m_MemoryFileHash . Insert ( pFile - > m_pFileName , pFile ) ] ;
pInTable - > m_nRegistered + + ;
pInTable - > AddRef ( ) ; // either for table or for ppExistingFileWithRef return
if ( pFile = = pInTable )
{
Assert ( pInTable - > m_nRegistered = = 1 ) ;
* ppExistingFileWithRef = NULL ;
return true ;
}
else
{
Assert ( pInTable - > m_nRegistered > 1 ) ;
* ppExistingFileWithRef = pInTable ;
return false ;
}
}
//-----------------------------------------------------------------------------
void CBaseFileSystem : : UnregisterMemoryFile ( CMemoryFileBacking * pFile )
{
Assert ( pFile - > m_pFS = = static_cast < IFileSystem * > ( this ) & & pFile - > m_nRegistered > 0 ) ;
bool bRelease = false ;
{
AUTO_LOCK ( m_MemoryFileMutex ) ;
pFile - > m_nRegistered - - ;
if ( pFile - > m_nRegistered = = 0 )
{
m_MemoryFileHash . Remove ( pFile - > m_pFileName ) ;
bRelease = true ;
}
}
// Release potentially a complex op, prefer to perform it outside of mutex.
if ( bRelease )
{
pFile - > Release ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Constructs a file handle
// Input : base file system handle
// Output :
//-----------------------------------------------------------------------------
CFileHandle : : CFileHandle ( CBaseFileSystem * fs )
{
Init ( fs ) ;
}
CFileHandle : : ~ CFileHandle ( )
{
Assert ( IsValid ( ) ) ;
# if !defined( _RETAIL )
delete [ ] m_pszTrueFileName ;
# endif
if ( m_pPackFileHandle )
{
delete m_pPackFileHandle ;
m_pPackFileHandle = NULL ;
}
if ( m_pFile )
{
m_fs - > Trace_FClose ( m_pFile ) ;
m_pFile = NULL ;
}
m_nMagic = FREE_MAGIC ;
}
void CFileHandle : : Init ( CBaseFileSystem * fs )
{
m_nMagic = MAGIC ;
m_pFile = NULL ;
m_nLength = 0 ;
m_type = FT_NORMAL ;
m_pPackFileHandle = NULL ;
m_fs = fs ;
# if !defined( _RETAIL )
m_pszTrueFileName = 0 ;
# endif
}
bool CFileHandle : : IsValid ( )
{
return ( m_nMagic = = MAGIC ) ;
}
int CFileHandle : : GetSectorSize ( )
{
Assert ( IsValid ( ) ) ;
if ( m_pFile )
{
return m_fs - > FS_GetSectorSize ( m_pFile ) ;
}
else if ( m_pPackFileHandle )
{
return m_pPackFileHandle - > GetSectorSize ( ) ;
}
else if ( m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return 1 ;
}
else
{
return - 1 ;
}
}
bool CFileHandle : : IsOK ( )
{
# if defined( SUPPORT_PACKED_STORE )
if ( m_VPKHandle )
{
return true ;
}
# endif
if ( m_pFile )
{
return ( IsValid ( ) & & m_fs - > FS_ferror ( m_pFile ) = = 0 ) ;
}
else if ( m_pPackFileHandle | | m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return IsValid ( ) ;
}
m_fs - > Warning ( FILESYSTEM_WARNING , " FS: Tried to IsOk NULL file pointer inside valid file handle! \n " ) ;
return false ;
}
void CFileHandle : : Flush ( )
{
Assert ( IsValid ( ) ) ;
if ( m_pFile )
{
m_fs - > FS_fflush ( m_pFile ) ;
}
}
void CFileHandle : : SetBufferSize ( int nBytes )
{
Assert ( IsValid ( ) ) ;
if ( m_pFile )
{
m_fs - > FS_setbufsize ( m_pFile , nBytes ) ;
}
else if ( m_pPackFileHandle )
{
m_pPackFileHandle - > SetBufferSize ( nBytes ) ;
}
}
int CFileHandle : : Read ( void * pBuffer , int nLength )
{
Assert ( IsValid ( ) ) ;
return Read ( pBuffer , - 1 , nLength ) ;
}
int CFileHandle : : Read ( void * pBuffer , int nDestSize , int nLength )
{
Assert ( IsValid ( ) ) ;
# if defined( SUPPORT_PACKED_STORE )
if ( m_VPKHandle )
{
if ( nDestSize > = 0 )
nLength = MIN ( nLength , nDestSize ) ;
return m_VPKHandle . Read ( pBuffer , nLength ) ;
}
# endif
// Is this a regular file or a pack file?
if ( m_pFile )
{
return m_fs - > FS_fread ( pBuffer , nDestSize , nLength , m_pFile ) ;
}
else if ( m_pPackFileHandle )
{
// Pack file handle handles clamping all the reads:
return m_pPackFileHandle - > Read ( pBuffer , nDestSize , nLength ) ;
}
else if ( m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return static_cast < CMemoryFileHandle * > ( this ) - > Read ( pBuffer , nDestSize , nLength ) ;
}
return 0 ;
}
int CFileHandle : : Write ( const void * pBuffer , int nLength )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
if ( ThreadInMainThread ( ) )
{
tmPlotI32 ( TELEMETRY_LEVEL0 , TMPT_MEMORY , 0 , nLength , " FileBytesWrite " ) ;
}
Assert ( IsValid ( ) ) ;
if ( ! m_pFile )
{
m_fs - > Warning ( FILESYSTEM_WARNING , " FS: Tried to Write NULL file pointer inside valid file handle! \n " ) ;
return 0 ;
}
size_t nBytesWritten = m_fs - > FS_fwrite ( ( void * ) pBuffer , nLength , m_pFile ) ;
m_fs - > Trace_FWrite ( nBytesWritten , m_pFile ) ;
return nBytesWritten ;
}
int CFileHandle : : Seek ( int64 nOffset , int nWhence )
{
Assert ( IsValid ( ) ) ;
# if defined( SUPPORT_PACKED_STORE )
if ( m_VPKHandle )
{
return m_VPKHandle . Seek ( nOffset , nWhence ) ;
}
# endif
if ( m_pFile )
{
m_fs - > FS_fseek ( m_pFile , nOffset , nWhence ) ;
// TODO - FS_fseek should return the resultant offset
return 0 ;
}
else if ( m_pPackFileHandle )
{
return m_pPackFileHandle - > Seek ( nOffset , nWhence ) ;
}
else if ( m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return static_cast < CMemoryFileHandle * > ( this ) - > Seek ( nOffset , nWhence ) ;
}
return - 1 ;
}
int CFileHandle : : Tell ( )
{
Assert ( IsValid ( ) ) ;
# if defined( SUPPORT_PACKED_STORE )
if ( m_VPKHandle )
{
return m_VPKHandle . Tell ( ) ;
}
# endif
if ( m_pFile )
{
return m_fs - > FS_ftell ( m_pFile ) ;
}
else if ( m_pPackFileHandle )
{
return m_pPackFileHandle - > Tell ( ) ;
}
else if ( m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return static_cast < CMemoryFileHandle * > ( this ) - > Tell ( ) ;
}
return - 1 ;
}
int CFileHandle : : Size ( )
{
Assert ( IsValid ( ) ) ;
int nReturnedSize = - 1 ;
# if defined( SUPPORT_PACKED_STORE )
if ( m_VPKHandle )
{
return m_VPKHandle . m_nFileSize ;
}
# endif
if ( m_pFile )
{
nReturnedSize = m_nLength ;
}
else if ( m_pPackFileHandle )
{
nReturnedSize = m_pPackFileHandle - > Size ( ) ;
}
else if ( m_type = = FT_MEMORY_BINARY | | m_type = = FT_MEMORY_TEXT )
{
return static_cast < CMemoryFileHandle * > ( this ) - > Size ( ) ;
}
return nReturnedSize ;
}
int64 CFileHandle : : AbsoluteBaseOffset ( )
{
Assert ( IsValid ( ) ) ;
if ( ! m_pFile & & m_pPackFileHandle )
{
return m_pPackFileHandle - > AbsoluteBaseOffset ( ) ;
}
else
{
return 0 ;
}
}
bool CFileHandle : : EndOfFile ( )
{
Assert ( IsValid ( ) ) ;
return ( Tell ( ) > = Size ( ) ) ;
}
//-----------------------------------------------------------------------------
int CMemoryFileHandle : : Read ( void * pBuffer , int nDestSize , int nLength )
{
nLength = min ( nLength , ( int ) m_nLength - m_nPosition ) ;
if ( nLength > 0 )
{
Assert ( m_nPosition > = 0 ) ;
memcpy ( pBuffer , m_pBacking - > m_pData + m_nPosition , nLength ) ;
m_nPosition + = nLength ;
return nLength ;
}
if ( m_nPosition > = ( int ) m_nLength )
{
return - 1 ;
}
return 0 ;
}
int CMemoryFileHandle : : Seek ( int64 nOffset64 , int nWhence )
{
if ( nWhence = = SEEK_SET )
{
m_nPosition = ( int ) clamp ( nOffset64 , 0ll , m_nLength ) ;
}
else if ( nWhence = = SEEK_CUR )
{
m_nPosition + = ( int ) clamp ( nOffset64 , - ( int64 ) m_nPosition , m_nLength - m_nPosition ) ;
}
else if ( nWhence = = SEEK_END )
{
m_nPosition = ( int ) m_nLength + ( int ) clamp ( nOffset64 , - m_nLength , 0ll ) ;
}
return m_nPosition ;
}
//-----------------------------------------------------------------------------
CBaseFileSystem : : CFileCacheObject : : CFileCacheObject ( CBaseFileSystem * pFS )
: m_pFS ( pFS )
{
}
void CBaseFileSystem : : CFileCacheObject : : AddFiles ( const char * * ppFileNames , int nFileNames )
{
CUtlVector < Info_t * > infos ;
infos . SetCount ( nFileNames ) ;
for ( int i = 0 ; i < nFileNames ; + + i )
{
infos [ i ] = new Info_t ;
Info_t & info = * infos [ i ] ;
info . pFileName = strdup ( ppFileNames [ i ] ) ;
V_FixSlashes ( ( char * ) info . pFileName ) ;
# ifdef _WIN32
Q_strlower ( ( char * ) info . pFileName ) ;
# endif
info . hIOAsync = NULL ;
info . pBacking = NULL ;
info . pOwner = NULL ;
}
AUTO_LOCK ( m_InfosMutex ) ;
int offset = m_Infos . AddMultipleToTail ( nFileNames , infos . Base ( ) ) ;
m_nPending + = nFileNames ;
ProcessNewEntries ( offset ) ;
}
void CBaseFileSystem : : CFileCacheObject : : ProcessNewEntries ( int start )
{
for ( int i = start ; i < m_Infos . Count ( ) ; + + i )
{
Info_t & info = * m_Infos [ i ] ;
if ( ! info . pOwner )
{
info . pOwner = this ;
// NOTE: currently only caching files with GAME pathID
FileAsyncRequest_t request ;
request . pszFilename = info . pFileName ;
request . pszPathID = " GAME " ;
request . flags = FSASYNC_FLAGS_ALLOCNOFREE ;
request . pfnCallback = & IOCallback ;
request . pContext = & info ;
if ( m_pFS - > AsyncRead ( request , & info . hIOAsync ) ! = FSASYNC_OK )
{
- - m_nPending ;
}
}
}
}
void CBaseFileSystem : : CFileCacheObject : : IOCallback ( const FileAsyncRequest_t & request , int nBytesRead , FSAsyncStatus_t err )
{
Assert ( request . pContext ) ;
Info_t & info = * ( Info_t * ) request . pContext ;
CMemoryFileBacking * pBacking = new CMemoryFileBacking ( info . pOwner - > m_pFS ) ;
pBacking - > m_pData = NULL ;
pBacking - > m_pFileName = info . pFileName ;
pBacking - > m_nLength = ( err = = FSASYNC_OK & & nBytesRead = = 0 ) ? 0 : - 1 ;
if ( request . pData )
{
if ( err = = FSASYNC_OK & & nBytesRead > 0 )
{
pBacking - > m_pData = ( const char * ) request . pData ; // transfer data ownership
pBacking - > m_nLength = nBytesRead ;
}
else
{
info . pOwner - > m_pFS - > FreeOptimalReadBuffer ( request . pData ) ;
}
}
CMemoryFileBacking * pExistingBacking = NULL ;
if ( ! info . pOwner - > m_pFS - > RegisterMemoryFile ( pBacking , & pExistingBacking ) )
{
// Someone already registered this file
info . pFileName = pExistingBacking - > m_pFileName ;
pBacking - > Release ( ) ;
pBacking = pExistingBacking ;
}
//DevWarning("preload %s %d\n", request.pszFilename, pBacking->m_nLength);
info . pBacking = pBacking ;
info . pOwner - > m_nPending - - ;
}
CBaseFileSystem : : CFileCacheObject : : ~ CFileCacheObject ( )
{
AUTO_LOCK ( m_InfosMutex ) ;
for ( int i = 0 ; i < m_Infos . Count ( ) ; + + i )
{
Info_t & info = * m_Infos [ i ] ;
if ( info . hIOAsync )
{
// job is guaranteed to not be running after abort
m_pFS - > AsyncAbort ( info . hIOAsync ) ;
m_pFS - > AsyncRelease ( info . hIOAsync ) ;
}
if ( info . pBacking )
{
m_pFS - > UnregisterMemoryFile ( info . pBacking ) ;
info . pBacking - > Release ( ) ;
}
else
{
free ( ( char * ) info . pFileName ) ;
}
delete m_Infos [ i ] ;
}
Assert ( m_nPending = = 0 ) ;
}