Counter Strike : Global Offensive Source Code
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.

1091 lines
26 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #ifndef UTLCACHEDFILEDATA_H
  9. #define UTLCACHEDFILEDATA_H
  10. #if defined( WIN32 )
  11. #pragma once
  12. #endif
  13. #include "filesystem.h" // FileNameHandle_t
  14. #include "utlrbtree.h"
  15. #include "utlbuffer.h"
  16. #include "utlsortvector.h"
  17. #include "tier1/strtools.h"
  18. #include "tier0/memdbgon.h"
  19. // If you change to serialization protocols, this must be bumped...
  20. #define UTL_CACHE_SYSTEM_VERSION 2
  21. #define UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO (int)-2
  22. // Cacheable types must derive from this and implement the appropriate methods...
  23. abstract_class IBaseCacheInfo
  24. {
  25. public:
  26. virtual void Save( CUtlBuffer& buf ) = 0;
  27. virtual void Restore( CUtlBuffer& buf ) = 0;
  28. virtual void Rebuild( char const *filename ) = 0;
  29. };
  30. typedef unsigned int (*PFNCOMPUTECACHEMETACHECKSUM)( void );
  31. typedef enum
  32. {
  33. UTL_CACHED_FILE_USE_TIMESTAMP = 0,
  34. UTL_CACHED_FILE_USE_FILESIZE,
  35. } UtlCachedFileDataType_t;
  36. template <class T>
  37. class CUtlCachedFileData
  38. {
  39. public:
  40. CUtlCachedFileData
  41. (
  42. char const *repositoryFileName,
  43. int version,
  44. PFNCOMPUTECACHEMETACHECKSUM checksumfunc = NULL,
  45. UtlCachedFileDataType_t fileCheckType = UTL_CACHED_FILE_USE_TIMESTAMP,
  46. bool nevercheckdisk = false,
  47. bool readonly = false,
  48. bool savemanifest = false
  49. )
  50. : m_Elements( 0, 0, FileNameHandleLessFunc ),
  51. m_sRepositoryFileName( repositoryFileName ),
  52. m_nVersion( version ),
  53. m_pfnMetaChecksum( checksumfunc ),
  54. m_bDirty( false ),
  55. m_bInitialized( false ),
  56. m_uCurrentMetaChecksum( 0u ),
  57. m_fileCheckType( fileCheckType ),
  58. m_bNeverCheckDisk( nevercheckdisk ),
  59. m_bReadOnly( readonly ),
  60. m_bSaveManifest( savemanifest )
  61. {
  62. Assert( m_sRepositoryFileName.Length() > 0 );
  63. }
  64. virtual ~CUtlCachedFileData()
  65. {
  66. m_Elements.RemoveAll();
  67. int c = m_Data.Count();
  68. for ( int i = 0; i < c ; ++i )
  69. {
  70. delete m_Data[ i ];
  71. }
  72. m_Data.RemoveAll();
  73. }
  74. T* Get( char const *filename );
  75. const T* Get( char const *filename ) const;
  76. T* operator[]( int i );
  77. const T* operator[]( int i ) const;
  78. int Count() const;
  79. void GetElementName( int i, char *buf, int buflen )
  80. {
  81. buf[ 0 ] = 0;
  82. if ( !m_Elements.IsValidIndex( i ) )
  83. return;
  84. g_pFullFileSystem->String( m_Elements[ i ].handle, buf, buflen );
  85. }
  86. bool EntryExists( char const *filename ) const
  87. {
  88. ElementType_t element;
  89. element.handle = g_pFullFileSystem->FindOrAddFileName( filename );
  90. int idx = m_Elements.Find( element );
  91. return idx != m_Elements.InvalidIndex() ? true : false;
  92. }
  93. void SetElement( char const *name, int fileinfo, T* src )
  94. {
  95. SetDirty( true );
  96. int idx = GetIndex( name );
  97. Assert( idx != m_Elements.InvalidIndex() );
  98. ElementType_t& e = m_Elements[ idx ];
  99. CUtlBuffer buf( 0, 0, 0 );
  100. Assert( e.dataIndex != m_Data.InvalidIndex() );
  101. T *dest = m_Data[ e.dataIndex ];
  102. Assert( dest );
  103. // I suppose we could do an assignment operator, but this should save/restore the data element just fine for
  104. // tool purposes
  105. ((IBaseCacheInfo *)src)->Save( buf );
  106. ((IBaseCacheInfo *)dest)->Restore( buf );
  107. e.fileinfo = fileinfo;
  108. if ( ( e.fileinfo == -1 ) &&
  109. ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) )
  110. {
  111. e.fileinfo = 0;
  112. }
  113. // Force recheck
  114. e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
  115. }
  116. // If you create a cache and don't call init/shutdown, you can call this to do a quick check to see if the checksum/version
  117. // will cause a rebuild...
  118. bool IsUpToDate();
  119. void Shutdown();
  120. bool Init();
  121. void Save();
  122. void Reload();
  123. void ForceRecheckDiskInfo();
  124. // Iterates all entries and gets filesystem info and optionally causes rebuild on any existing items which are out of date
  125. void CheckDiskInfo( bool force_rebuild, int cacheFileTime = 0L );
  126. void SaveManifest();
  127. bool ManifestExists();
  128. int GetFileInfo( char const *filename )
  129. {
  130. ElementType_t element;
  131. element.handle = g_pFullFileSystem->FindOrAddFileName( filename );
  132. int idx = m_Elements.Find( element );
  133. if ( idx == m_Elements.InvalidIndex() )
  134. {
  135. return 0L;
  136. }
  137. return m_Elements[ idx ].fileinfo;
  138. }
  139. int GetFileInfo( int idx )
  140. {
  141. if ( !m_Elements.IsValidIndex( idx ) )
  142. return 0L;
  143. return m_Elements[ idx ].fileinfo;
  144. }
  145. int GetNumElements()
  146. {
  147. return m_Elements.Count();
  148. }
  149. bool IsDirty() const
  150. {
  151. return m_bDirty;
  152. }
  153. T *RebuildItem( const char *filename );
  154. void SetNeverCheckDisk( bool bNeverCheckDisk );
  155. void RecheckItem( char const *filename );
  156. private:
  157. void InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile );
  158. void InitLargeBuffer( FileHandle_t& fh, bool& deleteFile );
  159. int GetIndex( const char *filename )
  160. {
  161. ElementType_t element;
  162. element.handle = g_pFullFileSystem->FindOrAddFileName( filename );
  163. int idx = m_Elements.Find( element );
  164. if ( idx == m_Elements.InvalidIndex() )
  165. {
  166. T *data = new T();
  167. int dataIndex = m_Data.AddToTail( data );
  168. idx = m_Elements.Insert( element );
  169. m_Elements[ idx ].dataIndex = dataIndex;
  170. }
  171. return idx;
  172. }
  173. void CheckInit();
  174. void SetDirty( bool dirty )
  175. {
  176. m_bDirty = dirty;
  177. }
  178. void RebuildCache( char const *filename, T *data );
  179. struct ElementType_t
  180. {
  181. ElementType_t() :
  182. handle( 0 ),
  183. fileinfo( 0 ),
  184. diskfileinfo( UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ),
  185. dataIndex( -1 )
  186. {
  187. }
  188. FileNameHandle_t handle;
  189. int fileinfo;
  190. int diskfileinfo;
  191. int dataIndex;
  192. };
  193. static bool FileNameHandleLessFunc( ElementType_t const &lhs, ElementType_t const &rhs )
  194. {
  195. return lhs.handle < rhs.handle;
  196. }
  197. CUtlRBTree< ElementType_t > m_Elements;
  198. CUtlVector< T * > m_Data;
  199. CUtlString m_sRepositoryFileName;
  200. int m_nVersion;
  201. PFNCOMPUTECACHEMETACHECKSUM m_pfnMetaChecksum;
  202. unsigned int m_uCurrentMetaChecksum;
  203. UtlCachedFileDataType_t m_fileCheckType;
  204. bool m_bNeverCheckDisk : 1;
  205. bool m_bReadOnly : 1;
  206. bool m_bSaveManifest : 1;
  207. bool m_bDirty : 1;
  208. bool m_bInitialized : 1;
  209. };
  210. template <class T>
  211. T* CUtlCachedFileData<T>::Get( char const *filename )
  212. {
  213. int idx = GetIndex( filename );
  214. ElementType_t& e = m_Elements[ idx ];
  215. if ( e.fileinfo == -1 &&
  216. m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE )
  217. {
  218. e.fileinfo = 0;
  219. }
  220. int cachefileinfo = e.fileinfo;
  221. // Set the disk fileinfo the first time we encounter the filename
  222. if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO )
  223. {
  224. if ( m_bNeverCheckDisk )
  225. {
  226. e.diskfileinfo = cachefileinfo;
  227. }
  228. else
  229. {
  230. if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE )
  231. {
  232. e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" );
  233. // Missing files get a disk file size of 0
  234. if ( e.diskfileinfo == -1 )
  235. {
  236. e.diskfileinfo = 0;
  237. }
  238. }
  239. else
  240. {
  241. e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" );
  242. }
  243. }
  244. }
  245. Assert( e.dataIndex != m_Data.InvalidIndex() );
  246. T *data = m_Data[ e.dataIndex ];
  247. Assert( data );
  248. // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
  249. if ( cachefileinfo != e.diskfileinfo )
  250. {
  251. if ( !m_bReadOnly )
  252. {
  253. RebuildCache( filename, data );
  254. }
  255. e.fileinfo = e.diskfileinfo;
  256. }
  257. return data;
  258. }
  259. template <class T>
  260. const T* CUtlCachedFileData<T>::Get( char const *filename ) const
  261. {
  262. return const_cast< CUtlCachedFileData<T> * >(this)->Get( filename );
  263. }
  264. template <class T>
  265. T* CUtlCachedFileData<T>::operator[]( int i )
  266. {
  267. return m_Data[ m_Elements[ i ].dataIndex ];
  268. }
  269. template <class T>
  270. const T* CUtlCachedFileData<T>::operator[]( int i ) const
  271. {
  272. return m_Data[ m_Elements[ i ].dataIndex ];
  273. }
  274. template <class T>
  275. int CUtlCachedFileData<T>::Count() const
  276. {
  277. return m_Elements.Count();
  278. }
  279. template <class T>
  280. void CUtlCachedFileData<T>::Reload()
  281. {
  282. Shutdown();
  283. Init();
  284. }
  285. template <class T>
  286. bool CUtlCachedFileData<T>::IsUpToDate()
  287. {
  288. // Don't call Init/Shutdown if using this method!!!
  289. Assert( !m_bInitialized );
  290. if ( !m_sRepositoryFileName.Length() )
  291. {
  292. Error( "CUtlCachedFileData: Can't IsUpToDate, no repository file specified." );
  293. return false;
  294. }
  295. // Always compute meta checksum
  296. m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0;
  297. FileHandle_t fh;
  298. fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" );
  299. if ( fh == FILESYSTEM_INVALID_HANDLE )
  300. {
  301. return false;
  302. }
  303. // Version data is in first 12 bytes of file
  304. byte header[ 12 ];
  305. g_pFullFileSystem->Read( header, sizeof( header ), fh );
  306. g_pFullFileSystem->Close( fh );
  307. int cacheversion = *( int *)&header[ 0 ];
  308. if ( UTL_CACHE_SYSTEM_VERSION != cacheversion )
  309. {
  310. DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() );
  311. Assert( !m_bReadOnly );
  312. if ( !m_bReadOnly )
  313. {
  314. g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" );
  315. }
  316. return false;
  317. }
  318. // Now parse data from the buffer
  319. int version = *( int *)&header[ 4 ];
  320. if ( version != m_nVersion )
  321. {
  322. DevMsg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() );
  323. Assert( !m_bReadOnly );
  324. if ( !m_bReadOnly )
  325. {
  326. g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" );
  327. }
  328. return false;
  329. }
  330. // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
  331. // meta data function
  332. if ( m_pfnMetaChecksum )
  333. {
  334. unsigned int cache_meta_checksum = *( unsigned int *)&header[ 8 ];
  335. if ( cache_meta_checksum != m_uCurrentMetaChecksum )
  336. {
  337. DevMsg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() );
  338. Assert( !m_bReadOnly );
  339. if ( !m_bReadOnly )
  340. {
  341. g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" );
  342. }
  343. return false;
  344. }
  345. }
  346. // Looks valid
  347. return true;
  348. }
  349. template <class T>
  350. void CUtlCachedFileData<T>::InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile )
  351. {
  352. deleteFile = false;
  353. CUtlBuffer loadBuf;
  354. g_pFullFileSystem->ReadToBuffer( fh, loadBuf );
  355. g_pFullFileSystem->Close( fh );
  356. int cacheversion = 0;
  357. loadBuf.Get( &cacheversion, sizeof( cacheversion ) );
  358. if ( UTL_CACHE_SYSTEM_VERSION == cacheversion )
  359. {
  360. // Now parse data from the buffer
  361. int version = loadBuf.GetInt();
  362. if ( version == m_nVersion )
  363. {
  364. // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
  365. // meta data function
  366. unsigned int cache_meta_checksum = loadBuf.GetInt();
  367. if ( !m_pfnMetaChecksum ||
  368. ( cache_meta_checksum == m_uCurrentMetaChecksum ) )
  369. {
  370. int count = loadBuf.GetInt();
  371. Assert( count < 2000000 );
  372. CUtlBuffer buf( 0, 0, 0 );
  373. for ( int i = 0 ; i < count; ++i )
  374. {
  375. int bufsize = loadBuf.GetInt();
  376. Assert( bufsize < 1000000 );
  377. buf.Clear();
  378. buf.EnsureCapacity( bufsize );
  379. loadBuf.Get( buf.Base(), bufsize );
  380. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  381. buf.SeekPut( CUtlBuffer::SEEK_HEAD, bufsize );
  382. // Read the element name
  383. char elementFileName[ 512 ];
  384. buf.GetString( elementFileName, sizeof( elementFileName ) );
  385. // Now read the element
  386. int slot = GetIndex( elementFileName );
  387. Assert( slot != m_Elements.InvalidIndex() );
  388. ElementType_t& element = m_Elements[ slot ];
  389. element.fileinfo = buf.GetInt();
  390. if ( ( element.fileinfo == -1 ) &&
  391. ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) )
  392. {
  393. element.fileinfo = 0;
  394. }
  395. Assert( element.dataIndex != m_Data.InvalidIndex() );
  396. T *data = m_Data[ element.dataIndex ];
  397. Assert( data );
  398. ((IBaseCacheInfo *)data)->Restore( buf );
  399. }
  400. }
  401. else
  402. {
  403. Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() );
  404. deleteFile = true;
  405. }
  406. }
  407. else
  408. {
  409. Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() );
  410. deleteFile = true;
  411. }
  412. }
  413. else
  414. {
  415. DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() );
  416. deleteFile = true;
  417. }
  418. }
  419. template <class T>
  420. void CUtlCachedFileData<T>::InitLargeBuffer( FileHandle_t& fh, bool& deleteFile )
  421. {
  422. deleteFile = false;
  423. int cacheversion = 0;
  424. g_pFullFileSystem->Read( &cacheversion, sizeof( cacheversion ), fh );
  425. if ( UTL_CACHE_SYSTEM_VERSION == cacheversion )
  426. {
  427. // Now parse data from the buffer
  428. int version = 0;
  429. g_pFullFileSystem->Read( &version, sizeof( version ), fh );
  430. if ( version == m_nVersion )
  431. {
  432. // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
  433. // meta data function
  434. unsigned int cache_meta_checksum = 0;
  435. g_pFullFileSystem->Read( &cache_meta_checksum, sizeof( cache_meta_checksum ), fh );
  436. if ( !m_pfnMetaChecksum ||
  437. ( cache_meta_checksum == m_uCurrentMetaChecksum ) )
  438. {
  439. int count = 0;
  440. g_pFullFileSystem->Read( &count, sizeof( count ), fh );
  441. Assert( count < 2000000 );
  442. CUtlBuffer buf( 0, 0, 0 );
  443. for ( int i = 0 ; i < count; ++i )
  444. {
  445. int bufsize = 0;
  446. g_pFullFileSystem->Read( &bufsize, sizeof( bufsize ), fh );
  447. Assert( bufsize < 1000000 );
  448. if ( bufsize > 1000000 )
  449. {
  450. Msg( "Discarding repository '%s' due to corruption\n", m_sRepositoryFileName.String() );
  451. deleteFile = true;
  452. break;
  453. }
  454. buf.Clear();
  455. buf.EnsureCapacity( bufsize );
  456. int nBytesRead = g_pFullFileSystem->Read( buf.Base(), bufsize, fh );
  457. buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  458. buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  459. // Read the element name
  460. char elementFileName[ 512 ];
  461. buf.GetString( elementFileName, sizeof( elementFileName ) );
  462. // Now read the element
  463. int slot = GetIndex( elementFileName );
  464. Assert( slot != m_Elements.InvalidIndex() );
  465. ElementType_t& element = m_Elements[ slot ];
  466. element.fileinfo = buf.GetInt();
  467. if ( ( element.fileinfo == -1 ) &&
  468. ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) )
  469. {
  470. element.fileinfo = 0;
  471. }
  472. Assert( element.dataIndex != m_Data.InvalidIndex() );
  473. T *data = m_Data[ element.dataIndex ];
  474. Assert( data );
  475. ((IBaseCacheInfo *)data)->Restore( buf );
  476. }
  477. }
  478. else
  479. {
  480. Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() );
  481. deleteFile = true;
  482. }
  483. }
  484. else
  485. {
  486. Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() );
  487. deleteFile = true;
  488. }
  489. }
  490. else
  491. {
  492. DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() );
  493. deleteFile = true;
  494. }
  495. g_pFullFileSystem->Close( fh );
  496. }
  497. template <class T>
  498. bool CUtlCachedFileData<T>::Init()
  499. {
  500. if ( m_bInitialized )
  501. {
  502. return true;
  503. }
  504. m_bInitialized = true;
  505. if ( !m_sRepositoryFileName.Length() )
  506. {
  507. Error( "CUtlCachedFileData: Can't Init, no repository file specified." );
  508. return false;
  509. }
  510. // Always compute meta checksum
  511. m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0;
  512. FileHandle_t fh;
  513. fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" );
  514. if ( fh == FILESYSTEM_INVALID_HANDLE )
  515. {
  516. // Nothing on disk, we'll recreate everything from scratch...
  517. if ( !m_bReadOnly )
  518. {
  519. SetDirty( true );
  520. }
  521. return true;
  522. }
  523. int fileTime = g_pFullFileSystem->GetFileTime( m_sRepositoryFileName.String(), "MOD" );
  524. int size = g_pFullFileSystem->Size( fh );
  525. bool deletefile = false;
  526. if ( size > 1024 * 1024 )
  527. {
  528. InitLargeBuffer( fh, deletefile );
  529. }
  530. else
  531. {
  532. InitSmallBuffer( fh, size, deletefile );
  533. }
  534. if ( deletefile )
  535. {
  536. Assert( !m_bReadOnly );
  537. if ( !m_bReadOnly )
  538. {
  539. g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" );
  540. SetDirty( true );
  541. }
  542. }
  543. CheckDiskInfo( false, fileTime );
  544. return true;
  545. }
  546. class CSortedCacheFile
  547. {
  548. public:
  549. FileNameHandle_t handle;
  550. int index;
  551. bool Less( const CSortedCacheFile &file0, const CSortedCacheFile &file1, void * )
  552. {
  553. char name0[ 512 ];
  554. char name1[ 512 ];
  555. g_pFullFileSystem->String( file0.handle, name0, sizeof( name0 ) );
  556. g_pFullFileSystem->String( file1.handle, name1, sizeof( name1 ) );
  557. return Q_stricmp( name0, name1 ) < 0 ? true : false;
  558. }
  559. };
  560. template <class T>
  561. void CUtlCachedFileData<T>::Save()
  562. {
  563. char path[ 512 ];
  564. Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) );
  565. Q_StripFilename( path );
  566. g_pFullFileSystem->CreateDirHierarchy( path, "MOD" );
  567. if ( g_pFullFileSystem->FileExists( m_sRepositoryFileName.String(), "MOD" ) &&
  568. !g_pFullFileSystem->IsFileWritable( m_sRepositoryFileName.String(), "MOD" ) )
  569. {
  570. g_pFullFileSystem->SetFileWritable( m_sRepositoryFileName.String(), true, "MOD" );
  571. }
  572. // Now write to file
  573. FileHandle_t fh;
  574. fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "wb" );
  575. if ( FILESYSTEM_INVALID_HANDLE == fh )
  576. {
  577. Warning( "Unable to persist cache '%s', check file permissions\n", m_sRepositoryFileName.String() );
  578. }
  579. else
  580. {
  581. SetDirty( false );
  582. int v = UTL_CACHE_SYSTEM_VERSION;
  583. g_pFullFileSystem->Write( &v, sizeof( v ), fh );
  584. v = m_nVersion;
  585. g_pFullFileSystem->Write( &v, sizeof( v ), fh );
  586. v = (int)m_uCurrentMetaChecksum;
  587. g_pFullFileSystem->Write( &v, sizeof( v ), fh );
  588. // Element count
  589. int c = Count();
  590. g_pFullFileSystem->Write( &c, sizeof( c ), fh );
  591. // Save repository back out to disk...
  592. CUtlBuffer buf( 0, 0, 0 );
  593. // Sort file alphabetically
  594. CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list;
  595. for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) )
  596. {
  597. ElementType_t& element = m_Elements[ i ];
  598. CSortedCacheFile insert;
  599. insert.handle = element.handle;
  600. insert.index = i;
  601. list.InsertNoSort( insert );
  602. }
  603. list.RedoSort();
  604. for ( int i = 0; i < list.Count(); ++i )
  605. {
  606. ElementType_t &element = m_Elements[ list[ i ].index ];
  607. buf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  608. char fn[ 512 ];
  609. g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) );
  610. buf.PutString( fn );
  611. buf.PutInt( element.fileinfo );
  612. Assert( element.dataIndex != m_Data.InvalidIndex() );
  613. T *data = m_Data[ element.dataIndex ];
  614. Assert( data );
  615. ((IBaseCacheInfo *)data)->Save( buf );
  616. int bufsize = buf.TellPut();
  617. g_pFullFileSystem->Write( &bufsize, sizeof( bufsize ), fh );
  618. g_pFullFileSystem->Write( buf.Base(), bufsize, fh );
  619. }
  620. g_pFullFileSystem->Close( fh );
  621. }
  622. if ( m_bSaveManifest )
  623. {
  624. SaveManifest();
  625. }
  626. }
  627. template <class T>
  628. void CUtlCachedFileData<T>::Shutdown()
  629. {
  630. if ( !m_bInitialized )
  631. return;
  632. m_bInitialized = false;
  633. if ( IsDirty() )
  634. {
  635. Save();
  636. }
  637. // No matter what, create the manifest if it doesn't exist on the HD yet
  638. else if ( m_bSaveManifest && !ManifestExists() )
  639. {
  640. SaveManifest();
  641. }
  642. m_Elements.RemoveAll();
  643. }
  644. template <class T>
  645. bool CUtlCachedFileData<T>::ManifestExists()
  646. {
  647. char manifest_name[ 512 ];
  648. Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) );
  649. Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) );
  650. return g_pFullFileSystem->FileExists( manifest_name, "MOD" );
  651. }
  652. template <class T>
  653. void CUtlCachedFileData<T>::SaveManifest()
  654. {
  655. // Save manifest out to disk...
  656. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  657. // Sort file alphabetically
  658. CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list;
  659. for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) )
  660. {
  661. ElementType_t& element = m_Elements[ i ];
  662. CSortedCacheFile insert;
  663. insert.handle = element.handle;
  664. insert.index = i;
  665. list.InsertNoSort( insert );
  666. }
  667. list.RedoSort();
  668. for ( int i = 0; i < list.Count(); ++i )
  669. {
  670. ElementType_t &element = m_Elements[ list[ i ].index ];
  671. char fn[ 512 ];
  672. g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) );
  673. buf.Printf( "\"%s\"\r\n", fn );
  674. }
  675. char path[ 512 ];
  676. Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) );
  677. Q_StripFilename( path );
  678. g_pFullFileSystem->CreateDirHierarchy( path, "MOD" );
  679. char manifest_name[ 512 ];
  680. Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) );
  681. Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) );
  682. if ( g_pFullFileSystem->FileExists( manifest_name, "MOD" ) &&
  683. !g_pFullFileSystem->IsFileWritable( manifest_name, "MOD" ) )
  684. {
  685. g_pFullFileSystem->SetFileWritable( manifest_name, true, "MOD" );
  686. }
  687. // Now write to file
  688. FileHandle_t fh;
  689. fh = g_pFullFileSystem->Open( manifest_name, "wb" );
  690. if ( FILESYSTEM_INVALID_HANDLE != fh )
  691. {
  692. g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), fh );
  693. g_pFullFileSystem->Close( fh );
  694. // DevMsg( "Persisting cache manifest '%s' (%d entries)\n", manifest_name, c );
  695. }
  696. else
  697. {
  698. Warning( "Unable to persist cache manifest '%s', check file permissions\n", manifest_name );
  699. }
  700. }
  701. template <class T>
  702. void CUtlCachedFileData<T>::RecheckItem( char const *filename )
  703. {
  704. int idx = GetIndex( filename );
  705. ElementType_t& e = m_Elements[ idx ];
  706. e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
  707. int cachefileinfo = e.fileinfo;
  708. // Set the disk fileinfo the first time we encounter the filename
  709. if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO )
  710. {
  711. if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE )
  712. {
  713. e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" );
  714. // Missing files get a disk file size of 0
  715. if ( e.diskfileinfo == -1 )
  716. {
  717. e.diskfileinfo = 0;
  718. }
  719. }
  720. else
  721. {
  722. e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" );
  723. }
  724. }
  725. Assert( e.dataIndex != m_Data.InvalidIndex() );
  726. T *data = m_Data[ e.dataIndex ];
  727. Assert( data );
  728. // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
  729. if ( cachefileinfo != e.diskfileinfo )
  730. {
  731. if ( !m_bReadOnly )
  732. {
  733. RebuildCache( filename, data );
  734. }
  735. }
  736. e.fileinfo = e.diskfileinfo;
  737. }
  738. template <class T>
  739. T *CUtlCachedFileData<T>::RebuildItem( const char *filename )
  740. {
  741. int idx = GetIndex( filename );
  742. ElementType_t& e = m_Elements[ idx ];
  743. e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
  744. int cachefileinfo = e.fileinfo;
  745. // Set the disk fileinfo the first time we encounter the filename
  746. if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO )
  747. {
  748. if ( m_bNeverCheckDisk )
  749. {
  750. e.diskfileinfo = cachefileinfo;
  751. }
  752. else
  753. {
  754. if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE )
  755. {
  756. e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" );
  757. // Missing files get a disk file size of 0
  758. if ( e.diskfileinfo == -1 )
  759. {
  760. e.diskfileinfo = 0;
  761. }
  762. }
  763. else
  764. {
  765. e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" );
  766. }
  767. }
  768. }
  769. Assert( e.dataIndex != m_Data.InvalidIndex() );
  770. T *data = m_Data[ e.dataIndex ];
  771. Assert( data );
  772. // Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
  773. if ( !m_bReadOnly )
  774. {
  775. RebuildCache( filename, data );
  776. }
  777. e.fileinfo = e.diskfileinfo;
  778. return data;
  779. }
  780. template <class T>
  781. void CUtlCachedFileData<T>::RebuildCache( char const *filename, T *data )
  782. {
  783. Assert( !m_bReadOnly );
  784. // Recache item, mark self as dirty
  785. SetDirty( true );
  786. ((IBaseCacheInfo *)data)->Rebuild( filename );
  787. }
  788. template <class T>
  789. void CUtlCachedFileData<T>::ForceRecheckDiskInfo()
  790. {
  791. for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) )
  792. {
  793. ElementType_t& element = m_Elements[ i ];
  794. element.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
  795. }
  796. }
  797. // Iterates all entries and causes rebuild on any existing items which are out of date
  798. template <class T>
  799. void CUtlCachedFileData<T>::CheckDiskInfo( bool forcerebuild, int cacheFileTime )
  800. {
  801. char fn[ 512 ];
  802. int i;
  803. if ( forcerebuild )
  804. {
  805. for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) )
  806. {
  807. ElementType_t& element = m_Elements[ i ];
  808. g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) );
  809. Get(fn);
  810. }
  811. return;
  812. }
  813. CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list;
  814. for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) )
  815. {
  816. ElementType_t& element = m_Elements[ i ];
  817. CSortedCacheFile insert;
  818. insert.handle = element.handle;
  819. insert.index = i;
  820. list.InsertNoSort( insert );
  821. if ( m_bNeverCheckDisk &&
  822. element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO )
  823. {
  824. element.diskfileinfo = element.fileinfo;
  825. }
  826. }
  827. if ( !list.Count() || m_bNeverCheckDisk )
  828. return;
  829. // Actually sorting by filename here doesn't appear to be a win since FileNameHandle_t is basically sorted by Path anyway (path is stored in the high part of the DWORD)
  830. // list.RedoSort();
  831. bool bSteam = g_pFullFileSystem->IsSteam();
  832. for ( int listStart = 0, listEnd = 0; listStart < list.Count(); listStart = listEnd+1 )
  833. {
  834. int pathIndex = g_pFullFileSystem->GetPathIndex( m_Elements[list[listStart].index].handle );
  835. for ( listEnd = listStart; listEnd < list.Count(); listEnd++ )
  836. {
  837. ElementType_t& element = m_Elements[ list[listEnd].index ];
  838. int pathTest = g_pFullFileSystem->GetPathIndex( element.handle );
  839. if ( pathTest != pathIndex )
  840. break;
  841. }
  842. g_pFullFileSystem->String( m_Elements[list[listStart].index].handle, fn, sizeof( fn ) );
  843. Q_StripFilename( fn );
  844. bool bCheck = true;
  845. if ( !bSteam )
  846. {
  847. int pathTime = g_pFullFileSystem->GetPathTime( fn, "GAME" );
  848. bCheck = (pathTime > cacheFileTime) ? true : false;
  849. }
  850. for ( i = listStart; i < listEnd; i++ )
  851. {
  852. ElementType_t& element = m_Elements[ list[i].index ];
  853. if ( element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO )
  854. {
  855. if ( !bCheck )
  856. {
  857. element.diskfileinfo = element.fileinfo;
  858. }
  859. else
  860. {
  861. g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) );
  862. if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE )
  863. {
  864. element.diskfileinfo = g_pFullFileSystem->Size( fn, "GAME" );
  865. // Missing files get a disk file size of 0
  866. if ( element.diskfileinfo == -1 )
  867. {
  868. element.diskfileinfo = 0;
  869. }
  870. }
  871. else
  872. {
  873. element.diskfileinfo = g_pFullFileSystem->GetFileTime( fn, "GAME" );
  874. }
  875. }
  876. }
  877. }
  878. }
  879. }
  880. template <class T>
  881. void CUtlCachedFileData<T>::SetNeverCheckDisk( bool bNeverCheckDisk )
  882. {
  883. m_bNeverCheckDisk = bNeverCheckDisk;
  884. }
  885. #include "tier0/memdbgoff.h"
  886. #endif // UTLCACHEDFILEDATA_H