mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-12-17 08:57:09 +00:00
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:
320
src/Core/GameProject/CDependencyTree.cpp
Normal file
320
src/Core/GameProject/CDependencyTree.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
156
src/Core/GameProject/CDependencyTree.h
Normal file
156
src/Core/GameProject/CDependencyTree.h
Normal 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ bool CGameProject::Load(const TWideString& rkPath)
|
||||
|
||||
// All loaded!
|
||||
mProjectRoot = rkPath.GetFileDirectory();
|
||||
mProjectRoot.Replace(L"/", L"\\");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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\\"; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -11,7 +11,7 @@ class CResourceIterator
|
||||
CResourceEntry *mpCurEntry;
|
||||
|
||||
public:
|
||||
CResourceIterator(CResourceStore *pStore)
|
||||
CResourceIterator(CResourceStore *pStore = gpResourceStore)
|
||||
: mpStore(pStore)
|
||||
, mpCurEntry(nullptr)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user