Added dependency tree system, methods for generating dependency trees from resources, and saving/loading dependency trees to the project cache folder

This commit is contained in:
parax0
2016-07-25 02:12:30 -06:00
parent c1405bfac1
commit 9341c11ac8
30 changed files with 886 additions and 56 deletions

View File

@@ -0,0 +1,320 @@
#include "CDependencyTree.h"
#include "Core/Resource/Script/CScriptLayer.h"
#include "Core/Resource/Script/CScriptObject.h"
// ************ CResourceDependency ************
EDependencyNodeType CResourceDependency::Type() const
{
return eDNT_ResourceDependency;
}
void CResourceDependency::Read(IInputStream& rFile, EUIDLength IDLength)
{
mID = CUniqueID(rFile, IDLength);
}
void CResourceDependency::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
if (IDLength == e32Bit)
rFile.WriteLong(mID.ToLong());
else
rFile.WriteLongLong(mID.ToLongLong());
}
// ************ CAnimSetDependency ************
EDependencyNodeType CAnimSetDependency::Type() const
{
return eDNT_AnimSet;
}
void CAnimSetDependency::Read(IInputStream& rFile, EUIDLength IDLength)
{
CResourceDependency::Read(rFile, IDLength);
mUsedChar = rFile.ReadLong();
}
void CAnimSetDependency::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
CResourceDependency::Write(rFile, IDLength);
rFile.WriteLong(mUsedChar);
}
// Static
CAnimSetDependency* CAnimSetDependency::BuildDependency(TCharacterProperty *pProp)
{
ASSERT(pProp && pProp->Type() == eCharacterProperty && pProp->Instance()->Area()->Game() <= eEchoes);
CAnimationParameters Params = pProp->Get();
if (!Params.ID().IsValid()) return nullptr;
CAnimSetDependency *pDepend = new CAnimSetDependency;
pDepend->SetID(Params.ID());
pDepend->SetUsedChar(Params.CharacterIndex());
return pDepend;
}
// ************ CDependencyTree ************
CDependencyTree::~CDependencyTree()
{
for (u32 iRef = 0; iRef < mReferencedResources.size(); iRef++)
delete mReferencedResources[iRef];
}
EDependencyNodeType CDependencyTree::Type() const
{
return eDNT_Root;
}
void CDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
{
mID = CUniqueID(rFile, IDLength);
u32 NumDepends = rFile.ReadLong();
mReferencedResources.reserve(NumDepends);
for (u32 iDep = 0; iDep < NumDepends; iDep++)
{
CResourceDependency *pDepend = new CResourceDependency;
pDepend->Read(rFile, IDLength);
mReferencedResources.push_back(pDepend);
}
}
void CDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
if (IDLength == e32Bit)
rFile.WriteLong(mID.ToLong());
else
rFile.WriteLongLong(mID.ToLongLong());
rFile.WriteLong( mReferencedResources.size() );
for (u32 iDep = 0; iDep < mReferencedResources.size(); iDep++)
mReferencedResources[iDep]->Write(rFile, IDLength);
}
u32 CDependencyTree::NumDependencies() const
{
return mReferencedResources.size();
}
bool CDependencyTree::HasDependency(const CUniqueID& rkID)
{
for (u32 iDep = 0; iDep < mReferencedResources.size(); iDep++)
{
if (mReferencedResources[iDep]->ID() == rkID)
return true;
}
return false;
}
CUniqueID CDependencyTree::DependencyByIndex(u32 Index) const
{
ASSERT(Index >= 0 && Index < mReferencedResources.size());
return mReferencedResources[Index]->ID();
}
void CDependencyTree::AddDependency(CResource *pRes)
{
if (!pRes || HasDependency(pRes->ResID())) return;
CResourceDependency *pDepend = new CResourceDependency(pRes->ResID());
mReferencedResources.push_back(pDepend);
}
void CDependencyTree::AddDependency(const CUniqueID& rkID)
{
if (!rkID.IsValid() || HasDependency(rkID)) return;
CResourceDependency *pDepend = new CResourceDependency(rkID);
mReferencedResources.push_back(pDepend);
}
// ************ CAnimSetDependencyTree ************
EDependencyNodeType CAnimSetDependencyTree::Type() const
{
return eDNT_AnimSet;
}
void CAnimSetDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
{
CDependencyTree::Read(rFile, IDLength);
u32 NumChars = rFile.ReadLong();
mCharacterOffsets.reserve(NumChars);
for (u32 iChar = 0; iChar < NumChars; iChar++)
mCharacterOffsets.push_back( rFile.ReadLong() );
}
void CAnimSetDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
CDependencyTree::Write(rFile, IDLength);
rFile.WriteLong(mCharacterOffsets.size());
for (u32 iChar = 0; iChar < mCharacterOffsets.size(); iChar++)
rFile.WriteLong( mCharacterOffsets[iChar] );
}
// ************ CScriptInstanceDependencyTree ************
CScriptInstanceDependencyTree::~CScriptInstanceDependencyTree()
{
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
delete mDependencies[iDep];
}
EDependencyNodeType CScriptInstanceDependencyTree::Type() const
{
return eDNT_ScriptInstance;
}
void CScriptInstanceDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
{
mObjectType = rFile.ReadLong();
u32 NumDepends = rFile.ReadLong();
mDependencies.reserve(NumDepends);
for (u32 iDep = 0; iDep < NumDepends; iDep++)
{
CUniqueID ID(rFile, IDLength);
CResourceEntry *pEntry = gpResourceStore->FindEntry(ID);
if (pEntry && pEntry->ResourceType() == eAnimSet && pEntry->Game() <= eEchoes)
{
CAnimSetDependency *pSet = new CAnimSetDependency();
pSet->SetID(ID);
pSet->SetUsedChar( rFile.ReadLong() );
mDependencies.push_back(pSet);
}
else
{
CResourceDependency *pRes = new CResourceDependency(ID);
mDependencies.push_back(pRes);
}
}
}
void CScriptInstanceDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
rFile.WriteLong(mObjectType);
rFile.WriteLong(mDependencies.size());
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
mDependencies[iDep]->Write(rFile, IDLength);
}
bool CScriptInstanceDependencyTree::HasDependency(const CUniqueID& rkID)
{
if (!rkID.IsValid()) return false;
for (u32 iDep = 0; iDep < mDependencies.size(); iDep++)
{
CResourceDependency *pDep = mDependencies[iDep];
if (pDep->ID() == rkID) return true;
}
return false;
}
// Static
CScriptInstanceDependencyTree* CScriptInstanceDependencyTree::BuildTree(CScriptObject *pInstance)
{
CScriptInstanceDependencyTree *pTree = new CScriptInstanceDependencyTree();
pTree->mObjectType = pInstance->ObjectTypeID();
ParseStructDependencies(pTree, pInstance->Properties());
return pTree;
}
void CScriptInstanceDependencyTree::ParseStructDependencies(CScriptInstanceDependencyTree *pTree, CPropertyStruct *pStruct)
{
for (u32 iProp = 0; iProp < pStruct->Count(); iProp++)
{
IProperty *pProp = pStruct->PropertyByIndex(iProp);
if (pProp->Type() == eStructProperty || pProp->Type() == eArrayProperty)
ParseStructDependencies(pTree, static_cast<CPropertyStruct*>(pProp));
else if (pProp->Type() == eFileProperty)
{
CUniqueID ID = static_cast<TFileProperty*>(pProp)->Get().ID();
if (ID.IsValid() && !pTree->HasDependency(ID))
{
CResourceDependency *pDep = new CResourceDependency(ID);
pTree->mDependencies.push_back(pDep);
}
}
else if (pProp->Type() == eCharacterProperty)
{
TCharacterProperty *pChar = static_cast<TCharacterProperty*>(pProp);
CUniqueID ID = pChar->Get().ID();
if (ID.IsValid() && !pTree->HasDependency(ID))
pTree->mDependencies.push_back( CAnimSetDependency::BuildDependency(pChar) );
}
}
}
// ************ CAreaDependencyTree ************
CAreaDependencyTree::~CAreaDependencyTree()
{
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
delete mScriptInstances[iInst];
}
EDependencyNodeType CAreaDependencyTree::Type() const
{
return eDNT_Area;
}
void CAreaDependencyTree::Read(IInputStream& rFile, EUIDLength IDLength)
{
// Base dependency list contains non-script dependencies (world geometry textures + PATH/PTLA/EGMC)
CDependencyTree::Read(rFile, IDLength);
u32 NumScriptInstances = rFile.ReadLong();
mScriptInstances.reserve(NumScriptInstances);
for (u32 iInst = 0; iInst < NumScriptInstances; iInst++)
{
CScriptInstanceDependencyTree *pInst = new CScriptInstanceDependencyTree;
pInst->Read(rFile, IDLength);
mScriptInstances.push_back(pInst);
}
u32 NumLayers = rFile.ReadLong();
mLayerOffsets.reserve(NumLayers);
for (u32 iLyr = 0; iLyr < NumLayers; iLyr++)
mLayerOffsets.push_back( rFile.ReadLong() );
}
void CAreaDependencyTree::Write(IOutputStream& rFile, EUIDLength IDLength) const
{
CDependencyTree::Write(rFile, IDLength);
rFile.WriteLong(mScriptInstances.size());
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
mScriptInstances[iInst]->Write(rFile, IDLength);
rFile.WriteLong(mLayerOffsets.size());
for (u32 iLyr = 0; iLyr < mLayerOffsets.size(); iLyr++)
rFile.WriteLong(mLayerOffsets[iLyr]);
}
void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer)
{
if (!pLayer) return;
mLayerOffsets.push_back(mScriptInstances.size());
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
{
CScriptInstanceDependencyTree *pTree = CScriptInstanceDependencyTree::BuildTree( pLayer->InstanceByIndex(iInst) );
ASSERT(pTree != nullptr);
if (pTree->NumDependencies() > 0)
mScriptInstances.push_back(pTree);
else
delete pTree;
}
}

