Made a bunch of changes to make the resource store system more friendly to multiple stores instead of just a single active one, and set up a resource database for editor assets

This commit is contained in:
parax0
2016-08-31 02:09:13 -06:00
parent 1de2691f33
commit a7b381f301
35 changed files with 848 additions and 178 deletions

View File

@@ -18,17 +18,13 @@
#define EXPORT_COOKED 1
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
: mStore(this)
{
mGameDir = FileUtil::MakeAbsolute(rkInputDir);
mExportDir = FileUtil::MakeAbsolute(rkOutputDir);
mpProject = new CGameProject(mExportDir);
mDiscDir = mpProject->DiscDir(true);
mContentDir = mpProject->ContentDir(false);
mCookedDir = mpProject->CookedDir(false);
mWorldsDirName = L"Worlds\\";
mStore.SetActiveProject(mpProject);
}
#if PUBLIC_RELEASE
@@ -39,18 +35,27 @@ bool CGameExporter::Export()
{
SCOPED_TIMER(ExportGame);
CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = &mStore;
FileUtil::CreateDirectory(mExportDir);
FileUtil::ClearDirectory(mExportDir);
CopyDiscData();
mpStore = new CResourceStore(this, L"Content\\", L"Cooked\\", mpProject->Game());
mpStore->SetProject(mpProject);
mContentDir = mpStore->RawDir(false);
mCookedDir = mpStore->CookedDir(false);
CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = mpStore;
LoadAssetList();
LoadPaks();
ExportWorlds();
ExportCookedResources();
gpResourceStore = pOldStore;
delete mpStore;
mpStore = nullptr;
return true;
}
@@ -464,7 +469,7 @@ void CGameExporter::ExportWorlds()
if (rkRes.Type == "MLVL" && !rkRes.Name.EndsWith("NODEPEND"))
{
// Load world
CWorld *pWorld = (CWorld*) mStore.LoadResource(rkRes.ID, rkRes.Type);
CWorld *pWorld = (CWorld*) mpStore->LoadResource(rkRes.ID, rkRes.Type);
if (!pWorld)
{
@@ -510,7 +515,7 @@ void CGameExporter::ExportWorlds()
}
}
mStore.DestroyUnreferencedResources();
mpStore->DestroyUnreferencedResources();
}
#endif
}
@@ -530,7 +535,7 @@ void CGameExporter::ExportCookedResources()
{
SCOPED_TIMER(SaveResourceDatabase);
#if EXPORT_COOKED
mStore.SaveResourceDatabase();
mpStore->SaveResourceDatabase();
#endif
mpProject->Save();
}
@@ -544,7 +549,7 @@ void CGameExporter::ExportCookedResources()
// todo: we're wasting a ton of time loading the same resources over and over because most resources automatically
// load all their dependencies and then we just clear it out from memory even though we'll need it again later. we
// should really be doing this by dependency order instead of by ID order.
for (CResourceIterator It(&mStore); It; ++It)
for (CResourceIterator It(mpStore); It; ++It)
{
if (!It->IsTransient())
{
@@ -571,7 +576,7 @@ void CGameExporter::ExportCookedResources()
{
// All resources should have dependencies generated, so save the cache file
SCOPED_TIMER(SaveResourceCacheData);
mStore.SaveCacheFile();
mpStore->SaveCacheFile();
}
}
@@ -596,7 +601,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
if (OutDir.IsEmpty()) OutDir = L"Uncategorized\\";
// Register resource and write to file
CResourceEntry *pEntry = mStore.RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
CResourceEntry *pEntry = mpStore->RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
#if EXPORT_COOKED
// Save cooked asset

View File

@@ -13,7 +13,7 @@ class CGameExporter
{
// Project
CGameProject *mpProject;
CResourceStore mStore;
CResourceStore *mpStore;
// Directories
TWideString mGameDir;

View File

@@ -7,10 +7,7 @@ CGameProject *CGameProject::mspActiveProject = nullptr;
CGameProject::~CGameProject()
{
if (IsActive())
{
mspActiveProject = nullptr;
gpResourceStore->SetActiveProject(nullptr);
}
}
bool CGameProject::Load(const TWideString& rkPath)
@@ -21,6 +18,8 @@ bool CGameProject::Load(const TWideString& rkPath)
TString ProjPath = rkPath.ToUTF8();
CXMLReader Reader(ProjPath);
Serialize(Reader);
mpResourceStore->LoadResourceDatabase();
return true;
}
@@ -72,7 +71,7 @@ void CGameProject::SetActive()
if (mspActiveProject != this)
{
mspActiveProject = this;
gpResourceStore->SetActiveProject(this);
gpResourceStore = mpResourceStore;
}
}

