This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

902 lines
27 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#ifndef UTLCACHEDFILEDATA_H
#define UTLCACHEDFILEDATA_H
#if defined(WIN32)
#pragma once
#endif
#include "UtlSortVector.h"
#include "filesystem.h" // FileNameHandle_t
#include "tier1/strtools.h"
#include "utlbuffer.h"
#include "utlrbtree.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 (long)-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.IsEmpty());
}
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, long 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, long cacheFileTime = 0L);
void SaveManifest();
bool ManifestExists();
const char *GetRepositoryFileName() const { return m_sRepositoryFileName; }
long 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 GetNumElements() { return m_Elements.Count(); }
bool IsDirty() const { return m_bDirty; }
T *RebuildItem(const char *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;
long fileinfo;
long 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;
}
long 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.IsEmpty()) {
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, "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, "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, "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
unsigned int cache_meta_checksum = (unsigned int)*(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, "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 (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);
// 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 (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);
// 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.IsEmpty()) {
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, "rb", "MOD");
if (fh == FILESYSTEM_INVALID_HANDLE) {
// Nothing on disk, we'll recreate everything from scratch...
SetDirty(true);
return true;
}
long fileTime =
g_pFullFileSystem->GetFileTime(m_sRepositoryFileName, "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, "MOD");
}
SetDirty(true);
}
CheckDiskInfo(false, fileTime);
return true;
}
template <class T>
void CUtlCachedFileData<T>::Save() {
char path[512];
Q_strncpy(path, m_sRepositoryFileName, sizeof(path));
Q_StripFilename(path);
g_pFullFileSystem->CreateDirHierarchy(path, "MOD");
if (g_pFullFileSystem->FileExists(m_sRepositoryFileName, "MOD") &&
!g_pFullFileSystem->IsFileWritable(m_sRepositoryFileName, "MOD")) {
g_pFullFileSystem->SetFileWritable(m_sRepositoryFileName, true, "MOD");
}
// Now write to file
FileHandle_t fh;
fh = g_pFullFileSystem->Open(m_sRepositoryFileName, "wb");
if (FILESYSTEM_INVALID_HANDLE == fh) {
ExecuteNTimes(
25,
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);
for (int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex();
i = m_Elements.NextInorder(i)) {
buf.SeekPut(CUtlBuffer::SEEK_HEAD, 0);
ElementType_t &element = m_Elements[i];
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, 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);
for (int i = m_Elements.FirstInorder(); i != m_Elements.InvalidIndex();
i = m_Elements.NextInorder(i)) {
ElementType_t &element = m_Elements[i];
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, sizeof(path));
Q_StripFilename(path);
g_pFullFileSystem->CreateDirHierarchy(path, "MOD");
char manifest_name[512];
Q_strncpy(manifest_name, m_sRepositoryFileName, 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>
T *CUtlCachedFileData<T>::RebuildItem(const char *filename) {
int idx = GetIndex(filename);
ElementType_t &e = m_Elements[idx];
ForceRecheckDiskInfo();
long 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;
}
}
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;
}
};
// Iterates all entries and causes rebuild on any existing items which are out
// of date
template <class T>
void CUtlCachedFileData<T>::CheckDiskInfo(bool forcerebuild,
long 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);
}
list.RedoSort();
if (!list.Count()) return;
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 (m_bNeverCheckDisk) {
bCheck = false;
} else {
long 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");
}
}
}
}
}
}
#include "tier0/memdbgoff.h"
#endif // UTLCACHEDFILEDATA_H