View File

@@ -0,0 +1,156 @@
#ifndef CDEPENDENCYTREE
#define CDEPENDENCYTREE
#include "CResourceEntry.h"
#include <FileIO/FileIO.h>
#include <Common/AssertMacro.h>
#include <Common/CUniqueID.h>
class CScriptLayer;
class CScriptObject;
class CPropertyStruct;
class TCharacterProperty;
// Group of node classes forming a tree of cached resource dependencies.
enum EDependencyNodeType
{
eDNT_Root,
eDNT_AnimSet,
eDNT_ScriptInstance,
eDNT_Area,
eDNT_ResourceDependency,
eDNT_AnimSetDependency
};
// Base class providing an interface for reading/writing to cache file and determining type.
class IDependencyNode
{
public:
virtual ~IDependencyNode() {}
virtual EDependencyNodeType Type() const = 0;
virtual void Read(IInputStream& rFile, EUIDLength IDLength) = 0;
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const = 0;
};
// Node representing a single resource dependency.
class CResourceDependency : public IDependencyNode
{
CUniqueID mID;
public:
CResourceDependency() {}
CResourceDependency(const CUniqueID& rkID) : mID(rkID) {}
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
// Accessors
inline CUniqueID ID() const { return mID; }
inline void SetID(const CUniqueID& rkID) { mID = rkID; }
};
// Node representing a single animset dependency contained in a script object. Indicates which character is being used.
class CAnimSetDependency : public CResourceDependency
{
protected:
u32 mUsedChar;
public:
CAnimSetDependency() : CResourceDependency(), mUsedChar(-1) {}
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
// Accessors
inline u32 UsedChar() const { return mUsedChar; }
inline void SetUsedChar(u32 CharIdx) { mUsedChar = CharIdx; }
// Static
static CAnimSetDependency* BuildDependency(TCharacterProperty *pProp);
};
// Tree root node, representing a resource.
class CDependencyTree : public IDependencyNode
{
protected:
CUniqueID mID;
std::vector<CResourceDependency*> mReferencedResources;
public:
CDependencyTree(const CUniqueID& rkID) : mID(rkID) {}
~CDependencyTree();
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
u32 NumDependencies() const;
bool HasDependency(const CUniqueID& rkID);
CUniqueID DependencyByIndex(u32 Index) const;
void AddDependency(const CUniqueID& rkID);
void AddDependency(CResource *pRes);
// Accessors
inline void SetID(const CUniqueID& rkID) { mID = rkID; }
inline CUniqueID ID() const { return mID; }
};
// Node representing an animset resource; allows for lookup of dependencies of a particular character in the set.
class CAnimSetDependencyTree : public CDependencyTree
{
protected:
std::vector<u32> mCharacterOffsets;
public:
CAnimSetDependencyTree(const CUniqueID& rkID) : CDependencyTree(rkID) {}
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
};
// Node representing a script object. Indicates the type of object.
class CScriptInstanceDependencyTree : public IDependencyNode
{
protected:
u32 mObjectType;
std::vector<CResourceDependency*> mDependencies;
public:
~CScriptInstanceDependencyTree();
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
bool HasDependency(const CUniqueID& rkID);
// Accessors
u32 NumDependencies() const { return mDependencies.size(); }
// Static
static CScriptInstanceDependencyTree* BuildTree(CScriptObject *pInstance);
static void ParseStructDependencies(CScriptInstanceDependencyTree *pTree, CPropertyStruct *pStruct);
};
// Node representing an area. Tracks dependencies on a per-instance basis and can separate dependencies of different script layers.
class CAreaDependencyTree : public CDependencyTree
{
protected:
std::vector<CScriptInstanceDependencyTree*> mScriptInstances;
std::vector<u32> mLayerOffsets;
public:
CAreaDependencyTree(const CUniqueID& rkID) : CDependencyTree(rkID) {}
~CAreaDependencyTree();
virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EUIDLength IDLength);
virtual void Write(IOutputStream& rFile, EUIDLength IDLength) const;
void AddScriptLayer(CScriptLayer *pLayer);
};
#endif // CDEPENDENCYTREE