View File

@@ -16,6 +16,7 @@ class CGameProject
TWideString mProjectRoot;
TWideString mResourceDBPath;
std::vector<CPackage*> mPackages;
CResourceStore *mpResourceStore;
enum EProjectVersion
{
@@ -31,7 +32,9 @@ public:
CGameProject()
: mGame(eUnknownGame)
, mProjectName("Unnamed Project")
{}
{
mpResourceStore = new CResourceStore(this);
}
CGameProject(const TWideString& rkProjRootDir)
: mGame(eUnknownGame)
@@ -39,6 +42,7 @@ public:
, mProjectRoot(rkProjRootDir)
, mResourceDBPath(L"ResourceDB.rdb")
{
mpResourceStore = new CResourceStore(this);
mProjectRoot.Replace(L"/", L"\\");
}
@@ -55,8 +59,6 @@ public:
inline TWideString ResourceDBPath(bool Relative) const { return Relative ? mResourceDBPath : mProjectRoot + mResourceDBPath; }
inline TWideString DiscDir(bool Relative) const { return Relative ? L"Disc\\" : mProjectRoot + L"Disc\\"; }
inline TWideString CacheDir(bool Relative) const { return Relative ? L"Cache\\" : mProjectRoot + L"Cache\\"; }
inline TWideString ContentDir(bool Relative) const { return Relative ? L"Content\\" : mProjectRoot + L"Content\\"; }
inline TWideString CookedDir(bool Relative) const { return Relative ? L"Cooked\\" : mProjectRoot + L"Cooked\\"; }
inline TWideString PackagesDir(bool Relative) const { return Relative ? L"Packages\\" : mProjectRoot + L"Packages\\"; }
inline TWideString ProjectPath() const { return mProjectRoot + FileUtil::SanitizeName(mProjectName.ToUTF16(), false) + L".prj"; }
inline TWideString ResourceCachePath(bool Relative) const { return ResourceDBPath(Relative).GetFileDirectory() + L"ResourceCacheData.rcd"; }
@@ -68,7 +70,7 @@ public:
inline u32 NumPackages() const { return mPackages.size(); }
inline CPackage* PackageByIndex(u32 Index) const { return mPackages[Index]; }
inline void AddPackage(CPackage *pPackage) { mPackages.push_back(pPackage); }
inline CResourceStore* ResourceStore() const { return mpResourceStore; }
inline EGame Game() const { return mGame; }
inline bool IsActive() const { return mspActiveProject == this; }

View File

@@ -25,7 +25,7 @@ CResourceEntry::CResourceEntry(CResourceStore *pStore, const CAssetID& rkID,
mpDirectory = mpStore->GetVirtualDirectory(rkDir, Transient, true);
if (mpDirectory) mpDirectory->AddChild(L"", this);
mGame = ((Transient || !mpStore->ActiveProject()) ? eUnknownGame : mpStore->ActiveProject()->Game());
mGame = ((Transient || !mpStore) ? eUnknownGame : mpStore->Game());
}
CResourceEntry::~CResourceEntry()
@@ -80,11 +80,6 @@ void CResourceEntry::UpdateDependencies()
mpStore->DestroyUnreferencedResources();
}
TWideString CResourceEntry::CacheDataPath(bool Relative) const
{
return mpStore->ActiveProject()->CacheDir(Relative) + mID.ToString().ToUTF16() + L".rcd";
}
bool CResourceEntry::HasRawVersion() const
{
return FileUtil::Exists(RawAssetPath());
@@ -100,7 +95,12 @@ TString CResourceEntry::RawAssetPath(bool Relative) const
TWideString Ext = GetResourceRawExtension(mType, mGame).ToUTF16();
TWideString Path = mpDirectory ? mpDirectory->FullPath() : L"";
TWideString Name = mName + L"." + Ext;
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->ContentDir(false) + Path + Name);
return ((IsTransient() || Relative) ? Path + Name : mpStore->RawDir(false) + Path + Name);
}
TString CResourceEntry::RawExtension() const
{
return GetResourceRawExtension(mType, mGame);
}
TString CResourceEntry::CookedAssetPath(bool Relative) const
@@ -108,7 +108,7 @@ TString CResourceEntry::CookedAssetPath(bool Relative) const
TWideString Ext = GetResourceCookedExtension(mType, mGame).ToUTF16();
TWideString Path = mpDirectory ? mpDirectory->FullPath() : L"";
TWideString Name = mName + L"." + Ext;
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->CookedDir(false) + Path + Name);
return ((IsTransient() || Relative) ? Path + Name : mpStore->CookedDir(false) + Path + Name);
}
CFourCC CResourceEntry::CookedExtension() const
@@ -218,8 +218,14 @@ CResource* CResourceEntry::Load()
if (mpResource)
{
// Set gpResourceStore to ensure the correct resource store is accessed by loader functions
CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = mpStore;
CXMLReader Reader(RawAssetPath());
mpResource->Serialize(Reader);
gpResourceStore = pOldStore;
}
return mpResource;
@@ -251,8 +257,14 @@ CResource* CResourceEntry::LoadCooked(IInputStream& rInput)
if (mpResource) return mpResource;
if (!rInput.IsValid()) return nullptr;
// Set gpResourceStore to ensure the correct resource store is accessed by loader functions
CResourceStore *pOldStore = gpResourceStore;
gpResourceStore = mpStore;
mpResource = CResourceFactory::LoadCookedResource(this, rInput);
mpStore->TrackLoadedResource(this);
gpResourceStore = pOldStore;
return mpResource;
}

