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

5807 lines
160 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #ifdef POSIX
  8. #define _snwprintf swprintf
  9. #endif
  10. #include "basefilesystem.h"
  11. #include "tier0/vprof.h"
  12. #include "tier1/characterset.h"
  13. #include "tier1/utlbuffer.h"
  14. #include "tier1/convar.h"
  15. #include "tier1/KeyValues.h"
  16. #include "tier0/icommandline.h"
  17. #include "generichash.h"
  18. #include "tier1/utllinkedlist.h"
  19. #include "filesystem/IQueuedLoader.h"
  20. #include "tier2/tier2.h"
  21. #include "zip_utils.h"
  22. #include "packfile.h"
  23. #ifdef _X360
  24. #include "xbox/xbox_launch.h"
  25. #endif
  26. #ifndef DEDICATED
  27. #include "keyvaluescompiler.h"
  28. #endif
  29. #include "ifilelist.h"
  30. #ifdef IS_WINDOWS_PC
  31. // Needed for getting file type string
  32. #define WIN32_LEAN_AND_MEAN
  33. #include <shellapi.h>
  34. #endif
  35. #if defined( _X360 )
  36. #include "xbox\xbox_win32stubs.h"
  37. #undef GetCurrentDirectory
  38. #endif
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. #pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
  42. ConVar fs_report_sync_opens( "fs_report_sync_opens", "0", 0, "0:Off, 1:Blocking only, 2:All" );
  43. ConVar fs_warning_mode( "fs_warning_mode", "0", 0, "0:Off, 1:Warn main thread, 2:Warn other threads" );
  44. #define BSPOUTPUT 0 // bsp output flag -- determines type of fs_log output to generate
  45. static void AddSeperatorAndFixPath( char *str );
  46. // Case-insensitive symbol table for path IDs.
  47. CUtlSymbolTableMT g_PathIDTable( 0, 32, true );
  48. int g_iNextSearchPathID = 1;
  49. // This can be used to easily fix a filename on the stack.
  50. #ifdef DBGFLAG_ASSERT
  51. // Look for cases like materials\\blah.vmt.
  52. static bool V_CheckDoubleSlashes( const char *pStr )
  53. {
  54. int len = V_strlen( pStr );
  55. for ( int i=1; i < len-1; i++ )
  56. {
  57. if ( (pStr[i] == '/' || pStr[i] == '\\') && (pStr[i+1] == '/' || pStr[i+1] == '\\') )
  58. {
  59. Msg( "Double slashes found in path '%s'\n", pStr );
  60. return true;
  61. }
  62. }
  63. return false;
  64. }
  65. #define CHECK_DOUBLE_SLASHES( x ) V_CheckDoubleSlashes(x)
  66. #else
  67. #define CHECK_DOUBLE_SLASHES( x )
  68. #endif
  69. static void LogFileOpen( const char *vpk, const char *pFilename, const char *pAbsPath )
  70. {
  71. static const char *mode = NULL;
  72. // Figure out if we should be doing this at all, the first time we are acalled
  73. if ( mode == NULL )
  74. {
  75. if ( CommandLine()->FindParm( "-log_opened_files" ) )
  76. mode = "wt";
  77. else
  78. mode = "";
  79. }
  80. if ( *mode == '\0' )
  81. return;
  82. // Open file for write or append
  83. FILE *f = fopen( "opened_files.txt", mode );
  84. Assert( f );
  85. if ( f )
  86. {
  87. fprintf( f, "%s, %s, %s\n", vpk, pFilename, pAbsPath );
  88. //fprintf( f, "%s\n", pFilename );
  89. fclose(f);
  90. // If this was the first time, switch from write to append for further writes
  91. mode = "at";
  92. }
  93. }
  94. static CBaseFileSystem *g_pBaseFileSystem;
  95. CBaseFileSystem *BaseFileSystem()
  96. {
  97. return g_pBaseFileSystem;
  98. }
  99. ConVar filesystem_buffer_size( "filesystem_buffer_size", "0", 0, "Size of per file buffers. 0 for none" );
  100. #if defined( TRACK_BLOCKING_IO )
  101. // If we hit more than 100 items in a frame, we're probably doing a level load...
  102. #define MAX_ITEMS 100
  103. class CBlockingFileItemList : public IBlockingFileItemList
  104. {
  105. public:
  106. CBlockingFileItemList( CBaseFileSystem *fs )
  107. :
  108. m_pFS( fs ),
  109. m_bLocked( false )
  110. {
  111. }
  112. // You can't call any of the below calls without calling these methods!!!!
  113. virtual void LockMutex()
  114. {
  115. Assert( !m_bLocked );
  116. if ( m_bLocked )
  117. return;
  118. m_bLocked = true;
  119. m_pFS->BlockingFileAccess_EnterCriticalSection();
  120. }
  121. virtual void UnlockMutex()
  122. {
  123. Assert( m_bLocked );
  124. if ( !m_bLocked )
  125. return;
  126. m_pFS->BlockingFileAccess_LeaveCriticalSection();
  127. m_bLocked = false;
  128. }
  129. virtual int First() const
  130. {
  131. if ( !m_bLocked )
  132. {
  133. Error( "CBlockingFileItemList::First() w/o calling EnterCriticalSectionFirst!" );
  134. }
  135. return m_Items.Head();
  136. }
  137. virtual int Next( int i ) const
  138. {
  139. if ( !m_bLocked )
  140. {
  141. Error( "CBlockingFileItemList::Next() w/o calling EnterCriticalSectionFirst!" );
  142. }
  143. return m_Items.Next( i );
  144. }
  145. virtual int InvalidIndex() const
  146. {
  147. return m_Items.InvalidIndex();
  148. }
  149. virtual const FileBlockingItem& Get( int index ) const
  150. {
  151. if ( !m_bLocked )
  152. {
  153. Error( "CBlockingFileItemList::Get( %d ) w/o calling EnterCriticalSectionFirst!", index );
  154. }
  155. return m_Items[ index ];
  156. }
  157. virtual void Reset()
  158. {
  159. if ( !m_bLocked )
  160. {
  161. Error( "CBlockingFileItemList::Reset() w/o calling EnterCriticalSectionFirst!" );
  162. }
  163. m_Items.RemoveAll();
  164. }
  165. void Add( const FileBlockingItem& item )
  166. {
  167. // Ack, should use a linked list probably...
  168. while ( m_Items.Count() > MAX_ITEMS )
  169. {
  170. m_Items.Remove( m_Items.Head() );
  171. }
  172. m_Items.AddToTail( item );
  173. }
  174. private:
  175. CUtlLinkedList< FileBlockingItem, unsigned short > m_Items;
  176. CBaseFileSystem *m_pFS;
  177. bool m_bLocked;
  178. };
  179. #endif
  180. CUtlSymbol CBaseFileSystem::m_GamePathID;
  181. CUtlSymbol CBaseFileSystem::m_BSPPathID;
  182. DVDMode_t CBaseFileSystem::m_DVDMode;
  183. CUtlVector< FileNameHandle_t > CBaseFileSystem::m_ExcludePaths;
  184. class CStoreIDEntry
  185. {
  186. public:
  187. CStoreIDEntry() {}
  188. CStoreIDEntry( const char *pPathIDStr, int storeID )
  189. {
  190. m_PathIDString = pPathIDStr;
  191. m_StoreID = storeID;
  192. }
  193. public:
  194. CUtlSymbol m_PathIDString;
  195. int m_StoreID;
  196. };
  197. #if 0
  198. static CStoreIDEntry* FindPrevFileByStoreID( CUtlDict< CUtlVector<CStoreIDEntry>* ,int> &filesByStoreID, const char *pFilename, const char *pPathIDStr, int foundStoreID )
  199. {
  200. int iEntry = filesByStoreID.Find( pFilename );
  201. if ( iEntry == filesByStoreID.InvalidIndex() )
  202. {
  203. CUtlVector<CStoreIDEntry> *pList = new CUtlVector<CStoreIDEntry>;
  204. pList->AddToTail( CStoreIDEntry(pPathIDStr, foundStoreID) );
  205. filesByStoreID.Insert( pFilename, pList );
  206. return NULL;
  207. }
  208. else
  209. {
  210. // Now is there a previous entry with a different path ID string and the same store ID?
  211. CUtlVector<CStoreIDEntry> *pList = filesByStoreID[iEntry];
  212. for ( int i=0; i < pList->Count(); i++ )
  213. {
  214. CStoreIDEntry &entry = pList->Element( i );
  215. if ( entry.m_StoreID == foundStoreID && V_stricmp( entry.m_PathIDString.String(), pPathIDStr ) != 0 )
  216. return &entry;
  217. }
  218. return NULL;
  219. }
  220. }
  221. #endif
  222. //-----------------------------------------------------------------------------
  223. class CBaseFileSystem::CFileCacheObject
  224. {
  225. public:
  226. CFileCacheObject( CBaseFileSystem* pFS );
  227. ~CFileCacheObject();
  228. void AddFiles( const char **ppFileNames, int nFileNames );
  229. bool IsReady() const { return m_nPending == 0; }
  230. static void IOCallback( const FileAsyncRequest_t &request, int nBytesRead, FSAsyncStatus_t err );
  231. struct Info_t
  232. {
  233. const char* pFileName;
  234. FSAsyncControl_t hIOAsync;
  235. CMemoryFileBacking* pBacking;
  236. CFileCacheObject* pOwner;
  237. };
  238. CBaseFileSystem* m_pFS;
  239. CInterlockedInt m_nPending;
  240. CThreadFastMutex m_InfosMutex;
  241. CUtlVector< Info_t* > m_Infos;
  242. private:
  243. void ProcessNewEntries( int start );
  244. CFileCacheObject( const CFileCacheObject& ); // not implemented
  245. CFileCacheObject & operator=(const CFileCacheObject& ); // not implemented
  246. };
  247. //-----------------------------------------------------------------------------
  248. // constructor
  249. //-----------------------------------------------------------------------------
  250. CBaseFileSystem::CBaseFileSystem()
  251. : m_FileTracker2( this )
  252. {
  253. g_pBaseFileSystem = this;
  254. g_pFullFileSystem = this;
  255. m_WhitelistFileTrackingEnabled = -1;
  256. // If this changes then FileNameHandleInternal_t/FileNameHandle_t needs to be fixed!!!
  257. Assert( sizeof( CUtlSymbol ) == sizeof( short ) );
  258. // Clear out statistics
  259. memset( &m_Stats, 0, sizeof(m_Stats) );
  260. m_fwLevel = FILESYSTEM_WARNING_REPORTUNCLOSED;
  261. m_pfnWarning = NULL;
  262. m_pLogFile = NULL;
  263. m_bOutputDebugString = false;
  264. m_WhitelistSpewFlags = 0;
  265. m_DirtyDiskReportFunc = NULL;
  266. m_pPureServerWhitelist = NULL;
  267. m_pThreadPool = NULL;
  268. #if defined( TRACK_BLOCKING_IO )
  269. m_pBlockingItems = new CBlockingFileItemList( this );
  270. m_bBlockingFileAccessReportingEnabled = false;
  271. m_bAllowSynchronousLogging = true;
  272. #endif
  273. m_iMapLoad = 0;
  274. Q_memset( m_PreloadData, 0, sizeof( m_PreloadData ) );
  275. // allows very specifc constrained behavior
  276. m_DVDMode = DVDMODE_OFF;
  277. if ( IsX360() )
  278. {
  279. if ( CommandLine()->FindParm( "-dvd" ) )
  280. {
  281. m_DVDMode = DVDMODE_STRICT;
  282. }
  283. else if ( CommandLine()->FindParm( "-dvddev" ) )
  284. {
  285. m_DVDMode = DVDMODE_DEV;
  286. }
  287. }
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. //-----------------------------------------------------------------------------
  292. CBaseFileSystem::~CBaseFileSystem()
  293. {
  294. m_PathIDInfos.PurgeAndDeleteElements();
  295. #if defined( TRACK_BLOCKING_IO )
  296. delete m_pBlockingItems;
  297. #endif
  298. // Free the whitelist.
  299. RegisterFileWhitelist( NULL, NULL );
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Methods of IAppSystem
  303. //-----------------------------------------------------------------------------
  304. void *CBaseFileSystem::QueryInterface( const char *pInterfaceName )
  305. {
  306. // We also implement the IMatSystemSurface interface
  307. if (!Q_strncmp( pInterfaceName, BASEFILESYSTEM_INTERFACE_VERSION, Q_strlen(BASEFILESYSTEM_INTERFACE_VERSION) + 1))
  308. return (IBaseFileSystem*)this;
  309. return NULL;
  310. }
  311. InitReturnVal_t CBaseFileSystem::Init()
  312. {
  313. InitReturnVal_t nRetVal = BaseClass::Init();
  314. if ( nRetVal != INIT_OK )
  315. return nRetVal;
  316. // 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
  317. m_BSPPathID = g_PathIDTable.AddString( "BSP" );
  318. m_GamePathID = g_PathIDTable.AddString( "GAME" );
  319. if ( getenv( "fs_debug" ) )
  320. {
  321. m_bOutputDebugString = true;
  322. }
  323. const char *logFileName = CommandLine()->ParmValue( "-fs_log" );
  324. if( logFileName )
  325. {
  326. m_pLogFile = fopen( logFileName, "w" ); // STEAM OK
  327. if ( !m_pLogFile )
  328. return INIT_FAILED;
  329. fprintf( m_pLogFile, "@echo off\n" );
  330. fprintf( m_pLogFile, "setlocal\n" );
  331. const char *fs_target = CommandLine()->ParmValue( "-fs_target" );
  332. if( fs_target )
  333. {
  334. fprintf( m_pLogFile, "set fs_target=\"%s\"\n", fs_target );
  335. }
  336. fprintf( m_pLogFile, "if \"%%fs_target%%\" == \"\" goto error\n" );
  337. fprintf( m_pLogFile, "@echo on\n" );
  338. }
  339. InitAsync();
  340. if ( IsX360() && m_DVDMode == DVDMODE_DEV )
  341. {
  342. // exclude paths are valid ony in dvddev mode
  343. char szExcludeFile[MAX_PATH];
  344. const char *pRemotePath = CommandLine()->ParmValue( "-remote" );
  345. const char *pBasePath = CommandLine()->ParmValue( "-basedir" );
  346. if ( pRemotePath && pBasePath )
  347. {
  348. // the optional exclude path file only exists at the remote path
  349. V_ComposeFileName( pRemotePath, "xbox_exclude_paths.txt", szExcludeFile, sizeof( szExcludeFile ) );
  350. // populate the exclusion list
  351. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  352. if ( ReadFile( szExcludeFile, NULL, buf, 0, 0 ) )
  353. {
  354. characterset_t breakSet;
  355. CharacterSetBuild( &breakSet, "" );
  356. char szPath[MAX_PATH];
  357. char szToken[MAX_PATH];
  358. for ( ;; )
  359. {
  360. int nTokenSize = buf.ParseToken( &breakSet, szToken, sizeof( szToken ) );
  361. if ( nTokenSize <= 0 )
  362. {
  363. break;
  364. }
  365. char *pToken = szToken;
  366. if ( pToken[0] == '\\' )
  367. {
  368. // skip past possible initial seperator
  369. pToken++;
  370. }
  371. V_ComposeFileName( pBasePath, pToken, szPath, sizeof( szPath ) );
  372. V_AppendSlash( szPath, sizeof( szPath ) );
  373. FileNameHandle_t hFileName = FindOrAddFileName( szPath );
  374. if ( m_ExcludePaths.Find( hFileName ) == -1 )
  375. {
  376. m_ExcludePaths.AddToTail( hFileName );
  377. }
  378. }
  379. }
  380. }
  381. }
  382. return INIT_OK;
  383. }
  384. void CBaseFileSystem::Shutdown()
  385. {
  386. ShutdownAsync();
  387. m_FileTracker2.ShutdownAsync();
  388. #ifndef _X360
  389. if( m_pLogFile )
  390. {
  391. if( CommandLine()->FindParm( "-fs_logbins" ) >= 0 )
  392. {
  393. char cwd[MAX_FILEPATH];
  394. getcwd( cwd, MAX_FILEPATH-1 );
  395. fprintf( m_pLogFile, "set binsrc=\"%s\"\n", cwd );
  396. fprintf( m_pLogFile, "mkdir \"%%fs_target%%\"\n" );
  397. fprintf( m_pLogFile, "copy \"%%binsrc%%\\hl2.exe\" \"%%fs_target%%\"\n" );
  398. fprintf( m_pLogFile, "copy \"%%binsrc%%\\hl2.dat\" \"%%fs_target%%\"\n" );
  399. fprintf( m_pLogFile, "mkdir \"%%fs_target%%\\bin\"\n" );
  400. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\*.asi\" \"%%fs_target%%\\bin\"\n" );
  401. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\materialsystem.dll\" \"%%fs_target%%\\bin\"\n" );
  402. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\shaderapidx9.dll\" \"%%fs_target%%\\bin\"\n" );
  403. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\filesystem_stdio.dll\" \"%%fs_target%%\\bin\"\n" );
  404. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\soundemittersystem.dll\" \"%%fs_target%%\\bin\"\n" );
  405. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\stdshader*.dll\" \"%%fs_target%%\\bin\"\n" );
  406. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\shader_nv*.dll\" \"%%fs_target%%\\bin\"\n" );
  407. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\launcher.dll\" \"%%fs_target%%\\bin\"\n" );
  408. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\engine.dll\" \"%%fs_target%%\\bin\"\n" );
  409. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\mss32.dll\" \"%%fs_target%%\\bin\"\n" );
  410. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\tier0.dll\" \"%%fs_target%%\\bin\"\n" );
  411. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\vgui2.dll\" \"%%fs_target%%\\bin\"\n" );
  412. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\vguimatsurface.dll\" \"%%fs_target%%\\bin\"\n" );
  413. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\voice_miles.dll\" \"%%fs_target%%\\bin\"\n" );
  414. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\vphysics.dll\" \"%%fs_target%%\\bin\"\n" );
  415. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\vstdlib.dll\" \"%%fs_target%%\\bin\"\n" );
  416. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\studiorender.dll\" \"%%fs_target%%\\bin\"\n" );
  417. fprintf( m_pLogFile, "copy \"%%binsrc%%\\bin\\vaudio_miles.dll\" \"%%fs_target%%\\bin\"\n" );
  418. fprintf( m_pLogFile, "copy \"%%binsrc%%\\hl2\\resource\\*.ttf\" \"%%fs_target%%\\hl2\\resource\"\n" );
  419. fprintf( m_pLogFile, "copy \"%%binsrc%%\\hl2\\bin\\gameui.dll\" \"%%fs_target%%\\hl2\\bin\"\n" );
  420. }
  421. fprintf( m_pLogFile, "goto done\n" );
  422. fprintf( m_pLogFile, ":error\n" );
  423. fprintf( m_pLogFile, "echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\"\n" );
  424. fprintf( m_pLogFile, "echo ERROR: must set fs_target=targetpath (ie. \"set fs_target=u:\\destdir\")!\n" );
  425. fprintf( m_pLogFile, "echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\"\n" );
  426. fprintf( m_pLogFile, ":done\n" );
  427. fclose( m_pLogFile ); // STEAM OK
  428. }
  429. #endif
  430. UnloadCompiledKeyValues();
  431. RemoveAllSearchPaths();
  432. Trace_DumpUnclosedFiles();
  433. BaseClass::Shutdown();
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Computes a full write path
  437. //-----------------------------------------------------------------------------
  438. inline void CBaseFileSystem::ComputeFullWritePath( char* pDest, int maxlen, const char *pRelativePath, const char *pWritePathID )
  439. {
  440. Q_strncpy( pDest, GetWritePath( pRelativePath, pWritePathID ), maxlen );
  441. Q_strncat( pDest, pRelativePath, maxlen, COPY_ALL_CHARACTERS );
  442. Q_FixSlashes( pDest );
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose:
  446. // Input : src1 -
  447. // src2 -
  448. // Output : Returns true on success, false on failure.
  449. //-----------------------------------------------------------------------------
  450. bool CBaseFileSystem::OpenedFileLessFunc( COpenedFile const& src1, COpenedFile const& src2 )
  451. {
  452. return src1.m_pFile < src2.m_pFile;
  453. }
  454. void CBaseFileSystem::InstallDirtyDiskReportFunc( FSDirtyDiskReportFunc_t func )
  455. {
  456. m_DirtyDiskReportFunc = func;
  457. }
  458. //-----------------------------------------------------------------------------
  459. // Purpose:
  460. // Input : *fullpath -
  461. //-----------------------------------------------------------------------------
  462. void CBaseFileSystem::LogAccessToFile( char const *accesstype, char const *fullpath, char const *options )
  463. {
  464. LOCAL_THREAD_LOCK();
  465. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES && !V_stristr( fullpath, "console.log" ) )
  466. {
  467. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES, "---FS%s: %s %s (%.3f)\n", ThreadInMainThread() ? "" : "[a]", accesstype, fullpath, Plat_FloatTime() );
  468. }
  469. int c = m_LogFuncs.Count();
  470. if ( !c )
  471. return;
  472. for ( int i = 0; i < c; ++i )
  473. {
  474. ( m_LogFuncs[ i ] )( fullpath, options );
  475. }
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose:
  479. // Input : *filename -
  480. // *options -
  481. // Output : FILE
  482. //-----------------------------------------------------------------------------
  483. FILE *CBaseFileSystem::Trace_FOpen( const char *filenameT, const char *options, unsigned flags, int64 *size )
  484. {
  485. AUTOBLOCKREPORTER_FN( Trace_FOpen, this, true, filenameT, FILESYSTEM_BLOCKING_SYNCHRONOUS, FileBlockingItem::FB_ACCESS_OPEN );
  486. char filename[MAX_PATH];
  487. FixUpPath ( filenameT, filename, sizeof( filename ) );
  488. FILE *fp = FS_fopen( filename, options, flags, size );
  489. if ( fp )
  490. {
  491. if ( options[0] == 'r' )
  492. {
  493. FS_setbufsize(fp, filesystem_buffer_size.GetInt() );
  494. }
  495. else
  496. {
  497. FS_setbufsize(fp, 32*1024 );
  498. }
  499. AUTO_LOCK( m_OpenedFilesMutex );
  500. COpenedFile file;
  501. file.SetName( filename );
  502. file.m_pFile = fp;
  503. m_OpenedFiles.AddToTail( file );
  504. LogAccessToFile( "open", filename, options );
  505. }
  506. return fp;
  507. }
  508. void CBaseFileSystem::GetFileNameForHandle( FileHandle_t handle, char *buf, size_t buflen )
  509. {
  510. V_strncpy( buf, "Unknown", buflen );
  511. /*
  512. CFileHandle *fh = ( CFileHandle *)handle;
  513. if ( !fh )
  514. {
  515. buf[ 0 ] = 0;
  516. return;
  517. }
  518. #if !defined( _RETAIL )
  519. // Pack file filehandles store the underlying name for convenience
  520. if ( fh->IsPack() )
  521. {
  522. Q_strncpy( buf, fh->Name(), buflen );
  523. return;
  524. }
  525. #endif
  526. AUTO_LOCK( m_OpenedFilesMutex );
  527. COpenedFile file;
  528. file.m_pFile = fh->GetFileHandle();
  529. int result = m_OpenedFiles.Find( file );
  530. if ( result != -1 )
  531. {
  532. COpenedFile found = m_OpenedFiles[ result ];
  533. Q_strncpy( buf, found.GetName(), buflen );
  534. }
  535. else
  536. {
  537. buf[ 0 ] = 0;
  538. }
  539. */
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Purpose:
  543. // Input : *fp -
  544. //-----------------------------------------------------------------------------
  545. void CBaseFileSystem::Trace_FClose( FILE *fp )
  546. {
  547. if ( fp )
  548. {
  549. m_OpenedFilesMutex.Lock();
  550. COpenedFile file;
  551. file.m_pFile = fp;
  552. int result = m_OpenedFiles.Find( file );
  553. if ( result != -1 /*m_OpenedFiles.InvalidIdx()*/ )
  554. {
  555. COpenedFile found = m_OpenedFiles[ result ];
  556. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES && !V_stristr( found.GetName(), "console.log" ) )
  557. {
  558. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES, "---FS%s: close %s %p %i (%.3f)\n", ThreadInMainThread() ? "" : "[a]", found.GetName(), fp, m_OpenedFiles.Count(), Plat_FloatTime() );
  559. }
  560. m_OpenedFiles.Remove( result );
  561. }
  562. else
  563. {
  564. Assert( 0 );
  565. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTALLACCESSES )
  566. {
  567. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES, "Tried to close unknown file pointer %p\n", fp );
  568. }
  569. }
  570. m_OpenedFilesMutex.Unlock();
  571. FS_fclose( fp );
  572. }
  573. }
  574. void CBaseFileSystem::Trace_FRead( int size, FILE* fp )
  575. {
  576. if ( !fp || m_fwLevel < FILESYSTEM_WARNING_REPORTALLACCESSES_READ )
  577. return;
  578. AUTO_LOCK( m_OpenedFilesMutex );
  579. COpenedFile file;
  580. file.m_pFile = fp;
  581. int result = m_OpenedFiles.Find( file );
  582. if( result != -1 )
  583. {
  584. COpenedFile found = m_OpenedFiles[ result ];
  585. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES_READ, "---FS%s: read %s %i %p (%.3f)\n", ThreadInMainThread() ? "" : "[a]", found.GetName(), size, fp, Plat_FloatTime() );
  586. }
  587. else
  588. {
  589. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES_READ, "Tried to read %i bytes from unknown file pointer %p\n", size, fp );
  590. }
  591. }
  592. void CBaseFileSystem::Trace_FWrite( int size, FILE* fp )
  593. {
  594. if ( !fp || m_fwLevel < FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE )
  595. return;
  596. COpenedFile file;
  597. file.m_pFile = fp;
  598. AUTO_LOCK( m_OpenedFilesMutex );
  599. int result = m_OpenedFiles.Find( file );
  600. if( result != -1 )
  601. {
  602. COpenedFile found = m_OpenedFiles[ result ];
  603. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, "---FS%s: write %s %i %p\n", ThreadInMainThread() ? "" : "[a]", found.GetName(), size, fp );
  604. }
  605. else
  606. {
  607. Warning( FILESYSTEM_WARNING_REPORTALLACCESSES_READWRITE, "Tried to write %i bytes from unknown file pointer %p\n", size, fp );
  608. }
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose:
  612. //-----------------------------------------------------------------------------
  613. void CBaseFileSystem::Trace_DumpUnclosedFiles( void )
  614. {
  615. /*
  616. AUTO_LOCK( m_OpenedFilesMutex );
  617. for ( int i = 0 ; i < m_OpenedFiles.Count(); i++ )
  618. {
  619. COpenedFile *found = &m_OpenedFiles[ i ];
  620. if ( m_fwLevel >= FILESYSTEM_WARNING_REPORTUNCLOSED )
  621. {
  622. // This warning can legitimately trigger because the log file is, by design, not
  623. // closed. It is not closed at shutdown in order to avoid race conditions. It is
  624. // not closed at each call to log information because the performance costs,
  625. // especially if Microsoft Security Essentials is running, are extreme.
  626. // In addition, when this warning triggers it can, due to the state of the process,
  627. // actually cause a crash.
  628. Warning( FILESYSTEM_WARNING_REPORTUNCLOSED, "File %s was never closed\n", found->GetName() );
  629. }
  630. }
  631. */
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose:
  635. //-----------------------------------------------------------------------------
  636. void CBaseFileSystem::PrintOpenedFiles( void )
  637. {
  638. FileWarningLevel_t saveLevel = m_fwLevel;
  639. m_fwLevel = FILESYSTEM_WARNING_REPORTUNCLOSED;
  640. Trace_DumpUnclosedFiles();
  641. m_fwLevel = saveLevel;
  642. }
  643. #if defined( SUPPORT_PACKED_STORE )
  644. CPackedStoreRefCount::CPackedStoreRefCount( char const *pFileBasename, char *pszFName, IBaseFileSystem *pFS )
  645. : CPackedStore( pFileBasename, pszFName, pFS, false )
  646. {
  647. // If the VPK is signed, check the signature
  648. //
  649. // !FIXME! This code is simple a linchpin that a hacker could detour to bypass sv_pure
  650. #ifdef VPK_ENABLE_SIGNING
  651. CPackedStore::ESignatureCheckResult eSigResult = CPackedStore::CheckSignature( 0, NULL );
  652. m_bSignatureValid = ( eSigResult == CPackedStore::eSignatureCheckResult_ValidSignature );
  653. #else
  654. m_bSignatureValid = false;
  655. #endif
  656. }
  657. #endif
  658. void CBaseFileSystem::AddVPKFile( char const *pPath, const char *pPathID, SearchPathAdd_t addType )
  659. {
  660. #if defined( SUPPORT_PACKED_STORE )
  661. char nameBuf[MAX_PATH];
  662. Q_MakeAbsolutePath( nameBuf, sizeof( nameBuf ), pPath );
  663. Q_FixSlashes( nameBuf );
  664. CUtlSymbol pathIDSym = g_PathIDTable.AddString( pPathID );
  665. // See if we already have this vpk file as a search path
  666. CPackedStoreRefCount *pVPK = NULL;
  667. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  668. {
  669. CPackedStoreRefCount *p = m_SearchPaths[i].GetPackedStore();
  670. if ( p && V_stricmp( p->FullPathName(), nameBuf ) == 0 )
  671. {
  672. // Already present
  673. if ( m_SearchPaths[i].GetPath() == pathIDSym )
  674. return;
  675. // Present, but for a different path
  676. pVPK = p;
  677. }
  678. }
  679. // Create a new VPK if we didn't don't already have it opened
  680. if ( pVPK == NULL )
  681. {
  682. char pszFName[MAX_PATH];
  683. pVPK = new CPackedStoreRefCount( nameBuf, pszFName, this );
  684. if ( pVPK->IsEmpty() )
  685. {
  686. delete pVPK;
  687. return;
  688. }
  689. pVPK->RegisterFileTracker( (IThreadedFileMD5Processor *)&m_FileTracker2 );
  690. pVPK->m_PackFileID = m_FileTracker2.NotePackFileOpened( pVPK->FullPathName(), pPathID, 0 );
  691. }
  692. else
  693. {
  694. pVPK->AddRef();
  695. }
  696. // Crete a search path for this
  697. CSearchPath *sp = &m_SearchPaths[ ( addType == PATH_ADD_TO_TAIL ) ? m_SearchPaths.AddToTail() : m_SearchPaths.AddToHead() ];
  698. sp->SetPackedStore( pVPK );
  699. sp->m_storeId = g_iNextSearchPathID++;
  700. sp->SetPath( pathIDSym );
  701. sp->m_pPathIDInfo = FindOrAddPathIDInfo( g_PathIDTable.AddString( pPathID ), -1 );
  702. // Check if we're trusted or not
  703. SetSearchPathIsTrustedSource( sp );
  704. #endif // SUPPORT_PACKED_STORE
  705. }
  706. bool CBaseFileSystem::RemoveVPKFile( const char *pPath, const char *pPathID )
  707. {
  708. #if defined( SUPPORT_PACKED_STORE )
  709. char nameBuf[MAX_PATH];
  710. Q_MakeAbsolutePath( nameBuf, sizeof( nameBuf ), pPath );
  711. Q_FixSlashes( nameBuf );
  712. CUtlSymbol pathIDSym = g_PathIDTable.AddString( pPathID );
  713. // See if we already have this vpk file as a search path
  714. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  715. {
  716. CPackedStoreRefCount *p = m_SearchPaths[i].GetPackedStore();
  717. if ( p && V_stricmp( p->FullPathName(), nameBuf ) == 0 )
  718. {
  719. // remove if we find one
  720. if ( m_SearchPaths[i].GetPath() == pathIDSym )
  721. {
  722. m_SearchPaths.Remove( i );
  723. return true;
  724. }
  725. }
  726. }
  727. #endif // SUPPORT_PACKED_STORE
  728. return false;
  729. }
  730. //-----------------------------------------------------------------------------
  731. // Purpose: Adds the specified pack file to the list
  732. // Output : Returns true on success, false on failure.
  733. //-----------------------------------------------------------------------------
  734. bool CBaseFileSystem::AddPackFile( const char *pFileName, const char *pathID )
  735. {
  736. CHECK_DOUBLE_SLASHES( pFileName );
  737. AsyncFinishAll();
  738. return AddPackFileFromPath( "", pFileName, true, pathID );
  739. }
  740. //-----------------------------------------------------------------------------
  741. // Purpose: Adds a pack file from the specified path
  742. // Output : Returns true on success, false on failure.
  743. //-----------------------------------------------------------------------------
  744. bool CBaseFileSystem::AddPackFileFromPath( const char *pPath, const char *pakfile, bool bCheckForAppendedPack, const char *pathID )
  745. {
  746. char fullpath[ MAX_PATH ];
  747. _snprintf( fullpath, sizeof(fullpath), "%s%s", pPath, pakfile );
  748. Q_FixSlashes( fullpath );
  749. struct _stat buf;
  750. if ( FS_stat( fullpath, &buf ) == -1 )
  751. return false;
  752. CPackFile *pf = new CZipPackFile( this );
  753. pf->m_hPackFileHandleFS = Trace_FOpen( fullpath, "rb", 0, NULL );
  754. if ( !pf->m_hPackFileHandleFS )
  755. {
  756. delete pf;
  757. return false;
  758. }
  759. // NOTE: Opening .bsp fiels inside of VPK files is not supported
  760. // Get the length of the pack file:
  761. FS_fseek( ( FILE * )pf->m_hPackFileHandleFS, 0, FILESYSTEM_SEEK_TAIL );
  762. int64 len = FS_ftell( ( FILE * )pf->m_hPackFileHandleFS );
  763. FS_fseek( ( FILE * )pf->m_hPackFileHandleFS, 0, FILESYSTEM_SEEK_HEAD );
  764. if ( !pf->Prepare( len ) )
  765. {
  766. // Failed for some reason, ignore it
  767. Trace_FClose( pf->m_hPackFileHandleFS );
  768. pf->m_hPackFileHandleFS = NULL;
  769. delete pf;
  770. return false;
  771. }
  772. // Add this pack file to the search path:
  773. CSearchPath *sp = &m_SearchPaths[ m_SearchPaths.AddToTail() ];
  774. pf->SetPath( sp->GetPath() );
  775. pf->m_lPackFileTime = GetFileTime( pakfile );
  776. sp->SetPath( pPath );
  777. sp->m_pPathIDInfo->SetPathID( pathID );
  778. sp->SetPackFile( pf );
  779. return true;
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Purpose: Search pPath for pak?.pak files and add to search path if found
  783. // Input : *pPath -
  784. //-----------------------------------------------------------------------------
  785. #if !defined( _X360 )
  786. #define PACK_NAME_FORMAT "zip%i.zip"
  787. #else
  788. #define PACK_NAME_FORMAT "zip%i.360.zip"
  789. #define PACK_LOCALIZED_NAME_FORMAT "zip%i_%s.360.zip"
  790. #endif
  791. void CBaseFileSystem::AddPackFiles( const char *pPath, const CUtlSymbol &pathID, SearchPathAdd_t addType )
  792. {
  793. Assert( ThreadInMainThread() );
  794. DISK_INTENSIVE();
  795. CUtlVector< CUtlString > pakNames;
  796. CUtlVector< int64 > pakSizes;
  797. // determine pak files, [zip0..zipN]
  798. for ( int i = 0; ; i++ )
  799. {
  800. char pakfile[MAX_PATH];
  801. char fullpath[MAX_PATH];
  802. V_snprintf( pakfile, sizeof( pakfile ), PACK_NAME_FORMAT, i );
  803. V_ComposeFileName( pPath, pakfile, fullpath, sizeof( fullpath ) );
  804. struct _stat buf;
  805. if ( FS_stat( fullpath, &buf ) == -1 )
  806. break;
  807. MEM_ALLOC_CREDIT();
  808. pakNames.AddToTail( pakfile );
  809. pakSizes.AddToTail( (int64)((unsigned int)buf.st_size) );
  810. }
  811. #if defined( _X360 )
  812. // localized zips are added last to ensure they appear first in the search path construction
  813. // localized zips can only appear in the game or mod directories
  814. bool bUseEnglishAudio = XboxLaunch()->GetForceEnglish();
  815. if ( XBX_IsLocalized() && ( bUseEnglishAudio == false ) &&
  816. ( V_stricmp( g_PathIDTable.String( pathID ), "game" ) == 0 || V_stricmp( g_PathIDTable.String( pathID ), "mod" ) == 0 ) )
  817. {
  818. // determine localized pak files, [zip0_language..zipN_language]
  819. for ( int i = 0; ; i++ )
  820. {
  821. char pakfile[MAX_PATH];
  822. char fullpath[MAX_PATH];
  823. V_snprintf( pakfile, sizeof( pakfile ), PACK_LOCALIZED_NAME_FORMAT, i, XBX_GetLanguageString() );
  824. V_ComposeFileName( pPath, pakfile, fullpath, sizeof( fullpath ) );
  825. struct _stat buf;
  826. if ( FS_stat( fullpath, &buf ) == -1 )
  827. break;
  828. pakNames.AddToTail( pakfile );
  829. pakSizes.AddToTail( (int64)((unsigned int)buf.st_size) );
  830. }
  831. }
  832. #endif
  833. // Add any zip files in the format zip1.zip ... zip0.zip
  834. // Add them backwards so zip(N) is higher priority than zip(N-1), etc.
  835. int pakcount = pakSizes.Count();
  836. int nCount = 0;
  837. for ( int i = pakcount-1; i >= 0; i-- )
  838. {
  839. char fullpath[MAX_PATH];
  840. V_ComposeFileName( pPath, pakNames[i].Get(), fullpath, sizeof( fullpath ) );
  841. int nIndex;
  842. if ( addType == PATH_ADD_TO_TAIL )
  843. {
  844. nIndex = m_SearchPaths.AddToTail();
  845. }
  846. else
  847. {
  848. nIndex = m_SearchPaths.InsertBefore( nCount );
  849. ++nCount;
  850. }
  851. CSearchPath *sp = &m_SearchPaths[ nIndex ];
  852. sp->m_pPathIDInfo = FindOrAddPathIDInfo( pathID, -1 );
  853. sp->m_storeId = g_iNextSearchPathID++;
  854. sp->SetPath( g_PathIDTable.AddString( pPath ) );
  855. CPackFile *pf = NULL;
  856. for ( int iPackFile = 0; iPackFile < m_ZipFiles.Count(); iPackFile++ )
  857. {
  858. if ( !Q_stricmp( m_ZipFiles[iPackFile]->m_ZipName.Get(), fullpath ) )
  859. {
  860. pf = m_ZipFiles[iPackFile];
  861. sp->SetPackFile( pf );
  862. pf->AddRef();
  863. }
  864. }
  865. if ( !pf )
  866. {
  867. MEM_ALLOC_CREDIT();
  868. pf = new CZipPackFile( this );
  869. pf->SetPath( sp->GetPath() );
  870. pf->m_ZipName = fullpath;
  871. m_ZipFiles.AddToTail( pf );
  872. sp->SetPackFile( pf );
  873. pf->m_lPackFileTime = GetFileTime( fullpath );
  874. pf->m_hPackFileHandleFS = Trace_FOpen( fullpath, "rb", 0, NULL );
  875. if ( pf->m_hPackFileHandleFS )
  876. {
  877. FS_setbufsize( pf->m_hPackFileHandleFS, 32*1024 ); // 32k buffer.
  878. if ( pf->Prepare( pakSizes[i] ) )
  879. {
  880. FS_setbufsize( pf->m_hPackFileHandleFS, filesystem_buffer_size.GetInt() );
  881. }
  882. else
  883. {
  884. // Failed for some reason, ignore it
  885. if ( pf->m_hPackFileHandleFS )
  886. {
  887. Trace_FClose( pf->m_hPackFileHandleFS );
  888. pf->m_hPackFileHandleFS = NULL;
  889. }
  890. m_SearchPaths.Remove( nIndex );
  891. }
  892. }
  893. else
  894. {
  895. // !NOTE! Pack files inside of VPK not supported
  896. //pf->m_hPackFileHandleVPK = FindFileInVPKs( fullpath );
  897. //if ( !pf->m_hPackFileHandleVPK || !pf->Prepare( pakSizes[i] ) )
  898. {
  899. m_SearchPaths.Remove( nIndex );
  900. }
  901. }
  902. }
  903. }
  904. }
  905. //-----------------------------------------------------------------------------
  906. // Purpose: Wipe all map (.bsp) pak file search paths
  907. //-----------------------------------------------------------------------------
  908. void CBaseFileSystem::RemoveAllMapSearchPaths( void )
  909. {
  910. AsyncFinishAll();
  911. int c = m_SearchPaths.Count();
  912. for ( int i = c - 1; i >= 0; i-- )
  913. {
  914. if ( !( m_SearchPaths[i].GetPackFile() && m_SearchPaths[i].GetPackFile()->m_bIsMapPath ) )
  915. {
  916. continue;
  917. }
  918. m_SearchPaths.Remove( i );
  919. }
  920. }
  921. //-----------------------------------------------------------------------------
  922. // Purpose:
  923. //-----------------------------------------------------------------------------
  924. void CBaseFileSystem::AddMapPackFile( const char *pPath, const char *pPathID, SearchPathAdd_t addType )
  925. {
  926. char tempPathID[MAX_PATH];
  927. ParsePathID( pPath, pPathID, tempPathID );
  928. // Security nightmares already, should not let things explicitly loading from e.g. "MOD" get surprise untrusted
  929. // files unless you really really know what you're doing.
  930. AssertMsg( V_strcasecmp( pPathID, "GAME" ) == 0,
  931. "Mounting map files anywhere besides GAME is asking for pain" );
  932. char newPath[ MAX_FILEPATH ];
  933. // +2 for '\0' and potential slash added at end.
  934. V_strncpy( newPath, pPath, sizeof( newPath ) );
  935. #ifdef _WIN32 // don't do this on linux!
  936. V_strlower( newPath );
  937. #endif
  938. V_FixSlashes( newPath );
  939. // Open the .bsp and find the map lump
  940. char fullpath[ MAX_FILEPATH ];
  941. if ( V_IsAbsolutePath( newPath ) ) // If it's an absolute path, just use that.
  942. {
  943. V_strncpy( fullpath, newPath, sizeof(fullpath) );
  944. }
  945. else
  946. {
  947. if ( !GetLocalPath( newPath, fullpath, sizeof(fullpath) ) )
  948. {
  949. // Couldn't find that .bsp file!!!
  950. return;
  951. }
  952. }
  953. int c = m_SearchPaths.Count();
  954. for ( int i = c - 1; i >= 0; i-- )
  955. {
  956. if ( !( m_SearchPaths[i].GetPackFile() && m_SearchPaths[i].GetPackFile()->m_bIsMapPath ) )
  957. continue;
  958. if ( V_stricmp( m_SearchPaths[i].GetPackFile()->m_ZipName.Get(), fullpath ) == 0 )
  959. {
  960. // Already set as map path
  961. return;
  962. }
  963. }
  964. RemoveAllMapSearchPaths();
  965. CUtlSymbol pathSymbol = g_PathIDTable.AddString( newPath );
  966. 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
  967. // Look through already-open packfiles in case we intentionally
  968. // preserved this ZIP across a map reload via refcount holding
  969. FOR_EACH_VEC( m_ZipFiles, i )
  970. {
  971. CPackFile* pf = m_ZipFiles[i];
  972. if ( pf && pf->m_bIsMapPath && pf->GetPath() == pathSymbol && V_stricmp( pf->m_ZipName.Get(), fullpath ) == 0 )
  973. {
  974. CSearchPath *sp = &m_SearchPaths[ ( addType == PATH_ADD_TO_TAIL ) ? m_SearchPaths.AddToTail() : m_SearchPaths.AddToHead() ];
  975. pf->AddRef();
  976. sp->SetPackFile( pf );
  977. sp->m_storeId = iStoreId;
  978. sp->SetPath( pathSymbol );
  979. sp->m_pPathIDInfo = FindOrAddPathIDInfo( g_PathIDTable.AddString( pPathID ), -1 );
  980. if ( IsX360() && !V_strnicmp( newPath, "net:", 4 ) )
  981. {
  982. sp->m_bIsRemotePath = true;
  983. }
  984. SetSearchPathIsTrustedSource( sp );
  985. return;
  986. }
  987. }
  988. {
  989. FILE *fp = Trace_FOpen( fullpath, "rb", 0, NULL );
  990. if ( !fp )
  991. {
  992. // Couldn't open it
  993. Warning( FILESYSTEM_WARNING, "Couldn't open .bsp %s for embedded pack file check\n", fullpath );
  994. return;
  995. }
  996. // Get the .bsp file header
  997. dheader_t header;
  998. memset( &header, 0, sizeof(dheader_t) );
  999. m_Stats.nBytesRead += FS_fread( &header, sizeof( header ), fp );
  1000. m_Stats.nReads++;
  1001. if ( header.ident != IDBSPHEADER || header.version < MINBSPVERSION || header.version > BSPVERSION )
  1002. {
  1003. Trace_FClose( fp );
  1004. return;
  1005. }
  1006. // Find the LUMP_PAKFILE offset
  1007. lump_t *packfile = &header.lumps[ LUMP_PAKFILE ];
  1008. if ( packfile->filelen <= sizeof( lump_t ) )
  1009. {
  1010. // It's empty or only contains a file header ( so there are no entries ), so don't add to search paths
  1011. Trace_FClose( fp );
  1012. return;
  1013. }
  1014. // Seek to correct position
  1015. FS_fseek( fp, packfile->fileofs, FILESYSTEM_SEEK_HEAD );
  1016. CPackFile *pf = new CZipPackFile( this );
  1017. pf->m_bIsMapPath = true;
  1018. pf->m_hPackFileHandleFS = fp;
  1019. MEM_ALLOC_CREDIT();
  1020. pf->m_ZipName = fullpath;
  1021. if ( pf->Prepare( packfile->filelen, packfile->fileofs ) )
  1022. {
  1023. int nIndex;
  1024. if ( addType == PATH_ADD_TO_TAIL )
  1025. {
  1026. nIndex = m_SearchPaths.AddToTail();
  1027. }
  1028. else
  1029. {
  1030. nIndex = m_SearchPaths.AddToHead();
  1031. }
  1032. CSearchPath *sp = &m_SearchPaths[ nIndex ];
  1033. sp->SetPackFile( pf );
  1034. sp->m_storeId = iStoreId;
  1035. sp->SetPath( pathSymbol );
  1036. sp->m_pPathIDInfo = FindOrAddPathIDInfo( g_PathIDTable.AddString( pPathID ), -1 );
  1037. if ( IsX360() && !V_strnicmp( newPath, "net:", 4 ) )
  1038. {
  1039. sp->m_bIsRemotePath = true;
  1040. }
  1041. pf->SetPath( pathSymbol );
  1042. pf->m_lPackFileTime = GetFileTime( newPath );
  1043. Trace_FClose( pf->m_hPackFileHandleFS );
  1044. pf->m_hPackFileHandleFS = NULL;
  1045. m_ZipFiles.AddToTail( pf );
  1046. SetSearchPathIsTrustedSource( sp );
  1047. }
  1048. else
  1049. {
  1050. delete pf;
  1051. }
  1052. }
  1053. }
  1054. void CBaseFileSystem::BeginMapAccess()
  1055. {
  1056. if ( m_iMapLoad++ == 0 )
  1057. {
  1058. int c = m_SearchPaths.Count();
  1059. for( int i = 0; i < c; i++ )
  1060. {
  1061. CSearchPath *pSearchPath = &m_SearchPaths[i];
  1062. CPackFile *pPackFile = pSearchPath->GetPackFile();
  1063. if ( pPackFile && pPackFile->m_bIsMapPath )
  1064. {
  1065. pPackFile->AddRef();
  1066. pPackFile->m_mutex.Lock();
  1067. #if defined( SUPPORT_PACKED_STORE )
  1068. if ( pPackFile->m_nOpenFiles == 0 && pPackFile->m_hPackFileHandleFS == NULL && !pPackFile->m_hPackFileHandleVPK )
  1069. #else
  1070. if ( pPackFile->m_nOpenFiles == 0 && pPackFile->m_hPackFileHandleFS == NULL )
  1071. #endif
  1072. {
  1073. // Try opening the file as a regular file
  1074. pPackFile->m_hPackFileHandleFS = Trace_FOpen( pPackFile->m_ZipName, "rb", 0, NULL );
  1075. // !NOTE! Pack files inside of VPK not supported
  1076. //#if defined( SUPPORT_PACKED_STORE )
  1077. // if ( !pPackFile->m_hPackFileHandleFS )
  1078. // {
  1079. // pPackFile->m_hPackFileHandleVPK = FindFileInVPKs( pPackFile->m_ZipName );
  1080. // }
  1081. //#endif
  1082. }
  1083. pPackFile->m_nOpenFiles++;
  1084. pPackFile->m_mutex.Unlock();
  1085. }
  1086. }
  1087. }
  1088. }
  1089. void CBaseFileSystem::EndMapAccess()
  1090. {
  1091. if ( m_iMapLoad-- == 1 )
  1092. {
  1093. int c = m_SearchPaths.Count();
  1094. for( int i = 0; i < c; i++ )
  1095. {
  1096. CSearchPath *pSearchPath = &m_SearchPaths[i];
  1097. CPackFile *pPackFile = pSearchPath->GetPackFile();
  1098. if ( pPackFile && pPackFile->m_bIsMapPath )
  1099. {
  1100. pPackFile->m_mutex.Lock();
  1101. pPackFile->m_nOpenFiles--;
  1102. if ( pPackFile->m_nOpenFiles == 0 )
  1103. {
  1104. if ( pPackFile->m_hPackFileHandleFS )
  1105. {
  1106. Trace_FClose( pPackFile->m_hPackFileHandleFS );
  1107. pPackFile->m_hPackFileHandleFS = NULL;
  1108. }
  1109. }
  1110. pPackFile->m_mutex.Unlock();
  1111. pPackFile->Release();
  1112. }
  1113. }
  1114. }
  1115. }
  1116. void CBaseFileSystem::PrintSearchPaths( void )
  1117. {
  1118. Msg( "---------------\n" );
  1119. Msg( "Paths:\n" );
  1120. int c = m_SearchPaths.Count();
  1121. for( int i = 0; i < c; i++ )
  1122. {
  1123. CSearchPath *pSearchPath = &m_SearchPaths[i];
  1124. const char *pszPack = "";
  1125. const char *pszType = "";
  1126. if ( pSearchPath->GetPackFile() && pSearchPath->GetPackFile()->m_bIsMapPath )
  1127. {
  1128. pszType = "(map)";
  1129. }
  1130. else if ( pSearchPath->GetPackFile() )
  1131. {
  1132. pszType = "(pack) ";
  1133. pszPack = pSearchPath->GetPackFile()->m_ZipName;
  1134. }
  1135. #ifdef SUPPORT_PACKED_STORE
  1136. else if ( pSearchPath->GetPackedStore() )
  1137. {
  1138. pszType = "(VPK)";
  1139. pszPack = pSearchPath->GetPackedStore()->FullPathName();
  1140. }
  1141. #endif
  1142. Msg( "\"%s\" \"%s\" %s%s\n", pSearchPath->GetPathString(), (const char *)pSearchPath->GetPathIDString(), pszType, pszPack );
  1143. }
  1144. if ( IsX360() && m_ExcludePaths.Count() )
  1145. {
  1146. // dump current list
  1147. Msg( "\nExclude:\n" );
  1148. char szPath[MAX_PATH];
  1149. for ( int i = 0; i < m_ExcludePaths.Count(); i++ )
  1150. {
  1151. if ( String( m_ExcludePaths[i], szPath, sizeof( szPath ) ) )
  1152. {
  1153. Msg( "\"%s\"\n", szPath );
  1154. }
  1155. }
  1156. }
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. // Purpose: This is where search paths are created. map files are created at head of list (they occur after
  1160. // file system paths have already been set ) so they get highest priority. Otherwise, we add the disk (non-packfile)
  1161. // path and then the paks if they exist for the path
  1162. // Input : *pPath -
  1163. //-----------------------------------------------------------------------------
  1164. void CBaseFileSystem::AddSearchPathInternal( const char *pPath, const char *pathID, SearchPathAdd_t addType, bool bAddPackFiles )
  1165. {
  1166. AsyncFinishAll();
  1167. Assert( ThreadInMainThread() );
  1168. // Map pak files have their own handler
  1169. if ( V_stristr( pPath, ".bsp" ) )
  1170. {
  1171. AddMapPackFile( pPath, pathID, addType );
  1172. return;
  1173. }
  1174. // So do VPK files
  1175. if ( V_stristr( pPath, ".vpk" ) )
  1176. {
  1177. AddVPKFile( pPath, pathID, addType );
  1178. return;
  1179. }
  1180. // Clean up the name
  1181. char newPath[ MAX_FILEPATH ];
  1182. if ( pPath[0] == 0 )
  1183. {
  1184. newPath[0] = newPath[1] = 0;
  1185. }
  1186. else
  1187. {
  1188. if ( IsX360() || Q_IsAbsolutePath( pPath ) )
  1189. {
  1190. Q_strncpy( newPath, pPath, sizeof( newPath ) );
  1191. }
  1192. else
  1193. {
  1194. Q_MakeAbsolutePath( newPath, sizeof(newPath), pPath );
  1195. }
  1196. #ifdef _WIN32
  1197. Q_strlower( newPath );
  1198. #endif
  1199. AddSeperatorAndFixPath( newPath );
  1200. }
  1201. // Make sure that it doesn't already exist
  1202. CUtlSymbol pathSym, pathIDSym;
  1203. pathSym = g_PathIDTable.AddString( newPath );
  1204. pathIDSym = g_PathIDTable.AddString( pathID );
  1205. int i;
  1206. int c = m_SearchPaths.Count();
  1207. int id = 0;
  1208. for ( i = 0; i < c; i++ )
  1209. {
  1210. CSearchPath *pSearchPath = &m_SearchPaths[i];
  1211. if ( pSearchPath->GetPath() == pathSym && pSearchPath->GetPathID() == pathIDSym )
  1212. {
  1213. if ( ( addType == PATH_ADD_TO_HEAD && i == 0 ) || ( addType == PATH_ADD_TO_TAIL ) )
  1214. {
  1215. return; // this entry is already at the head
  1216. }
  1217. else
  1218. {
  1219. m_SearchPaths.Remove(i); // remove it from its current position so we can add it back to the head
  1220. i--;
  1221. c--;
  1222. }
  1223. }
  1224. if ( !id && pSearchPath->GetPath() == pathSym )
  1225. {
  1226. // get first found - all reference the same store
  1227. id = pSearchPath->m_storeId;
  1228. }
  1229. }
  1230. if (!id)
  1231. {
  1232. id = g_iNextSearchPathID++;
  1233. }
  1234. if ( IsX360() && bAddPackFiles && ( !Q_stricmp( pathID, "DEFAULT_WRITE_PATH" ) || !Q_stricmp( pathID, "LOGDIR" ) ) )
  1235. {
  1236. // xbox can be assured that no zips would ever be loaded on its write path
  1237. // otherwise xbox reloads zips because of mirrored drive mappings
  1238. bAddPackFiles = false;
  1239. }
  1240. // Add to list
  1241. bool bAdded = false;
  1242. int nIndex = m_SearchPaths.Count();
  1243. if ( bAddPackFiles )
  1244. {
  1245. // Add pack files for this path next
  1246. AddPackFiles( newPath, pathIDSym, addType );
  1247. bAdded = m_SearchPaths.Count() != nIndex;
  1248. }
  1249. if ( addType == PATH_ADD_TO_HEAD )
  1250. {
  1251. nIndex = m_SearchPaths.Count() - nIndex;
  1252. Assert( nIndex >= 0 );
  1253. }
  1254. if ( IsPC() || !bAddPackFiles || !bAdded )
  1255. {
  1256. // Grab last entry and set the path
  1257. m_SearchPaths.InsertBefore( nIndex );
  1258. }
  1259. else if ( IsX360() && bAddPackFiles && bAdded )
  1260. {
  1261. // 360 needs to find files (for the preload hit) in the zip first for fast loading
  1262. // 360 always adds the non-pack search path *after* the pack file but respects the overall list ordering
  1263. if ( addType == PATH_ADD_TO_HEAD )
  1264. {
  1265. m_SearchPaths.InsertBefore( nIndex );
  1266. }
  1267. else
  1268. {
  1269. nIndex = m_SearchPaths.Count() - 1;
  1270. m_SearchPaths.InsertAfter( nIndex );
  1271. nIndex++;
  1272. }
  1273. }
  1274. CSearchPath *sp = &m_SearchPaths[ nIndex ];
  1275. sp->SetPath( pathSym );
  1276. sp->m_pPathIDInfo = FindOrAddPathIDInfo( pathIDSym, -1 );
  1277. // all matching paths have a reference to the same store
  1278. sp->m_storeId = id;
  1279. if ( IsX360() && !V_strnicmp( newPath, "net:", 4 ) )
  1280. {
  1281. sp->m_bIsRemotePath = true;
  1282. }
  1283. }
  1284. //-----------------------------------------------------------------------------
  1285. CBaseFileSystem::CSearchPath *CBaseFileSystem::FindSearchPathByStoreId( int storeId )
  1286. {
  1287. FOR_EACH_VEC( m_SearchPaths, i )
  1288. {
  1289. if ( m_SearchPaths[i].m_storeId == storeId )
  1290. return &m_SearchPaths[i];
  1291. }
  1292. return NULL;
  1293. }
  1294. //-----------------------------------------------------------------------------
  1295. // Create the search path.
  1296. //-----------------------------------------------------------------------------
  1297. void CBaseFileSystem::AddSearchPath( const char *pPath, const char *pathID, SearchPathAdd_t addType )
  1298. {
  1299. int currCount = m_SearchPaths.Count();
  1300. AddSearchPathInternal( pPath, pathID, addType, true );
  1301. if ( IsX360() && m_DVDMode == DVDMODE_DEV )
  1302. {
  1303. // dvd development mode clones a search path based on the remote path for fall through
  1304. const char *pRemotePath = CommandLine()->ParmValue( "-remote" );
  1305. const char *pBasePath = CommandLine()->ParmValue( "-basedir" );
  1306. if ( pRemotePath && pBasePath && !V_stristr( pPath, ".bsp" ) )
  1307. {
  1308. // isolate the search path from the base path
  1309. if ( !V_strnicmp( pPath, pBasePath, strlen( pBasePath ) ) )
  1310. {
  1311. // substitue the remote path
  1312. char szRemotePath[MAX_PATH];
  1313. V_strncpy( szRemotePath, pRemotePath, sizeof( szRemotePath ) );
  1314. V_strncat( szRemotePath, pPath + strlen( pBasePath ), sizeof( szRemotePath ) );
  1315. // no pack files are allowed on the fall through remote path
  1316. AddSearchPathInternal( szRemotePath, pathID, addType, false );
  1317. }
  1318. }
  1319. }
  1320. if ( currCount != m_SearchPaths.Count() )
  1321. {
  1322. #if !defined( DEDICATED )
  1323. if ( IsDebug() )
  1324. {
  1325. // spew updated search paths
  1326. // PrintSearchPaths();
  1327. }
  1328. #endif
  1329. }
  1330. }
  1331. //-----------------------------------------------------------------------------
  1332. // Returns the search path, each path is separated by ;s. Returns the length of the string returned
  1333. // Pack search paths include the pack name, so that callers can still form absolute paths
  1334. // and that absolute path can be sent to the filesystem, and mounted as a file inside a pack.
  1335. //-----------------------------------------------------------------------------
  1336. int CBaseFileSystem::GetSearchPath( const char *pathID, bool bGetPackFiles, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars )
  1337. {
  1338. AUTO_LOCK( m_SearchPathsMutex );
  1339. if ( maxLenInChars )
  1340. {
  1341. pDest[0] = 0;
  1342. }
  1343. // Build up result into string object
  1344. CUtlString sResult;
  1345. CSearchPathsIterator iter( this, pathID, bGetPackFiles ? FILTER_NONE : FILTER_CULLPACK );
  1346. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  1347. {
  1348. if ( !sResult.IsEmpty() )
  1349. sResult += ";";
  1350. CUtlString sName;
  1351. if ( pSearchPath->GetPackFile() )
  1352. {
  1353. sResult += pSearchPath->GetPackFile()->m_ZipName.String();
  1354. sResult += CORRECT_PATH_SEPARATOR_S;
  1355. }
  1356. #ifdef SUPPORT_PACKED_STORE
  1357. else if ( pSearchPath->GetPackedStore() )
  1358. {
  1359. sResult += pSearchPath->GetPackedStore()->FullPathName();
  1360. }
  1361. #endif
  1362. else
  1363. {
  1364. sResult += pSearchPath->GetPathString();
  1365. }
  1366. }
  1367. // Copy into user's buffer, possibly truncating
  1368. if ( maxLenInChars )
  1369. {
  1370. V_strncpy( pDest, sResult.String(), maxLenInChars );
  1371. }
  1372. // Return 1 extra for the NULL terminator
  1373. return sResult.Length()+1;
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. // Purpose:
  1377. // Input : *pPath -
  1378. // Output : Returns true on success, false on failure.
  1379. //-----------------------------------------------------------------------------
  1380. bool CBaseFileSystem::RemoveSearchPath( const char *pPath, const char *pathID )
  1381. {
  1382. AsyncFinishAll();
  1383. char newPath[ MAX_FILEPATH ];
  1384. newPath[ 0 ] = 0;
  1385. if ( pPath )
  1386. {
  1387. // +2 for '\0' and potential slash added at end.
  1388. Q_strncpy( newPath, pPath, sizeof( newPath ) );
  1389. #ifdef _WIN32 // don't do this on linux!
  1390. Q_strlower( newPath );
  1391. #endif
  1392. if ( V_stristr( newPath, ".bsp" ) )
  1393. {
  1394. Q_FixSlashes( newPath );
  1395. }
  1396. else if ( V_stristr( newPath, ".vpk" ) )
  1397. {
  1398. return RemoveVPKFile( newPath, pathID );
  1399. }
  1400. else
  1401. {
  1402. AddSeperatorAndFixPath( newPath );
  1403. }
  1404. }
  1405. CUtlSymbol lookup = g_PathIDTable.AddString( newPath );
  1406. CUtlSymbol id = g_PathIDTable.AddString( pathID );
  1407. bool bret = false;
  1408. // Count backward since we're possibly deleting one or more pack files, too
  1409. int i;
  1410. int c = m_SearchPaths.Count();
  1411. for( i = c - 1; i >= 0; i-- )
  1412. {
  1413. if ( newPath[0] && m_SearchPaths[i].GetPath() != lookup )
  1414. continue;
  1415. if ( FilterByPathID( &m_SearchPaths[i], id ) )
  1416. continue;
  1417. m_SearchPaths.Remove( i );
  1418. bret = true;
  1419. }
  1420. return bret;
  1421. }
  1422. //-----------------------------------------------------------------------------
  1423. // Purpose: Removes all search paths for a given pathID, such as all "GAME" paths.
  1424. // Input : pathID -
  1425. //-----------------------------------------------------------------------------
  1426. void CBaseFileSystem::RemoveSearchPaths( const char *pathID )
  1427. {
  1428. AsyncFinishAll();
  1429. int nCount = m_SearchPaths.Count();
  1430. for (int i = nCount - 1; i >= 0; i--)
  1431. {
  1432. if (!Q_stricmp(m_SearchPaths.Element(i).GetPathIDString(), pathID))
  1433. {
  1434. m_SearchPaths.FastRemove(i);
  1435. }
  1436. }
  1437. }
  1438. //-----------------------------------------------------------------------------
  1439. // Purpose:
  1440. //-----------------------------------------------------------------------------
  1441. CBaseFileSystem::CSearchPath *CBaseFileSystem::FindWritePath( const char *pFilename, const char *pathID )
  1442. {
  1443. CUtlSymbol lookup = g_PathIDTable.AddString( pathID );
  1444. AUTO_LOCK( m_SearchPathsMutex );
  1445. // a pathID has been specified, find the first match in the path list
  1446. int c = m_SearchPaths.Count();
  1447. for ( int i = 0; i < c; i++ )
  1448. {
  1449. // pak files are not allowed to be written to...
  1450. CSearchPath *pSearchPath = &m_SearchPaths[i];
  1451. if ( pSearchPath->GetPackFile() || pSearchPath->GetPackedStore() )
  1452. {
  1453. continue;
  1454. }
  1455. if ( IsX360() && ( m_DVDMode == DVDMODE_DEV ) && pFilename && !pSearchPath->m_bIsRemotePath )
  1456. {
  1457. bool bIgnorePath = false;
  1458. char szExcludePath[MAX_PATH];
  1459. char szFilename[MAX_PATH];
  1460. V_ComposeFileName( pSearchPath->GetPathString(), pFilename, szFilename, sizeof( szFilename ) );
  1461. for ( int j = 0; j < m_ExcludePaths.Count(); j++ )
  1462. {
  1463. if ( g_pFullFileSystem->String( m_ExcludePaths[j], szExcludePath, sizeof( szExcludePath ) ) )
  1464. {
  1465. if ( !V_strnicmp( szFilename, szExcludePath, strlen( szExcludePath ) ) )
  1466. {
  1467. bIgnorePath = true;
  1468. break;
  1469. }
  1470. }
  1471. }
  1472. if ( bIgnorePath )
  1473. {
  1474. // filename matches exclusion path, skip it
  1475. // favoring the next path which should be the path fall through hit
  1476. continue;
  1477. }
  1478. }
  1479. if ( !pathID || ( pSearchPath->GetPathID() == lookup ) )
  1480. {
  1481. return pSearchPath;
  1482. }
  1483. }
  1484. return NULL;
  1485. }
  1486. //-----------------------------------------------------------------------------
  1487. // Purpose: Finds a search path that should be used for writing to, given a pathID.
  1488. //-----------------------------------------------------------------------------
  1489. const char *CBaseFileSystem::GetWritePath( const char *pFilename, const char *pathID )
  1490. {
  1491. CSearchPath *pSearchPath = NULL;
  1492. if ( pathID && pathID[ 0 ] != '\0' )
  1493. {
  1494. // Check for "game_write" and "mod_write"
  1495. if ( V_stricmp( pathID, "game" ) == 0 )
  1496. pSearchPath = FindWritePath( pFilename, "game_write" );
  1497. else if ( V_stricmp( pathID, "mod" ) == 0 )
  1498. pSearchPath = FindWritePath( pFilename, "mod_write" );
  1499. if ( pSearchPath == NULL )
  1500. pSearchPath = FindWritePath( pFilename, pathID );
  1501. if ( pSearchPath )
  1502. {
  1503. return pSearchPath->GetPathString();
  1504. }
  1505. Warning( FILESYSTEM_WARNING, "Requested non-existent write path %s!\n", pathID );
  1506. }
  1507. pSearchPath = FindWritePath( pFilename, "DEFAULT_WRITE_PATH" );
  1508. if ( pSearchPath )
  1509. {
  1510. return pSearchPath->GetPathString();
  1511. }
  1512. pSearchPath = FindWritePath( pFilename, NULL ); // okay, just return the first search path added!
  1513. if ( pSearchPath )
  1514. {
  1515. return pSearchPath->GetPathString();
  1516. }
  1517. // Hope this is reasonable!!
  1518. return ".\\";
  1519. }
  1520. //-----------------------------------------------------------------------------
  1521. // Reads/writes files to utlbuffers. Attempts alignment fixups for optimal read
  1522. //-----------------------------------------------------------------------------
  1523. CThreadLocal<char *> g_pszReadFilename;
  1524. bool CBaseFileSystem::ReadToBuffer( FileHandle_t fp, CUtlBuffer &buf, int nMaxBytes, FSAllocFunc_t pfnAlloc )
  1525. {
  1526. SetBufferSize( fp, 0 ); // TODO: what if it's a pack file? restore buffer size?
  1527. int nBytesToRead = Size( fp );
  1528. if ( nBytesToRead == 0 )
  1529. {
  1530. // no data in file
  1531. return true;
  1532. }
  1533. if ( nMaxBytes > 0 )
  1534. {
  1535. // can't read more than file has
  1536. nBytesToRead = min( nMaxBytes, nBytesToRead );
  1537. }
  1538. int nBytesRead = 0;
  1539. int nBytesOffset = 0;
  1540. int iStartPos = Tell( fp );
  1541. if ( nBytesToRead != 0 )
  1542. {
  1543. int nBytesDestBuffer = nBytesToRead;
  1544. unsigned nSizeAlign = 0, nBufferAlign = 0, nOffsetAlign = 0;
  1545. bool bBinary = !( buf.IsText() && !buf.ContainsCRLF() );
  1546. if ( bBinary && !IsLinux() && !buf.IsExternallyAllocated() && !pfnAlloc &&
  1547. ( buf.TellPut() == 0 ) && ( buf.TellGet() == 0 ) && ( iStartPos % 4 == 0 ) &&
  1548. GetOptimalIOConstraints( fp, &nOffsetAlign, &nSizeAlign, &nBufferAlign ) )
  1549. {
  1550. // correct conditions to allow an optimal read
  1551. if ( iStartPos % nOffsetAlign != 0 )
  1552. {
  1553. // move starting position back to nearest alignment
  1554. nBytesOffset = ( iStartPos % nOffsetAlign );
  1555. Assert ( ( iStartPos - nBytesOffset ) % nOffsetAlign == 0 );
  1556. Seek( fp, -nBytesOffset, FILESYSTEM_SEEK_CURRENT );
  1557. // going to read from aligned start, increase target buffer size by offset alignment
  1558. nBytesDestBuffer += nBytesOffset;
  1559. }
  1560. // snap target buffer size to its size alignment
  1561. // add additional alignment slop for target pointer adjustment
  1562. nBytesDestBuffer = AlignValue( nBytesDestBuffer, nSizeAlign ) + nBufferAlign;
  1563. }
  1564. if ( !pfnAlloc )
  1565. {
  1566. buf.EnsureCapacity( nBytesDestBuffer + buf.TellPut() );
  1567. }
  1568. else
  1569. {
  1570. // caller provided allocator
  1571. void *pMemory = (*pfnAlloc)( g_pszReadFilename.Get(), nBytesDestBuffer );
  1572. buf.SetExternalBuffer( pMemory, nBytesDestBuffer, 0, buf.GetFlags() & ~CUtlBuffer::EXTERNAL_GROWABLE );
  1573. }
  1574. int seekGet = -1;
  1575. if ( nBytesDestBuffer != nBytesToRead )
  1576. {
  1577. // doing optimal read, align target pointer
  1578. int nAlignedBase = AlignValue( (byte *)buf.Base(), nBufferAlign ) - (byte *)buf.Base();
  1579. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nAlignedBase );
  1580. // the buffer read position is slid forward to ignore the addtional
  1581. // starting offset alignment
  1582. seekGet = nAlignedBase + nBytesOffset;
  1583. }
  1584. nBytesRead = ReadEx( buf.PeekPut(), buf.Size() - buf.TellPut(), nBytesToRead + nBytesOffset, fp );
  1585. buf.SeekPut( CUtlBuffer::SEEK_CURRENT, nBytesRead );
  1586. if ( seekGet != -1 )
  1587. {
  1588. // can only seek the get after data has been put, otherwise buffer sets overflow error
  1589. buf.SeekGet( CUtlBuffer::SEEK_HEAD, seekGet );
  1590. }
  1591. Seek( fp, iStartPos + ( nBytesRead - nBytesOffset ), FILESYSTEM_SEEK_HEAD );
  1592. }
  1593. return (nBytesRead != 0);
  1594. }
  1595. //-----------------------------------------------------------------------------
  1596. // Reads/writes files to utlbuffers
  1597. // NOTE NOTE!!
  1598. // If you change this implementation, copy it into CBaseVMPIFileSystem::ReadFile
  1599. // in vmpi_filesystem.cpp
  1600. //-----------------------------------------------------------------------------
  1601. bool CBaseFileSystem::ReadFile( const char *pFileName, const char *pPath, CUtlBuffer &buf, int nMaxBytes, int nStartingByte, FSAllocFunc_t pfnAlloc )
  1602. {
  1603. CHECK_DOUBLE_SLASHES( pFileName );
  1604. bool bBinary = !( buf.IsText() && !buf.ContainsCRLF() );
  1605. FileHandle_t fp = Open( pFileName, ( bBinary ) ? "rb" : "rt", pPath );
  1606. if ( !fp )
  1607. return false;
  1608. if ( nStartingByte != 0 )
  1609. {
  1610. Seek( fp, nStartingByte, FILESYSTEM_SEEK_HEAD );
  1611. }
  1612. if ( pfnAlloc )
  1613. {
  1614. g_pszReadFilename.Set( (char *)pFileName );
  1615. }
  1616. bool bSuccess = ReadToBuffer( fp, buf, nMaxBytes, pfnAlloc );
  1617. Close( fp );
  1618. return bSuccess;
  1619. }
  1620. //-----------------------------------------------------------------------------
  1621. //
  1622. //-----------------------------------------------------------------------------
  1623. int CBaseFileSystem::ReadFileEx( const char *pFileName, const char *pPath, void **ppBuf, bool bNullTerminate, bool bOptimalAlloc, int nMaxBytes, int nStartingByte, FSAllocFunc_t pfnAlloc )
  1624. {
  1625. FileHandle_t fp = Open( pFileName, "rb", pPath );
  1626. if ( !fp )
  1627. {
  1628. return 0;
  1629. }
  1630. if ( IsX360() )
  1631. {
  1632. // callers are sloppy, always want optimal
  1633. bOptimalAlloc = true;
  1634. }
  1635. SetBufferSize( fp, 0 ); // TODO: what if it's a pack file? restore buffer size?
  1636. int nBytesToRead = Size( fp );
  1637. int nBytesRead = 0;
  1638. if ( nMaxBytes > 0 )
  1639. {
  1640. nBytesToRead = min( nMaxBytes, nBytesToRead );
  1641. if ( bNullTerminate )
  1642. {
  1643. nBytesToRead--;
  1644. }
  1645. }
  1646. if ( nBytesToRead != 0 )
  1647. {
  1648. int nBytesBuf;
  1649. if ( !*ppBuf )
  1650. {
  1651. nBytesBuf = nBytesToRead + ( ( bNullTerminate ) ? 1 : 0 );
  1652. if ( !pfnAlloc && !bOptimalAlloc )
  1653. {
  1654. // AllocOptimalReadBuffer does malloc...
  1655. *ppBuf = malloc( nBytesBuf );
  1656. }
  1657. else if ( !pfnAlloc )
  1658. {
  1659. *ppBuf = AllocOptimalReadBuffer( fp, nBytesBuf, 0 );
  1660. nBytesBuf = GetOptimalReadSize( fp, nBytesBuf );
  1661. }
  1662. else
  1663. {
  1664. *ppBuf = (*pfnAlloc)( pFileName, nBytesBuf );
  1665. }
  1666. }
  1667. else
  1668. {
  1669. nBytesBuf = nMaxBytes;
  1670. }
  1671. if ( nStartingByte != 0 )
  1672. {
  1673. Seek( fp, nStartingByte, FILESYSTEM_SEEK_HEAD );
  1674. }
  1675. nBytesRead = ReadEx( *ppBuf, nBytesBuf, nBytesToRead, fp );
  1676. if ( bNullTerminate )
  1677. {
  1678. ((byte *)(*ppBuf))[nBytesToRead] = 0;
  1679. }
  1680. }
  1681. Close( fp );
  1682. return nBytesRead;
  1683. }
  1684. //-----------------------------------------------------------------------------
  1685. // NOTE NOTE!!
  1686. // If you change this implementation, copy it into CBaseVMPIFileSystem::WriteFile
  1687. // in vmpi_filesystem.cpp
  1688. //-----------------------------------------------------------------------------
  1689. bool CBaseFileSystem::WriteFile( const char *pFileName, const char *pPath, CUtlBuffer &buf )
  1690. {
  1691. CHECK_DOUBLE_SLASHES( pFileName );
  1692. const char *pWriteFlags = "wb";
  1693. if ( buf.IsText() && !buf.ContainsCRLF() )
  1694. {
  1695. pWriteFlags = "wt";
  1696. }
  1697. FileHandle_t fp = Open( pFileName, pWriteFlags, pPath );
  1698. if ( !fp )
  1699. return false;
  1700. int nBytesWritten = Write( buf.Base(), buf.TellPut(), fp );
  1701. Close( fp );
  1702. return (nBytesWritten == buf.TellPut());
  1703. }
  1704. bool CBaseFileSystem::UnzipFile( const char *pFileName, const char *pPath, const char *pDestination )
  1705. {
  1706. IZip *pZip = IZip::CreateZip( NULL, true );
  1707. HANDLE hZipFile = pZip->ParseFromDisk( pFileName );
  1708. if ( !hZipFile )
  1709. {
  1710. Msg( "Bad or missing zip file, failed to open '%s'\n", pFileName );
  1711. return false;
  1712. }
  1713. int iZipIndex = -1;
  1714. int iFileSize;
  1715. char szFileName[MAX_PATH];
  1716. // Create Directories
  1717. CreateDirHierarchy( pDestination, pPath );
  1718. while ( 1 )
  1719. {
  1720. // Get the next file in the zip
  1721. szFileName[0] = '\0';
  1722. iFileSize = 0;
  1723. iZipIndex = pZip->GetNextFilename( iZipIndex, szFileName, sizeof( szFileName ), iFileSize );
  1724. // If there aren't any more files then break out of this while
  1725. if ( iZipIndex == -1 )
  1726. break;
  1727. int iFileNameLength = Q_strlen( szFileName );
  1728. if ( szFileName[ iFileNameLength - 1 ] == '/' )
  1729. {
  1730. // Its a directory, so create it
  1731. szFileName[ iFileNameLength - 1 ] = '\0';
  1732. char szFinalName[ MAX_PATH ];
  1733. Q_snprintf( szFinalName, sizeof( szFinalName ), "%s%c%s", pDestination, CORRECT_PATH_SEPARATOR, szFileName );
  1734. CreateDirHierarchy( szFinalName, pPath );
  1735. }
  1736. }
  1737. // Write Files
  1738. while ( 1 )
  1739. {
  1740. szFileName[0] = '\0';
  1741. iFileSize = 0;
  1742. iZipIndex = pZip->GetNextFilename( iZipIndex, szFileName, sizeof( szFileName ), iFileSize );
  1743. // If there aren't any more files then break out of this while
  1744. if ( iZipIndex == -1 )
  1745. break;
  1746. int iFileNameLength = Q_strlen( szFileName );
  1747. if ( szFileName[ iFileNameLength - 1 ] != '/' )
  1748. {
  1749. // It's not a directory, so write the file
  1750. CUtlBuffer fileBuffer;
  1751. fileBuffer.Purge();
  1752. if ( pZip->ReadFileFromZip( hZipFile, szFileName, false, fileBuffer ) )
  1753. {
  1754. char szFinalName[ MAX_PATH ];
  1755. Q_snprintf( szFinalName, sizeof( szFinalName ), "%s%c%s", pDestination, CORRECT_PATH_SEPARATOR, szFileName );
  1756. // Make sure the directory actually exists in case the ZIP doesn't list it (our zip utils create zips like this)
  1757. char szFilePath[ MAX_PATH ];
  1758. Q_strncpy( szFilePath, szFinalName, sizeof(szFilePath) );
  1759. Q_StripFilename( szFilePath );
  1760. CreateDirHierarchy( szFilePath, pPath );
  1761. WriteFile( szFinalName, pPath, fileBuffer );
  1762. }
  1763. }
  1764. }
  1765. #ifdef WIN32
  1766. ::CloseHandle( hZipFile );
  1767. #else
  1768. fclose( (FILE *)hZipFile );
  1769. #endif
  1770. IZip::ReleaseZip( pZip );
  1771. return true;
  1772. }
  1773. //-----------------------------------------------------------------------------
  1774. // Purpose:
  1775. //-----------------------------------------------------------------------------
  1776. void CBaseFileSystem::RemoveAllSearchPaths( void )
  1777. {
  1778. AUTO_LOCK( m_SearchPathsMutex );
  1779. m_SearchPaths.Purge();
  1780. //m_PackFileHandles.Purge();
  1781. }
  1782. void CBaseFileSystem::LogFileAccess( const char *pFullFileName )
  1783. {
  1784. if( !m_pLogFile )
  1785. {
  1786. return;
  1787. }
  1788. char buf[1024];
  1789. #if BSPOUTPUT
  1790. Q_snprintf( buf, sizeof( buf ), "%s\n%s\n", pShortFileName, pFullFileName);
  1791. fprintf( m_pLogFile, "%s", buf ); // STEAM OK
  1792. #else
  1793. char cwd[MAX_FILEPATH];
  1794. getcwd( cwd, MAX_FILEPATH-1 );
  1795. Q_strcat( cwd, "\\", sizeof( cwd ) );
  1796. if( Q_strnicmp( cwd, pFullFileName, strlen( cwd ) ) == 0 )
  1797. {
  1798. const char *pFileNameWithoutExeDir = pFullFileName + strlen( cwd );
  1799. char targetPath[ MAX_FILEPATH ];
  1800. strcpy( targetPath, "%fs_target%\\" );
  1801. strcat( targetPath, pFileNameWithoutExeDir );
  1802. Q_snprintf( buf, sizeof( buf ), "copy \"%s\" \"%s\"\n", pFullFileName, targetPath );
  1803. char tmp[ MAX_FILEPATH ];
  1804. Q_strncpy( tmp, targetPath, sizeof( tmp ) );
  1805. Q_StripFilename( tmp );
  1806. fprintf( m_pLogFile, "mkdir \"%s\"\n", tmp ); // STEAM OK
  1807. fprintf( m_pLogFile, "%s", buf ); // STEAM OK
  1808. }
  1809. else
  1810. {
  1811. Assert( 0 );
  1812. }
  1813. #endif
  1814. }
  1815. class CPackedStore;
  1816. class CFileOpenInfo
  1817. {
  1818. public:
  1819. CFileOpenInfo( CBaseFileSystem *pFileSystem, const char *pFileName, const CBaseFileSystem::CSearchPath *path, const char *pOptions, int flags, char **ppszResolvedFilename ) :
  1820. m_pFileSystem( pFileSystem ), m_pFileName( pFileName ), m_pSearchPath( path ), m_pOptions( pOptions ), m_Flags( flags ), m_ppszResolvedFilename( ppszResolvedFilename )
  1821. {
  1822. m_pFileHandle = NULL;
  1823. m_ePureFileClass = ePureServerFileClass_Any;
  1824. if ( m_pFileSystem->m_pPureServerWhitelist )
  1825. {
  1826. m_ePureFileClass = m_pFileSystem->m_pPureServerWhitelist->GetFileClass( pFileName );
  1827. }
  1828. if ( m_ppszResolvedFilename )
  1829. *m_ppszResolvedFilename = NULL;
  1830. m_pPackFile = NULL;
  1831. m_pVPKFile = NULL;
  1832. m_AbsolutePath[0] = '\0';
  1833. }
  1834. ~CFileOpenInfo()
  1835. {
  1836. if ( IsX360() )
  1837. {
  1838. return;
  1839. }
  1840. }
  1841. void SetAbsolutePath( const char *pFormat, ... )
  1842. {
  1843. va_list marker;
  1844. va_start( marker, pFormat );
  1845. V_vsnprintf( m_AbsolutePath, sizeof( m_AbsolutePath ), pFormat, marker );
  1846. va_end( marker );
  1847. V_FixSlashes( m_AbsolutePath );
  1848. }
  1849. void SetResolvedFilename( const char *pStr )
  1850. {
  1851. if ( m_ppszResolvedFilename )
  1852. {
  1853. Assert( !( *m_ppszResolvedFilename ) );
  1854. *m_ppszResolvedFilename = strdup( pStr );
  1855. }
  1856. }
  1857. // Handles telling CFileTracker about the file we just opened so it can remember
  1858. // where the file came from, and possibly calculate a CRC if necessary.
  1859. void HandleFileCRCTracking( const char *pRelativeFileName )
  1860. {
  1861. if ( IsX360() )
  1862. {
  1863. return;
  1864. }
  1865. if ( m_pFileSystem->m_WhitelistFileTrackingEnabled == 0 )
  1866. return;
  1867. if ( m_pFileHandle && m_pSearchPath )
  1868. {
  1869. FILE *fp = m_pFileHandle->m_pFile;
  1870. int64 nLength = m_pFileHandle->m_nLength;
  1871. #ifdef SUPPORT_PACKED_STORE
  1872. if ( m_pVPKFile )
  1873. {
  1874. m_pFileSystem->m_FileTracker2.NotePackFileAccess( pRelativeFileName, m_pSearchPath->GetPathIDString(), m_pSearchPath->m_storeId, m_pFileHandle->m_VPKHandle );
  1875. return;
  1876. }
  1877. #endif
  1878. // we always record hashes of everything we load. we may filter later.
  1879. m_pFileSystem->m_FileTracker2.NoteFileLoadedFromDisk( pRelativeFileName, m_pSearchPath->GetPathIDString(), m_pSearchPath->m_storeId, fp, nLength );
  1880. }
  1881. }
  1882. #ifdef SUPPORT_PACKED_STORE
  1883. void SetFromPackedStoredFileHandle( const CPackedStoreFileHandle &fHandle, CBaseFileSystem *pFileSystem )
  1884. {
  1885. Assert( fHandle );
  1886. Assert( m_pFileHandle == NULL );
  1887. m_pFileHandle = new CFileHandle(pFileSystem);
  1888. m_pFileHandle->m_VPKHandle = fHandle;
  1889. m_pFileHandle->m_type = FT_NORMAL;
  1890. m_pFileHandle->m_nLength = fHandle.m_nFileSize;
  1891. }
  1892. #endif
  1893. public:
  1894. CBaseFileSystem *m_pFileSystem;
  1895. // These are output parameters.
  1896. CFileHandle *m_pFileHandle;
  1897. char **m_ppszResolvedFilename;
  1898. CPackFile *m_pPackFile;
  1899. CPackedStore *m_pVPKFile;
  1900. const char *m_pFileName;
  1901. const CBaseFileSystem::CSearchPath *m_pSearchPath;
  1902. const char *m_pOptions;
  1903. int m_Flags;
  1904. EPureServerFileClass m_ePureFileClass;
  1905. char m_AbsolutePath[MAX_FILEPATH]; // This is set
  1906. };
  1907. void CBaseFileSystem::HandleOpenRegularFile( CFileOpenInfo &openInfo, bool bIsAbsolutePath )
  1908. {
  1909. openInfo.m_pFileHandle = NULL;
  1910. int64 size;
  1911. FILE *fp = Trace_FOpen( openInfo.m_AbsolutePath, openInfo.m_pOptions, openInfo.m_Flags, &size );
  1912. if ( fp )
  1913. {
  1914. if ( m_pLogFile )
  1915. {
  1916. LogFileAccess( openInfo.m_AbsolutePath );
  1917. }
  1918. if ( m_bOutputDebugString )
  1919. {
  1920. #ifdef _WIN32
  1921. Plat_DebugString( "fs_debug: " );
  1922. Plat_DebugString( openInfo.m_AbsolutePath );
  1923. Plat_DebugString( "\n" );
  1924. #elif POSIX
  1925. fprintf(stderr, "fs_debug: %s\n", openInfo.m_AbsolutePath );
  1926. #endif
  1927. }
  1928. openInfo.m_pFileHandle = new CFileHandle(this);
  1929. openInfo.m_pFileHandle->m_pFile = fp;
  1930. openInfo.m_pFileHandle->m_type = FT_NORMAL;
  1931. openInfo.m_pFileHandle->m_nLength = size;
  1932. openInfo.SetResolvedFilename( openInfo.m_AbsolutePath );
  1933. LogFileOpen( "Loose", openInfo.m_pFileName, openInfo.m_AbsolutePath );
  1934. }
  1935. }
  1936. //-----------------------------------------------------------------------------
  1937. // Purpose: The base file search goes through here
  1938. // Input : *path -
  1939. // *pFileName -
  1940. // *pOptions -
  1941. // packfile -
  1942. // *filetime -
  1943. // Output : FileHandle_t
  1944. //-----------------------------------------------------------------------------
  1945. FileHandle_t CBaseFileSystem::FindFileInSearchPath( CFileOpenInfo &openInfo )
  1946. {
  1947. VPROF( "CBaseFileSystem::FindFile" );
  1948. Assert( openInfo.m_pSearchPath );
  1949. openInfo.m_pFileHandle = NULL;
  1950. // Loading from pack file?
  1951. CPackFile *pPackFile = openInfo.m_pSearchPath->GetPackFile();
  1952. if ( pPackFile )
  1953. {
  1954. openInfo.m_pFileHandle = pPackFile->OpenFile( openInfo.m_pFileName, openInfo.m_pOptions );
  1955. openInfo.m_pPackFile = pPackFile;
  1956. if ( openInfo.m_pFileHandle )
  1957. {
  1958. char tempStr[MAX_PATH*2+2];
  1959. V_snprintf( tempStr, sizeof( tempStr ), "%s%c%s", pPackFile->m_ZipName.String(), CORRECT_PATH_SEPARATOR, openInfo.m_pFileName );
  1960. openInfo.SetResolvedFilename( tempStr );
  1961. }
  1962. // If it's a BSP file, then the BSP file got CRC'd elsewhere so no need to verify stuff in there.
  1963. return (FileHandle_t)openInfo.m_pFileHandle;
  1964. }
  1965. // Loading from VPK?
  1966. #ifdef SUPPORT_PACKED_STORE
  1967. CPackedStore *pVPK = openInfo.m_pSearchPath->GetPackedStore();
  1968. if ( pVPK )
  1969. {
  1970. CPackedStoreFileHandle fHandle = pVPK->OpenFile( openInfo.m_pFileName );
  1971. if ( fHandle )
  1972. {
  1973. openInfo.SetFromPackedStoredFileHandle( fHandle, this );
  1974. openInfo.SetResolvedFilename( openInfo.m_pFileName );
  1975. openInfo.m_pVPKFile = pVPK;
  1976. LogFileOpen( "VPK", openInfo.m_pFileName, pVPK->BaseName() );
  1977. openInfo.HandleFileCRCTracking( openInfo.m_pFileName );
  1978. return ( FileHandle_t ) openInfo.m_pFileHandle;
  1979. }
  1980. return NULL;
  1981. }
  1982. #endif
  1983. // Load loose file specified as relative filename
  1984. // Convert filename to lowercase. All files in the
  1985. // game logical filesystem must be accessed by lowercase name
  1986. char szLowercaseFilename[ MAX_PATH ];
  1987. V_strcpy_safe( szLowercaseFilename, openInfo.m_pFileName );
  1988. V_strlower( szLowercaseFilename );
  1989. openInfo.SetAbsolutePath( "%s%s", openInfo.m_pSearchPath->GetPathString(), szLowercaseFilename );
  1990. // now have an absolute name
  1991. HandleOpenRegularFile( openInfo, false );
  1992. return (FileHandle_t)openInfo.m_pFileHandle;
  1993. }
  1994. //-----------------------------------------------------------------------------
  1995. // Purpose:
  1996. //-----------------------------------------------------------------------------
  1997. FileHandle_t CBaseFileSystem::OpenForRead( const char *pFileNameT, const char *pOptions, unsigned flags, const char *pathID, char **ppszResolvedFilename )
  1998. {
  1999. VPROF( "CBaseFileSystem::OpenForRead" );
  2000. char pFileNameBuff[MAX_PATH];
  2001. const char *pFileName = pFileNameBuff;
  2002. FixUpPath ( pFileNameT, pFileNameBuff, sizeof( pFileNameBuff ) );
  2003. // Try the memory cache for un-restricted searches or "GAME" items.
  2004. if ( !pathID || Q_stricmp( pathID, "GAME" ) == 0 )
  2005. {
  2006. CMemoryFileBacking* pBacking = NULL;
  2007. {
  2008. AUTO_LOCK( m_MemoryFileMutex );
  2009. CUtlHashtable< const char*, CMemoryFileBacking* >& table = m_MemoryFileHash;
  2010. UtlHashHandle_t idx = table.Find( pFileName );
  2011. if ( idx != table.InvalidHandle() )
  2012. {
  2013. pBacking = table[idx];
  2014. pBacking->AddRef();
  2015. }
  2016. }
  2017. if ( pBacking )
  2018. {
  2019. if ( pBacking->m_nLength != -1 )
  2020. {
  2021. CFileHandle* pFile = new CMemoryFileHandle( this, pBacking );
  2022. pFile->m_type = strstr( pOptions, "b" ) ? FT_MEMORY_BINARY : FT_MEMORY_TEXT;
  2023. return ( FileHandle_t )pFile;
  2024. }
  2025. else
  2026. {
  2027. // length -1 == cached failure to read
  2028. return ( FileHandle_t )NULL;
  2029. }
  2030. }
  2031. else if ( ThreadInMainThread() && fs_report_sync_opens.GetInt() > 0 )
  2032. {
  2033. DevWarning("blocking load %s\n", pFileName);
  2034. }
  2035. }
  2036. CFileOpenInfo openInfo( this, pFileName, NULL, pOptions, flags, ppszResolvedFilename );
  2037. // Already have an absolute path?
  2038. // If so, don't bother iterating search paths.
  2039. if ( V_IsAbsolutePath( pFileName ) )
  2040. {
  2041. openInfo.SetAbsolutePath( "%s", pFileName );
  2042. // Check if it's of the form C:/a/b/c/blah.zip/materials/blah.vtf
  2043. // an absolute path can encode a zip pack file (i.e. caller wants to open the file from within the pack file)
  2044. // format must strictly be ????.zip\????
  2045. // assuming a reasonable restriction that the zip must be a pre-existing search path zip
  2046. char *pZipExt = V_stristr( openInfo.m_AbsolutePath, ".zip" CORRECT_PATH_SEPARATOR_S );
  2047. if ( !pZipExt )
  2048. pZipExt = V_stristr( openInfo.m_AbsolutePath, ".bsp" CORRECT_PATH_SEPARATOR_S );
  2049. #if defined( SUPPORT_PACKED_STORE )
  2050. if ( !pZipExt )
  2051. pZipExt = V_stristr( openInfo.m_AbsolutePath, ".vpk" CORRECT_PATH_SEPARATOR_S );
  2052. #endif
  2053. if ( pZipExt && pZipExt[5] )
  2054. {
  2055. // Cut string at the slash
  2056. char *pSlash = pZipExt + 4;
  2057. Assert( *pSlash == CORRECT_PATH_SEPARATOR );
  2058. *pSlash = '\0';
  2059. // want relative portion only, everything after the slash
  2060. char *pRelativeFileName = pSlash + 1;
  2061. // Find the zip or VPK in the search paths
  2062. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  2063. {
  2064. // In VPK?
  2065. #if defined( SUPPORT_PACKED_STORE )
  2066. CPackedStore *pVPK = m_SearchPaths[i].GetPackedStore();
  2067. if ( pVPK )
  2068. {
  2069. if ( V_stricmp( pVPK->FullPathName(), openInfo.m_AbsolutePath ) == 0 )
  2070. {
  2071. CPackedStoreFileHandle fHandle = pVPK->OpenFile( pRelativeFileName );
  2072. if ( fHandle )
  2073. {
  2074. openInfo.m_pSearchPath = &m_SearchPaths[i];
  2075. openInfo.SetFromPackedStoredFileHandle( fHandle, this );
  2076. }
  2077. break;
  2078. }
  2079. continue;
  2080. }
  2081. #endif
  2082. // In .zip?
  2083. CPackFile *pPackFile = m_SearchPaths[i].GetPackFile();
  2084. if ( pPackFile )
  2085. {
  2086. if ( Q_stricmp( pPackFile->m_ZipName.Get(), openInfo.m_AbsolutePath ) == 0 )
  2087. {
  2088. openInfo.m_pSearchPath = &m_SearchPaths[i];
  2089. openInfo.m_pFileHandle = pPackFile->OpenFile( pRelativeFileName, openInfo.m_pOptions );
  2090. openInfo.m_pPackFile = pPackFile;
  2091. break;
  2092. }
  2093. }
  2094. }
  2095. if ( openInfo.m_pFileHandle )
  2096. {
  2097. openInfo.SetResolvedFilename( openInfo.m_pFileName );
  2098. openInfo.HandleFileCRCTracking( pRelativeFileName );
  2099. }
  2100. return (FileHandle_t)openInfo.m_pFileHandle;
  2101. }
  2102. // Otherwise, it must be a regular file, specified by absolute filename
  2103. HandleOpenRegularFile( openInfo, true );
  2104. // !FIXME! We probably need to deal with CRC tracking, right?
  2105. return (FileHandle_t)openInfo.m_pFileHandle;
  2106. }
  2107. // Run through all the search paths.
  2108. PathTypeFilter_t pathFilter = FILTER_NONE;
  2109. if ( IsX360() )
  2110. {
  2111. if ( flags & FSOPEN_NEVERINPACK )
  2112. {
  2113. pathFilter = FILTER_CULLPACK;
  2114. }
  2115. else if ( m_DVDMode == DVDMODE_STRICT )
  2116. {
  2117. // most all files on the dvd are expected to be in the pack
  2118. // don't allow disk paths to be searched, which is very expensive on the dvd
  2119. pathFilter = FILTER_CULLNONPACK;
  2120. }
  2121. }
  2122. CSearchPathsIterator iter( this, &pFileName, pathID, pathFilter );
  2123. for ( openInfo.m_pSearchPath = iter.GetFirst(); openInfo.m_pSearchPath != NULL; openInfo.m_pSearchPath = iter.GetNext() )
  2124. {
  2125. FileHandle_t filehandle = FindFileInSearchPath( openInfo );
  2126. if ( filehandle )
  2127. {
  2128. // Check if search path is excluded due to pure server white list,
  2129. // then we should make a note of this fact, and keep searching
  2130. if ( !openInfo.m_pSearchPath->m_bIsTrustedForPureServer && openInfo.m_ePureFileClass == ePureServerFileClass_AnyTrusted )
  2131. {
  2132. #ifdef PURE_SERVER_DEBUG_SPEW
  2133. Msg( "Ignoring %s from %s for pure server operation\n", openInfo.m_pFileName, openInfo.m_pSearchPath->GetDebugString() );
  2134. #endif
  2135. m_FileTracker2.NoteFileIgnoredForPureServer( openInfo.m_pFileName, pathID, openInfo.m_pSearchPath->m_storeId );
  2136. Close( filehandle );
  2137. openInfo.m_pFileHandle = NULL;
  2138. if ( ppszResolvedFilename && *ppszResolvedFilename )
  2139. {
  2140. free( *ppszResolvedFilename );
  2141. *ppszResolvedFilename = NULL;
  2142. }
  2143. continue;
  2144. }
  2145. //
  2146. openInfo.HandleFileCRCTracking( openInfo.m_pFileName );
  2147. return filehandle;
  2148. }
  2149. }
  2150. LogFileOpen( "[Failed]", pFileName, "" );
  2151. return ( FileHandle_t )0;
  2152. }
  2153. //-----------------------------------------------------------------------------
  2154. // Purpose:
  2155. //-----------------------------------------------------------------------------
  2156. FileHandle_t CBaseFileSystem::OpenForWrite( const char *pFileName, const char *pOptions, const char *pathID )
  2157. {
  2158. char tempPathID[MAX_PATH];
  2159. ParsePathID( pFileName, pathID, tempPathID );
  2160. if ( ThreadInMainThread() && fs_report_sync_opens.GetInt() )
  2161. {
  2162. DevWarning("blocking write %s\n", pFileName);
  2163. }
  2164. // Opening for write or append uses the write path
  2165. // Unless an absolute path is specified...
  2166. const char *pTmpFileName;
  2167. char szScratchFileName[MAX_PATH];
  2168. if ( Q_IsAbsolutePath( pFileName ) )
  2169. {
  2170. pTmpFileName = pFileName;
  2171. }
  2172. else
  2173. {
  2174. ComputeFullWritePath( szScratchFileName, sizeof( szScratchFileName ), pFileName, pathID );
  2175. pTmpFileName = szScratchFileName;
  2176. }
  2177. int64 size;
  2178. FILE *fp = Trace_FOpen( pTmpFileName, pOptions, 0, &size );
  2179. if ( !fp )
  2180. {
  2181. return ( FileHandle_t )0;
  2182. }
  2183. CFileHandle *fh = new CFileHandle( this );
  2184. fh->m_nLength = size;
  2185. fh->m_type = FT_NORMAL;
  2186. fh->m_pFile = fp;
  2187. return ( FileHandle_t )fh;
  2188. }
  2189. // This looks for UNC-type filename specifiers, which should be used instead of
  2190. // passing in path ID. So if it finds //mod/cfg/config.cfg, it translates
  2191. // pFilename to "cfg/config.cfg" and pPathID to "mod" (mod is placed in tempPathID).
  2192. void CBaseFileSystem::ParsePathID( const char* &pFilename, const char* &pPathID, char tempPathID[MAX_PATH] )
  2193. {
  2194. tempPathID[0] = 0;
  2195. if ( !pFilename || pFilename[0] == 0 )
  2196. return;
  2197. // FIXME: Pain! Backslashes are used to denote network drives, forward to denote path ids
  2198. // HOORAY! We call FixSlashes everywhere. That will definitely not work
  2199. // I'm not changing it yet though because I don't know how painful the bugs would be that would be generated
  2200. bool bIsForwardSlash = ( pFilename[0] == '/' && pFilename[1] == '/' );
  2201. // bool bIsBackwardSlash = ( pFilename[0] == '\\' && pFilename[1] == '\\' );
  2202. if ( !bIsForwardSlash ) //&& !bIsBackwardSlash )
  2203. return;
  2204. // They're specifying two path IDs. Ignore the one passed-in.
  2205. if ( pPathID )
  2206. {
  2207. Warning( FILESYSTEM_WARNING, "FS: Specified two path IDs (%s, %s).\n", pFilename, pPathID );
  2208. }
  2209. // Parse out the path ID.
  2210. const char *pIn = &pFilename[2];
  2211. char *pOut = tempPathID;
  2212. while ( *pIn && !PATHSEPARATOR( *pIn ) && (pOut - tempPathID) < (MAX_PATH-1) )
  2213. {
  2214. *pOut++ = *pIn++;
  2215. }
  2216. *pOut = 0;
  2217. if ( tempPathID[0] == '*' )
  2218. {
  2219. // * means NULL.
  2220. pPathID = NULL;
  2221. }
  2222. else
  2223. {
  2224. pPathID = tempPathID;
  2225. }
  2226. // Move pFilename up past the part with the path ID.
  2227. if ( *pIn == 0 )
  2228. pFilename = pIn;
  2229. else
  2230. pFilename = pIn + 1;
  2231. }
  2232. //-----------------------------------------------------------------------------
  2233. // Purpose:
  2234. //-----------------------------------------------------------------------------
  2235. FileHandle_t CBaseFileSystem::Open( const char *pFileName, const char *pOptions, const char *pathID )
  2236. {
  2237. return OpenEx( pFileName, pOptions, 0, pathID );
  2238. }
  2239. //-----------------------------------------------------------------------------
  2240. // Purpose:
  2241. //-----------------------------------------------------------------------------
  2242. FileHandle_t CBaseFileSystem::OpenEx( const char *pFileName, const char *pOptions, unsigned flags, const char *pathID, char **ppszResolvedFilename )
  2243. {
  2244. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s(%s, %s, %u %s )", __FUNCTION__, tmDynamicString( TELEMETRY_LEVEL0, pFileName ), tmDynamicString( TELEMETRY_LEVEL0, pOptions ), flags, tmDynamicString( TELEMETRY_LEVEL0, pathID ) );
  2245. VPROF_BUDGET( "CBaseFileSystem::Open", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2246. if ( !pFileName )
  2247. return (FileHandle_t)0;
  2248. CHECK_DOUBLE_SLASHES( pFileName );
  2249. if ( ThreadInMainThread() && fs_report_sync_opens.GetInt() > 1 )
  2250. {
  2251. ::Warning( "Open( %s )\n", pFileName );
  2252. }
  2253. // Allow for UNC-type syntax to specify the path ID.
  2254. char tempPathID[MAX_PATH];
  2255. ParsePathID( pFileName, pathID, tempPathID );
  2256. // Try each of the search paths in succession
  2257. // FIXME: call createdirhierarchy upon opening for write.
  2258. if ( strstr( pOptions, "r" ) && !strstr( pOptions, "+" ) )
  2259. {
  2260. return OpenForRead( pFileName, pOptions, flags, pathID, ppszResolvedFilename );
  2261. }
  2262. return OpenForWrite( pFileName, pOptions, pathID );
  2263. }
  2264. //-----------------------------------------------------------------------------
  2265. // Purpose:
  2266. //-----------------------------------------------------------------------------
  2267. void CBaseFileSystem::Close( FileHandle_t file )
  2268. {
  2269. VPROF_BUDGET( "CBaseFileSystem::Close", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2270. if ( !file )
  2271. {
  2272. Warning( FILESYSTEM_WARNING, "FS: Tried to Close NULL file handle!\n" );
  2273. return;
  2274. }
  2275. delete (CFileHandle*)file;
  2276. }
  2277. //-----------------------------------------------------------------------------
  2278. // Purpose:
  2279. //-----------------------------------------------------------------------------
  2280. void CBaseFileSystem::Seek( FileHandle_t file, int pos, FileSystemSeek_t whence )
  2281. {
  2282. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s (pos=%d, whence=%d)", __FUNCTION__, pos, whence );
  2283. VPROF_BUDGET( "CBaseFileSystem::Seek", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2284. CFileHandle *fh = ( CFileHandle *)file;
  2285. if ( !fh )
  2286. {
  2287. Warning( FILESYSTEM_WARNING, "Tried to Seek NULL file handle!\n" );
  2288. return;
  2289. }
  2290. fh->Seek( pos, whence );
  2291. }
  2292. //-----------------------------------------------------------------------------
  2293. // Purpose:
  2294. // Input : file -
  2295. // Output : unsigned int
  2296. //-----------------------------------------------------------------------------
  2297. unsigned int CBaseFileSystem::Tell( FileHandle_t file )
  2298. {
  2299. VPROF_BUDGET( "CBaseFileSystem::Tell", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2300. if ( !file )
  2301. {
  2302. Warning( FILESYSTEM_WARNING, "FS: Tried to Tell NULL file handle!\n" );
  2303. return 0;
  2304. }
  2305. // Pack files are relative
  2306. return (( CFileHandle *)file)->Tell();
  2307. }
  2308. //-----------------------------------------------------------------------------
  2309. // Purpose:
  2310. // Input : file -
  2311. // Output : unsigned int
  2312. //-----------------------------------------------------------------------------
  2313. unsigned int CBaseFileSystem::Size( FileHandle_t file )
  2314. {
  2315. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  2316. VPROF_BUDGET( "CBaseFileSystem::Size", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2317. if ( !file )
  2318. {
  2319. Warning( FILESYSTEM_WARNING, "FS: Tried to Size NULL file handle!\n" );
  2320. return 0;
  2321. }
  2322. return ((CFileHandle *)file)->Size();
  2323. }
  2324. //-----------------------------------------------------------------------------
  2325. // Purpose:
  2326. // Input : file -
  2327. // Output : unsigned int
  2328. //-----------------------------------------------------------------------------
  2329. unsigned int CBaseFileSystem::Size( const char* pFileName, const char *pPathID )
  2330. {
  2331. VPROF_BUDGET( "CBaseFileSystem::Size", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2332. CHECK_DOUBLE_SLASHES( pFileName );
  2333. // handle the case where no name passed...
  2334. if ( !pFileName || !pFileName[0] )
  2335. {
  2336. Warning( FILESYSTEM_WARNING, "FS: Tried to Size NULL filename!\n" );
  2337. return 0;
  2338. }
  2339. // Ok, fall through to the fast path.
  2340. unsigned result = 0;
  2341. FileHandle_t h = Open( pFileName, "rb", pPathID );
  2342. if ( h )
  2343. {
  2344. result = Size( h );
  2345. Close(h);
  2346. }
  2347. return result;
  2348. }
  2349. //-----------------------------------------------------------------------------
  2350. // Purpose:
  2351. // Input : *path -
  2352. // *pFileName -
  2353. // Output : long
  2354. //-----------------------------------------------------------------------------
  2355. long CBaseFileSystem::FastFileTime( const CSearchPath *path, const char *pFileName )
  2356. {
  2357. struct _stat buf;
  2358. if ( path->GetPackFile() )
  2359. {
  2360. // If we found the file:
  2361. if ( path->GetPackFile()->ContainsFile( pFileName ) )
  2362. {
  2363. return (path->GetPackFile()->m_lPackFileTime);
  2364. }
  2365. }
  2366. #ifdef SUPPORT_PACKED_STORE
  2367. else if ( path->GetPackedStore() )
  2368. {
  2369. // Hm, should we support this in some way?
  2370. return 0L;
  2371. }
  2372. #endif
  2373. else
  2374. {
  2375. // Is it an absolute path?
  2376. char pTmpFileName[ MAX_FILEPATH ];
  2377. if ( Q_IsAbsolutePath( pFileName ) )
  2378. {
  2379. Q_strncpy( pTmpFileName, pFileName, sizeof( pTmpFileName ) );
  2380. }
  2381. else
  2382. {
  2383. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", path->GetPathString(), pFileName );
  2384. }
  2385. Q_FixSlashes( pTmpFileName );
  2386. if( FS_stat( pTmpFileName, &buf ) != -1 )
  2387. {
  2388. return buf.st_mtime;
  2389. }
  2390. #ifdef LINUX
  2391. char caseFixedName[ MAX_PATH ];
  2392. bool found = findFileInDirCaseInsensitive_safe( pTmpFileName, caseFixedName );
  2393. if ( found && FS_stat( caseFixedName, &buf ) != -1 )
  2394. {
  2395. return buf.st_mtime;
  2396. }
  2397. #endif
  2398. }
  2399. return ( 0L );
  2400. }
  2401. //-----------------------------------------------------------------------------
  2402. // Purpose:
  2403. //-----------------------------------------------------------------------------
  2404. bool CBaseFileSystem::EndOfFile( FileHandle_t file )
  2405. {
  2406. if ( !file )
  2407. {
  2408. Warning( FILESYSTEM_WARNING, "FS: Tried to EndOfFile NULL file handle!\n" );
  2409. return true;
  2410. }
  2411. return ((CFileHandle *)file)->EndOfFile();
  2412. }
  2413. //-----------------------------------------------------------------------------
  2414. // Purpose:
  2415. //-----------------------------------------------------------------------------
  2416. int CBaseFileSystem::Read( void *pOutput, int size, FileHandle_t file )
  2417. {
  2418. return ReadEx( pOutput, size, size, file );
  2419. }
  2420. //-----------------------------------------------------------------------------
  2421. // Purpose:
  2422. //-----------------------------------------------------------------------------
  2423. int CBaseFileSystem::ReadEx( void *pOutput, int destSize, int size, FileHandle_t file )
  2424. {
  2425. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s (%d bytes)", __FUNCTION__, size );
  2426. VPROF_BUDGET( "CBaseFileSystem::Read", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2427. if ( !file )
  2428. {
  2429. Warning( FILESYSTEM_WARNING, "FS: Tried to Read NULL file handle!\n" );
  2430. return 0;
  2431. }
  2432. if ( size < 0 )
  2433. {
  2434. return 0;
  2435. }
  2436. return ((CFileHandle*)file)->Read(pOutput, destSize, size );
  2437. }
  2438. //-----------------------------------------------------------------------------
  2439. // Purpose: Blow away current readers
  2440. // Input : -
  2441. //-----------------------------------------------------------------------------
  2442. void CBaseFileSystem::UnloadCompiledKeyValues()
  2443. {
  2444. #ifndef DEDICATED
  2445. for ( int i = 0; i < IFileSystem::NUM_PRELOAD_TYPES; ++i )
  2446. {
  2447. delete m_PreloadData[ i ].m_pReader;
  2448. m_PreloadData[ i ].m_pReader = NULL;
  2449. }
  2450. #endif
  2451. }
  2452. //-----------------------------------------------------------------------------
  2453. // Purpose: Put data file into list of at specific slot, will be loaded when ::SetupPreloadData() gets called
  2454. // Input : type -
  2455. // *archiveFile -
  2456. //-----------------------------------------------------------------------------
  2457. void CBaseFileSystem::LoadCompiledKeyValues( KeyValuesPreloadType_t type, char const *archiveFile )
  2458. {
  2459. // Just add to list for appropriate loader
  2460. Assert( type >= 0 && type < IFileSystem::NUM_PRELOAD_TYPES );
  2461. CompiledKeyValuesPreloaders_t& loader = m_PreloadData[ type ];
  2462. Assert( loader.m_CacheFile == 0 );
  2463. loader.m_CacheFile = FindOrAddFileName( archiveFile );
  2464. }
  2465. //-----------------------------------------------------------------------------
  2466. // Purpose: Takes a passed in KeyValues& head and fills in the precompiled keyvalues data into it.
  2467. // Input : head -
  2468. // type -
  2469. // *filename -
  2470. // *pPathID -
  2471. // Output : Returns true on success, false on failure.
  2472. //-----------------------------------------------------------------------------
  2473. bool CBaseFileSystem::LoadKeyValues( KeyValues& head, KeyValuesPreloadType_t type, char const *filename, char const *pPathID /*= 0*/ )
  2474. {
  2475. bool bret = true;
  2476. #ifndef DEDICATED
  2477. char tempPathID[MAX_PATH];
  2478. ParsePathID( filename, pPathID, tempPathID );
  2479. // FIXME: THIS STUFF DOESN'T TRACK pPathID AT ALL RIGHT NOW!!!!!
  2480. if ( !m_PreloadData[ type ].m_pReader || !m_PreloadData[ type ].m_pReader->InstanceInPlace( head, filename ) )
  2481. {
  2482. bret = head.LoadFromFile( this, filename, pPathID );
  2483. }
  2484. return bret;
  2485. #else
  2486. bret = head.LoadFromFile( this, filename, pPathID );
  2487. return bret;
  2488. #endif
  2489. }
  2490. //-----------------------------------------------------------------------------
  2491. // Purpose: If the "PreloadedData" hasn't been purged, then this'll try and instance the KeyValues using the fast path of
  2492. /// compiled keyvalues loaded during startup.
  2493. // Otherwise, it'll just fall through to the regular KeyValues loading routines
  2494. // Input : type -
  2495. // *filename -
  2496. // *pPathID -
  2497. // Output : KeyValues
  2498. //-----------------------------------------------------------------------------
  2499. KeyValues *CBaseFileSystem::LoadKeyValues( KeyValuesPreloadType_t type, char const *filename, char const *pPathID /*= 0*/ )
  2500. {
  2501. KeyValues *kv = NULL;
  2502. if ( !m_PreloadData[ type ].m_pReader )
  2503. {
  2504. kv = new KeyValues( filename );
  2505. if ( kv )
  2506. {
  2507. kv->LoadFromFile( this, filename, pPathID );
  2508. }
  2509. }
  2510. else
  2511. {
  2512. #ifndef DEDICATED
  2513. // FIXME: THIS STUFF DOESN'T TRACK pPathID AT ALL RIGHT NOW!!!!!
  2514. kv = m_PreloadData[ type ].m_pReader->Instance( filename );
  2515. if ( !kv )
  2516. {
  2517. kv = new KeyValues( filename );
  2518. if ( kv )
  2519. {
  2520. kv->LoadFromFile( this, filename, pPathID );
  2521. }
  2522. }
  2523. #endif
  2524. }
  2525. return kv;
  2526. }
  2527. //-----------------------------------------------------------------------------
  2528. // Purpose: This is the fallback method of reading the name of the first key in the file
  2529. // Input : *filename -
  2530. // *pPathID -
  2531. // *rootName -
  2532. // bufsize -
  2533. // Output : Returns true on success, false on failure.
  2534. //-----------------------------------------------------------------------------
  2535. bool CBaseFileSystem::LookupKeyValuesRootKeyName( char const *filename, char const *pPathID, char *rootName, size_t bufsize )
  2536. {
  2537. if ( FileExists( filename, pPathID ) )
  2538. {
  2539. // open file and get shader name
  2540. FileHandle_t hFile = Open( filename, "r", pPathID );
  2541. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  2542. {
  2543. return false;
  2544. }
  2545. char buf[ 128 ];
  2546. ReadLine( buf, sizeof( buf ), hFile );
  2547. Close( hFile );
  2548. // The name will possibly come in as "foo"\n
  2549. // So we need to strip the starting " character
  2550. char *pStart = buf;
  2551. if ( *pStart == '\"' )
  2552. {
  2553. ++pStart;
  2554. }
  2555. // Then copy the rest of the string
  2556. Q_strncpy( rootName, pStart, bufsize );
  2557. // And then strip off the \n and the " character at the end, in that order
  2558. int len = Q_strlen( pStart );
  2559. while ( len > 0 && rootName[ len - 1 ] == '\n' )
  2560. {
  2561. rootName[ len - 1 ] = 0;
  2562. --len;
  2563. }
  2564. while ( len > 0 && rootName[ len - 1 ] == '\"' )
  2565. {
  2566. rootName[ len - 1 ] = 0;
  2567. --len;
  2568. }
  2569. }
  2570. else
  2571. {
  2572. return false;
  2573. }
  2574. return true;
  2575. }
  2576. //-----------------------------------------------------------------------------
  2577. // Purpose: Tries to look up the name of the first key in the file from the compiled data
  2578. // Input : type -
  2579. // *outbuf -
  2580. // bufsize -
  2581. // *filename -
  2582. // *pPathID -
  2583. // Output : Returns true on success, false on failure.
  2584. //-----------------------------------------------------------------------------
  2585. bool CBaseFileSystem::ExtractRootKeyName( KeyValuesPreloadType_t type, char *outbuf, size_t bufsize, char const *filename, char const *pPathID /*= 0*/ )
  2586. {
  2587. char tempPathID[MAX_PATH];
  2588. ParsePathID( filename, pPathID, tempPathID );
  2589. bool bret = true;
  2590. if ( !m_PreloadData[ type ].m_pReader )
  2591. {
  2592. // Use fallback
  2593. bret = LookupKeyValuesRootKeyName( filename, pPathID, outbuf, bufsize );
  2594. }
  2595. else
  2596. {
  2597. #ifndef DEDICATED
  2598. // Try to use cache
  2599. bret = m_PreloadData[ type ].m_pReader->LookupKeyValuesRootKeyName( filename, outbuf, bufsize );
  2600. if ( !bret )
  2601. {
  2602. // Not in cache, use fallback
  2603. bret = LookupKeyValuesRootKeyName( filename, pPathID, outbuf, bufsize );
  2604. }
  2605. #endif
  2606. }
  2607. return bret;
  2608. }
  2609. //-----------------------------------------------------------------------------
  2610. // Purpose:
  2611. //-----------------------------------------------------------------------------
  2612. void CBaseFileSystem::SetupPreloadData()
  2613. {
  2614. int i;
  2615. for ( i = 0; i < m_SearchPaths.Count(); i++ )
  2616. {
  2617. CPackFile* pf = m_SearchPaths[i].GetPackFile();
  2618. if ( pf )
  2619. {
  2620. pf->SetupPreloadData();
  2621. }
  2622. }
  2623. #ifndef DEDICATED
  2624. if ( !CommandLine()->FindParm( "-fs_nopreloaddata" ) )
  2625. {
  2626. // Loads in the precompiled keyvalues data for each type
  2627. for ( i = 0; i < NUM_PRELOAD_TYPES; ++i )
  2628. {
  2629. CompiledKeyValuesPreloaders_t& preloader = m_PreloadData[ i ];
  2630. Assert( !preloader.m_pReader );
  2631. char fn[MAX_PATH];
  2632. if ( preloader.m_CacheFile != 0 &&
  2633. String( preloader.m_CacheFile, fn, sizeof( fn ) ) )
  2634. {
  2635. preloader.m_pReader = new CCompiledKeyValuesReader();
  2636. preloader.m_pReader->LoadFile( fn );
  2637. }
  2638. }
  2639. }
  2640. #endif
  2641. }
  2642. //-----------------------------------------------------------------------------
  2643. // Purpose:
  2644. //-----------------------------------------------------------------------------
  2645. void CBaseFileSystem::DiscardPreloadData()
  2646. {
  2647. int i;
  2648. for( i = 0; i < m_SearchPaths.Count(); i++ )
  2649. {
  2650. CPackFile* pf = m_SearchPaths[i].GetPackFile();
  2651. if ( pf )
  2652. {
  2653. pf->DiscardPreloadData();
  2654. }
  2655. }
  2656. UnloadCompiledKeyValues();
  2657. }
  2658. //-----------------------------------------------------------------------------
  2659. // Purpose:
  2660. //-----------------------------------------------------------------------------
  2661. int CBaseFileSystem::Write( void const* pInput, int size, FileHandle_t file )
  2662. {
  2663. VPROF_BUDGET( "CBaseFileSystem::Write", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2664. AUTOBLOCKREPORTER_FH( Write, this, true, file, FILESYSTEM_BLOCKING_SYNCHRONOUS, FileBlockingItem::FB_ACCESS_WRITE );
  2665. CFileHandle *fh = ( CFileHandle *)file;
  2666. if ( !fh )
  2667. {
  2668. Warning( FILESYSTEM_WARNING, "FS: Tried to Write NULL file handle!\n" );
  2669. return 0;
  2670. }
  2671. return fh->Write( pInput, size );
  2672. }
  2673. //-----------------------------------------------------------------------------
  2674. // Purpose:
  2675. //-----------------------------------------------------------------------------
  2676. int CBaseFileSystem::FPrintf( FileHandle_t file, const char *pFormat, ... )
  2677. {
  2678. va_list args;
  2679. va_start( args, pFormat );
  2680. VPROF_BUDGET( "CBaseFileSystem::FPrintf", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2681. CFileHandle *fh = ( CFileHandle *)file;
  2682. if ( !fh )
  2683. {
  2684. Warning( FILESYSTEM_WARNING, "FS: Tried to FPrintf NULL file handle!\n" );
  2685. return 0;
  2686. }
  2687. /*
  2688. if ( !fh->GetFileHandle() )
  2689. {
  2690. Warning( FILESYSTEM_WARNING, "FS: Tried to FPrintf NULL file pointer inside valid file handle!\n" );
  2691. return 0;
  2692. }
  2693. */
  2694. char buffer[65535];
  2695. int len = vsnprintf( buffer, sizeof( buffer), pFormat, args );
  2696. len = fh->Write( buffer, len );
  2697. //int len = FS_vfprintf( fh->GetFileHandle() , pFormat, args );
  2698. va_end( args );
  2699. return len;
  2700. }
  2701. //-----------------------------------------------------------------------------
  2702. // Purpose:
  2703. //-----------------------------------------------------------------------------
  2704. void CBaseFileSystem::SetBufferSize( FileHandle_t file, unsigned nBytes )
  2705. {
  2706. CFileHandle *fh = ( CFileHandle *)file;
  2707. if ( !fh )
  2708. {
  2709. Warning( FILESYSTEM_WARNING, "FS: Tried to SetBufferSize NULL file handle!\n" );
  2710. return;
  2711. }
  2712. fh->SetBufferSize( nBytes );
  2713. }
  2714. //-----------------------------------------------------------------------------
  2715. // Purpose:
  2716. //-----------------------------------------------------------------------------
  2717. bool CBaseFileSystem::IsOk( FileHandle_t file )
  2718. {
  2719. CFileHandle *fh = ( CFileHandle *)file;
  2720. if ( !fh )
  2721. {
  2722. Warning( FILESYSTEM_WARNING, "FS: Tried to IsOk NULL file handle!\n" );
  2723. return false;
  2724. }
  2725. return fh->IsOK();
  2726. }
  2727. //-----------------------------------------------------------------------------
  2728. // Purpose:
  2729. //-----------------------------------------------------------------------------
  2730. void CBaseFileSystem::Flush( FileHandle_t file )
  2731. {
  2732. VPROF_BUDGET( "CBaseFileSystem::Flush", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2733. CFileHandle *fh = ( CFileHandle *)file;
  2734. if ( !fh )
  2735. {
  2736. Warning( FILESYSTEM_WARNING, "FS: Tried to Flush NULL file handle!\n" );
  2737. return;
  2738. }
  2739. fh->Flush();
  2740. }
  2741. bool CBaseFileSystem::Precache( const char *pFileName, const char *pPathID)
  2742. {
  2743. CHECK_DOUBLE_SLASHES( pFileName );
  2744. // Allow for UNC-type syntax to specify the path ID.
  2745. char tempPathID[MAX_PATH];
  2746. ParsePathID( pFileName, pPathID, tempPathID );
  2747. Assert( pPathID );
  2748. // Really simple, just open, the file, read it all in and close it.
  2749. // We probably want to use file mapping to do this eventually.
  2750. FileHandle_t f = Open( pFileName, "rb", pPathID );
  2751. if ( !f )
  2752. return false;
  2753. // not for consoles, the read discard is a negative benefit, slow and clobbers small drive caches
  2754. if ( IsPC() )
  2755. {
  2756. char buffer[16384];
  2757. while( sizeof(buffer) == Read(buffer,sizeof(buffer),f) );
  2758. }
  2759. Close( f );
  2760. return true;
  2761. }
  2762. //-----------------------------------------------------------------------------
  2763. // Purpose:
  2764. //-----------------------------------------------------------------------------
  2765. char *CBaseFileSystem::ReadLine( char *pOutput, int maxChars, FileHandle_t file )
  2766. {
  2767. VPROF_BUDGET( "CBaseFileSystem::ReadLine", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2768. CFileHandle *fh = ( CFileHandle *)file;
  2769. if ( !fh )
  2770. {
  2771. Warning( FILESYSTEM_WARNING, "FS: Tried to ReadLine NULL file handle!\n" );
  2772. return NULL;
  2773. }
  2774. m_Stats.nReads++;
  2775. int nRead = 0;
  2776. // Read up to maxchars:
  2777. while( nRead < ( maxChars - 1 ) )
  2778. {
  2779. // Are we at the end of the file?
  2780. if( 1 != fh->Read( pOutput + nRead, 1 ) )
  2781. break;
  2782. // Translate for text mode files:
  2783. if( ( fh->m_type == FT_PACK_TEXT || fh->m_type == FT_MEMORY_TEXT ) && pOutput[nRead] == '\r' )
  2784. {
  2785. // Ignore \r
  2786. continue;
  2787. }
  2788. // We're done when we hit a '\n'
  2789. if( pOutput[nRead] == '\n' )
  2790. {
  2791. nRead++;
  2792. break;
  2793. }
  2794. // Get outta here if we find a NULL.
  2795. if( pOutput[nRead] == '\0' )
  2796. {
  2797. pOutput[nRead] = '\n';
  2798. nRead++;
  2799. break;
  2800. }
  2801. nRead++;
  2802. }
  2803. if( nRead < maxChars )
  2804. pOutput[nRead] = '\0';
  2805. m_Stats.nBytesRead += nRead;
  2806. return ( nRead ) ? pOutput : NULL;
  2807. }
  2808. //-----------------------------------------------------------------------------
  2809. // Purpose:
  2810. // Input : *pFileName -
  2811. // Output : long
  2812. //-----------------------------------------------------------------------------
  2813. long CBaseFileSystem::GetFileTime( const char *pFileName, const char *pPathID )
  2814. {
  2815. VPROF_BUDGET( "CBaseFileSystem::GetFileTime", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2816. CHECK_DOUBLE_SLASHES( pFileName );
  2817. CSearchPathsIterator iter( this, &pFileName, pPathID );
  2818. char tempFileName[MAX_PATH];
  2819. Q_strncpy( tempFileName, pFileName, sizeof(tempFileName) );
  2820. Q_FixSlashes( tempFileName );
  2821. #ifdef _WIN32
  2822. Q_strlower( tempFileName );
  2823. #endif
  2824. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  2825. {
  2826. long ft = FastFileTime( pSearchPath, tempFileName );
  2827. if ( ft != 0L )
  2828. {
  2829. if ( !pSearchPath->GetPackFile() && m_LogFuncs.Count() )
  2830. {
  2831. char pTmpFileName[ MAX_FILEPATH ];
  2832. if ( strchr( tempFileName, ':' ) )
  2833. {
  2834. Q_strncpy( pTmpFileName, tempFileName, sizeof( pTmpFileName ) );
  2835. }
  2836. else
  2837. {
  2838. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), tempFileName );
  2839. }
  2840. Q_FixSlashes( tempFileName );
  2841. LogAccessToFile( "filetime", pTmpFileName, "" );
  2842. }
  2843. return ft;
  2844. }
  2845. }
  2846. return 0L;
  2847. }
  2848. long CBaseFileSystem::GetPathTime( const char *pFileName, const char *pPathID )
  2849. {
  2850. VPROF_BUDGET( "CBaseFileSystem::GetFileTime", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  2851. CSearchPathsIterator iter( this, &pFileName, pPathID );
  2852. char tempFileName[MAX_PATH];
  2853. Q_strncpy( tempFileName, pFileName, sizeof(tempFileName) );
  2854. Q_FixSlashes( tempFileName );
  2855. #ifdef _WIN32
  2856. Q_strlower( tempFileName );
  2857. #endif
  2858. long pathTime = 0L;
  2859. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  2860. {
  2861. long ft = FastFileTime( pSearchPath, tempFileName );
  2862. if ( ft > pathTime )
  2863. pathTime = ft;
  2864. if ( ft != 0L )
  2865. {
  2866. if ( !pSearchPath->GetPackFile() && m_LogFuncs.Count() )
  2867. {
  2868. char pTmpFileName[ MAX_FILEPATH ];
  2869. if ( strchr( tempFileName, ':' ) )
  2870. {
  2871. Q_strncpy( pTmpFileName, tempFileName, sizeof( pTmpFileName ) );
  2872. }
  2873. else
  2874. {
  2875. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), tempFileName );
  2876. }
  2877. Q_FixSlashes( tempFileName );
  2878. LogAccessToFile( "filetime", pTmpFileName, "" );
  2879. }
  2880. }
  2881. }
  2882. return pathTime;
  2883. }
  2884. void CBaseFileSystem::MarkAllCRCsUnverified()
  2885. {
  2886. if ( IsX360() )
  2887. {
  2888. return;
  2889. }
  2890. m_FileTracker2.MarkAllCRCsUnverified();
  2891. }
  2892. void CBaseFileSystem::CacheFileCRCs( const char *pPathname, ECacheCRCType eType, IFileList *pFilter )
  2893. {
  2894. if ( IsX360() )
  2895. {
  2896. return;
  2897. }
  2898. }
  2899. EFileCRCStatus CBaseFileSystem::CheckCachedFileHash( const char *pPathID, const char *pRelativeFilename, int nFileFraction, FileHash_t *pFileHash )
  2900. {
  2901. return m_FileTracker2.CheckCachedFileHash( pPathID, pRelativeFilename, nFileFraction, pFileHash );
  2902. }
  2903. void CBaseFileSystem::EnableWhitelistFileTracking( bool bEnable, bool bCacheAllVPKHashes, bool bRecalculateAndCheckHashes )
  2904. {
  2905. if ( IsX360() )
  2906. {
  2907. m_WhitelistFileTrackingEnabled = false;
  2908. return;
  2909. }
  2910. if ( m_WhitelistFileTrackingEnabled != -1 )
  2911. {
  2912. Error( "CBaseFileSystem::EnableWhitelistFileTracking called more than once." );
  2913. }
  2914. m_WhitelistFileTrackingEnabled = bEnable;
  2915. if ( m_WhitelistFileTrackingEnabled && bCacheAllVPKHashes )
  2916. {
  2917. CacheAllVPKFileHashes( bCacheAllVPKHashes, bRecalculateAndCheckHashes );
  2918. }
  2919. }
  2920. void CBaseFileSystem::CacheAllVPKFileHashes( bool bCacheAllVPKHashes, bool bRecalculateAndCheckHashes )
  2921. {
  2922. #ifdef SUPPORT_PACKED_STORE
  2923. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  2924. {
  2925. CPackedStore *pVPK = m_SearchPaths[i].GetPackedStore();
  2926. if ( pVPK == NULL )
  2927. continue;
  2928. if ( !pVPK->BTestDirectoryHash() )
  2929. {
  2930. Msg( "VPK dir file hash does not match. File corrupted or modified.\n" );
  2931. }
  2932. if ( !pVPK->BTestMasterChunkHash() )
  2933. {
  2934. Msg( "VPK chunk hash hash does not match. File corrupted or modified.\n" );
  2935. }
  2936. CUtlVector<ChunkHashFraction_t> &vecChunkHash = pVPK->AccessPackFileHashes();
  2937. CPackedStoreFileHandle fhandle = pVPK->GetHandleForHashingFiles();
  2938. CUtlVector<ChunkHashFraction_t> vecChunkHashFractionCopy;
  2939. if ( bRecalculateAndCheckHashes )
  2940. {
  2941. CUtlString sPackFileErrors;
  2942. pVPK->GetPackFileLoadErrorSummary( sPackFileErrors );
  2943. if ( sPackFileErrors.Length() )
  2944. {
  2945. Msg( "Errors occured loading files.\n" );
  2946. Msg( "%s", sPackFileErrors.String() );
  2947. Msg( "Verify integrity of your game files, perform memory and disk diagnostics on your system.\n" );
  2948. }
  2949. else
  2950. Msg( "No VPK Errors occured loading files.\n" );
  2951. Msg( "Recomputing all VPK file hashes.\n" );
  2952. vecChunkHashFractionCopy.Swap( vecChunkHash );
  2953. }
  2954. int cFailures = 0;
  2955. if ( vecChunkHash.Count() == 0 )
  2956. {
  2957. if ( vecChunkHashFractionCopy.Count() == 0 )
  2958. Msg( "File hash information not found: Hashing all VPK files for pure server operation.\n" );
  2959. pVPK->HashAllChunkFiles();
  2960. if ( vecChunkHashFractionCopy.Count() != 0 )
  2961. {
  2962. if ( vecChunkHash.Count() != vecChunkHashFractionCopy.Count() )
  2963. {
  2964. Msg( "VPK hash count does not match. VPK content may be corrupt.\n" );
  2965. }
  2966. else if ( Q_memcmp( vecChunkHash.Base(), vecChunkHashFractionCopy.Base(), vecChunkHash.Count()*sizeof(vecChunkHash[0])) != 0 )
  2967. {
  2968. Msg( "VPK hashes do not match. VPK content may be corrupt.\n" );
  2969. // find the actual mismatch
  2970. FOR_EACH_VEC( vecChunkHashFractionCopy, iHash )
  2971. {
  2972. if ( Q_memcmp( vecChunkHashFractionCopy[iHash].m_md5contents.bits, vecChunkHash[iHash].m_md5contents.bits, sizeof( vecChunkHashFractionCopy[iHash].m_md5contents.bits ) ) != 0 )
  2973. {
  2974. Msg( "VPK hash for file %d failure at offset %x.\n", vecChunkHashFractionCopy[iHash].m_nPackFileNumber, vecChunkHashFractionCopy[iHash].m_nFileFraction );
  2975. cFailures++;
  2976. }
  2977. }
  2978. }
  2979. }
  2980. }
  2981. if ( bCacheAllVPKHashes )
  2982. {
  2983. Msg( "Loaded %d VPK file hashes from %s for pure server operation.\n", vecChunkHash.Count(), pVPK->FullPathName() );
  2984. FOR_EACH_VEC( vecChunkHash, i )
  2985. {
  2986. m_FileTracker2.AddFileHashForVPKFile( vecChunkHash[i].m_nPackFileNumber, vecChunkHash[i].m_nFileFraction, vecChunkHash[i].m_cbChunkLen, vecChunkHash[i].m_md5contents, fhandle );
  2987. }
  2988. }
  2989. else
  2990. {
  2991. if ( cFailures == 0 && vecChunkHash.Count() == vecChunkHashFractionCopy.Count() )
  2992. Msg( "File hashes checked. %d matches. no failures.\n", vecChunkHash.Count() );
  2993. else
  2994. Msg( "File hashes checked. %d matches. %d failures.\n", vecChunkHash.Count(), cFailures );
  2995. }
  2996. }
  2997. #endif
  2998. }
  2999. bool CBaseFileSystem::CheckVPKFileHash( int PackFileID, int nPackFileNumber, int nFileFraction, MD5Value_t &md5Value )
  3000. {
  3001. #ifdef SUPPORT_PACKED_STORE
  3002. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  3003. {
  3004. CPackedStore *pVPK = m_SearchPaths[i].GetPackedStore();
  3005. if ( pVPK == NULL || pVPK->m_PackFileID != PackFileID )
  3006. continue;
  3007. ChunkHashFraction_t fileHashFraction;
  3008. if ( pVPK->FindFileHashFraction( nPackFileNumber, nFileFraction, fileHashFraction ) )
  3009. {
  3010. CPackedStoreFileHandle fhandle = pVPK->GetHandleForHashingFiles();
  3011. fhandle.m_nFileNumber = nPackFileNumber;
  3012. char szFileName[MAX_PATH];
  3013. pVPK->GetPackFileName( fhandle, szFileName, sizeof(szFileName) );
  3014. char hex[ 34 ];
  3015. Q_memset( hex, 0, sizeof( hex ) );
  3016. Q_binarytohex( (const byte *)md5Value.bits, sizeof( md5Value.bits ), hex, sizeof( hex ) );
  3017. char hex2[ 34 ];
  3018. Q_memset( hex2, 0, sizeof( hex2 ) );
  3019. Q_binarytohex( (const byte *)fileHashFraction.m_md5contents.bits, sizeof( fileHashFraction.m_md5contents.bits ), hex2, sizeof( hex2 ) );
  3020. if ( Q_memcmp( fileHashFraction.m_md5contents.bits, md5Value.bits, sizeof(md5Value.bits) ) != 0 )
  3021. {
  3022. Msg( "File %s offset %x hash %s does not match ( should be %s ) \n", szFileName, nFileFraction, hex, hex2 );
  3023. return false;
  3024. }
  3025. else
  3026. {
  3027. return true;
  3028. }
  3029. }
  3030. }
  3031. #else
  3032. Error("CBaseFileSystem::CheckVPKFileHash should not be called, SUPPORT_PACKED_STORE not defined" );
  3033. #endif
  3034. return false;
  3035. }
  3036. void CBaseFileSystem::RegisterFileWhitelist( IPureServerWhitelist *pWhiteList, IFileList **pFilesToReload )
  3037. {
  3038. if ( pFilesToReload )
  3039. *pFilesToReload = NULL;
  3040. if ( IsX360() )
  3041. {
  3042. return;
  3043. }
  3044. if ( m_pPureServerWhitelist )
  3045. {
  3046. m_pPureServerWhitelist->Release();
  3047. m_pPureServerWhitelist = NULL;
  3048. }
  3049. if ( pWhiteList )
  3050. {
  3051. pWhiteList->AddRef();
  3052. m_pPureServerWhitelist = pWhiteList;
  3053. }
  3054. // update which search paths are considered trusted
  3055. FOR_EACH_VEC( m_SearchPaths, i )
  3056. {
  3057. SetSearchPathIsTrustedSource( &m_SearchPaths[i] );
  3058. }
  3059. // See if we need to reload any files
  3060. if ( pFilesToReload )
  3061. *pFilesToReload = m_FileTracker2.GetFilesToUnloadForWhitelistChange( pWhiteList );
  3062. }
  3063. void CBaseFileSystem::NotifyFileUnloaded( const char *pszFilename, const char *pPathId )
  3064. {
  3065. m_FileTracker2.NoteFileUnloaded( pszFilename, pPathId );
  3066. }
  3067. void CBaseFileSystem::SetSearchPathIsTrustedSource( CSearchPath *pSearchPath )
  3068. {
  3069. // Most paths are not considered trusted
  3070. pSearchPath->m_bIsTrustedForPureServer = false;
  3071. // If we don't have a pure server whitelist, we cannot say that any
  3072. // particular files are trusted. (But then again, all files will be
  3073. // accepted, so it won't really matter.)
  3074. if ( m_pPureServerWhitelist == NULL )
  3075. return;
  3076. // Treat map packs as trusted, because we will send the CRC of the map pack to the server
  3077. if ( pSearchPath->GetPackFile() && pSearchPath->GetPackFile()->m_bIsMapPath )
  3078. {
  3079. #ifdef PURE_SERVER_DEBUG_SPEW
  3080. Msg( "Setting map pack search path %s as trusted\n", pSearchPath->GetDebugString() );
  3081. #endif
  3082. pSearchPath->m_bIsTrustedForPureServer = true;
  3083. return;
  3084. }
  3085. #ifdef SUPPORT_PACKED_STORE
  3086. // Only signed VPK's can be trusted
  3087. CPackedStoreRefCount *pVPK = pSearchPath->GetPackedStore();
  3088. if ( pVPK == NULL )
  3089. {
  3090. #ifdef PURE_SERVER_DEBUG_SPEW
  3091. Msg( "Setting %s as untrusted (loose files)\n", pSearchPath->GetDebugString() );
  3092. #endif
  3093. return;
  3094. }
  3095. if ( !pVPK->m_bSignatureValid )
  3096. {
  3097. #ifdef PURE_SERVER_DEBUG_SPEW
  3098. Msg( "Setting %s as untrusted (unsigned VPK)\n", pSearchPath->GetDebugString() );
  3099. #endif
  3100. return;
  3101. }
  3102. const CUtlVector<uint8> &key = pVPK->GetSignaturePublicKey();
  3103. for ( int iKeyIndex = 0 ; iKeyIndex < m_pPureServerWhitelist->GetTrustedKeyCount() ; ++iKeyIndex )
  3104. {
  3105. int nKeySz = 0;
  3106. const byte *pbKey = m_pPureServerWhitelist->GetTrustedKey( iKeyIndex, &nKeySz );
  3107. Assert( pbKey != NULL && nKeySz > 0 );
  3108. if ( key.Count() == nKeySz && V_memcmp( pbKey, key.Base(), nKeySz ) == 0 )
  3109. {
  3110. #ifdef PURE_SERVER_DEBUG_SPEW
  3111. Msg( "Setting %s as untrusted\n", pSearchPath->GetDebugString() );
  3112. #endif
  3113. pSearchPath->m_bIsTrustedForPureServer = true;
  3114. return;
  3115. }
  3116. }
  3117. #ifdef PURE_SERVER_DEBUG_SPEW
  3118. Msg( "Setting %s as untrusted. (Key not in trusted key list)\n", pSearchPath->GetDebugString() );
  3119. #endif
  3120. #endif
  3121. }
  3122. int CBaseFileSystem::GetUnverifiedFileHashes( CUnverifiedFileHash *pFiles, int nMaxFiles )
  3123. {
  3124. return m_FileTracker2.GetUnverifiedFileHashes( pFiles, nMaxFiles );
  3125. }
  3126. int CBaseFileSystem::GetWhitelistSpewFlags()
  3127. {
  3128. return m_WhitelistSpewFlags;
  3129. }
  3130. void CBaseFileSystem::SetWhitelistSpewFlags( int flags )
  3131. {
  3132. m_WhitelistSpewFlags = flags;
  3133. }
  3134. //-----------------------------------------------------------------------------
  3135. // Purpose:
  3136. // Input : *pString -
  3137. // maxCharsIncludingTerminator -
  3138. // fileTime -
  3139. //-----------------------------------------------------------------------------
  3140. void CBaseFileSystem::FileTimeToString( char *pString, int maxCharsIncludingTerminator, long fileTime )
  3141. {
  3142. if ( IsX360() )
  3143. {
  3144. char szTemp[ 256 ];
  3145. time_t time = fileTime;
  3146. V_strncpy( szTemp, ctime( &time ), sizeof( szTemp ) );
  3147. char *pFinalColon = Q_strrchr( szTemp, ':' );
  3148. if ( pFinalColon )
  3149. *pFinalColon = '\0';
  3150. // Clip off the day of the week
  3151. V_strncpy( pString, szTemp + 4, maxCharsIncludingTerminator );
  3152. }
  3153. else
  3154. {
  3155. time_t time = fileTime;
  3156. V_strncpy( pString, ctime( &time ), maxCharsIncludingTerminator );
  3157. // We see a linefeed at the end of these strings...if there is one, gobble it up
  3158. int len = V_strlen( pString );
  3159. if ( pString[ len - 1 ] == '\n' )
  3160. {
  3161. pString[ len - 1 ] = '\0';
  3162. }
  3163. pString[maxCharsIncludingTerminator-1] = '\0';
  3164. }
  3165. }
  3166. //-----------------------------------------------------------------------------
  3167. // Purpose:
  3168. // Input : *pFileName -
  3169. // Output : Returns true on success, false on failure.
  3170. //-----------------------------------------------------------------------------
  3171. bool CBaseFileSystem::FileExists( const char *pFileName, const char *pPathID )
  3172. {
  3173. VPROF_BUDGET( "CBaseFileSystem::FileExists", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  3174. CHECK_DOUBLE_SLASHES( pFileName );
  3175. FileHandle_t h = Open( pFileName, "rb", pPathID );
  3176. if ( h )
  3177. {
  3178. Close(h);
  3179. return true;
  3180. }
  3181. return false;
  3182. }
  3183. bool CBaseFileSystem::IsFileWritable( char const *pFileName, char const *pPathID /*=0*/ )
  3184. {
  3185. CHECK_DOUBLE_SLASHES( pFileName );
  3186. struct _stat buf;
  3187. char tempPathID[MAX_PATH];
  3188. ParsePathID( pFileName, pPathID, tempPathID );
  3189. if ( Q_IsAbsolutePath( pFileName ) )
  3190. {
  3191. if( FS_stat( pFileName, &buf ) != -1 )
  3192. {
  3193. #ifdef WIN32
  3194. if( buf.st_mode & _S_IWRITE )
  3195. #elif LINUX
  3196. if( buf.st_mode & S_IWRITE )
  3197. #else
  3198. if( buf.st_mode & S_IWRITE )
  3199. #endif
  3200. {
  3201. return true;
  3202. }
  3203. }
  3204. return false;
  3205. }
  3206. CSearchPathsIterator iter( this, &pFileName, pPathID, FILTER_CULLPACK );
  3207. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  3208. {
  3209. char pTmpFileName[ MAX_FILEPATH ];
  3210. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), pFileName );
  3211. Q_FixSlashes( pTmpFileName );
  3212. if ( FS_stat( pTmpFileName, &buf ) != -1 )
  3213. {
  3214. #ifdef WIN32
  3215. if ( buf.st_mode & _S_IWRITE )
  3216. #elif LINUX
  3217. if ( buf.st_mode & S_IWRITE )
  3218. #else
  3219. if ( buf.st_mode & S_IWRITE )
  3220. #endif
  3221. {
  3222. return true;
  3223. }
  3224. }
  3225. }
  3226. return false;
  3227. }
  3228. bool CBaseFileSystem::SetFileWritable( char const *pFileName, bool writable, const char *pPathID /*= 0*/ )
  3229. {
  3230. CHECK_DOUBLE_SLASHES( pFileName );
  3231. #ifdef _WIN32
  3232. int pmode = writable ? ( _S_IWRITE | _S_IREAD ) : ( _S_IREAD );
  3233. #else
  3234. int pmode = writable ? ( S_IWRITE | S_IREAD ) : ( S_IREAD );
  3235. #endif
  3236. char tempPathID[MAX_PATH];
  3237. ParsePathID( pFileName, pPathID, tempPathID );
  3238. if ( Q_IsAbsolutePath( pFileName ) )
  3239. {
  3240. return ( FS_chmod( pFileName, pmode ) == 0 );
  3241. }
  3242. CSearchPathsIterator iter( this, &pFileName, pPathID, FILTER_CULLPACK );
  3243. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  3244. {
  3245. char pTmpFileName[ MAX_FILEPATH ];
  3246. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), pFileName );
  3247. Q_FixSlashes( pTmpFileName );
  3248. if ( FS_chmod( pTmpFileName, pmode ) == 0 )
  3249. {
  3250. return true;
  3251. }
  3252. }
  3253. // Failure
  3254. return false;
  3255. }
  3256. //-----------------------------------------------------------------------------
  3257. // Purpose:
  3258. // Input : *pFileName -
  3259. // Output : Returns true on success, false on failure.
  3260. //-----------------------------------------------------------------------------
  3261. bool CBaseFileSystem::IsDirectory( const char *pFileName, const char *pathID )
  3262. {
  3263. CHECK_DOUBLE_SLASHES( pFileName );
  3264. // Allow for UNC-type syntax to specify the path ID.
  3265. struct _stat buf;
  3266. char pTempBuf[MAX_PATH];
  3267. Q_strncpy( pTempBuf, pFileName, sizeof(pTempBuf) );
  3268. Q_StripTrailingSlash( pTempBuf );
  3269. pFileName = pTempBuf;
  3270. char tempPathID[MAX_PATH];
  3271. ParsePathID( pFileName, pathID, tempPathID );
  3272. if ( Q_IsAbsolutePath( pFileName ) )
  3273. {
  3274. if ( FS_stat( pFileName, &buf ) != -1 )
  3275. {
  3276. if ( buf.st_mode & _S_IFDIR )
  3277. return true;
  3278. }
  3279. return false;
  3280. }
  3281. CSearchPathsIterator iter( this, &pFileName, pathID, FILTER_CULLPACK );
  3282. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  3283. {
  3284. #ifdef SUPPORT_PACKED_STORE
  3285. if ( pSearchPath->GetPackedStore() )
  3286. {
  3287. CUtlStringList outDir, outFile;
  3288. pSearchPath->GetPackedStore()->GetFileAndDirLists( outDir, outFile, false );
  3289. FOR_EACH_VEC( outDir, i )
  3290. {
  3291. if ( !Q_stricmp( outDir[i], pFileName ) )
  3292. return true;
  3293. }
  3294. }
  3295. else
  3296. #endif // SUPPORT_PACKED_STORE
  3297. {
  3298. char pTmpFileName[ MAX_FILEPATH ];
  3299. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), pFileName );
  3300. Q_FixSlashes( pTmpFileName );
  3301. if ( FS_stat( pTmpFileName, &buf ) != -1 )
  3302. {
  3303. if ( buf.st_mode & _S_IFDIR )
  3304. return true;
  3305. }
  3306. }
  3307. }
  3308. return false;
  3309. }
  3310. //-----------------------------------------------------------------------------
  3311. // Purpose:
  3312. // Input : *path -
  3313. //-----------------------------------------------------------------------------
  3314. void CBaseFileSystem::CreateDirHierarchy( const char *pRelativePathT, const char *pathID )
  3315. {
  3316. // Allow for UNC-type syntax to specify the path ID.
  3317. char tempPathID[MAX_PATH];
  3318. ParsePathID(pRelativePathT, pathID, tempPathID); // use the original path param to preserve "//"
  3319. char pRelativePathBuff[ MAX_PATH ];
  3320. const char *pRelativePath = pRelativePathBuff;
  3321. FixUpPath ( pRelativePathT, pRelativePathBuff, sizeof( pRelativePathBuff ) );
  3322. CHECK_DOUBLE_SLASHES( pRelativePath );
  3323. char szScratchFileName[MAX_PATH];
  3324. if ( !Q_IsAbsolutePath( pRelativePath ) )
  3325. {
  3326. Assert( pathID );
  3327. ComputeFullWritePath( szScratchFileName, sizeof( szScratchFileName ), pRelativePath, pathID );
  3328. }
  3329. else
  3330. {
  3331. Q_strncpy( szScratchFileName, pRelativePath, sizeof(szScratchFileName) );
  3332. }
  3333. int len = strlen( szScratchFileName ) + 1;
  3334. char *end = szScratchFileName + len;
  3335. char *s = szScratchFileName;
  3336. while( s < end )
  3337. {
  3338. if( *s == CORRECT_PATH_SEPARATOR && s != szScratchFileName && ( IsLinux() || *( s - 1 ) != ':' ) )
  3339. {
  3340. *s = '\0';
  3341. #if defined( _WIN32 )
  3342. _mkdir( szScratchFileName );
  3343. #elif defined( POSIX )
  3344. mkdir( szScratchFileName, S_IRWXU | S_IRGRP | S_IROTH );// owner has rwx, rest have r
  3345. #endif
  3346. *s = CORRECT_PATH_SEPARATOR;
  3347. }
  3348. s++;
  3349. }
  3350. #if defined( _WIN32 )
  3351. _mkdir( szScratchFileName );
  3352. #elif defined( POSIX )
  3353. mkdir( szScratchFileName, S_IRWXU | S_IRGRP | S_IROTH );
  3354. #endif
  3355. }
  3356. //-----------------------------------------------------------------------------
  3357. // Purpose:
  3358. // Input : *pWildCard -
  3359. // *pHandle -
  3360. // Output : const char
  3361. //-----------------------------------------------------------------------------
  3362. const char *CBaseFileSystem::FindFirstEx( const char *pWildCard, const char *pPathID, FileFindHandle_t *pHandle )
  3363. {
  3364. CHECK_DOUBLE_SLASHES( pWildCard );
  3365. return FindFirstHelper( pWildCard, pPathID, pHandle, NULL );
  3366. }
  3367. const char *CBaseFileSystem::FindFirstHelper( const char *pWildCardT, const char *pPathID, FileFindHandle_t *pHandle, int *pFoundStoreID )
  3368. {
  3369. VPROF_BUDGET( "CBaseFileSystem::FindFirst", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  3370. Assert(pWildCardT);
  3371. Assert(pHandle);
  3372. FileFindHandle_t hTmpHandle = m_FindData.AddToTail();
  3373. FindData_t *pFindData = &m_FindData[hTmpHandle];
  3374. Assert( pFindData );
  3375. if ( pPathID )
  3376. {
  3377. pFindData->m_FilterPathID = g_PathIDTable.AddString( pPathID );
  3378. }
  3379. char pWildCard[ MAX_PATH ];
  3380. FixUpPath ( pWildCardT, pWildCard, sizeof( pWildCard ) );
  3381. int maxlen = strlen( pWildCard ) + 1;
  3382. pFindData->wildCardString.AddMultipleToTail( maxlen );
  3383. Q_strncpy( pFindData->wildCardString.Base(), pWildCard, maxlen );
  3384. pFindData->findHandle = INVALID_HANDLE_VALUE;
  3385. if ( Q_IsAbsolutePath( pWildCard ) )
  3386. {
  3387. // Absolute path, cannot be VPK or Pak
  3388. pFindData->findHandle = FS_FindFirstFile( pWildCard, &pFindData->findData );
  3389. pFindData->currentSearchPathID = -1;
  3390. }
  3391. else
  3392. {
  3393. int c = m_SearchPaths.Count();
  3394. for( pFindData->currentSearchPathID = 0;
  3395. pFindData->currentSearchPathID < c;
  3396. pFindData->currentSearchPathID++ )
  3397. {
  3398. CSearchPath *pSearchPath = &m_SearchPaths[pFindData->currentSearchPathID];
  3399. if ( FilterByPathID( pSearchPath, pFindData->m_FilterPathID ) )
  3400. continue;
  3401. // already visited this path?
  3402. if ( pFindData->m_VisitedSearchPaths.MarkVisit( *pSearchPath ) )
  3403. continue;
  3404. // If this is a VPK or Pak file, build list of matches now and use helper
  3405. bool bIsVPKOrPak = false;
  3406. if ( pSearchPath->GetPackFile() )
  3407. {
  3408. // XXX(johns) This support didn't exist for a long time, and I'm now worried about various things
  3409. // looking for misc files suddenly finding them in the (untrusted) BSP and causing security
  3410. // nightmares. For now, restricting FindFirst() support to BSPs only when the BSP search path
  3411. // is explicitly requested, but this would otherwise work fine.
  3412. if ( !pPathID || V_strcmp( pPathID, "BSP" ) != 0 )
  3413. {
  3414. continue;
  3415. }
  3416. Assert( pFindData->m_dirMatchesFromVPKOrPak.Count() == 0 );
  3417. Assert( pFindData->m_fileMatchesFromVPKOrPak.Count() == 0 );
  3418. pSearchPath->GetPackFile()->GetFileAndDirLists( pWildCard, pFindData->m_dirMatchesFromVPKOrPak, pFindData->m_fileMatchesFromVPKOrPak, true );
  3419. bIsVPKOrPak = true;
  3420. }
  3421. #ifdef SUPPORT_PACKED_STORE
  3422. if ( pSearchPath->GetPackedStore() )
  3423. {
  3424. Assert( pFindData->m_dirMatchesFromVPKOrPak.Count() == 0 );
  3425. Assert( pFindData->m_fileMatchesFromVPKOrPak.Count() == 0 );
  3426. pSearchPath->GetPackedStore()->GetFileAndDirLists( pWildCard, pFindData->m_dirMatchesFromVPKOrPak, pFindData->m_fileMatchesFromVPKOrPak, true );
  3427. bIsVPKOrPak = true;
  3428. }
  3429. #endif
  3430. if ( bIsVPKOrPak )
  3431. {
  3432. if ( FindNextFileInVPKOrPakHelper( pFindData ) )
  3433. {
  3434. // Remember that we visited this file already.
  3435. pFindData->m_VisitedFiles.Insert( pFindData->findData.cFileName, 0 );
  3436. *pHandle = hTmpHandle;
  3437. return pFindData->findData.cFileName;
  3438. }
  3439. continue;
  3440. }
  3441. // Otherwise, raw FS find for relative path
  3442. char pTmpFileName[ MAX_FILEPATH ];
  3443. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), pFindData->wildCardString.Base() );
  3444. Q_FixSlashes( pTmpFileName );
  3445. pFindData->findHandle = FS_FindFirstFile( pTmpFileName, &pFindData->findData );
  3446. pFindData->m_CurrentStoreID = pSearchPath->m_storeId;
  3447. if( pFindData->findHandle != INVALID_HANDLE_VALUE )
  3448. break;
  3449. }
  3450. }
  3451. // We have a result from the filesystem
  3452. if( pFindData->findHandle != INVALID_HANDLE_VALUE )
  3453. {
  3454. // Remember that we visited this file already.
  3455. pFindData->m_VisitedFiles.Insert( pFindData->findData.cFileName, 0 );
  3456. if ( pFoundStoreID )
  3457. *pFoundStoreID = pFindData->m_CurrentStoreID;
  3458. *pHandle = hTmpHandle;
  3459. return pFindData->findData.cFileName;
  3460. }
  3461. // Handle failure here
  3462. pFindData = 0;
  3463. m_FindData.Remove(hTmpHandle);
  3464. *pHandle = -1;
  3465. return NULL;
  3466. }
  3467. const char *CBaseFileSystem::FindFirst( const char *pWildCard, FileFindHandle_t *pHandle )
  3468. {
  3469. return FindFirstEx( pWildCard, NULL, pHandle );
  3470. }
  3471. // Get the next file, trucking through the path. . don't check for duplicates.
  3472. bool CBaseFileSystem::FindNextFileHelper( FindData_t *pFindData, int *pFoundStoreID )
  3473. {
  3474. // Try the same search path that we were already searching on.
  3475. if( FS_FindNextFile( pFindData->findHandle, &pFindData->findData ) )
  3476. {
  3477. if ( pFoundStoreID )
  3478. *pFoundStoreID = pFindData->m_CurrentStoreID;
  3479. return true;
  3480. }
  3481. if ( FindNextFileInVPKOrPakHelper( pFindData ) )
  3482. return true;
  3483. // This happens when we searched a full path
  3484. if ( pFindData->currentSearchPathID < 0 )
  3485. return false;
  3486. pFindData->currentSearchPathID++;
  3487. if ( pFindData->findHandle != INVALID_HANDLE_VALUE )
  3488. {
  3489. FS_FindClose( pFindData->findHandle );
  3490. }
  3491. pFindData->findHandle = INVALID_HANDLE_VALUE;
  3492. int c = m_SearchPaths.Count();
  3493. for( ; pFindData->currentSearchPathID < c; ++pFindData->currentSearchPathID )
  3494. {
  3495. CSearchPath *pSearchPath = &m_SearchPaths[pFindData->currentSearchPathID];
  3496. if ( FilterByPathID( pSearchPath, pFindData->m_FilterPathID ) )
  3497. continue;
  3498. // already visited this path
  3499. if ( pFindData->m_VisitedSearchPaths.MarkVisit( *pSearchPath ) )
  3500. continue;
  3501. if ( pSearchPath->GetPackFile() )
  3502. {
  3503. // XXX(johns) This support didn't exist for a long time, and I'm now worried about various things
  3504. // looking for misc files suddenly finding them in the (untrusted) BSP and causing security
  3505. // nightmares. For now, restricting FindFirst() support to BSPs only when the BSP search path
  3506. // is explicitly requested, but this would otherwise work fine.
  3507. if ( !pFindData->m_FilterPathID || V_strcmp( g_PathIDTable.String( pFindData->m_FilterPathID ), "BSP" ) != 0 )
  3508. {
  3509. continue;
  3510. }
  3511. Assert( pFindData->m_dirMatchesFromVPKOrPak.Count() == 0 );
  3512. Assert( pFindData->m_fileMatchesFromVPKOrPak.Count() == 0 );
  3513. pSearchPath->GetPackFile()->GetFileAndDirLists( pFindData->wildCardString.Base(), pFindData->m_dirMatchesFromVPKOrPak, pFindData->m_fileMatchesFromVPKOrPak, true );
  3514. if ( FindNextFileInVPKOrPakHelper( pFindData ) )
  3515. return true;
  3516. continue;
  3517. }
  3518. #ifdef SUPPORT_PACKED_STORE
  3519. if ( pSearchPath->GetPackedStore() )
  3520. {
  3521. Assert( pFindData->m_dirMatchesFromVPKOrPak.Count() == 0 );
  3522. Assert( pFindData->m_fileMatchesFromVPKOrPak.Count() == 0 );
  3523. pSearchPath->GetPackedStore()->GetFileAndDirLists( pFindData->wildCardString.Base(), pFindData->m_dirMatchesFromVPKOrPak, pFindData->m_fileMatchesFromVPKOrPak, true );
  3524. if ( FindNextFileInVPKOrPakHelper( pFindData ) )
  3525. return true;
  3526. continue;
  3527. }
  3528. #endif
  3529. char pTmpFileName[ MAX_FILEPATH ];
  3530. Q_snprintf( pTmpFileName, sizeof( pTmpFileName ), "%s%s", pSearchPath->GetPathString(), pFindData->wildCardString.Base() );
  3531. Q_FixSlashes( pTmpFileName );
  3532. pFindData->findHandle = FS_FindFirstFile( pTmpFileName, &pFindData->findData );
  3533. pFindData->m_CurrentStoreID = pSearchPath->m_storeId;
  3534. if( pFindData->findHandle != INVALID_HANDLE_VALUE )
  3535. {
  3536. if ( pFoundStoreID )
  3537. *pFoundStoreID = pFindData->m_CurrentStoreID;
  3538. return true;
  3539. }
  3540. }
  3541. return false;
  3542. }
  3543. bool CBaseFileSystem::FindNextFileInVPKOrPakHelper( FindData_t *pFindData )
  3544. {
  3545. // Return the next one from the list of VPK matches if there is one
  3546. if ( pFindData->m_fileMatchesFromVPKOrPak.Count() > 0 )
  3547. {
  3548. V_strncpy( pFindData->findData.cFileName, V_UnqualifiedFileName( pFindData->m_fileMatchesFromVPKOrPak[0] ), sizeof( pFindData->findData.cFileName ) );
  3549. pFindData->findData.dwFileAttributes = 0;
  3550. delete pFindData->m_fileMatchesFromVPKOrPak.Head();
  3551. pFindData->m_fileMatchesFromVPKOrPak.RemoveMultipleFromHead( 1 );
  3552. return true;
  3553. }
  3554. // Return the next one from the list of VPK matches if there is one
  3555. if ( pFindData->m_dirMatchesFromVPKOrPak.Count() > 0 )
  3556. {
  3557. V_strncpy( pFindData->findData.cFileName, V_UnqualifiedFileName( pFindData->m_dirMatchesFromVPKOrPak[0] ), sizeof( pFindData->findData.cFileName ) );
  3558. pFindData->findData.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  3559. delete pFindData->m_dirMatchesFromVPKOrPak.Head();
  3560. pFindData->m_dirMatchesFromVPKOrPak.RemoveMultipleFromHead( 1 );
  3561. return true;
  3562. }
  3563. return false;
  3564. }
  3565. //-----------------------------------------------------------------------------
  3566. // Purpose:
  3567. // Input : handle -
  3568. // Output : const char
  3569. //-----------------------------------------------------------------------------
  3570. const char *CBaseFileSystem::FindNext( FileFindHandle_t handle )
  3571. {
  3572. VPROF_BUDGET( "CBaseFileSystem::FindNext", VPROF_BUDGETGROUP_OTHER_FILESYSTEM );
  3573. FindData_t *pFindData = &m_FindData[handle];
  3574. while( 1 )
  3575. {
  3576. if( FindNextFileHelper( pFindData, NULL ) )
  3577. {
  3578. if ( pFindData->m_VisitedFiles.Find( pFindData->findData.cFileName ) == -1 )
  3579. {
  3580. pFindData->m_VisitedFiles.Insert( pFindData->findData.cFileName, 0 );
  3581. return pFindData->findData.cFileName;
  3582. }
  3583. }
  3584. else
  3585. {
  3586. return NULL;
  3587. }
  3588. }
  3589. }
  3590. //-----------------------------------------------------------------------------
  3591. // Purpose:
  3592. // Input : handle -
  3593. // Output : Returns true on success, false on failure.
  3594. //-----------------------------------------------------------------------------
  3595. bool CBaseFileSystem::FindIsDirectory( FileFindHandle_t handle )
  3596. {
  3597. FindData_t *pFindData = &m_FindData[handle];
  3598. return !!( pFindData->findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY );
  3599. }
  3600. //-----------------------------------------------------------------------------
  3601. // Purpose:
  3602. // Input : handle -
  3603. //-----------------------------------------------------------------------------
  3604. void CBaseFileSystem::FindClose( FileFindHandle_t handle )
  3605. {
  3606. if ( ( handle < 0 ) || ( !m_FindData.IsInList( handle ) ) )
  3607. return;
  3608. FindData_t *pFindData = &m_FindData[handle];
  3609. Assert(pFindData);
  3610. if ( pFindData->findHandle != INVALID_HANDLE_VALUE)
  3611. {
  3612. FS_FindClose( pFindData->findHandle );
  3613. }
  3614. pFindData->findHandle = INVALID_HANDLE_VALUE;
  3615. pFindData->wildCardString.Purge();
  3616. pFindData->m_fileMatchesFromVPKOrPak.PurgeAndDeleteElements();
  3617. pFindData->m_dirMatchesFromVPKOrPak.PurgeAndDeleteElements();
  3618. m_FindData.Remove( handle );
  3619. }
  3620. //-----------------------------------------------------------------------------
  3621. // Purpose:
  3622. // Input : *pFileName -
  3623. //-----------------------------------------------------------------------------
  3624. void CBaseFileSystem::GetLocalCopy( const char *pFileName )
  3625. {
  3626. // do nothing. . everything is local.
  3627. }
  3628. //-----------------------------------------------------------------------------
  3629. // Purpose: Fixes up Path names. Will fix up platform specific slashes, remove
  3630. // any ../ or ./, fix //s, and lowercase anything under the directory
  3631. // that the game is installed to. We expect all files to be lower cased
  3632. // there - especially on Linux (where case sensitivity is the norm).
  3633. //
  3634. // Input : *pFileName - Original name to convert
  3635. // *pFixedUpFileName - a buffer to put the converted filename into
  3636. // sizeFixedUpFileName - the size of the above buffer in chars
  3637. //-----------------------------------------------------------------------------
  3638. bool CBaseFileSystem::FixUpPath( const char *pFileName, char *pFixedUpFileName, int sizeFixedUpFileName )
  3639. {
  3640. // If appropriate fixes up the filename to ensure that it's handled properly by the system.
  3641. //
  3642. V_strncpy( pFixedUpFileName, pFileName, sizeFixedUpFileName );
  3643. V_FixSlashes( pFixedUpFileName, CORRECT_PATH_SEPARATOR );
  3644. // V_RemoveDotSlashes( pFixedUpFileName, CORRECT_PATH_SEPARATOR, true );
  3645. V_FixDoubleSlashes( pFixedUpFileName );
  3646. if ( !V_IsAbsolutePath( pFixedUpFileName ) )
  3647. {
  3648. V_strlower( pFixedUpFileName );
  3649. }
  3650. else
  3651. {
  3652. // Get the BASE_PATH, skip past - if necessary, and lowercase the rest
  3653. // Not just yet...
  3654. int iBaseLength = 0;
  3655. char pBaseDir[MAX_PATH];
  3656. // Need to get "BASE_PATH" from the filesystem paths, and then check this name against it.
  3657. //
  3658. iBaseLength = GetSearchPath( "BASE_PATH", true, pBaseDir, sizeof( pBaseDir ) );
  3659. if ( iBaseLength )
  3660. {
  3661. // If the first part of the pFixedUpFilename is pBaseDir
  3662. // then lowercase the part after that.
  3663. if ( *pBaseDir && (iBaseLength+1 < V_strlen( pFixedUpFileName ) ) && (0 != V_strncmp( pBaseDir, pFixedUpFileName, iBaseLength ) ) )
  3664. {
  3665. V_strlower( &pFixedUpFileName[iBaseLength-1] );
  3666. }
  3667. }
  3668. }
  3669. // Msg("CBaseFileSystem::FixUpPath: Converted %s to %s\n", pFileName, pFixedUpFileName); // too noisy
  3670. #ifdef NEVER // Useful if you're trying to see why your file may not be found (if you have a mixed case file)
  3671. if (strncmp(pFixedUpFileName, pFileName, 256))
  3672. {
  3673. printf("FixUpPath->Converting %s to %s\n",pFileName, pFixedUpFileName);
  3674. }
  3675. #endif // NEVER
  3676. return true;
  3677. }
  3678. //-----------------------------------------------------------------------------
  3679. // Converts a partial path into a full path
  3680. // Relative paths that are pack based are returned as an absolute path .../zip?.zip/foo
  3681. // A pack absolute path can be sent back in for opening, and the file will be properly
  3682. // detected as pack based and mounted inside the pack.
  3683. //-----------------------------------------------------------------------------
  3684. const char *CBaseFileSystem::RelativePathToFullPath( const char *pFileName, const char *pPathID, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars, PathTypeFilter_t pathFilter, PathTypeQuery_t *pPathType )
  3685. {
  3686. CHECK_DOUBLE_SLASHES( pFileName );
  3687. struct _stat buf;
  3688. if ( pPathType )
  3689. {
  3690. *pPathType = PATH_IS_NORMAL;
  3691. }
  3692. // Convert filename to lowercase. All files in the
  3693. // game logical filesystem must be accessed by lowercase name
  3694. char szLowercaseFilename[ MAX_PATH ];
  3695. FixUpPath( pFileName, szLowercaseFilename, sizeof( szLowercaseFilename ) );
  3696. pFileName = szLowercaseFilename;
  3697. // Fill in the default in case it's not found...
  3698. V_strncpy( pDest, pFileName, maxLenInChars );
  3699. // @FD This is arbitrary and seems broken. If the caller needs this filter, they should
  3700. // request it with the flag themselves. As it is, I cannot search all the file paths
  3701. // for a file using this function because there is no option that says, "No, really, I
  3702. // mean ALL SEARCH PATHS." The current problem I'm trying to fix is that sounds are not
  3703. // working if they are in the BSP. I wrote code that assumed that I could just ask for
  3704. // the absolute path of a file, since we are able to open files with these absolute
  3705. // filenames, and that each particular filesystem call wouldn't have its own individual
  3706. // quirks.
  3707. // if ( IsPC() && pathFilter == FILTER_NONE )
  3708. // {
  3709. // // X360TBD: PC legacy behavior never returned pack paths
  3710. // // do legacy behavior to ensure naive callers don't break
  3711. // pathFilter = FILTER_CULLPACK;
  3712. // }
  3713. CSearchPathsIterator iter( this, &pFileName, pPathID, pathFilter );
  3714. for ( CSearchPath *pSearchPath = iter.GetFirst(); pSearchPath != NULL; pSearchPath = iter.GetNext() )
  3715. {
  3716. CPackFile *pPack = pSearchPath->GetPackFile();
  3717. if ( pPack )
  3718. {
  3719. if ( pPack->ContainsFile( pFileName ) )
  3720. {
  3721. if ( pPathType )
  3722. {
  3723. if ( pPack->m_bIsMapPath )
  3724. {
  3725. *pPathType |= PATH_IS_MAPPACKFILE;
  3726. }
  3727. else
  3728. {
  3729. *pPathType |= PATH_IS_PACKFILE;
  3730. }
  3731. if ( pSearchPath->m_bIsRemotePath )
  3732. {
  3733. *pPathType |= PATH_IS_REMOTE;
  3734. }
  3735. }
  3736. // form an encoded absolute path that can be decoded by our FS as pak based
  3737. const char *pszPackName = pPack->m_ZipName.String();
  3738. int len = V_strlen( pszPackName );
  3739. int nTotalLen = len + 1 + V_strlen( pFileName );
  3740. if ( nTotalLen >= maxLenInChars )
  3741. {
  3742. ::Warning( "File %s was found in %s, but resulting abs filename won't fit in callers buffer of %d bytes\n",
  3743. pFileName, pszPackName, maxLenInChars );
  3744. Assert( false );
  3745. return NULL;
  3746. }
  3747. V_strncpy( pDest, pszPackName, maxLenInChars );
  3748. V_AppendSlash( pDest, maxLenInChars );
  3749. V_strncat( pDest, pFileName, maxLenInChars );
  3750. Assert( V_strlen( pDest ) == nTotalLen );
  3751. return pDest;
  3752. }
  3753. continue;
  3754. }
  3755. // Found in VPK?
  3756. #ifdef SUPPORT_PACKED_STORE
  3757. CPackedStore *pVPK = pSearchPath->GetPackedStore();
  3758. if ( pVPK )
  3759. {
  3760. CPackedStoreFileHandle vpkHandle = pVPK->OpenFile( pFileName );
  3761. if ( vpkHandle )
  3762. {
  3763. const char *pszVpkName = vpkHandle.m_pOwner->FullPathName();
  3764. Assert( V_GetFileExtension( pszVpkName ) != NULL );
  3765. int len = V_strlen( pszVpkName );
  3766. int nTotalLen = len + 1 + V_strlen( pFileName );
  3767. if ( nTotalLen >= maxLenInChars )
  3768. {
  3769. ::Warning( "File %s was found in %s, but resulting abs filename won't fit in callers buffer of %d bytes\n",
  3770. pFileName, pszVpkName, maxLenInChars );
  3771. Assert( false );
  3772. return NULL;
  3773. }
  3774. V_strncpy( pDest, pszVpkName, maxLenInChars );
  3775. V_AppendSlash( pDest, maxLenInChars );
  3776. V_strncat( pDest, pFileName, maxLenInChars );
  3777. V_FixSlashes( pDest );
  3778. return pDest;
  3779. }
  3780. continue;
  3781. }
  3782. #endif
  3783. char pTmpFileName[ MAX_FILEPATH ];
  3784. V_sprintf_safe( pTmpFileName, "%s%s", pSearchPath->GetPathString(), pFileName );
  3785. V_FixSlashes( pTmpFileName );
  3786. if ( FS_stat( pTmpFileName, &buf ) != -1 )
  3787. {
  3788. V_strncpy( pDest, pTmpFileName, maxLenInChars );
  3789. if ( pPathType && pSearchPath->m_bIsRemotePath )
  3790. {
  3791. *pPathType |= PATH_IS_REMOTE;
  3792. }
  3793. return pDest;
  3794. }
  3795. }
  3796. // not found
  3797. return NULL;
  3798. }
  3799. const char *CBaseFileSystem::GetLocalPath( const char *pFileName, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars )
  3800. {
  3801. CHECK_DOUBLE_SLASHES( pFileName );
  3802. return RelativePathToFullPath( pFileName, NULL, pDest, maxLenInChars );
  3803. }
  3804. //-----------------------------------------------------------------------------
  3805. // Returns true on success, otherwise false if it can't be resolved
  3806. //-----------------------------------------------------------------------------
  3807. bool CBaseFileSystem::FullPathToRelativePathEx( const char *pFullPath, const char *pPathId, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars )
  3808. {
  3809. CHECK_DOUBLE_SLASHES( pFullPath );
  3810. int nInlen = V_strlen( pFullPath );
  3811. if ( nInlen <= 0 )
  3812. {
  3813. pDest[ 0 ] = 0;
  3814. return false;
  3815. }
  3816. V_strncpy( pDest, pFullPath, maxLenInChars );
  3817. char pInPath[ MAX_FILEPATH ];
  3818. V_strcpy_safe( pInPath, pFullPath );
  3819. #ifdef _WIN32
  3820. V_strlower( pInPath );
  3821. #endif
  3822. V_FixSlashes( pInPath );
  3823. CUtlSymbol lookup;
  3824. if ( pPathId )
  3825. {
  3826. lookup = g_PathIDTable.AddString( pPathId );
  3827. }
  3828. int c = m_SearchPaths.Count();
  3829. for( int i = 0; i < c; i++ )
  3830. {
  3831. // FIXME: Should this work with embedded pak files?
  3832. if ( m_SearchPaths[i].GetPackFile() && m_SearchPaths[i].GetPackFile()->m_bIsMapPath )
  3833. continue;
  3834. // Skip paths that are not on the specified search path
  3835. if ( FilterByPathID( &m_SearchPaths[i], lookup ) )
  3836. continue;
  3837. char pSearchBase[ MAX_FILEPATH ];
  3838. V_strncpy( pSearchBase, m_SearchPaths[i].GetPathString(), sizeof( pSearchBase ) );
  3839. #ifdef _WIN32
  3840. V_strlower( pSearchBase );
  3841. #endif
  3842. V_FixSlashes( pSearchBase );
  3843. int nSearchLen = V_strlen( pSearchBase );
  3844. if ( V_strnicmp( pSearchBase, pInPath, nSearchLen ) )
  3845. continue;
  3846. V_strncpy( pDest, &pInPath[ nSearchLen ], maxLenInChars );
  3847. return true;
  3848. }
  3849. return false;
  3850. }
  3851. //-----------------------------------------------------------------------------
  3852. // Obsolete version
  3853. //-----------------------------------------------------------------------------
  3854. bool CBaseFileSystem::FullPathToRelativePath( const char *pFullPath, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars )
  3855. {
  3856. return FullPathToRelativePathEx( pFullPath, NULL, pDest, maxLenInChars );
  3857. }
  3858. //-----------------------------------------------------------------------------
  3859. // Returns true on successfully retrieve case-sensitive full path, otherwise false
  3860. //-----------------------------------------------------------------------------
  3861. bool CBaseFileSystem::GetCaseCorrectFullPath_Ptr( const char *pFullPath, OUT_Z_CAP(maxLenInChars) char *pDest, int maxLenInChars )
  3862. {
  3863. CHECK_DOUBLE_SLASHES( pFullPath );
  3864. #ifndef _WIN32
  3865. V_strncpy( pDest, pFullPath, maxLenInChars );
  3866. return true;
  3867. #else
  3868. char szCurrentDir[MAX_PATH];
  3869. V_strcpy_safe( szCurrentDir, pFullPath );
  3870. V_StripLastDir( szCurrentDir, sizeof( szCurrentDir ) );
  3871. CUtlString strSearchName = pFullPath;
  3872. strSearchName.StripTrailingSlash();
  3873. strSearchName = strSearchName.Slice( V_strlen( szCurrentDir ), strSearchName.Length() );
  3874. CUtlString strSearchPath = szCurrentDir;
  3875. strSearchPath += "*";
  3876. CUtlString strFoundCaseCorrectName;
  3877. FileFindHandle_t findHandle;
  3878. const char *pszCaseCorrectName = FindFirst( strSearchPath.Get(), &findHandle );
  3879. while ( pszCaseCorrectName )
  3880. {
  3881. if ( V_stricmp( strSearchName.String(), pszCaseCorrectName ) == 0 )
  3882. {
  3883. strFoundCaseCorrectName = pszCaseCorrectName;
  3884. break;
  3885. }
  3886. pszCaseCorrectName = FindNext( findHandle );
  3887. }
  3888. FindClose( findHandle );
  3889. // Not found
  3890. if ( strFoundCaseCorrectName.IsEmpty() )
  3891. {
  3892. V_strncpy( pDest, pFullPath, maxLenInChars );
  3893. return false;
  3894. }
  3895. // If drive path, no need to recurse anymore.
  3896. bool bResult = false;
  3897. char szDir[MAX_PATH];
  3898. if ( !IsDirectory( szCurrentDir, NULL ) )
  3899. {
  3900. V_strupr( szCurrentDir );
  3901. V_strncpy( szDir, szCurrentDir, sizeof( szDir ) );
  3902. bResult = true;
  3903. }
  3904. else
  3905. {
  3906. bResult = GetCaseCorrectFullPath( szCurrentDir, szDir );
  3907. }
  3908. // connect the current path with the case-correct dir/file name
  3909. V_MakeAbsolutePath( pDest, maxLenInChars, strFoundCaseCorrectName.String(), szDir );
  3910. return bResult;
  3911. #endif // _WIN32
  3912. }
  3913. //-----------------------------------------------------------------------------
  3914. // Deletes a file
  3915. //-----------------------------------------------------------------------------
  3916. void CBaseFileSystem::RemoveFile( char const* pRelativePath, const char *pathID )
  3917. {
  3918. CHECK_DOUBLE_SLASHES( pRelativePath );
  3919. // Allow for UNC-type syntax to specify the path ID.
  3920. char tempPathID[MAX_PATH];
  3921. ParsePathID( pRelativePath, pathID, tempPathID );
  3922. Assert( pathID || !IsX360() );
  3923. // Opening for write or append uses Write Path
  3924. char szScratchFileName[MAX_PATH];
  3925. if ( Q_IsAbsolutePath( pRelativePath ) )
  3926. {
  3927. Q_strncpy( szScratchFileName, pRelativePath, sizeof( szScratchFileName ) );
  3928. }
  3929. else
  3930. {
  3931. ComputeFullWritePath( szScratchFileName, sizeof( szScratchFileName ), pRelativePath, pathID );
  3932. }
  3933. int fail = unlink( szScratchFileName );
  3934. if ( fail != 0 )
  3935. {
  3936. Warning( FILESYSTEM_WARNING, "Unable to remove %s!\n", szScratchFileName );
  3937. }
  3938. }
  3939. //-----------------------------------------------------------------------------
  3940. // Renames a file
  3941. //-----------------------------------------------------------------------------
  3942. bool CBaseFileSystem::RenameFile( char const *pOldPath, char const *pNewPath, const char *pathID )
  3943. {
  3944. Assert( pOldPath && pNewPath );
  3945. CHECK_DOUBLE_SLASHES( pOldPath );
  3946. CHECK_DOUBLE_SLASHES( pNewPath );
  3947. // Allow for UNC-type syntax to specify the path ID.
  3948. char pPathIdCopy[MAX_PATH];
  3949. const char *pOldPathId = pathID;
  3950. if ( pathID )
  3951. {
  3952. Q_strncpy( pPathIdCopy, pathID, sizeof( pPathIdCopy ) );
  3953. pOldPathId = pPathIdCopy;
  3954. }
  3955. char tempOldPathID[MAX_PATH];
  3956. ParsePathID( pOldPath, pOldPathId, tempOldPathID );
  3957. Assert( pOldPathId );
  3958. // Allow for UNC-type syntax to specify the path ID.
  3959. char tempNewPathID[MAX_PATH];
  3960. ParsePathID( pNewPath, pathID, tempNewPathID );
  3961. Assert( pathID );
  3962. char pNewFileName[ MAX_PATH ];
  3963. char szScratchFileName[MAX_PATH];
  3964. // The source file may be in a fallback directory, so just resolve the actual path, don't assume pathid...
  3965. RelativePathToFullPath( pOldPath, pOldPathId, szScratchFileName, sizeof( szScratchFileName ) );
  3966. // Figure out the dest path
  3967. if ( !Q_IsAbsolutePath( pNewPath ) )
  3968. {
  3969. ComputeFullWritePath( pNewFileName, sizeof( pNewFileName ), pNewPath, pathID );
  3970. }
  3971. else
  3972. {
  3973. Q_strncpy( pNewFileName, pNewPath, sizeof(pNewFileName) );
  3974. }
  3975. // Make sure the directory exitsts, too
  3976. char pPathOnly[ MAX_PATH ];
  3977. Q_strncpy( pPathOnly, pNewFileName, sizeof( pPathOnly ) );
  3978. Q_StripFilename( pPathOnly );
  3979. CreateDirHierarchy( pPathOnly, pathID );
  3980. // Now copy the file over
  3981. int fail = rename( szScratchFileName, pNewFileName );
  3982. if (fail != 0)
  3983. {
  3984. Warning( FILESYSTEM_WARNING, "Unable to rename %s to %s!\n", szScratchFileName, pNewFileName );
  3985. return false;
  3986. }
  3987. return true;
  3988. }
  3989. //-----------------------------------------------------------------------------
  3990. // Purpose:
  3991. // Input : **ppdir -
  3992. //-----------------------------------------------------------------------------
  3993. bool CBaseFileSystem::GetCurrentDirectory( char* pDirectory, int maxlen )
  3994. {
  3995. #if defined( _WIN32 ) && !defined( _X360 )
  3996. if ( !::GetCurrentDirectoryA( maxlen, pDirectory ) )
  3997. #elif defined( POSIX ) || defined( _X360 )
  3998. if ( !getcwd( pDirectory, maxlen ) )
  3999. #endif
  4000. return false;
  4001. Q_FixSlashes(pDirectory);
  4002. // Strip the last slash
  4003. int len = strlen(pDirectory);
  4004. if ( pDirectory[ len-1 ] == CORRECT_PATH_SEPARATOR )
  4005. {
  4006. pDirectory[ len-1 ] = 0;
  4007. }
  4008. return true;
  4009. }
  4010. //-----------------------------------------------------------------------------
  4011. // Purpose:
  4012. // Input : pfnWarning - warning function callback
  4013. //-----------------------------------------------------------------------------
  4014. void CBaseFileSystem::SetWarningFunc( void (*pfnWarning)( const char *fmt, ... ) )
  4015. {
  4016. m_pfnWarning = pfnWarning;
  4017. }
  4018. //-----------------------------------------------------------------------------
  4019. // Purpose:
  4020. // Input : level -
  4021. //-----------------------------------------------------------------------------
  4022. void CBaseFileSystem::SetWarningLevel( FileWarningLevel_t level )
  4023. {
  4024. m_fwLevel = level;
  4025. }
  4026. const FileSystemStatistics *CBaseFileSystem::GetFilesystemStatistics()
  4027. {
  4028. return &m_Stats;
  4029. }
  4030. //-----------------------------------------------------------------------------
  4031. // Purpose:
  4032. // Input : level -
  4033. // *fmt -
  4034. // ... -
  4035. //-----------------------------------------------------------------------------
  4036. void CBaseFileSystem::Warning( FileWarningLevel_t level, const char *fmt, ... )
  4037. {
  4038. if ( level > m_fwLevel )
  4039. return;
  4040. if ( ( fs_warning_mode.GetInt() == 1 && !ThreadInMainThread() ) || ( fs_warning_mode.GetInt() == 2 && ThreadInMainThread() ) )
  4041. return;
  4042. va_list argptr;
  4043. char warningtext[ 4096 ];
  4044. va_start( argptr, fmt );
  4045. Q_vsnprintf( warningtext, sizeof( warningtext ), fmt, argptr );
  4046. va_end( argptr );
  4047. // Dump to stdio
  4048. printf( "%s", warningtext );
  4049. if ( m_pfnWarning )
  4050. {
  4051. (*m_pfnWarning)( warningtext );
  4052. }
  4053. else
  4054. {
  4055. #ifdef _WIN32
  4056. Plat_DebugString( warningtext );
  4057. #endif
  4058. }
  4059. }
  4060. //-----------------------------------------------------------------------------
  4061. // Purpose:
  4062. //-----------------------------------------------------------------------------
  4063. CBaseFileSystem::COpenedFile::COpenedFile( void )
  4064. {
  4065. m_pFile = NULL;
  4066. m_pName = NULL;
  4067. }
  4068. //-----------------------------------------------------------------------------
  4069. // Purpose:
  4070. //-----------------------------------------------------------------------------
  4071. CBaseFileSystem::COpenedFile::~COpenedFile( void )
  4072. {
  4073. delete[] m_pName;
  4074. }
  4075. //-----------------------------------------------------------------------------
  4076. // Purpose:
  4077. // Input : src -
  4078. //-----------------------------------------------------------------------------
  4079. CBaseFileSystem::COpenedFile::COpenedFile( const COpenedFile& src )
  4080. {
  4081. m_pFile = src.m_pFile;
  4082. if ( src.m_pName )
  4083. {
  4084. int len = strlen( src.m_pName ) + 1;
  4085. m_pName = new char[ len ];
  4086. Q_strncpy( m_pName, src.m_pName, len );
  4087. }
  4088. else
  4089. {
  4090. m_pName = NULL;
  4091. }
  4092. }
  4093. //-----------------------------------------------------------------------------
  4094. // Purpose:
  4095. // Input : src -
  4096. // Output : Returns true on success, false on failure.
  4097. //-----------------------------------------------------------------------------
  4098. bool CBaseFileSystem::COpenedFile::operator==( const CBaseFileSystem::COpenedFile& src ) const
  4099. {
  4100. return src.m_pFile == m_pFile;
  4101. }
  4102. //-----------------------------------------------------------------------------
  4103. // Purpose:
  4104. // Input : *name -
  4105. //-----------------------------------------------------------------------------
  4106. void CBaseFileSystem::COpenedFile::SetName( char const *name )
  4107. {
  4108. delete[] m_pName;
  4109. int len = strlen( name ) + 1;
  4110. m_pName = new char[ len ];
  4111. Q_strncpy( m_pName, name, len );
  4112. }
  4113. //-----------------------------------------------------------------------------
  4114. // Purpose:
  4115. // Output : char
  4116. //-----------------------------------------------------------------------------
  4117. char const *CBaseFileSystem::COpenedFile::GetName( void )
  4118. {
  4119. return m_pName ? m_pName : "???";
  4120. }
  4121. //-----------------------------------------------------------------------------
  4122. // Purpose:
  4123. //-----------------------------------------------------------------------------
  4124. CBaseFileSystem::CSearchPath::CSearchPath( void )
  4125. {
  4126. m_Path = g_PathIDTable.AddString( "" );
  4127. m_pDebugPath = "";
  4128. m_storeId = 0;
  4129. m_pPackFile = NULL;
  4130. m_pPathIDInfo = NULL;
  4131. m_bIsRemotePath = false;
  4132. m_pPackedStore = NULL;
  4133. m_bIsTrustedForPureServer = false;
  4134. }
  4135. const char *CBaseFileSystem::CSearchPath::GetDebugString() const
  4136. {
  4137. if ( GetPackFile() )
  4138. {
  4139. return GetPackFile()->m_ZipName;
  4140. }
  4141. #ifdef SUPPORT_PACKED_STORE
  4142. if ( GetPackedStore() )
  4143. {
  4144. return GetPackedStore()->FullPathName();
  4145. }
  4146. #endif
  4147. return GetPathString();
  4148. }
  4149. bool CBaseFileSystem::CSearchPath::IsMapPath() const
  4150. {
  4151. return GetPackFile()->m_bIsMapPath;
  4152. }
  4153. //-----------------------------------------------------------------------------
  4154. // Purpose:
  4155. //-----------------------------------------------------------------------------
  4156. CBaseFileSystem::CSearchPath::~CSearchPath( void )
  4157. {
  4158. if ( m_pPackFile )
  4159. {
  4160. m_pPackFile->Release();
  4161. }
  4162. if ( m_pPackedStore )
  4163. {
  4164. m_pPackedStore->Release();
  4165. }
  4166. }
  4167. //-----------------------------------------------------------------------------
  4168. // Purpose:
  4169. //-----------------------------------------------------------------------------
  4170. CBaseFileSystem::CSearchPath *CBaseFileSystem::CSearchPathsIterator::GetFirst()
  4171. {
  4172. if ( m_SearchPaths.Count() )
  4173. {
  4174. m_visits.Reset();
  4175. m_iCurrent = -1;
  4176. return GetNext();
  4177. }
  4178. return &m_EmptySearchPath;
  4179. }
  4180. //-----------------------------------------------------------------------------
  4181. // Purpose:
  4182. //-----------------------------------------------------------------------------
  4183. CBaseFileSystem::CSearchPath *CBaseFileSystem::CSearchPathsIterator::GetNext()
  4184. {
  4185. CSearchPath *pSearchPath = NULL;
  4186. for ( m_iCurrent++; m_iCurrent < m_SearchPaths.Count(); m_iCurrent++ )
  4187. {
  4188. pSearchPath = &m_SearchPaths[m_iCurrent];
  4189. if ( m_PathTypeFilter == FILTER_CULLPACK && pSearchPath->GetPackFile() )
  4190. continue;
  4191. if ( m_PathTypeFilter == FILTER_CULLNONPACK && !pSearchPath->GetPackFile() )
  4192. continue;
  4193. if ( CBaseFileSystem::FilterByPathID( pSearchPath, m_pathID ) )
  4194. continue;
  4195. // 360 can optionally ignore a local search path in dvddev mode
  4196. // ignoring a local search path falls through to its cloned remote path
  4197. // map paths are exempt from this exclusion logic
  4198. if ( IsX360() && ( m_DVDMode == DVDMODE_DEV ) && m_Filename[0] && !pSearchPath->m_bIsRemotePath )
  4199. {
  4200. bool bIsMapPath = pSearchPath->GetPackFile() && pSearchPath->GetPackFile()->m_bIsMapPath;
  4201. if ( !bIsMapPath )
  4202. {
  4203. bool bIgnorePath = false;
  4204. char szExcludePath[MAX_PATH];
  4205. char szFilename[MAX_PATH];
  4206. V_ComposeFileName( pSearchPath->GetPathString(), m_Filename, szFilename, sizeof( szFilename ) );
  4207. for ( int i = 0; i < m_ExcludePaths.Count(); i++ )
  4208. {
  4209. if ( g_pFullFileSystem->String( m_ExcludePaths[i], szExcludePath, sizeof( szExcludePath ) ) )
  4210. {
  4211. if ( !V_strnicmp( szFilename, szExcludePath, strlen( szExcludePath ) ) )
  4212. {
  4213. bIgnorePath = true;
  4214. break;
  4215. }
  4216. }
  4217. }
  4218. if ( bIgnorePath )
  4219. {
  4220. // filename matches exclusion path, skip it
  4221. continue;
  4222. }
  4223. }
  4224. }
  4225. if ( !m_visits.MarkVisit( *pSearchPath ) )
  4226. break;
  4227. }
  4228. if ( m_iCurrent < m_SearchPaths.Count() )
  4229. {
  4230. return pSearchPath;
  4231. }
  4232. return NULL;
  4233. }
  4234. void CBaseFileSystem::CSearchPathsIterator::CopySearchPaths( const CUtlVector<CSearchPath> &searchPaths )
  4235. {
  4236. m_SearchPaths = searchPaths;
  4237. for ( int i = 0; i < m_SearchPaths.Count(); i++ )
  4238. {
  4239. if ( m_SearchPaths[i].GetPackFile() )
  4240. {
  4241. m_SearchPaths[i].GetPackFile()->AddRef();
  4242. }
  4243. else if ( m_SearchPaths[i].GetPackedStore() )
  4244. {
  4245. m_SearchPaths[i].GetPackedStore()->AddRef();
  4246. }
  4247. }
  4248. }
  4249. //-----------------------------------------------------------------------------
  4250. // Purpose: Load/unload a DLL
  4251. //-----------------------------------------------------------------------------
  4252. CSysModule *CBaseFileSystem::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
  4253. {
  4254. CHECK_DOUBLE_SLASHES( pFileName );
  4255. LogFileAccess( pFileName );
  4256. if ( !pPathID )
  4257. {
  4258. pPathID = "EXECUTABLE_PATH"; // default to the bin dir
  4259. }
  4260. char tempPathID[ MAX_PATH ];
  4261. ParsePathID( pFileName, pPathID, tempPathID );
  4262. CUtlSymbol lookup = g_PathIDTable.AddString( pPathID );
  4263. // a pathID has been specified, find the first match in the path list
  4264. int c = m_SearchPaths.Count();
  4265. for ( int i = 0; i < c; i++ )
  4266. {
  4267. // pak files don't have modules
  4268. if ( m_SearchPaths[i].GetPackFile() )
  4269. continue;
  4270. if ( FilterByPathID( &m_SearchPaths[i], lookup ) )
  4271. continue;
  4272. Q_snprintf( tempPathID, sizeof(tempPathID), "%s%s", m_SearchPaths[i].GetPathString(), pFileName ); // append the path to this dir.
  4273. CSysModule *pModule = Sys_LoadModule( tempPathID );
  4274. if ( pModule )
  4275. {
  4276. // we found the binary in one of our search paths
  4277. return pModule;
  4278. }
  4279. }
  4280. // couldn't load it from any of the search paths, let LoadLibrary try
  4281. return Sys_LoadModule( pFileName );
  4282. }
  4283. //-----------------------------------------------------------------------------
  4284. // Purpose:
  4285. //-----------------------------------------------------------------------------
  4286. void CBaseFileSystem::UnloadModule( CSysModule *pModule )
  4287. {
  4288. Sys_UnloadModule( pModule );
  4289. }
  4290. //-----------------------------------------------------------------------------
  4291. // Purpose: Adds a filesystem logging function
  4292. //-----------------------------------------------------------------------------
  4293. void CBaseFileSystem::AddLoggingFunc( FileSystemLoggingFunc_t logFunc )
  4294. {
  4295. Assert(!m_LogFuncs.IsValidIndex(m_LogFuncs.Find(logFunc)));
  4296. m_LogFuncs.AddToTail(logFunc);
  4297. }
  4298. //-----------------------------------------------------------------------------
  4299. // Purpose: Removes a filesystem logging function
  4300. //-----------------------------------------------------------------------------
  4301. void CBaseFileSystem::RemoveLoggingFunc( FileSystemLoggingFunc_t logFunc )
  4302. {
  4303. m_LogFuncs.FindAndRemove(logFunc);
  4304. }
  4305. //-----------------------------------------------------------------------------
  4306. // Make sure that slashes are of the right kind and that there is a slash at the
  4307. // end of the filename.
  4308. // WARNING!!: assumes that you have an extra byte allocated in the case that you need
  4309. // a slash at the end.
  4310. //-----------------------------------------------------------------------------
  4311. static void AddSeperatorAndFixPath( char *str )
  4312. {
  4313. char *lastChar = &str[strlen( str ) - 1];
  4314. if( *lastChar != CORRECT_PATH_SEPARATOR && *lastChar != INCORRECT_PATH_SEPARATOR )
  4315. {
  4316. lastChar[1] = CORRECT_PATH_SEPARATOR;
  4317. lastChar[2] = '\0';
  4318. }
  4319. Q_FixSlashes( str );
  4320. if ( IsX360() )
  4321. {
  4322. // 360 FS won't resolve any path with ../
  4323. V_RemoveDotSlashes( str );
  4324. }
  4325. }
  4326. //-----------------------------------------------------------------------------
  4327. // Purpose:
  4328. // Input : *pFileName -
  4329. // Output : FileNameHandle_t
  4330. //-----------------------------------------------------------------------------
  4331. FileNameHandle_t CBaseFileSystem::FindOrAddFileName( char const *pFileName )
  4332. {
  4333. return m_FileNames.FindOrAddFileName( pFileName );
  4334. }
  4335. FileNameHandle_t CBaseFileSystem::FindFileName( char const *pFileName )
  4336. {
  4337. return m_FileNames.FindFileName( pFileName );
  4338. }
  4339. //-----------------------------------------------------------------------------
  4340. // Purpose:
  4341. // Input : handle -
  4342. // Output : char const
  4343. //-----------------------------------------------------------------------------
  4344. bool CBaseFileSystem::String( const FileNameHandle_t& handle, char *buf, int buflen )
  4345. {
  4346. return m_FileNames.String( handle, buf, buflen );
  4347. }
  4348. int CBaseFileSystem::GetPathIndex( const FileNameHandle_t &handle )
  4349. {
  4350. return m_FileNames.PathIndex(handle);
  4351. }
  4352. CBaseFileSystem::CPathIDInfo* CBaseFileSystem::FindOrAddPathIDInfo( const CUtlSymbol &id, int bByRequestOnly )
  4353. {
  4354. for ( int i=0; i < m_PathIDInfos.Count(); i++ )
  4355. {
  4356. CBaseFileSystem::CPathIDInfo *pInfo = m_PathIDInfos[i];
  4357. if ( pInfo->GetPathID() == id )
  4358. {
  4359. if ( bByRequestOnly != -1 )
  4360. {
  4361. pInfo->m_bByRequestOnly = (bByRequestOnly != 0);
  4362. }
  4363. return pInfo;
  4364. }
  4365. }
  4366. // Add a new one.
  4367. CBaseFileSystem::CPathIDInfo *pInfo = new CBaseFileSystem::CPathIDInfo;
  4368. m_PathIDInfos.AddToTail( pInfo );
  4369. pInfo->SetPathID( id );
  4370. pInfo->m_bByRequestOnly = (bByRequestOnly == 1);
  4371. return pInfo;
  4372. }
  4373. void CBaseFileSystem::MarkPathIDByRequestOnly( const char *pPathID, bool bRequestOnly )
  4374. {
  4375. FindOrAddPathIDInfo( g_PathIDTable.AddString( pPathID ), bRequestOnly );
  4376. }
  4377. #if defined( TRACK_BLOCKING_IO )
  4378. void CBaseFileSystem::EnableBlockingFileAccessTracking( bool state )
  4379. {
  4380. m_bBlockingFileAccessReportingEnabled = state;
  4381. }
  4382. bool CBaseFileSystem::IsBlockingFileAccessEnabled() const
  4383. {
  4384. return m_bBlockingFileAccessReportingEnabled;
  4385. }
  4386. IBlockingFileItemList *CBaseFileSystem::RetrieveBlockingFileAccessInfo()
  4387. {
  4388. Assert( m_pBlockingItems );
  4389. return m_pBlockingItems;
  4390. }
  4391. void CBaseFileSystem::RecordBlockingFileAccess( bool synchronous, const FileBlockingItem& item )
  4392. {
  4393. AUTO_LOCK( m_BlockingFileMutex );
  4394. // Not tracking anything
  4395. if ( !m_bBlockingFileAccessReportingEnabled )
  4396. return;
  4397. if ( synchronous && !m_bAllowSynchronousLogging && ( item.m_ItemType == FILESYSTEM_BLOCKING_SYNCHRONOUS ) )
  4398. return;
  4399. // Track it
  4400. m_pBlockingItems->Add( item );
  4401. }
  4402. bool CBaseFileSystem::SetAllowSynchronousLogging( bool state )
  4403. {
  4404. bool oldState = m_bAllowSynchronousLogging;
  4405. m_bAllowSynchronousLogging = state;
  4406. return oldState;
  4407. }
  4408. void CBaseFileSystem::BlockingFileAccess_EnterCriticalSection()
  4409. {
  4410. m_BlockingFileMutex.Lock();
  4411. }
  4412. void CBaseFileSystem::BlockingFileAccess_LeaveCriticalSection()
  4413. {
  4414. m_BlockingFileMutex.Unlock();
  4415. }
  4416. #endif // TRACK_BLOCKING_IO
  4417. bool CBaseFileSystem::GetFileTypeForFullPath( char const *pFullPath, wchar_t *buf, size_t bufSizeInBytes )
  4418. {
  4419. #if !defined( _X360 ) && !defined( POSIX )
  4420. wchar_t wcharpath[512];
  4421. ::MultiByteToWideChar( CP_UTF8, 0, pFullPath, -1, wcharpath, sizeof( wcharpath ) / sizeof(wchar_t) );
  4422. wcharpath[(sizeof( wcharpath ) / sizeof(wchar_t)) - 1] = L'\0';
  4423. SHFILEINFOW info = { 0 };
  4424. DWORD_PTR dwResult = SHGetFileInfoW(
  4425. wcharpath,
  4426. 0,
  4427. &info,
  4428. sizeof( info ),
  4429. SHGFI_TYPENAME
  4430. );
  4431. if ( dwResult )
  4432. {
  4433. wcsncpy( buf, info.szTypeName, ( bufSizeInBytes / sizeof( wchar_t ) ) );
  4434. buf[( bufSizeInBytes / sizeof( wchar_t ) ) - 1] = L'\0';
  4435. return true;
  4436. }
  4437. else
  4438. #endif
  4439. {
  4440. char ext[32];
  4441. Q_ExtractFileExtension( pFullPath, ext, sizeof( ext ) );
  4442. #ifdef POSIX
  4443. _snwprintf( buf, ( bufSizeInBytes / sizeof( wchar_t ) ) - 1, L"%s File", V_strupr( ext ) ); // Matches what Windows does
  4444. #else
  4445. _snwprintf( buf, ( bufSizeInBytes / sizeof( wchar_t ) ) - 1, L".%S", ext );
  4446. #endif
  4447. buf[( bufSizeInBytes / sizeof( wchar_t ) ) - 1] = L'\0';
  4448. }
  4449. return false;
  4450. }
  4451. bool CBaseFileSystem::GetOptimalIOConstraints( FileHandle_t hFile, unsigned *pOffsetAlign, unsigned *pSizeAlign, unsigned *pBufferAlign )
  4452. {
  4453. if ( pOffsetAlign )
  4454. *pOffsetAlign = 1;
  4455. if ( pSizeAlign )
  4456. *pSizeAlign = 1;
  4457. if ( pBufferAlign )
  4458. *pBufferAlign = 1;
  4459. return false;
  4460. }
  4461. //-----------------------------------------------------------------------------
  4462. FileCacheHandle_t CBaseFileSystem::CreateFileCache( )
  4463. {
  4464. return new CFileCacheObject( this );
  4465. }
  4466. //-----------------------------------------------------------------------------
  4467. void CBaseFileSystem::AddFilesToFileCache( FileCacheHandle_t cacheId, const char **ppFileNames, int nFileNames, const char *pPathID )
  4468. {
  4469. // For now, assuming that we're only used with GAME.
  4470. Assert( pPathID && Q_strcasecmp( pPathID, "GAME" ) == 0 );
  4471. return static_cast< CFileCacheObject * >( cacheId )->AddFiles( ppFileNames, nFileNames );
  4472. }
  4473. //-----------------------------------------------------------------------------
  4474. bool CBaseFileSystem::IsFileCacheLoaded( FileCacheHandle_t cacheId )
  4475. {
  4476. return static_cast< CFileCacheObject * >( cacheId )->IsReady();
  4477. }
  4478. //-----------------------------------------------------------------------------
  4479. void CBaseFileSystem::DestroyFileCache( FileCacheHandle_t cacheId )
  4480. {
  4481. delete static_cast< CFileCacheObject * >( cacheId );
  4482. }
  4483. //-----------------------------------------------------------------------------
  4484. bool CBaseFileSystem::IsFileCacheFileLoaded( FileCacheHandle_t cacheId, const char* pFileName )
  4485. {
  4486. #ifdef _DEBUG
  4487. CFileCacheObject* pFileCache = static_cast< CFileCacheObject * >( cacheId );
  4488. bool bFileIsHeldByCache = false;
  4489. {
  4490. AUTO_LOCK( pFileCache->m_InfosMutex );
  4491. for ( int i = 0; i < pFileCache->m_Infos.Count(); ++i )
  4492. {
  4493. if ( pFileCache->m_Infos[i]->pFileName && Q_strcmp( pFileCache->m_Infos[i]->pFileName, pFileName ) )
  4494. {
  4495. bFileIsHeldByCache = true;
  4496. break;
  4497. }
  4498. }
  4499. }
  4500. Assert( bFileIsHeldByCache );
  4501. #endif
  4502. AUTO_LOCK( m_MemoryFileMutex );
  4503. return m_MemoryFileHash.Find( pFileName ) != m_MemoryFileHash.InvalidHandle();
  4504. }
  4505. //-----------------------------------------------------------------------------
  4506. bool CBaseFileSystem::RegisterMemoryFile( CMemoryFileBacking *pFile, CMemoryFileBacking **ppExistingFileWithRef )
  4507. {
  4508. Assert( pFile->m_pFS == static_cast< IFileSystem* >( this ) );
  4509. Assert( pFile->m_pFileName && pFile->m_pFileName[0] );
  4510. AUTO_LOCK( m_MemoryFileMutex );
  4511. CMemoryFileBacking *pInTable = m_MemoryFileHash[ m_MemoryFileHash.Insert( pFile->m_pFileName, pFile ) ];
  4512. pInTable->m_nRegistered++;
  4513. pInTable->AddRef(); // either for table or for ppExistingFileWithRef return
  4514. if ( pFile == pInTable )
  4515. {
  4516. Assert( pInTable->m_nRegistered == 1 );
  4517. *ppExistingFileWithRef = NULL;
  4518. return true;
  4519. }
  4520. else
  4521. {
  4522. Assert( pInTable->m_nRegistered > 1 );
  4523. *ppExistingFileWithRef = pInTable;
  4524. return false;
  4525. }
  4526. }
  4527. //-----------------------------------------------------------------------------
  4528. void CBaseFileSystem::UnregisterMemoryFile( CMemoryFileBacking *pFile )
  4529. {
  4530. Assert( pFile->m_pFS == static_cast< IFileSystem* >( this ) && pFile->m_nRegistered > 0 );
  4531. bool bRelease = false;
  4532. {
  4533. AUTO_LOCK( m_MemoryFileMutex );
  4534. pFile->m_nRegistered--;
  4535. if ( pFile->m_nRegistered == 0 )
  4536. {
  4537. m_MemoryFileHash.Remove( pFile->m_pFileName );
  4538. bRelease = true;
  4539. }
  4540. }
  4541. // Release potentially a complex op, prefer to perform it outside of mutex.
  4542. if (bRelease)
  4543. {
  4544. pFile->Release();
  4545. }
  4546. }
  4547. //-----------------------------------------------------------------------------
  4548. // Purpose: Constructs a file handle
  4549. // Input : base file system handle
  4550. // Output :
  4551. //-----------------------------------------------------------------------------
  4552. CFileHandle::CFileHandle( CBaseFileSystem* fs )
  4553. {
  4554. Init( fs );
  4555. }
  4556. CFileHandle::~CFileHandle()
  4557. {
  4558. Assert( IsValid() );
  4559. #if !defined( _RETAIL )
  4560. delete[] m_pszTrueFileName;
  4561. #endif
  4562. if ( m_pPackFileHandle )
  4563. {
  4564. delete m_pPackFileHandle;
  4565. m_pPackFileHandle = NULL;
  4566. }
  4567. if ( m_pFile )
  4568. {
  4569. m_fs->Trace_FClose( m_pFile );
  4570. m_pFile = NULL;
  4571. }
  4572. m_nMagic = FREE_MAGIC;
  4573. }
  4574. void CFileHandle::Init( CBaseFileSystem *fs )
  4575. {
  4576. m_nMagic = MAGIC;
  4577. m_pFile = NULL;
  4578. m_nLength = 0;
  4579. m_type = FT_NORMAL;
  4580. m_pPackFileHandle = NULL;
  4581. m_fs = fs;
  4582. #if !defined( _RETAIL )
  4583. m_pszTrueFileName = 0;
  4584. #endif
  4585. }
  4586. bool CFileHandle::IsValid()
  4587. {
  4588. return ( m_nMagic == MAGIC );
  4589. }
  4590. int CFileHandle::GetSectorSize()
  4591. {
  4592. Assert( IsValid() );
  4593. if ( m_pFile )
  4594. {
  4595. return m_fs->FS_GetSectorSize( m_pFile );
  4596. }
  4597. else if ( m_pPackFileHandle )
  4598. {
  4599. return m_pPackFileHandle->GetSectorSize();
  4600. }
  4601. else if ( m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4602. {
  4603. return 1;
  4604. }
  4605. else
  4606. {
  4607. return -1;
  4608. }
  4609. }
  4610. bool CFileHandle::IsOK()
  4611. {
  4612. #if defined( SUPPORT_PACKED_STORE )
  4613. if ( m_VPKHandle )
  4614. {
  4615. return true;
  4616. }
  4617. #endif
  4618. if ( m_pFile )
  4619. {
  4620. return ( IsValid() && m_fs->FS_ferror( m_pFile ) == 0 );
  4621. }
  4622. else if ( m_pPackFileHandle || m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4623. {
  4624. return IsValid();
  4625. }
  4626. m_fs->Warning( FILESYSTEM_WARNING, "FS: Tried to IsOk NULL file pointer inside valid file handle!\n" );
  4627. return false;
  4628. }
  4629. void CFileHandle::Flush()
  4630. {
  4631. Assert( IsValid() );
  4632. if ( m_pFile )
  4633. {
  4634. m_fs->FS_fflush( m_pFile );
  4635. }
  4636. }
  4637. void CFileHandle::SetBufferSize( int nBytes )
  4638. {
  4639. Assert( IsValid() );
  4640. if ( m_pFile )
  4641. {
  4642. m_fs->FS_setbufsize( m_pFile, nBytes );
  4643. }
  4644. else if ( m_pPackFileHandle )
  4645. {
  4646. m_pPackFileHandle->SetBufferSize( nBytes );
  4647. }
  4648. }
  4649. int CFileHandle::Read( void* pBuffer, int nLength )
  4650. {
  4651. Assert( IsValid() );
  4652. return Read( pBuffer, -1, nLength );
  4653. }
  4654. int CFileHandle::Read( void* pBuffer, int nDestSize, int nLength )
  4655. {
  4656. Assert( IsValid() );
  4657. #if defined( SUPPORT_PACKED_STORE )
  4658. if ( m_VPKHandle )
  4659. {
  4660. if ( nDestSize >= 0 )
  4661. nLength = MIN( nLength, nDestSize );
  4662. return m_VPKHandle.Read( pBuffer, nLength );
  4663. }
  4664. #endif
  4665. // Is this a regular file or a pack file?
  4666. if ( m_pFile )
  4667. {
  4668. return m_fs->FS_fread( pBuffer, nDestSize, nLength, m_pFile );
  4669. }
  4670. else if ( m_pPackFileHandle )
  4671. {
  4672. // Pack file handle handles clamping all the reads:
  4673. return m_pPackFileHandle->Read( pBuffer, nDestSize, nLength );
  4674. }
  4675. else if ( m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4676. {
  4677. return static_cast< CMemoryFileHandle* >( this )->Read( pBuffer, nDestSize, nLength );
  4678. }
  4679. return 0;
  4680. }
  4681. int CFileHandle::Write( const void* pBuffer, int nLength )
  4682. {
  4683. tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__ );
  4684. if( ThreadInMainThread() )
  4685. {
  4686. tmPlotI32( TELEMETRY_LEVEL0, TMPT_MEMORY, 0, nLength, "FileBytesWrite" );
  4687. }
  4688. Assert( IsValid() );
  4689. if ( !m_pFile )
  4690. {
  4691. m_fs->Warning( FILESYSTEM_WARNING, "FS: Tried to Write NULL file pointer inside valid file handle!\n" );
  4692. return 0;
  4693. }
  4694. size_t nBytesWritten = m_fs->FS_fwrite( (void*)pBuffer, nLength, m_pFile );
  4695. m_fs->Trace_FWrite(nBytesWritten,m_pFile);
  4696. return nBytesWritten;
  4697. }
  4698. int CFileHandle::Seek( int64 nOffset, int nWhence )
  4699. {
  4700. Assert( IsValid() );
  4701. #if defined( SUPPORT_PACKED_STORE )
  4702. if ( m_VPKHandle )
  4703. {
  4704. return m_VPKHandle.Seek( nOffset, nWhence );
  4705. }
  4706. #endif
  4707. if ( m_pFile )
  4708. {
  4709. m_fs->FS_fseek( m_pFile, nOffset, nWhence );
  4710. // TODO - FS_fseek should return the resultant offset
  4711. return 0;
  4712. }
  4713. else if ( m_pPackFileHandle )
  4714. {
  4715. return m_pPackFileHandle->Seek( nOffset, nWhence );
  4716. }
  4717. else if ( m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4718. {
  4719. return static_cast< CMemoryFileHandle* >( this )->Seek( nOffset, nWhence );
  4720. }
  4721. return -1;
  4722. }
  4723. int CFileHandle::Tell()
  4724. {
  4725. Assert( IsValid() );
  4726. #if defined( SUPPORT_PACKED_STORE )
  4727. if ( m_VPKHandle )
  4728. {
  4729. return m_VPKHandle.Tell();
  4730. }
  4731. #endif
  4732. if ( m_pFile )
  4733. {
  4734. return m_fs->FS_ftell( m_pFile );
  4735. }
  4736. else if ( m_pPackFileHandle )
  4737. {
  4738. return m_pPackFileHandle->Tell();
  4739. }
  4740. else if ( m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4741. {
  4742. return static_cast< CMemoryFileHandle* >( this )->Tell();
  4743. }
  4744. return -1;
  4745. }
  4746. int CFileHandle::Size()
  4747. {
  4748. Assert( IsValid() );
  4749. int nReturnedSize = -1;
  4750. #if defined( SUPPORT_PACKED_STORE )
  4751. if ( m_VPKHandle )
  4752. {
  4753. return m_VPKHandle.m_nFileSize;
  4754. }
  4755. #endif
  4756. if ( m_pFile )
  4757. {
  4758. nReturnedSize = m_nLength;
  4759. }
  4760. else if ( m_pPackFileHandle )
  4761. {
  4762. nReturnedSize = m_pPackFileHandle->Size();
  4763. }
  4764. else if ( m_type == FT_MEMORY_BINARY || m_type == FT_MEMORY_TEXT )
  4765. {
  4766. return static_cast< CMemoryFileHandle* >( this )->Size();
  4767. }
  4768. return nReturnedSize;
  4769. }
  4770. int64 CFileHandle::AbsoluteBaseOffset()
  4771. {
  4772. Assert( IsValid() );
  4773. if ( !m_pFile && m_pPackFileHandle )
  4774. {
  4775. return m_pPackFileHandle->AbsoluteBaseOffset();
  4776. }
  4777. else
  4778. {
  4779. return 0;
  4780. }
  4781. }
  4782. bool CFileHandle::EndOfFile()
  4783. {
  4784. Assert( IsValid() );
  4785. return ( Tell() >= Size() );
  4786. }
  4787. //-----------------------------------------------------------------------------
  4788. int CMemoryFileHandle::Read( void* pBuffer, int nDestSize, int nLength )
  4789. {
  4790. nLength = min( nLength, (int) m_nLength - m_nPosition );
  4791. if ( nLength > 0 )
  4792. {
  4793. Assert( m_nPosition >= 0 );
  4794. memcpy( pBuffer, m_pBacking->m_pData + m_nPosition, nLength );
  4795. m_nPosition += nLength;
  4796. return nLength;
  4797. }
  4798. if ( m_nPosition >= (int) m_nLength )
  4799. {
  4800. return -1;
  4801. }
  4802. return 0;
  4803. }
  4804. int CMemoryFileHandle::Seek( int64 nOffset64, int nWhence )
  4805. {
  4806. if ( nWhence == SEEK_SET )
  4807. {
  4808. m_nPosition = (int) clamp( nOffset64, 0ll, m_nLength );
  4809. }
  4810. else if ( nWhence == SEEK_CUR )
  4811. {
  4812. m_nPosition += (int) clamp( nOffset64, -(int64)m_nPosition, m_nLength - m_nPosition );
  4813. }
  4814. else if ( nWhence == SEEK_END )
  4815. {
  4816. m_nPosition = (int) m_nLength + (int) clamp( nOffset64, -m_nLength, 0ll );
  4817. }
  4818. return m_nPosition;
  4819. }
  4820. //-----------------------------------------------------------------------------
  4821. CBaseFileSystem::CFileCacheObject::CFileCacheObject( CBaseFileSystem* pFS )
  4822. : m_pFS( pFS )
  4823. {
  4824. }
  4825. void CBaseFileSystem::CFileCacheObject::AddFiles( const char **ppFileNames, int nFileNames )
  4826. {
  4827. CUtlVector< Info_t* > infos;
  4828. infos.SetCount( nFileNames );
  4829. for ( int i = 0; i < nFileNames; ++i )
  4830. {
  4831. infos[i] = new Info_t;
  4832. Info_t &info = *infos[i];
  4833. info.pFileName = strdup( ppFileNames[i] );
  4834. V_FixSlashes( (char*) info.pFileName );
  4835. #ifdef _WIN32
  4836. Q_strlower( (char*) info.pFileName );
  4837. #endif
  4838. info.hIOAsync = NULL;
  4839. info.pBacking = NULL;
  4840. info.pOwner = NULL;
  4841. }
  4842. AUTO_LOCK( m_InfosMutex );
  4843. int offset = m_Infos.AddMultipleToTail( nFileNames, infos.Base() );
  4844. m_nPending += nFileNames;
  4845. ProcessNewEntries(offset);
  4846. }
  4847. void CBaseFileSystem::CFileCacheObject::ProcessNewEntries( int start )
  4848. {
  4849. for ( int i = start; i < m_Infos.Count(); ++i )
  4850. {
  4851. Info_t &info = *m_Infos[i];
  4852. if ( !info.pOwner )
  4853. {
  4854. info.pOwner = this;
  4855. // NOTE: currently only caching files with GAME pathID
  4856. FileAsyncRequest_t request;
  4857. request.pszFilename = info.pFileName;
  4858. request.pszPathID = "GAME";
  4859. request.flags = FSASYNC_FLAGS_ALLOCNOFREE;
  4860. request.pfnCallback = &IOCallback;
  4861. request.pContext = &info;
  4862. if ( m_pFS->AsyncRead( request, &info.hIOAsync ) != FSASYNC_OK )
  4863. {
  4864. --m_nPending;
  4865. }
  4866. }
  4867. }
  4868. }
  4869. void CBaseFileSystem::CFileCacheObject::IOCallback( const FileAsyncRequest_t &request, int nBytesRead, FSAsyncStatus_t err )
  4870. {
  4871. Assert( request.pContext );
  4872. Info_t &info = *(Info_t *)request.pContext;
  4873. CMemoryFileBacking *pBacking = new CMemoryFileBacking( info.pOwner->m_pFS );
  4874. pBacking->m_pData = NULL;
  4875. pBacking->m_pFileName = info.pFileName;
  4876. pBacking->m_nLength = ( err == FSASYNC_OK && nBytesRead == 0 ) ? 0 : -1;
  4877. if ( request.pData )
  4878. {
  4879. if ( err == FSASYNC_OK && nBytesRead > 0 )
  4880. {
  4881. pBacking->m_pData = (const char*) request.pData; // transfer data ownership
  4882. pBacking->m_nLength = nBytesRead;
  4883. }
  4884. else
  4885. {
  4886. info.pOwner->m_pFS->FreeOptimalReadBuffer( request.pData );
  4887. }
  4888. }
  4889. CMemoryFileBacking *pExistingBacking = NULL;
  4890. if ( !info.pOwner->m_pFS->RegisterMemoryFile( pBacking, &pExistingBacking ) )
  4891. {
  4892. // Someone already registered this file
  4893. info.pFileName = pExistingBacking->m_pFileName;
  4894. pBacking->Release();
  4895. pBacking = pExistingBacking;
  4896. }
  4897. //DevWarning("preload %s %d\n", request.pszFilename, pBacking->m_nLength);
  4898. info.pBacking = pBacking;
  4899. info.pOwner->m_nPending--;
  4900. }
  4901. CBaseFileSystem::CFileCacheObject::~CFileCacheObject()
  4902. {
  4903. AUTO_LOCK( m_InfosMutex );
  4904. for ( int i = 0; i < m_Infos.Count(); ++i )
  4905. {
  4906. Info_t& info = *m_Infos[i];
  4907. if ( info.hIOAsync )
  4908. {
  4909. // job is guaranteed to not be running after abort
  4910. m_pFS->AsyncAbort( info.hIOAsync );
  4911. m_pFS->AsyncRelease( info.hIOAsync );
  4912. }
  4913. if ( info.pBacking )
  4914. {
  4915. m_pFS->UnregisterMemoryFile( info.pBacking );
  4916. info.pBacking->Release();
  4917. }
  4918. else
  4919. {
  4920. free( (char*) info.pFileName );
  4921. }
  4922. delete m_Infos[i];
  4923. }
  4924. Assert( m_nPending == 0 );
  4925. }