View File

@@ -1,4 +1,5 @@
#include "CGameExporter.h"
#include "Core/GameProject/CResourceIterator.h"
#include "Core/GameProject/CResourceStore.h"
#include "Core/Resource/CWorld.h"
#include "Core/Resource/Script/CMasterTemplate.h"
@@ -14,6 +15,7 @@
#define SAVE_PACKAGE_DEFINITIONS 1
#define EXPORT_WORLDS 1
#define EXPORT_COOKED 1
#define EXPORT_CACHE 1
CGameExporter::CGameExporter(const TString& rkInputDir, const TString& rkOutputDir)
: mStore(this)
@@ -476,7 +478,6 @@ void CGameExporter::ExportWorlds()
void CGameExporter::ExportCookedResources()
{
#if EXPORT_COOKED
{
SCOPED_TIMER(ExportCookedResources);
FileUtil::CreateDirectory(mCookedDir);
@@ -487,7 +488,6 @@ void CGameExporter::ExportCookedResources()
ExportResource(rRes);
}
}
#endif
{
SCOPED_TIMER(SaveResourceDatabase);
#if EXPORT_COOKED
@@ -495,6 +495,20 @@ void CGameExporter::ExportCookedResources()
#endif
mpProject->Save();
}
#if EXPORT_CACHE
{
SCOPED_TIMER(SaveCacheData);
for (CResourceIterator It(&mStore); It; ++It)
{
if (!It->IsTransient())
{
It->UpdateDependencies();
It->SaveCacheData();
}
}
}
#endif
}
void CGameExporter::ExportResource(SResourceInstance& rRes)
@@ -520,6 +534,7 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
// Register resource and write to file
CResourceEntry *pEntry = mStore.RegisterResource(rRes.ResourceID, CResource::ResTypeForExtension(rRes.ResourceType), OutDir, OutName);
#if EXPORT_COOKED
// Cooked (todo: save raw)
TWideString OutPath = pEntry->CookedAssetPath();
FileUtil::CreateDirectory(OutPath.GetFileDirectory());
@@ -530,5 +545,8 @@ void CGameExporter::ExportResource(SResourceInstance& rRes)
rRes.Exported = true;
ASSERT(pEntry->HasCookedVersion());
#else
(void) pEntry; // Prevent "unused local variable" compiler warning
#endif
}
}

