|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#ifndef UTLCACHEDFILEDATA_H
#define UTLCACHEDFILEDATA_H
#if defined( WIN32 )
#pragma once
#endif
#include "filesystem.h" // FileNameHandle_t
#include "utlrbtree.h"
#include "utlbuffer.h"
#include "utlsortvector.h"
#include "tier1/strtools.h"
#include "tier0/memdbgon.h"
// If you change to serialization protocols, this must be bumped...
#define UTL_CACHE_SYSTEM_VERSION 2
#define UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO (int)-2
// Cacheable types must derive from this and implement the appropriate methods...
abstract_class IBaseCacheInfo { public: virtual void Save( CUtlBuffer& buf ) = 0; virtual void Restore( CUtlBuffer& buf ) = 0;
virtual void Rebuild( char const *filename ) = 0; };
typedef unsigned int (*PFNCOMPUTECACHEMETACHECKSUM)( void );
typedef enum { UTL_CACHED_FILE_USE_TIMESTAMP = 0, UTL_CACHED_FILE_USE_FILESIZE, } UtlCachedFileDataType_t;
template <class T> class CUtlCachedFileData { public: CUtlCachedFileData ( char const *repositoryFileName, int version, PFNCOMPUTECACHEMETACHECKSUM checksumfunc = NULL, UtlCachedFileDataType_t fileCheckType = UTL_CACHED_FILE_USE_TIMESTAMP, bool nevercheckdisk = false, bool readonly = false, bool savemanifest = false ) : m_Elements( 0, 0, FileNameHandleLessFunc ), m_sRepositoryFileName( repositoryFileName ), m_nVersion( version ), m_pfnMetaChecksum( checksumfunc ), m_bDirty( false ), m_bInitialized( false ), m_uCurrentMetaChecksum( 0u ), m_fileCheckType( fileCheckType ), m_bNeverCheckDisk( nevercheckdisk ), m_bReadOnly( readonly ), m_bSaveManifest( savemanifest ) { Assert( m_sRepositoryFileName.Length() > 0 ); }
virtual ~CUtlCachedFileData() { m_Elements.RemoveAll(); int c = m_Data.Count(); for ( int i = 0; i < c ; ++i ) { delete m_Data[ i ]; } m_Data.RemoveAll(); }
T* Get( char const *filename ); const T* Get( char const *filename ) const;
T* operator[]( int i ); const T* operator[]( int i ) const;
int Count() const;
void GetElementName( int i, char *buf, int buflen ) { buf[ 0 ] = 0; if ( !m_Elements.IsValidIndex( i ) ) return;
g_pFullFileSystem->String( m_Elements[ i ].handle, buf, buflen ); }
bool EntryExists( char const *filename ) const { ElementType_t element; element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); int idx = m_Elements.Find( element ); return idx != m_Elements.InvalidIndex() ? true : false; }
void SetElement( char const *name, int fileinfo, T* src ) { SetDirty( true );
int idx = GetIndex( name );
Assert( idx != m_Elements.InvalidIndex() );
ElementType_t& e = m_Elements[ idx ];
CUtlBuffer buf( 0, 0, 0 );
Assert( e.dataIndex != m_Data.InvalidIndex() );
T *dest = m_Data[ e.dataIndex ];
Assert( dest );
// I suppose we could do an assignment operator, but this should save/restore the data element just fine for
// tool purposes
((IBaseCacheInfo *)src)->Save( buf ); ((IBaseCacheInfo *)dest)->Restore( buf );
e.fileinfo = fileinfo; if ( ( e.fileinfo == -1 ) && ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) { e.fileinfo = 0; } // Force recheck
e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; }
// 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
// will cause a rebuild...
bool IsUpToDate();
void Shutdown(); bool Init();
void Save();
void Reload();
void ForceRecheckDiskInfo(); // Iterates all entries and gets filesystem info and optionally causes rebuild on any existing items which are out of date
void CheckDiskInfo( bool force_rebuild, int cacheFileTime = 0L );
void SaveManifest(); bool ManifestExists();
int GetFileInfo( char const *filename ) { ElementType_t element; element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); int idx = m_Elements.Find( element ); if ( idx == m_Elements.InvalidIndex() ) { return 0L; }
return m_Elements[ idx ].fileinfo; } int GetFileInfo( int idx ) { if ( !m_Elements.IsValidIndex( idx ) ) return 0L; return m_Elements[ idx ].fileinfo; }
int GetNumElements() { return m_Elements.Count(); }
bool IsDirty() const { return m_bDirty; }
T *RebuildItem( const char *filename );
void SetNeverCheckDisk( bool bNeverCheckDisk );
void RecheckItem( char const *filename );
private:
void InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile ); void InitLargeBuffer( FileHandle_t& fh, bool& deleteFile );
int GetIndex( const char *filename ) { ElementType_t element; element.handle = g_pFullFileSystem->FindOrAddFileName( filename ); int idx = m_Elements.Find( element ); if ( idx == m_Elements.InvalidIndex() ) { T *data = new T();
int dataIndex = m_Data.AddToTail( data ); idx = m_Elements.Insert( element ); m_Elements[ idx ].dataIndex = dataIndex; }
return idx; }
void CheckInit();
void SetDirty( bool dirty ) { m_bDirty = dirty; }
void RebuildCache( char const *filename, T *data );
struct ElementType_t { ElementType_t() : handle( 0 ), fileinfo( 0 ), diskfileinfo( UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ), dataIndex( -1 ) { }
FileNameHandle_t handle; int fileinfo; int diskfileinfo; int dataIndex; };
static bool FileNameHandleLessFunc( ElementType_t const &lhs, ElementType_t const &rhs ) { return lhs.handle < rhs.handle; }
CUtlRBTree< ElementType_t > m_Elements; CUtlVector< T * > m_Data; CUtlString m_sRepositoryFileName; int m_nVersion; PFNCOMPUTECACHEMETACHECKSUM m_pfnMetaChecksum; unsigned int m_uCurrentMetaChecksum; UtlCachedFileDataType_t m_fileCheckType; bool m_bNeverCheckDisk : 1; bool m_bReadOnly : 1; bool m_bSaveManifest : 1; bool m_bDirty : 1; bool m_bInitialized : 1;
};
template <class T> T* CUtlCachedFileData<T>::Get( char const *filename ) { int idx = GetIndex( filename );
ElementType_t& e = m_Elements[ idx ];
if ( e.fileinfo == -1 && m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) { e.fileinfo = 0; } int cachefileinfo = e.fileinfo; // Set the disk fileinfo the first time we encounter the filename
if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) { if ( m_bNeverCheckDisk ) { e.diskfileinfo = cachefileinfo; } else { if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) { e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); // Missing files get a disk file size of 0
if ( e.diskfileinfo == -1 ) { e.diskfileinfo = 0; } } else { e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); } } }
Assert( e.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ e.dataIndex ];
Assert( data );
// Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
if ( cachefileinfo != e.diskfileinfo ) { if ( !m_bReadOnly ) { RebuildCache( filename, data ); } e.fileinfo = e.diskfileinfo; }
return data; }
template <class T> const T* CUtlCachedFileData<T>::Get( char const *filename ) const { return const_cast< CUtlCachedFileData<T> * >(this)->Get( filename ); }
template <class T> T* CUtlCachedFileData<T>::operator[]( int i ) { return m_Data[ m_Elements[ i ].dataIndex ]; }
template <class T> const T* CUtlCachedFileData<T>::operator[]( int i ) const { return m_Data[ m_Elements[ i ].dataIndex ]; }
template <class T> int CUtlCachedFileData<T>::Count() const { return m_Elements.Count(); }
template <class T> void CUtlCachedFileData<T>::Reload() { Shutdown(); Init(); }
template <class T> bool CUtlCachedFileData<T>::IsUpToDate() { // Don't call Init/Shutdown if using this method!!!
Assert( !m_bInitialized );
if ( !m_sRepositoryFileName.Length() ) { Error( "CUtlCachedFileData: Can't IsUpToDate, no repository file specified." ); return false; }
// Always compute meta checksum
m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0;
FileHandle_t fh;
fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" ); if ( fh == FILESYSTEM_INVALID_HANDLE ) { return false; }
// Version data is in first 12 bytes of file
byte header[ 12 ]; g_pFullFileSystem->Read( header, sizeof( header ), fh ); g_pFullFileSystem->Close( fh );
int cacheversion = *( int *)&header[ 0 ];
if ( UTL_CACHE_SYSTEM_VERSION != cacheversion ) { DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); Assert( !m_bReadOnly ); if ( !m_bReadOnly ) { g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); } return false; }
// Now parse data from the buffer
int version = *( int *)&header[ 4 ]; if ( version != m_nVersion ) { DevMsg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); Assert( !m_bReadOnly ); if ( !m_bReadOnly ) { g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); } return false; }
// This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
// meta data function
if ( m_pfnMetaChecksum ) { unsigned int cache_meta_checksum = *( unsigned int *)&header[ 8 ]; if ( cache_meta_checksum != m_uCurrentMetaChecksum ) { DevMsg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); Assert( !m_bReadOnly ); if ( !m_bReadOnly ) { g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); } return false; } }
// Looks valid
return true; }
template <class T> void CUtlCachedFileData<T>::InitSmallBuffer( FileHandle_t& fh, int fileSize, bool& deleteFile ) { deleteFile = false;
CUtlBuffer loadBuf; g_pFullFileSystem->ReadToBuffer( fh, loadBuf ); g_pFullFileSystem->Close( fh );
int cacheversion = 0; loadBuf.Get( &cacheversion, sizeof( cacheversion ) );
if ( UTL_CACHE_SYSTEM_VERSION == cacheversion ) { // Now parse data from the buffer
int version = loadBuf.GetInt(); if ( version == m_nVersion ) { // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
// meta data function
unsigned int cache_meta_checksum = loadBuf.GetInt(); if ( !m_pfnMetaChecksum || ( cache_meta_checksum == m_uCurrentMetaChecksum ) ) { int count = loadBuf.GetInt(); Assert( count < 2000000 );
CUtlBuffer buf( 0, 0, 0 );
for ( int i = 0 ; i < count; ++i ) { int bufsize = loadBuf.GetInt(); Assert( bufsize < 1000000 );
buf.Clear(); buf.EnsureCapacity( bufsize ); loadBuf.Get( buf.Base(), bufsize );
buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, bufsize );
// Read the element name
char elementFileName[ 512 ]; buf.GetString( elementFileName, sizeof( elementFileName ) );
// Now read the element
int slot = GetIndex( elementFileName );
Assert( slot != m_Elements.InvalidIndex() );
ElementType_t& element = m_Elements[ slot ];
element.fileinfo = buf.GetInt(); if ( ( element.fileinfo == -1 ) && ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) { element.fileinfo = 0; }
Assert( element.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ element.dataIndex ];
Assert( data );
((IBaseCacheInfo *)data)->Restore( buf ); } } else { Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); deleteFile = true; } } else { Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); deleteFile = true; } } else { DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); deleteFile = true; } }
template <class T> void CUtlCachedFileData<T>::InitLargeBuffer( FileHandle_t& fh, bool& deleteFile ) { deleteFile = false;
int cacheversion = 0; g_pFullFileSystem->Read( &cacheversion, sizeof( cacheversion ), fh );
if ( UTL_CACHE_SYSTEM_VERSION == cacheversion ) { // Now parse data from the buffer
int version = 0; g_pFullFileSystem->Read( &version, sizeof( version ), fh ); if ( version == m_nVersion ) { // This is a checksum based on any meta data files which the cache depends on (supplied by a passed in
// meta data function
unsigned int cache_meta_checksum = 0; g_pFullFileSystem->Read( &cache_meta_checksum, sizeof( cache_meta_checksum ), fh );
if ( !m_pfnMetaChecksum || ( cache_meta_checksum == m_uCurrentMetaChecksum ) ) { int count = 0; g_pFullFileSystem->Read( &count, sizeof( count ), fh );
Assert( count < 2000000 );
CUtlBuffer buf( 0, 0, 0 );
for ( int i = 0 ; i < count; ++i ) { int bufsize = 0; g_pFullFileSystem->Read( &bufsize, sizeof( bufsize ), fh );
Assert( bufsize < 1000000 ); if ( bufsize > 1000000 ) { Msg( "Discarding repository '%s' due to corruption\n", m_sRepositoryFileName.String() ); deleteFile = true; break; }
buf.Clear(); buf.EnsureCapacity( bufsize ); int nBytesRead = g_pFullFileSystem->Read( buf.Base(), bufsize, fh );
buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
// Read the element name
char elementFileName[ 512 ]; buf.GetString( elementFileName, sizeof( elementFileName ) );
// Now read the element
int slot = GetIndex( elementFileName );
Assert( slot != m_Elements.InvalidIndex() );
ElementType_t& element = m_Elements[ slot ];
element.fileinfo = buf.GetInt(); if ( ( element.fileinfo == -1 ) && ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) ) { element.fileinfo = 0; }
Assert( element.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ element.dataIndex ];
Assert( data );
((IBaseCacheInfo *)data)->Restore( buf ); } } else { Msg( "Discarding repository '%s' due to meta checksum change\n", m_sRepositoryFileName.String() ); deleteFile = true; } } else { Msg( "Discarding repository '%s' due to version change\n", m_sRepositoryFileName.String() ); deleteFile = true; } } else { DevMsg( "Discarding repository '%s' due to cache system version change\n", m_sRepositoryFileName.String() ); deleteFile = true; }
g_pFullFileSystem->Close( fh ); }
template <class T> bool CUtlCachedFileData<T>::Init() { if ( m_bInitialized ) { return true; }
m_bInitialized = true;
if ( !m_sRepositoryFileName.Length() ) { Error( "CUtlCachedFileData: Can't Init, no repository file specified." ); return false; }
// Always compute meta checksum
m_uCurrentMetaChecksum = m_pfnMetaChecksum ? (*m_pfnMetaChecksum)() : 0;
FileHandle_t fh;
fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "rb", "MOD" ); if ( fh == FILESYSTEM_INVALID_HANDLE ) { // Nothing on disk, we'll recreate everything from scratch...
if ( !m_bReadOnly ) { SetDirty( true ); } return true; } int fileTime = g_pFullFileSystem->GetFileTime( m_sRepositoryFileName.String(), "MOD" ); int size = g_pFullFileSystem->Size( fh );
bool deletefile = false;
if ( size > 1024 * 1024 ) { InitLargeBuffer( fh, deletefile ); } else { InitSmallBuffer( fh, size, deletefile ); }
if ( deletefile ) { Assert( !m_bReadOnly ); if ( !m_bReadOnly ) { g_pFullFileSystem->RemoveFile( m_sRepositoryFileName.String(), "MOD" ); SetDirty( true ); } } CheckDiskInfo( false, fileTime ); return true; }
class CSortedCacheFile { public: FileNameHandle_t handle; int index;
bool Less( const CSortedCacheFile &file0, const CSortedCacheFile &file1, void * ) { char name0[ 512 ]; char name1[ 512 ]; g_pFullFileSystem->String( file0.handle, name0, sizeof( name0 ) ); g_pFullFileSystem->String( file1.handle, name1, sizeof( name1 ) ); return Q_stricmp( name0, name1 ) < 0 ? true : false; } };
template <class T> void CUtlCachedFileData<T>::Save() { char path[ 512 ]; Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) ); Q_StripFilename( path );
g_pFullFileSystem->CreateDirHierarchy( path, "MOD" );
if ( g_pFullFileSystem->FileExists( m_sRepositoryFileName.String(), "MOD" ) && !g_pFullFileSystem->IsFileWritable( m_sRepositoryFileName.String(), "MOD" ) ) { g_pFullFileSystem->SetFileWritable( m_sRepositoryFileName.String(), true, "MOD" ); }
// Now write to file
FileHandle_t fh; fh = g_pFullFileSystem->Open( m_sRepositoryFileName.String(), "wb" ); if ( FILESYSTEM_INVALID_HANDLE == fh ) { Warning( "Unable to persist cache '%s', check file permissions\n", m_sRepositoryFileName.String() ); } else { SetDirty( false );
int v = UTL_CACHE_SYSTEM_VERSION; g_pFullFileSystem->Write( &v, sizeof( v ), fh ); v = m_nVersion; g_pFullFileSystem->Write( &v, sizeof( v ), fh ); v = (int)m_uCurrentMetaChecksum; g_pFullFileSystem->Write( &v, sizeof( v ), fh );
// Element count
int c = Count();
g_pFullFileSystem->Write( &c, sizeof( c ), fh );
// Save repository back out to disk...
CUtlBuffer buf( 0, 0, 0 );
// Sort file alphabetically
CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list; for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) { ElementType_t& element = m_Elements[ i ]; CSortedCacheFile insert; insert.handle = element.handle; insert.index = i; list.InsertNoSort( insert ); } list.RedoSort();
for ( int i = 0; i < list.Count(); ++i ) { ElementType_t &element = m_Elements[ list[ i ].index ];
buf.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
char fn[ 512 ]; g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); buf.PutString( fn ); buf.PutInt( element.fileinfo );
Assert( element.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ element.dataIndex ];
Assert( data );
((IBaseCacheInfo *)data)->Save( buf );
int bufsize = buf.TellPut(); g_pFullFileSystem->Write( &bufsize, sizeof( bufsize ), fh ); g_pFullFileSystem->Write( buf.Base(), bufsize, fh ); }
g_pFullFileSystem->Close( fh ); }
if ( m_bSaveManifest ) { SaveManifest(); } }
template <class T> void CUtlCachedFileData<T>::Shutdown() { if ( !m_bInitialized ) return;
m_bInitialized = false;
if ( IsDirty() ) { Save(); } // No matter what, create the manifest if it doesn't exist on the HD yet
else if ( m_bSaveManifest && !ManifestExists() ) { SaveManifest(); }
m_Elements.RemoveAll(); }
template <class T> bool CUtlCachedFileData<T>::ManifestExists() { char manifest_name[ 512 ]; Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) ); Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) ); return g_pFullFileSystem->FileExists( manifest_name, "MOD" ); }
template <class T> void CUtlCachedFileData<T>::SaveManifest() { // Save manifest out to disk...
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
// Sort file alphabetically
CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list; for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) { ElementType_t& element = m_Elements[ i ]; CSortedCacheFile insert; insert.handle = element.handle; insert.index = i; list.InsertNoSort( insert ); } list.RedoSort();
for ( int i = 0; i < list.Count(); ++i ) { ElementType_t &element = m_Elements[ list[ i ].index ];
char fn[ 512 ]; g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) );
buf.Printf( "\"%s\"\r\n", fn ); }
char path[ 512 ]; Q_strncpy( path, m_sRepositoryFileName.String(), sizeof( path ) ); Q_StripFilename( path );
g_pFullFileSystem->CreateDirHierarchy( path, "MOD" );
char manifest_name[ 512 ]; Q_strncpy( manifest_name, m_sRepositoryFileName.String(), sizeof( manifest_name ) );
Q_SetExtension( manifest_name, ".manifest", sizeof( manifest_name ) );
if ( g_pFullFileSystem->FileExists( manifest_name, "MOD" ) && !g_pFullFileSystem->IsFileWritable( manifest_name, "MOD" ) ) { g_pFullFileSystem->SetFileWritable( manifest_name, true, "MOD" ); }
// Now write to file
FileHandle_t fh; fh = g_pFullFileSystem->Open( manifest_name, "wb" ); if ( FILESYSTEM_INVALID_HANDLE != fh ) { g_pFullFileSystem->Write( buf.Base(), buf.TellPut(), fh ); g_pFullFileSystem->Close( fh );
// DevMsg( "Persisting cache manifest '%s' (%d entries)\n", manifest_name, c );
} else { Warning( "Unable to persist cache manifest '%s', check file permissions\n", manifest_name ); } }
template <class T> void CUtlCachedFileData<T>::RecheckItem( char const *filename ) { int idx = GetIndex( filename ); ElementType_t& e = m_Elements[ idx ];
e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
int cachefileinfo = e.fileinfo; // Set the disk fileinfo the first time we encounter the filename
if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) { if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) { e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); // Missing files get a disk file size of 0
if ( e.diskfileinfo == -1 ) { e.diskfileinfo = 0; } } else { e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); } }
Assert( e.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ e.dataIndex ];
Assert( data );
// Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
if ( cachefileinfo != e.diskfileinfo ) { if ( !m_bReadOnly ) { RebuildCache( filename, data ); } } e.fileinfo = e.diskfileinfo; }
template <class T> T *CUtlCachedFileData<T>::RebuildItem( const char *filename ) { int idx = GetIndex( filename ); ElementType_t& e = m_Elements[ idx ];
e.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO;
int cachefileinfo = e.fileinfo; // Set the disk fileinfo the first time we encounter the filename
if ( e.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) { if ( m_bNeverCheckDisk ) { e.diskfileinfo = cachefileinfo; } else { if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) { e.diskfileinfo = g_pFullFileSystem->Size( filename, "GAME" ); // Missing files get a disk file size of 0
if ( e.diskfileinfo == -1 ) { e.diskfileinfo = 0; } } else { e.diskfileinfo = g_pFullFileSystem->GetFileTime( filename, "GAME" ); } } }
Assert( e.dataIndex != m_Data.InvalidIndex() );
T *data = m_Data[ e.dataIndex ];
Assert( data );
// Compare fileinfo to disk fileinfo and rebuild cache if out of date or not correct...
if ( !m_bReadOnly ) { RebuildCache( filename, data ); } e.fileinfo = e.diskfileinfo;
return data; }
template <class T> void CUtlCachedFileData<T>::RebuildCache( char const *filename, T *data ) { Assert( !m_bReadOnly );
// Recache item, mark self as dirty
SetDirty( true );
((IBaseCacheInfo *)data)->Rebuild( filename ); }
template <class T> void CUtlCachedFileData<T>::ForceRecheckDiskInfo() { for ( int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) { ElementType_t& element = m_Elements[ i ]; element.diskfileinfo = UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO; } }
// Iterates all entries and causes rebuild on any existing items which are out of date
template <class T> void CUtlCachedFileData<T>::CheckDiskInfo( bool forcerebuild, int cacheFileTime ) { char fn[ 512 ]; int i; if ( forcerebuild ) { for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) { ElementType_t& element = m_Elements[ i ]; g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); Get(fn); } return; }
CUtlSortVector<CSortedCacheFile, CSortedCacheFile> list; for ( i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex(); i = m_Elements.NextInorder( i ) ) { ElementType_t& element = m_Elements[ i ]; CSortedCacheFile insert; insert.handle = element.handle; insert.index = i; list.InsertNoSort( insert ); if ( m_bNeverCheckDisk && element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) { element.diskfileinfo = element.fileinfo; } }
if ( !list.Count() || m_bNeverCheckDisk ) return;
// 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)
// list.RedoSort();
bool bSteam = g_pFullFileSystem->IsSteam();
for ( int listStart = 0, listEnd = 0; listStart < list.Count(); listStart = listEnd+1 ) { int pathIndex = g_pFullFileSystem->GetPathIndex( m_Elements[list[listStart].index].handle ); for ( listEnd = listStart; listEnd < list.Count(); listEnd++ ) { ElementType_t& element = m_Elements[ list[listEnd].index ];
int pathTest = g_pFullFileSystem->GetPathIndex( element.handle ); if ( pathTest != pathIndex ) break; } g_pFullFileSystem->String( m_Elements[list[listStart].index].handle, fn, sizeof( fn ) ); Q_StripFilename( fn );
bool bCheck = true; if ( !bSteam ) { int pathTime = g_pFullFileSystem->GetPathTime( fn, "GAME" ); bCheck = (pathTime > cacheFileTime) ? true : false; }
for ( i = listStart; i < listEnd; i++ ) { ElementType_t& element = m_Elements[ list[i].index ];
if ( element.diskfileinfo == UTL_CACHED_FILE_DATA_UNDEFINED_DISKINFO ) { if ( !bCheck ) { element.diskfileinfo = element.fileinfo; } else { g_pFullFileSystem->String( element.handle, fn, sizeof( fn ) ); if ( m_fileCheckType == UTL_CACHED_FILE_USE_FILESIZE ) { element.diskfileinfo = g_pFullFileSystem->Size( fn, "GAME" );
// Missing files get a disk file size of 0
if ( element.diskfileinfo == -1 ) { element.diskfileinfo = 0; } } else { element.diskfileinfo = g_pFullFileSystem->GetFileTime( fn, "GAME" ); } } } } } }
template <class T> void CUtlCachedFileData<T>::SetNeverCheckDisk( bool bNeverCheckDisk ) { m_bNeverCheckDisk = bNeverCheckDisk; }
#include "tier0/memdbgoff.h"
#endif // UTLCACHEDFILEDATA_H
|