View File

@@ -46,11 +46,11 @@ public:
void SerializeCacheData(IArchive& rArc);
void UpdateDependencies();
TWideString CacheDataPath(bool Relative = false) const;
bool HasRawVersion() const;
bool HasCookedVersion() const;
TString RawAssetPath(bool Relative = false) const;
TString RawExtension() const;
TString CookedAssetPath(bool Relative = false) const;
CFourCC CookedExtension() const;
bool IsInDirectory(CVirtualDirectory *pDir) const;
@@ -70,6 +70,7 @@ public:
inline bool IsLoaded() const { return mpResource != nullptr; }
inline CResource* Resource() const { return mpResource; }
inline CResourceStore* ResourceStore() const { return mpStore; }
inline CDependencyTree* Dependencies() const { return mpDependencies; }
inline CAssetID ID() const { return mID; }
inline EGame Game() const { return mGame; }

View File

@@ -11,23 +11,40 @@
#include <tinyxml2.h>
using namespace tinyxml2;
CResourceStore *gpResourceStore = new CResourceStore;
CResourceStore *gpResourceStore = nullptr;
CResourceStore *gpEditorStore = nullptr;
CResourceStore::CResourceStore()
CResourceStore::CResourceStore(const TWideString& rkDatabasePath)
: mpProj(nullptr)
, mpProjectRoot(nullptr)
, mGame(eUnknownGame)
, mpExporter(nullptr)
{}
{
mpDatabaseRoot = new CVirtualDirectory();
mDatabasePath = FileUtil::MakeAbsolute(rkDatabasePath.GetFileDirectory());
mDatabaseName = rkDatabasePath.GetFileName();
}
CResourceStore::CResourceStore(CGameExporter *pExporter)
CResourceStore::CResourceStore(CGameExporter *pExporter, const TWideString& rkRawDir, const TWideString& rkCookedDir, EGame Game)
: mpProj(nullptr)
, mpProjectRoot(nullptr)
, mGame(Game)
, mRawDir(rkRawDir)
, mCookedDir(rkCookedDir)
, mpExporter(pExporter)
{}
{
}
CResourceStore::CResourceStore(CGameProject *pProject)
: mpProj(nullptr)
, mGame(eUnknownGame)
, mpDatabaseRoot(nullptr)
, mpExporter(nullptr)
{
SetProject(pProject);
}
CResourceStore::~CResourceStore()
{
CloseActiveProject();
CloseProject();
DestroyUnreferencedResources();
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
@@ -66,7 +83,9 @@ void CResourceStore::SerializeResourceDatabase(IArchive& rArc)
}
// Serialize
rArc << SERIAL_CONTAINER_AUTO(Resources, "Resource");
rArc << SERIAL("RawDir", mRawDir)
<< SERIAL("CookedDir", mCookedDir)
<< SERIAL_CONTAINER_AUTO(Resources, "Resource");
// Register resources
if (rArc.IsReader())
@@ -81,27 +100,29 @@ void CResourceStore::SerializeResourceDatabase(IArchive& rArc)
void CResourceStore::LoadResourceDatabase()
{
ASSERT(mpProj);
TString Path = mpProj->ResourceDBPath(false).ToUTF8();
ASSERT(!mDatabasePath.IsEmpty());
TString Path = DatabasePath().ToUTF8();
if (!mpDatabaseRoot)
mpDatabaseRoot = new CVirtualDirectory();
CXMLReader Reader(Path);
if (!mpProj) mGame = Reader.Game();
SerializeResourceDatabase(Reader);
LoadCacheFile();
}
void CResourceStore::SaveResourceDatabase()
{
ASSERT(mpProj);
TString Path = mpProj->ResourceDBPath(false).ToUTF8();
CXMLWriter Writer(Path, "ResourceDB", 0, mpProj ? mpProj->Game() : eUnknownGame);
TString Path = DatabasePath().ToUTF8();
CXMLWriter Writer(Path, "ResourceDB", 0, mGame);
SerializeResourceDatabase(Writer);
}
void CResourceStore::LoadCacheFile()
{
TString CacheDataPath = mpProj->ResourceCachePath(false).ToUTF8();
CFileInStream CacheFile(CacheDataPath.ToStdString(), IOUtil::eBigEndian);
TString CachePath = CacheDataPath().ToUTF8();
CFileInStream CacheFile(CachePath.ToStdString(), IOUtil::eBigEndian);
ASSERT(CacheFile.IsValid());
// Cache header
@@ -141,13 +162,13 @@ void CResourceStore::LoadCacheFile()
void CResourceStore::SaveCacheFile()
{
TString CacheDataPath = mpProj->ResourceCachePath(false).ToUTF8();
CFileOutStream CacheFile(CacheDataPath.ToStdString(), IOUtil::eBigEndian);
TString CachePath = CacheDataPath().ToUTF8();
CFileOutStream CacheFile(CachePath.ToStdString(), IOUtil::eBigEndian);
ASSERT(CacheFile.IsValid());
// Cache header
CFourCC("CACH").Write(CacheFile);
CSerialVersion Version(0, 0, mpProj->Game());
CSerialVersion Version(0, 0, mGame);
Version.Write(CacheFile);
u32 ResCountOffset = CacheFile.Tell();
@@ -184,23 +205,26 @@ void CResourceStore::SaveCacheFile()
CacheFile.WriteLong(ResCount);
}
void CResourceStore::SetActiveProject(CGameProject *pProj)
void CResourceStore::SetProject(CGameProject *pProj)
{
if (mpProj == pProj) return;
CloseActiveProject();
if (mpProj)
CloseProject();
mpProj = pProj;
if (pProj)
if (mpProj)
{
mpProjectRoot = new CVirtualDirectory();
if (!mpExporter)
LoadResourceDatabase();
TWideString DatabasePath = mpProj->ResourceDBPath(false);
mDatabasePath = DatabasePath.GetFileDirectory();
mDatabaseName = DatabasePath.GetFileName();
mpDatabaseRoot = new CVirtualDirectory();
mGame = mpProj->Game();
}
}
void CResourceStore::CloseActiveProject()
void CResourceStore::CloseProject()
{
// Destroy unreferenced resources first. (This is necessary to avoid invalid memory accesses when
// various TResPtrs are destroyed. There might be a cleaner solution than this.)
@@ -233,14 +257,15 @@ void CResourceStore::CloseActiveProject()
It++;
}
delete mpProjectRoot;
mpProjectRoot = nullptr;
delete mpDatabaseRoot;
mpDatabaseRoot = nullptr;
mpProj = nullptr;
mGame = eUnknownGame;
}
CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate)
{
if (rkPath.IsEmpty()) return nullptr;
if (rkPath.IsEmpty()) return mpDatabaseRoot;
else if (Transient)
{
@@ -260,9 +285,9 @@ CVirtualDirectory* CResourceStore::GetVirtualDirectory(const TWideString& rkPath
else return nullptr;
}
else if (mpProjectRoot)
else if (mpDatabaseRoot)
{
return mpProjectRoot->FindChildDirectory(rkPath, AllowCreate);
return mpDatabaseRoot->FindChildDirectory(rkPath, AllowCreate);
}
else return nullptr;
@@ -276,6 +301,11 @@ CResourceEntry* CResourceStore::FindEntry(const CAssetID& rkID) const
else return Found->second;
}
CResourceEntry* CResourceStore::FindEntry(const TWideString& rkPath) const
{
return (mpDatabaseRoot ? mpDatabaseRoot->FindChildResource(rkPath) : nullptr);
}
bool CResourceStore::IsResourceRegistered(const CAssetID& rkID) const
{
return FindEntry(rkID) == nullptr;
@@ -382,10 +412,37 @@ CResource* CResourceStore::LoadResource(const CAssetID& rkID, const CFourCC& rkT
}
}
CResource* CResourceStore::LoadResource(const TString& rkPath)
CResource* CResourceStore::LoadResource(const TWideString& rkPath)
{
// todo - support loading raw resources from arbitrary directory
// Construct ID from string, check if resource is loaded already
// If this is a relative path then load via the resource DB
if (!FileUtil::IsAbsolute(rkPath))
{
CResourceEntry *pEntry = FindEntry(rkPath);
if (pEntry)
{
// Verify extension matches the entry + load resource
TString Ext = rkPath.ToUTF8().GetFileExtension();
if (!Ext.IsEmpty())
{
if (Ext.Length() == 4)
{
ASSERT(Ext.CaseInsensitiveCompare(pEntry->CookedExtension().ToString()));
}
else
{
ASSERT(Ext.CaseInsensitiveCompare(pEntry->RawExtension()));
}
}
return pEntry->Load();
}
else return nullptr;
}
// Otherwise create transient entry; construct ID from string, check if resource is loaded already
TWideString Dir = FileUtil::MakeAbsolute(TWideString(rkPath.GetFileDirectory()));
TString Name = rkPath.GetFileName(false);
CAssetID ID = (Name.IsHexString() ? Name.ToInt64() : rkPath.Hash64());
@@ -395,21 +452,22 @@ CResource* CResourceStore::LoadResource(const TString& rkPath)
return Find->second->Resource();
// Determine type
TString Extension = rkPath.GetFileExtension().ToUpper();
TString PathUTF8 = rkPath.ToUTF8();
TString Extension = TString(PathUTF8).GetFileExtension().ToUpper();
EResType Type = CResource::ResTypeForExtension(Extension);
if (Type == eInvalidResType)
{
Log::Error("Unable to load resource " + rkPath + "; unrecognized extension: " + Extension);
Log::Error("Unable to load resource " + PathUTF8 + "; unrecognized extension: " + Extension);
return nullptr;
}
// Open file
CFileInStream File(rkPath.ToStdString(), IOUtil::eBigEndian);
CFileInStream File(PathUTF8.ToStdString(), IOUtil::eBigEndian);
if (!File.IsValid())
{
Log::Error("Unable to load resource; couldn't open file: " + rkPath);
Log::Error("Unable to load resource; couldn't open file: " + PathUTF8);
return nullptr;
}

View File

@@ -5,6 +5,7 @@
#include "Core/Resource/EResType.h"
#include <Common/CAssetID.h>
#include <Common/CFourCC.h>
#include <Common/FileUtil.h>
#include <Common/TString.h>
#include <Common/types.h>
#include <map>
@@ -19,12 +20,17 @@ class CResourceStore
friend class CResourceIterator;
CGameProject *mpProj;
CVirtualDirectory *mpProjectRoot;
EGame mGame;
CVirtualDirectory *mpDatabaseRoot;
std::vector<CVirtualDirectory*> mTransientRoots;
std::map<CAssetID, CResourceEntry*> mResourceEntries;
std::map<CAssetID, CResourceEntry*> mLoadedResources;
// Directory to look for transient resources in
// Directory paths
TWideString mDatabasePath;
TWideString mDatabaseName;
TWideString mRawDir;
TWideString mCookedDir;
TWideString mTransientLoadDir;
// Game exporter currently in use - lets us load from paks being exported
@@ -39,26 +45,28 @@ class CResourceStore
};
public:
CResourceStore();
CResourceStore(CGameExporter *pExporter);
CResourceStore(const TWideString& rkDatabasePath);
CResourceStore(CGameExporter *pExporter, const TWideString& rkRawDir, const TWideString& rkCookedDir, EGame Game);
CResourceStore(CGameProject *pProject);
~CResourceStore();
void SerializeResourceDatabase(IArchive& rArc);
void LoadResourceDatabase();
void SaveResourceDatabase();
void LoadCacheFile();
void SaveCacheFile();
void SetActiveProject(CGameProject *pProj);
void CloseActiveProject();
void SetProject(CGameProject *pProj);
void CloseProject();
CVirtualDirectory* GetVirtualDirectory(const TWideString& rkPath, bool Transient, bool AllowCreate);
bool IsResourceRegistered(const CAssetID& rkID) const;
CResourceEntry* RegisterResource(const CAssetID& rkID, EResType Type, const TWideString& rkDir, const TWideString& rkFileName);
CResourceEntry* FindEntry(const CAssetID& rkID) const;
CResourceEntry* FindEntry(const TWideString& rkPath) const;
CResourceEntry* RegisterTransientResource(EResType Type, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
CResourceEntry* RegisterTransientResource(EResType Type, const CAssetID& rkID, const TWideString& rkDir = L"", const TWideString& rkFileName = L"");
CResource* LoadResource(const CAssetID& rkID, const CFourCC& rkType);
CResource* LoadResource(const TString& rkPath);
CResource* LoadResource(const TWideString& rkPath);
void TrackLoadedResource(CResourceEntry *pEntry);
CFourCC ResourceTypeByID(const CAssetID& rkID, const TStringList& rkPossibleTypes) const;
void DestroyUnreferencedResources();
@@ -66,12 +74,19 @@ public:
void SetTransientLoadDir(const TString& rkDir);
// Accessors
inline CGameProject* ActiveProject() const { return mpProj; }
inline CVirtualDirectory* RootDirectory() const { return mpProjectRoot; }
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
inline CGameProject* Project() const { return mpProj; }
inline EGame Game() const { return mGame; }
inline TWideString DatabaseRootPath() const { return mDatabasePath; }
inline TWideString RawDir(bool Relative) const { return Relative ? mRawDir : mDatabasePath + mRawDir; }
inline TWideString CookedDir(bool Relative) const { return Relative ? mCookedDir : mDatabasePath + mCookedDir; }
inline TWideString DatabasePath() const { return DatabaseRootPath() + mDatabaseName; }
inline TWideString CacheDataPath() const { return DatabaseRootPath() + L"ResourceCacheData.rcd"; }
inline CVirtualDirectory* RootDirectory() const { return mpDatabaseRoot; }
inline u32 NumTotalResources() const { return mResourceEntries.size(); }
inline u32 NumLoadedResources() const { return mLoadedResources.size(); }
};
extern CResourceStore *gpResourceStore;
extern CResourceStore *gpEditorStore;
#endif // CRESOURCEDATABASE_H

View File

@@ -78,12 +78,24 @@ CVirtualDirectory* CVirtualDirectory::FindChildDirectory(const TWideString& rkNa
return nullptr;
}
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkName) const
CResourceEntry* CVirtualDirectory::FindChildResource(const TWideString& rkPath)
{
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
TWideString Dir = rkPath.GetFileDirectory();
TWideString Name = rkPath.GetFileName(false);
if (!Dir.IsEmpty())
{
if (mResources[iRes]->Name() == rkName)
return mResources[iRes];
CVirtualDirectory *pDir = FindChildDirectory(Dir, false);
if (pDir) return pDir->FindChildResource(Name);
}
else
{
for (u32 iRes = 0; iRes < mResources.size(); iRes++)
{
if (mResources[iRes]->Name() == Name)
return mResources[iRes];
}
}
return nullptr;

View File

@@ -25,7 +25,7 @@ public:
TWideString FullPath() const;
CVirtualDirectory* GetRoot();
CVirtualDirectory* FindChildDirectory(const TWideString& rkName, bool AllowCreate);
CResourceEntry* FindChildResource(const TWideString& rkName) const;
CResourceEntry* FindChildResource(const TWideString& rkPath);
void AddChild(const TWideString& rkPath, CResourceEntry *pEntry);
bool RemoveChildDirectory(CVirtualDirectory *pSubdir);
bool RemoveChildResource(CResourceEntry *pEntry);