View File

@@ -68,6 +68,7 @@ bool CGameProject::Load(const TWideString& rkPath)
// All loaded!
mProjectRoot = rkPath.GetFileDirectory();
mProjectRoot.Replace(L"/", L"\\");
return true;
}

View File

@@ -38,7 +38,9 @@ public:
, mProjectName("Unnamed Project")
, mProjectRoot(rkProjRootDir)
, mResourceDBPath(L"ResourceDB.rdb")
{}
{
mProjectRoot.Replace(L"/", L"\\");
}
~CGameProject();
@@ -51,6 +53,7 @@ public:
inline TWideString ProjectRoot() const { return mProjectRoot; }
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\\"; }

View File

@@ -2,6 +2,7 @@
#include "CGameProject.h"
#include "CResourceStore.h"
#include "Core/Resource/CResource.h"
#include <FileIO/FileIO.h>
#include <Common/FileUtil.h>
#include <Common/TString.h>
@@ -11,6 +12,7 @@
#include "Core/Resource/Factory/CAnimSetLoader.h"
#include "Core/Resource/Factory/CAreaLoader.h"
#include "Core/Resource/Factory/CCollisionLoader.h"
#include "Core/Resource/Factory/CDependencyGroupLoader.h"
#include "Core/Resource/Factory/CFontLoader.h"
#include "Core/Resource/Factory/CMaterialLoader.h"
#include "Core/Resource/Factory/CModelLoader.h"
@@ -26,19 +28,20 @@
CResourceEntry::CResourceEntry(CResourceStore *pStore, const CUniqueID& rkID,
const TWideString& rkDir, const TWideString& rkFilename,
EResType Type, bool Transient /*= false*/)
: mpStore(pStore)
, mpResource(nullptr)
: mpResource(nullptr)
, mpStore(pStore)
, mpDependencies(nullptr)
, mID(rkID)
, mName(rkFilename)
, mType(Type)
, mNeedsRecook(false)
, mTransient(Transient)
, mCachedSize(-1)
, mCachedUppercaseName(rkFilename.ToUpper())
{
if (Transient) mFlags |= eREF_Transient;
mpDirectory = mpStore->GetVirtualDirectory(rkDir, Transient, true);
if (mpDirectory) mpDirectory->AddChild(L"", this);
mGame = ((mTransient || !mpStore->ActiveProject()) ? eUnknownVersion : mpStore->ActiveProject()->Game());
mGame = ((Transient || !mpStore->ActiveProject()) ? eUnknownVersion : mpStore->ActiveProject()->Game());
}
CResourceEntry::~CResourceEntry()
@@ -46,6 +49,104 @@ CResourceEntry::~CResourceEntry()
if (mpResource) delete mpResource;
}
bool CResourceEntry::LoadCacheData()
{
ASSERT(!IsTransient());
TWideString Path = CacheDataPath(false);
CFileInStream File(Path.ToUTF8().ToStdString(), IOUtil::eLittleEndian);
if (!File.IsValid())
{
Log::Error("Unable to load cache data " + Path.ToUTF8() + "; couldn't open file");
return false;
}
// Header
TString Magic = File.ReadString(4);
ASSERT(Magic == "CACH");
File.Seek(0x4, SEEK_CUR); // Skip Version
mFlags = File.ReadLong() & eREF_SavedFlags;
// Dependency Tree
u32 DepsTreeSize = File.ReadLong();
if (mpDependencies)
{
delete mpDependencies;
mpDependencies = nullptr;
}
if (DepsTreeSize > 0)
{
mpDependencies = new CDependencyTree(mID);
mpDependencies->Read(File, Game() <= eEchoes ? e32Bit : e64Bit);
}
return true;
}
bool CResourceEntry::SaveCacheData()
{
ASSERT(!IsTransient());
TWideString Path = CacheDataPath(false);
TWideString Dir = Path.GetFileDirectory();
FileUtil::CreateDirectory(Dir);
CFileOutStream File(Path.ToUTF8().ToStdString(), IOUtil::eLittleEndian);
if (!File.IsValid())
{
Log::Error("Unable to save cache data " + TString(Path.GetFileName()) + "; couldn't open file");
return false;
}
// Header
File.WriteString("CACH", 4);
File.WriteLong(0); // Reserved Space (Version)
File.WriteLong(mFlags & eREF_SavedFlags);
// Dependency Tree
if (!mpDependencies) UpdateDependencies();
u32 DepsSizeOffset = File.Tell();
File.WriteLong(0);
u32 DepsStart = File.Tell();
if (mpDependencies) mpDependencies->Write(File, Game() <= eEchoes ? e32Bit : e64Bit);
u32 DepsSize = File.Tell() - DepsStart;
File.Seek(DepsSizeOffset, SEEK_SET);
File.WriteLong(DepsSize);
return true;
}
void CResourceEntry::UpdateDependencies()
{
if (mpDependencies)
{
delete mpDependencies;
mpDependencies = nullptr;
}
if (!mpResource)
Load();
if (!mpResource)
{
Log::Error("Unable to update cached dependencies; failed to load resource");
return;
}
mpDependencies = mpResource->BuildDependencyTree();
gpResourceStore->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());
@@ -61,7 +162,7 @@ TString CResourceEntry::RawAssetPath(bool Relative) const
TWideString Ext = GetResourceRawExtension(mType, mGame).ToUTF16();
TWideString Path = mpDirectory ? mpDirectory->FullPath() : L"";
TWideString Name = mName + L"." + Ext;
return ((mTransient || Relative) ? Path + Name : mpStore->ActiveProject()->ContentDir(false) + Path + Name);
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->ContentDir(false) + Path + Name);
}
TString CResourceEntry::CookedAssetPath(bool Relative) const
@@ -69,7 +170,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 ((mTransient || Relative) ? Path + Name : mpStore->ActiveProject()->CookedDir(false) + Path + Name);
return ((IsTransient() || Relative) ? Path + Name : mpStore->ActiveProject()->CookedDir(false) + Path + Name);
}
bool CResourceEntry::IsInDirectory(CVirtualDirectory *pDir) const
@@ -101,11 +202,11 @@ u64 CResourceEntry::Size() const
bool CResourceEntry::NeedsRecook() const
{
// Assets that do not have a raw version can't be recooked since they will always just be saved cooked to begin with.
// We will recook any asset where the raw version has been updated but not recooked yet. mNeedsRecook can also be
// We will recook any asset where the raw version has been updated but not recooked yet. eREF_NeedsRecook can also be
// toggled to arbitrarily flag any asset for recook.
if (!HasRawVersion()) return false;
if (!HasCookedVersion()) return true;
if (mNeedsRecook) return true;
if (mFlags.HasFlag(eREF_NeedsRecook)) return true;
return (FileUtil::LastModifiedTime(CookedAssetPath()) < FileUtil::LastModifiedTime(RawAssetPath()));
}
@@ -152,6 +253,7 @@ CResource* CResourceEntry::Load(IInputStream& rInput)
case eAnimation: mpResource = CAnimationLoader::LoadANIM(rInput, this); break;
case eAnimSet: mpResource = CAnimSetLoader::LoadANCSOrCHAR(rInput, this); break;
case eArea: mpResource = CAreaLoader::LoadMREA(rInput, this); break;
case eDependencyGroup: mpResource = CDependencyGroupLoader::LoadDGRP(rInput, this);break;
case eDynamicCollision: mpResource = CCollisionLoader::LoadDCLN(rInput, this); break;
case eFont: mpResource = CFontLoader::LoadFONT(rInput, this); break;
case eModel: mpResource = CModelLoader::LoadCMDL(rInput, this); break;
@@ -185,7 +287,7 @@ void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
// Set new directory and name
bool HasDirectory = mpDirectory != nullptr;
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, mTransient, true);
CVirtualDirectory *pNewDir = mpStore->GetVirtualDirectory(rkDir, IsTransient(), true);
if (pNewDir != mpDirectory)
{
@@ -216,9 +318,9 @@ void CResourceEntry::Move(const TWideString& rkDir, const TWideString& rkName)
void CResourceEntry::AddToProject(const TWideString& rkDir, const TWideString& rkName)
{
if (mTransient)
if (mFlags.HasFlag(eREF_Transient))
{
mTransient = false;
mFlags.ClearFlag(eREF_Transient);
Move(rkDir, rkName);
}
@@ -230,10 +332,10 @@ void CResourceEntry::AddToProject(const TWideString& rkDir, const TWideString& r
void CResourceEntry::RemoveFromProject()
{
if (!mTransient)
if (!mFlags.HasFlag(eREF_Transient))
{
TString Dir = CookedAssetPath().GetFileDirectory();
mTransient = true;
mFlags.SetFlag(eREF_Transient);
Move(Dir, mName);
}

View File

@@ -4,31 +4,49 @@
#include "CVirtualDirectory.h"
#include "Core/Resource/EResType.h"
#include <Common/CUniqueID.h>
#include <Common/Flags.h>
#include <Common/types.h>
class CResource;
class CResourceStore;
class CDependencyTree;
enum EResEntryFlag
{
eREF_NeedsRecook = 0x1,
eREF_Transient = 0x2,
eREF_HasThumbnail = 0x4,
// Flags that save to the cache file
eREF_SavedFlags = eREF_NeedsRecook | eREF_HasThumbnail
};
DECLARE_FLAGS(EResEntryFlag, FResEntryFlags)
class CResourceEntry
{
CResourceStore *mpStore;
CResource *mpResource;
CResourceStore *mpStore;
CDependencyTree *mpDependencies;
CUniqueID mID;
EResType mType;
EGame mGame;
CVirtualDirectory *mpDirectory;
TWideString mName;
bool mNeedsRecook;
bool mTransient;
FResEntryFlags mFlags;
mutable u64 mCachedSize;
mutable TWideString mCachedUppercaseName; // This is used to speed up case-insensitive sorting.
mutable TWideString mCachedUppercaseName; // This is used to speed up case-insensitive sorting and filtering.
public:
CResourceEntry(CResourceStore *pStore, const CUniqueID& rkID,
const TWideString& rkDir, const TWideString& rkFilename,
EResType Type, bool Transient = false);
~CResourceEntry();
bool LoadCacheData();
bool SaveCacheData();
void UpdateDependencies();
TWideString CacheDataPath(bool Relative = false) const;
bool HasRawVersion() const;
bool HasCookedVersion() const;
TString RawAssetPath(bool Relative = false) const;
@@ -45,17 +63,18 @@ public:
void RemoveFromProject();
// Accessors
void SetDirty() { mNeedsRecook = true; }
void SetDirty() { mFlags.SetFlag(eREF_NeedsRecook); }
inline bool IsLoaded() const { return mpResource != nullptr; }
inline CResource* Resource() const { return mpResource; }
inline CUniqueID ID() const { return mID; }
inline EGame Game() const { return mGame; }
inline CVirtualDirectory* Directory() const { return mpDirectory; }
inline TWideString Name() const { return mName; }
inline TWideString UppercaseName() const { return mCachedUppercaseName; }
inline EResType ResourceType() const { return mType; }
inline bool IsTransient() const { return mTransient; }
inline bool IsLoaded() const { return mpResource != nullptr; }
inline CResource* Resource() const { return mpResource; }
inline CDependencyTree* Dependencies() const { return mpDependencies; }
inline CUniqueID ID() const { return mID; }
inline EGame Game() const { return mGame; }
inline CVirtualDirectory* Directory() const { return mpDirectory; }
inline TWideString Name() const { return mName; }
inline const TWideString& UppercaseName() const { return mCachedUppercaseName; }
inline EResType ResourceType() const { return mType; }
inline bool IsTransient() const { return mFlags.HasFlag(eREF_Transient); }
protected:
CResource* InternalLoad(IInputStream& rInput);

View File

@@ -11,7 +11,7 @@ class CResourceIterator
CResourceEntry *mpCurEntry;
public:
CResourceIterator(CResourceStore *pStore)
CResourceIterator(CResourceStore *pStore = gpResourceStore)
: mpStore(pStore)
, mpCurEntry(nullptr)
{

View File

@@ -69,6 +69,14 @@ void CResourceStore::LoadResourceDatabase(const TString& rkPath)
pRes = pRes->NextSiblingElement("Resource");
}
}
// All resources registered - load cache data
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
{
CResourceEntry *pEntry = It->second;
if (!pEntry->IsTransient())
pEntry->LoadCacheData();
}
}
void CResourceStore::SaveResourceDatabase(const TString& rkPath) const
@@ -108,10 +116,6 @@ void CResourceStore::SaveResourceDatabase(const TString& rkPath) const
XMLElement *pName = Doc.NewElement("FileName");
pName->SetText(*pEntry->Name().ToUTF8());
pRes->LinkEndChild(pName);
XMLElement *pRecook = Doc.NewElement("NeedsRecook");
pRecook->SetText(pEntry->NeedsRecook() ? "true" : "false");
pRes->LinkEndChild(pRecook);
}
Doc.SaveFile(*rkPath);
@@ -231,10 +235,7 @@ CResourceEntry* CResourceStore::RegisterTransientResource(EResType Type, const C
{
CResourceEntry *pEntry = FindEntry(rkID);
if (pEntry)
Log::Error("Attempted to register transient resource that already exists: " + rkID.ToString() + " / Dir: " + rkDir.ToUTF8() + " / Name: " + rkFileName.ToUTF8());
else
if (!pEntry)
{
pEntry = new CResourceEntry(this, rkID, rkDir, rkFileName, Type, true);
mResourceEntries[rkID] = pEntry;

View File

@@ -65,6 +65,8 @@ public:
// 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(); }
};
extern CResourceStore *gpResourceStore;