Heavily modified the dependency tree system and applied other various fixes and updates to work towards supporting Echoes package cooking
This commit is contained in:
parent
881bb28d84
commit
9a243f94ac
|
@ -6,7 +6,7 @@ CFourCC GetGameID(EGame Game)
|
|||
switch (Game)
|
||||
{
|
||||
case ePrimeDemo: return "MP1D";
|
||||
case ePrime: return "MP1 ";
|
||||
case ePrime: return "MPRM";
|
||||
case eEchoesDemo: return "MP2D";
|
||||
case eEchoes: return "MP2E";
|
||||
case eCorruptionProto: return "MP3P";
|
||||
|
@ -19,7 +19,7 @@ CFourCC GetGameID(EGame Game)
|
|||
EGame GetGameForID(const CFourCC& rkID)
|
||||
{
|
||||
if (rkID == "MP1D") return ePrimeDemo;
|
||||
if (rkID == "MP1 ") return ePrime;
|
||||
if (rkID == "MPRM") return ePrime;
|
||||
if (rkID == "MP2D") return eEchoesDemo;
|
||||
if (rkID == "MP2E") return eEchoes;
|
||||
if (rkID == "MP3P") return eCorruptionProto;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
mFileVersion = TString( mpCurElem->Attribute("FileVer") ).ToInt32(10);
|
||||
mArchiveVersion = TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10);
|
||||
const char *pkGameAttr = mpCurElem->Attribute("Game");
|
||||
mGame = pkGameAttr ? eUnknownGame : GetGameForID( CFourCC(pkGameAttr) );
|
||||
mGame = pkGameAttr ? GetGameForID( CFourCC(pkGameAttr) ) : eUnknownGame;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -299,4 +299,5 @@ SOURCES += \
|
|||
Resource/Factory/CDependencyGroupLoader.cpp \
|
||||
GameProject/CDependencyTree.cpp \
|
||||
Resource/Factory/CUnsupportedFormatLoader.cpp \
|
||||
Resource/Factory/CUnsupportedParticleLoader.cpp
|
||||
Resource/Factory/CUnsupportedParticleLoader.cpp \
|
||||
GameProject/DependencyListBuilders.cpp
|
||||
|
|
|
@ -3,117 +3,52 @@
|
|||
#include "Core/Resource/Script/CScriptLayer.h"
|
||||
#include "Core/Resource/Script/CScriptObject.h"
|
||||
|
||||
// ************ CResourceDependency ************
|
||||
EDependencyNodeType CResourceDependency::Type() const
|
||||
// ************ IDependencyNode ************
|
||||
IDependencyNode::~IDependencyNode()
|
||||
{
|
||||
return eDNT_ResourceDependency;
|
||||
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
|
||||
delete mChildren[iChild];
|
||||
}
|
||||
|
||||
void CResourceDependency::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
bool IDependencyNode::HasDependency(const CAssetID& rkID) const
|
||||
{
|
||||
mID = CAssetID(rFile, IDLength);
|
||||
}
|
||||
|
||||
void CResourceDependency::Write(IOutputStream& rFile, EIDLength 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, EIDLength IDLength)
|
||||
{
|
||||
CResourceDependency::Read(rFile, IDLength);
|
||||
mUsedChar = rFile.ReadLong();
|
||||
}
|
||||
|
||||
void CAnimSetDependency::Write(IOutputStream& rFile, EIDLength 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, EIDLength IDLength)
|
||||
{
|
||||
mID = CAssetID(rFile, IDLength);
|
||||
|
||||
u32 NumDepends = rFile.ReadLong();
|
||||
mReferencedResources.reserve(NumDepends);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDepends; iDep++)
|
||||
for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
|
||||
{
|
||||
CResourceDependency *pDepend = new CResourceDependency;
|
||||
pDepend->Read(rFile, IDLength);
|
||||
mReferencedResources.push_back(pDepend);
|
||||
}
|
||||
}
|
||||
|
||||
void CDependencyTree::Write(IOutputStream& rFile, EIDLength 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 CAssetID& rkID)
|
||||
{
|
||||
for (u32 iDep = 0; iDep < mReferencedResources.size(); iDep++)
|
||||
{
|
||||
if (mReferencedResources[iDep]->ID() == rkID)
|
||||
if (mChildren[iChild]->HasDependency(rkID))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CAssetID CDependencyTree::DependencyByIndex(u32 Index) const
|
||||
// ************ CDependencyTree ************
|
||||
EDependencyNodeType CDependencyTree::Type() const
|
||||
{
|
||||
ASSERT(Index >= 0 && Index < mReferencedResources.size());
|
||||
return mReferencedResources[Index]->ID();
|
||||
return eDNT_DependencyTree;
|
||||
}
|
||||
|
||||
void CDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
mRootID = CAssetID(rFile, IDLength);
|
||||
|
||||
u32 NumDepends = rFile.ReadLong();
|
||||
mChildren.reserve(NumDepends);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDepends; iDep++)
|
||||
{
|
||||
CResourceDependency *pDepend = new CResourceDependency;
|
||||
pDepend->Read(rFile, IDLength);
|
||||
mChildren.push_back(pDepend);
|
||||
}
|
||||
}
|
||||
|
||||
void CDependencyTree::Write(IOutputStream& rFile) const
|
||||
{
|
||||
mRootID.Write(rFile);
|
||||
rFile.WriteLong( mChildren.size() );
|
||||
|
||||
for (u32 iDep = 0; iDep < mChildren.size(); iDep++)
|
||||
mChildren[iDep]->Write(rFile);
|
||||
}
|
||||
|
||||
void CDependencyTree::AddDependency(CResource *pRes, bool AvoidDuplicates /*= true*/)
|
||||
|
@ -126,7 +61,153 @@ void CDependencyTree::AddDependency(const CAssetID& rkID, bool AvoidDuplicates /
|
|||
{
|
||||
if (!rkID.IsValid() || (AvoidDuplicates && HasDependency(rkID))) return;
|
||||
CResourceDependency *pDepend = new CResourceDependency(rkID);
|
||||
mReferencedResources.push_back(pDepend);
|
||||
mChildren.push_back(pDepend);
|
||||
}
|
||||
|
||||
// ************ CResourceDependency ************
|
||||
EDependencyNodeType CResourceDependency::Type() const
|
||||
{
|
||||
return eDNT_ResourceDependency;
|
||||
}
|
||||
|
||||
void CResourceDependency::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
mID = CAssetID(rFile, IDLength);
|
||||
}
|
||||
|
||||
void CResourceDependency::Write(IOutputStream& rFile) const
|
||||
{
|
||||
mID.Write(rFile);
|
||||
}
|
||||
|
||||
bool CResourceDependency::HasDependency(const CAssetID& rkID) const
|
||||
{
|
||||
return (mID == rkID);
|
||||
}
|
||||
|
||||
// ************ CPropertyDependency ************
|
||||
EDependencyNodeType CPropertyDependency::Type() const
|
||||
{
|
||||
return eDNT_ScriptProperty;
|
||||
}
|
||||
|
||||
void CPropertyDependency::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
mIDString = rFile.ReadString();
|
||||
CResourceDependency::Read(rFile, IDLength);
|
||||
}
|
||||
|
||||
void CPropertyDependency::Write(IOutputStream& rFile) const
|
||||
{
|
||||
rFile.WriteString(mIDString.ToStdString());
|
||||
CResourceDependency::Write(rFile);
|
||||
}
|
||||
|
||||
// ************ CCharacterPropertyDependency ************
|
||||
EDependencyNodeType CCharPropertyDependency::Type() const
|
||||
{
|
||||
return eDNT_CharacterProperty;
|
||||
}
|
||||
|
||||
void CCharPropertyDependency::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
CPropertyDependency::Read(rFile, IDLength);
|
||||
mUsedChar = rFile.ReadLong();
|
||||
}
|
||||
|
||||
void CCharPropertyDependency::Write(IOutputStream& rFile) const
|
||||
{
|
||||
CPropertyDependency::Write(rFile);
|
||||
rFile.WriteLong(mUsedChar);
|
||||
}
|
||||
|
||||
// ************ CScriptInstanceDependency ************
|
||||
EDependencyNodeType CScriptInstanceDependency::Type() const
|
||||
{
|
||||
return eDNT_ScriptInstance;
|
||||
}
|
||||
|
||||
void CScriptInstanceDependency::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
mObjectType = rFile.ReadLong();
|
||||
u32 NumProperties = rFile.ReadLong();
|
||||
mChildren.reserve(NumProperties);
|
||||
|
||||
for (u32 iProp = 0; iProp < NumProperties; iProp++)
|
||||
{
|
||||
bool IsCharacter = rFile.ReadBool();
|
||||
CPropertyDependency *pProp = (IsCharacter ? new CCharPropertyDependency() : new CPropertyDependency());
|
||||
pProp->Read(rFile, IDLength);
|
||||
mChildren.push_back(pProp);
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptInstanceDependency::Write(IOutputStream& rFile) const
|
||||
{
|
||||
rFile.WriteLong(mObjectType);
|
||||
rFile.WriteLong(mChildren.size());
|
||||
|
||||
for (u32 iProp = 0; iProp < mChildren.size(); iProp++)
|
||||
{
|
||||
CPropertyDependency *pProp = static_cast<CPropertyDependency*>(mChildren[iProp]);
|
||||
rFile.WriteBool( pProp->Type() == eDNT_CharacterProperty );
|
||||
pProp->Write(rFile);
|
||||
}
|
||||
}
|
||||
|
||||
// Static
|
||||
CScriptInstanceDependency* CScriptInstanceDependency::BuildTree(CScriptObject *pInstance)
|
||||
{
|
||||
CScriptInstanceDependency *pInst = new CScriptInstanceDependency();
|
||||
pInst->mObjectType = pInstance->ObjectTypeID();
|
||||
ParseStructDependencies(pInst, pInstance->Properties());
|
||||
return pInst;
|
||||
}
|
||||
|
||||
void CScriptInstanceDependency::ParseStructDependencies(CScriptInstanceDependency *pInst, CPropertyStruct *pStruct)
|
||||
{
|
||||
// Recursive function for parsing script dependencies and loading them into the script instance dependency
|
||||
for (u32 iProp = 0; iProp < pStruct->Count(); iProp++)
|
||||
{
|
||||
IProperty *pProp = pStruct->PropertyByIndex(iProp);
|
||||
EPropertyType Type = pProp->Type();
|
||||
|
||||
if (Type == eStructProperty || Type == eArrayProperty)
|
||||
ParseStructDependencies(pInst, static_cast<CPropertyStruct*>(pProp));
|
||||
|
||||
else if (Type == eFileProperty)
|
||||
{
|
||||
CAssetID ID = static_cast<TFileProperty*>(pProp)->Get().ID();
|
||||
|
||||
if (ID.IsValid())
|
||||
{
|
||||
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
|
||||
pInst->mChildren.push_back(pDep);
|
||||
}
|
||||
}
|
||||
|
||||
else if (Type == eCharacterProperty)
|
||||
{
|
||||
TCharacterProperty *pChar = static_cast<TCharacterProperty*>(pProp);
|
||||
CAnimationParameters Params = pChar->Get();
|
||||
CAssetID ID = Params.ID();
|
||||
|
||||
if (ID.IsValid())
|
||||
{
|
||||
// Character sets are removed starting in MP3, so we only need char property dependencies in Echoes and earlier
|
||||
if (pStruct->Instance()->Area()->Game() <= eEchoes)
|
||||
{
|
||||
CCharPropertyDependency *pDep = new CCharPropertyDependency(pProp->IDString(true), ID, Params.CharacterIndex());
|
||||
pInst->mChildren.push_back(pDep);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPropertyDependency *pDep = new CPropertyDependency(pProp->IDString(true), ID);
|
||||
pInst->mChildren.push_back(pDep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ CAnimSetDependencyTree ************
|
||||
|
@ -145,9 +226,9 @@ void CAnimSetDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
|
|||
mCharacterOffsets.push_back( rFile.ReadLong() );
|
||||
}
|
||||
|
||||
void CAnimSetDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) const
|
||||
void CAnimSetDependencyTree::Write(IOutputStream& rFile) const
|
||||
{
|
||||
CDependencyTree::Write(rFile, IDLength);
|
||||
CDependencyTree::Write(rFile);
|
||||
rFile.WriteLong(mCharacterOffsets.size());
|
||||
|
||||
for (u32 iChar = 0; iChar < mCharacterOffsets.size(); iChar++)
|
||||
|
@ -156,7 +237,7 @@ void CAnimSetDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) con
|
|||
|
||||
void CAnimSetDependencyTree::AddCharacter(const SSetCharacter *pkChar, const std::set<CAssetID>& rkBaseUsedSet)
|
||||
{
|
||||
mCharacterOffsets.push_back( NumDependencies() );
|
||||
mCharacterOffsets.push_back( NumChildren() );
|
||||
if (!pkChar) return;
|
||||
|
||||
std::set<CAssetID> UsedSet = rkBaseUsedSet;
|
||||
|
@ -195,120 +276,7 @@ void CAnimSetDependencyTree::AddCharDependency(CResource *pRes, std::set<CAssetI
|
|||
AddCharDependency(pRes->ID(), rUsedSet);
|
||||
}
|
||||
|
||||
// ************ 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, EIDLength IDLength)
|
||||
{
|
||||
mObjectType = rFile.ReadLong();
|
||||
u32 NumDepends = rFile.ReadLong();
|
||||
mDependencies.reserve(NumDepends);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumDepends; iDep++)
|
||||
{
|
||||
CAssetID 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, EIDLength 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 CAssetID& 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;
|
||||
}
|
||||
|
||||
CAssetID CScriptInstanceDependencyTree::DependencyByIndex(u32 Index) const
|
||||
{
|
||||
ASSERT(Index >= 0 && Index < mDependencies.size());
|
||||
return mDependencies[Index]->ID();
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
CAssetID 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);
|
||||
CAssetID 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;
|
||||
|
@ -316,16 +284,27 @@ EDependencyNodeType CAreaDependencyTree::Type() const
|
|||
|
||||
void CAreaDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
|
||||
{
|
||||
mRootID = CAssetID(rFile, IDLength);
|
||||
|
||||
// Base dependency list contains non-script dependencies (world geometry textures + PATH/PTLA/EGMC)
|
||||
CDependencyTree::Read(rFile, IDLength);
|
||||
u32 NumBaseDependencies = rFile.ReadLong();
|
||||
mChildren.reserve(NumBaseDependencies);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumBaseDependencies; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = new CResourceDependency;
|
||||
pDep->Read(rFile, IDLength);
|
||||
mChildren.push_back(pDep);
|
||||
}
|
||||
|
||||
u32 NumScriptInstances = rFile.ReadLong();
|
||||
mScriptInstances.reserve(NumScriptInstances);
|
||||
mChildren.reserve(mChildren.size() + NumScriptInstances);
|
||||
|
||||
for (u32 iInst = 0; iInst < NumScriptInstances; iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pInst = new CScriptInstanceDependencyTree;
|
||||
CScriptInstanceDependency *pInst = new CScriptInstanceDependency;
|
||||
pInst->Read(rFile, IDLength);
|
||||
mScriptInstances.push_back(pInst);
|
||||
mChildren.push_back(pInst);
|
||||
}
|
||||
|
||||
u32 NumLayers = rFile.ReadLong();
|
||||
|
@ -335,13 +314,20 @@ void CAreaDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
|
|||
mLayerOffsets.push_back( rFile.ReadLong() );
|
||||
}
|
||||
|
||||
void CAreaDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) const
|
||||
void CAreaDependencyTree::Write(IOutputStream& rFile) const
|
||||
{
|
||||
CDependencyTree::Write(rFile, IDLength);
|
||||
rFile.WriteLong(mScriptInstances.size());
|
||||
mRootID.Write(rFile);
|
||||
|
||||
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
|
||||
mScriptInstances[iInst]->Write(rFile, IDLength);
|
||||
u32 NumBaseDependencies = (mLayerOffsets.empty() ? mChildren.size() : mLayerOffsets.front());
|
||||
rFile.WriteLong(NumBaseDependencies);
|
||||
|
||||
for (u32 iDep = 0; iDep < NumBaseDependencies; iDep++)
|
||||
mChildren[iDep]->Write(rFile);
|
||||
|
||||
rFile.WriteLong(mChildren.size() - NumBaseDependencies);
|
||||
|
||||
for (u32 iDep = NumBaseDependencies; iDep < mChildren.size(); iDep++)
|
||||
mChildren[iDep]->Write(rFile);
|
||||
|
||||
rFile.WriteLong(mLayerOffsets.size());
|
||||
|
||||
|
@ -352,26 +338,20 @@ void CAreaDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) const
|
|||
void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer)
|
||||
{
|
||||
if (!pLayer) return;
|
||||
mLayerOffsets.push_back(mScriptInstances.size());
|
||||
mLayerOffsets.push_back(mChildren.size());
|
||||
|
||||
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pTree = CScriptInstanceDependencyTree::BuildTree( pLayer->InstanceByIndex(iInst) );
|
||||
CScriptInstanceDependency *pTree = CScriptInstanceDependency::BuildTree( pLayer->InstanceByIndex(iInst) );
|
||||
ASSERT(pTree != nullptr);
|
||||
|
||||
if (pTree->NumDependencies() > 0)
|
||||
mScriptInstances.push_back(pTree);
|
||||
if (pTree->NumChildren() > 0)
|
||||
mChildren.push_back(pTree);
|
||||
else
|
||||
delete pTree;
|
||||
}
|
||||
}
|
||||
|
||||
CScriptInstanceDependencyTree* CAreaDependencyTree::ScriptInstanceByIndex(u32 Index) const
|
||||
{
|
||||
ASSERT(Index >= 0 && Index < mScriptInstances.size());
|
||||
return mScriptInstances[Index];
|
||||
}
|
||||
|
||||
void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector<TString>& rModuleDepsOut, std::vector<u32>& rModuleLayerOffsetsOut) const
|
||||
{
|
||||
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(Game);
|
||||
|
@ -381,7 +361,7 @@ void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector<TString>
|
|||
for (u32 iLayer = 0; iLayer < mLayerOffsets.size(); iLayer++)
|
||||
{
|
||||
u32 StartIdx = mLayerOffsets[iLayer];
|
||||
u32 EndIdx = (iLayer == mLayerOffsets.size() - 1 ? mScriptInstances.size() : mLayerOffsets[iLayer + 1]);
|
||||
u32 EndIdx = (iLayer == mLayerOffsets.size() - 1 ? mChildren.size() : mLayerOffsets[iLayer + 1]);
|
||||
|
||||
u32 ModuleStartIdx = rModuleDepsOut.size();
|
||||
rModuleLayerOffsetsOut.push_back(ModuleStartIdx);
|
||||
|
@ -391,7 +371,8 @@ void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector<TString>
|
|||
|
||||
for (u32 iInst = StartIdx; iInst < EndIdx; iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pInst = mScriptInstances[iInst];
|
||||
CScriptInstanceDependency *pInst = static_cast<CScriptInstanceDependency*>(mChildren[iInst]);
|
||||
ASSERT(pInst->Type() == eDNT_ScriptInstance);
|
||||
u32 ObjType = pInst->ObjectType();
|
||||
|
||||
if (UsedObjectTypes.find(ObjType) == UsedObjectTypes.end())
|
||||
|
|
|
@ -9,33 +9,63 @@
|
|||
class CScriptLayer;
|
||||
class CScriptObject;
|
||||
class CPropertyStruct;
|
||||
class TCharacterProperty;
|
||||
struct SSetCharacter;
|
||||
|
||||
// Group of node classes forming a tree of cached resource dependencies.
|
||||
enum EDependencyNodeType
|
||||
{
|
||||
eDNT_Root,
|
||||
eDNT_AnimSet,
|
||||
eDNT_ScriptInstance,
|
||||
eDNT_Area,
|
||||
eDNT_DependencyTree,
|
||||
eDNT_ResourceDependency,
|
||||
eDNT_AnimSetDependency
|
||||
eDNT_ScriptInstance,
|
||||
eDNT_ScriptProperty,
|
||||
eDNT_CharacterProperty,
|
||||
eDNT_AnimSet,
|
||||
eDNT_Area,
|
||||
};
|
||||
|
||||
// Base class providing an interface for reading/writing to cache file and determining type.
|
||||
// Base class providing an interface for a basic dependency node.
|
||||
class IDependencyNode
|
||||
{
|
||||
protected:
|
||||
std::vector<IDependencyNode*> mChildren;
|
||||
|
||||
public:
|
||||
virtual ~IDependencyNode() {}
|
||||
virtual ~IDependencyNode();
|
||||
virtual EDependencyNodeType Type() const = 0;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength) = 0;
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const = 0;
|
||||
virtual void Write(IOutputStream& rFile) const = 0;
|
||||
virtual bool HasDependency(const CAssetID& rkID) const;
|
||||
|
||||
// Accessors
|
||||
u32 NumChildren() const { return mChildren.size(); }
|
||||
IDependencyNode* ChildByIndex(u32 Index) const { return mChildren[Index]; }
|
||||
};
|
||||
|
||||
// Basic dependency tree; this class is sufficient for most resource types.
|
||||
class CDependencyTree : public IDependencyNode
|
||||
{
|
||||
protected:
|
||||
CAssetID mRootID;
|
||||
|
||||
public:
|
||||
CDependencyTree(const CAssetID& rkID) : mRootID(rkID) {}
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
|
||||
void AddDependency(const CAssetID& rkID, bool AvoidDuplicates = true);
|
||||
void AddDependency(CResource *pRes, bool AvoidDuplicates = true);
|
||||
|
||||
// Accessors
|
||||
inline void SetID(const CAssetID& rkID) { mRootID = rkID; }
|
||||
inline CAssetID ID() const { return mRootID; }
|
||||
};
|
||||
|
||||
// Node representing a single resource dependency.
|
||||
class CResourceDependency : public IDependencyNode
|
||||
{
|
||||
protected:
|
||||
CAssetID mID;
|
||||
|
||||
public:
|
||||
|
@ -44,59 +74,80 @@ public:
|
|||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
virtual bool HasDependency(const CAssetID& rkID) const;
|
||||
|
||||
// Accessors
|
||||
inline CAssetID ID() const { return mID; }
|
||||
inline void SetID(const CAssetID& rkID) { mID = rkID; }
|
||||
};
|
||||
|
||||
// Node representing a single animset dependency contained in a script object. Indicates which character is being used.
|
||||
class CAnimSetDependency : public CResourceDependency
|
||||
// Node representing a single resource dependency referenced by a script property.
|
||||
class CPropertyDependency : public CResourceDependency
|
||||
{
|
||||
TString mIDString;
|
||||
|
||||
public:
|
||||
CPropertyDependency()
|
||||
: CResourceDependency()
|
||||
{}
|
||||
|
||||
CPropertyDependency(const TString& rkPropID, const CAssetID& rkAssetID)
|
||||
: CResourceDependency(rkAssetID)
|
||||
, mIDString(rkPropID)
|
||||
{}
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
|
||||
// Accessors
|
||||
inline TString PropertyID() const { return mIDString; }
|
||||
};
|
||||
|
||||
// Node representing a single animset dependency referenced by a script property. Indicates which character is being used.
|
||||
class CCharPropertyDependency : public CPropertyDependency
|
||||
{
|
||||
protected:
|
||||
u32 mUsedChar;
|
||||
|
||||
public:
|
||||
CAnimSetDependency() : CResourceDependency(), mUsedChar(-1) {}
|
||||
CCharPropertyDependency()
|
||||
: CPropertyDependency()
|
||||
, mUsedChar(-1)
|
||||
{}
|
||||
|
||||
CCharPropertyDependency(const TString& rkPropID, const CAssetID& rkAssetID, u32 UsedChar)
|
||||
: CPropertyDependency(rkPropID, rkAssetID)
|
||||
, mUsedChar(UsedChar)
|
||||
{}
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
virtual void Write(IOutputStream& rFile) 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
|
||||
// Node representing a script object. Indicates the type of object.
|
||||
class CScriptInstanceDependency : public IDependencyNode
|
||||
{
|
||||
protected:
|
||||
CAssetID mID;
|
||||
std::vector<CResourceDependency*> mReferencedResources;
|
||||
u32 mObjectType;
|
||||
|
||||
public:
|
||||
CDependencyTree(const CAssetID& rkID) : mID(rkID) {}
|
||||
~CDependencyTree();
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
|
||||
u32 NumDependencies() const;
|
||||
bool HasDependency(const CAssetID& rkID);
|
||||
CAssetID DependencyByIndex(u32 Index) const;
|
||||
void AddDependency(const CAssetID& rkID, bool AvoidDuplicates = true);
|
||||
void AddDependency(CResource *pRes, bool AvoidDuplicates = true);
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
|
||||
// Accessors
|
||||
inline void SetID(const CAssetID& rkID) { mID = rkID; }
|
||||
inline CAssetID ID() const { return mID; }
|
||||
inline u32 ObjectType() const { return mObjectType; }
|
||||
|
||||
// Static
|
||||
static CScriptInstanceDependency* BuildTree(CScriptObject *pInstance);
|
||||
protected:
|
||||
static void ParseStructDependencies(CScriptInstanceDependency *pTree, CPropertyStruct *pStruct);
|
||||
};
|
||||
|
||||
// Node representing an animset resource; allows for lookup of dependencies of a particular character in the set.
|
||||
|
@ -109,7 +160,7 @@ public:
|
|||
CAnimSetDependencyTree(const CAssetID& rkID) : CDependencyTree(rkID) {}
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
|
||||
void AddCharacter(const SSetCharacter *pkChar, const std::set<CAssetID>& rkBaseUsedSet);
|
||||
void AddCharDependency(const CAssetID& rkID, std::set<CAssetID>& rUsedSet);
|
||||
|
@ -120,53 +171,24 @@ public:
|
|||
inline u32 CharacterOffset(u32 CharIdx) const { return mCharacterOffsets[CharIdx]; }
|
||||
};
|
||||
|
||||
// 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, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
bool HasDependency(const CAssetID& rkID);
|
||||
CAssetID DependencyByIndex(u32 Index) const;
|
||||
|
||||
// Accessors
|
||||
inline u32 NumDependencies() const { return mDependencies.size(); }
|
||||
inline u32 ObjectType() const { return mObjectType; }
|
||||
|
||||
// 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 CAssetID& rkID) : CDependencyTree(rkID) {}
|
||||
~CAreaDependencyTree();
|
||||
|
||||
virtual EDependencyNodeType Type() const;
|
||||
virtual void Read(IInputStream& rFile, EIDLength IDLength);
|
||||
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const;
|
||||
virtual void Write(IOutputStream& rFile) const;
|
||||
|
||||
void AddScriptLayer(CScriptLayer *pLayer);
|
||||
CScriptInstanceDependencyTree* ScriptInstanceByIndex(u32 Index) const;
|
||||
void GetModuleDependencies(EGame Game, std::vector<TString>& rModuleDepsOut, std::vector<u32>& rModuleLayerOffsetsOut) const;
|
||||
|
||||
// Accessors
|
||||
inline u32 NumScriptLayers() const { return mLayerOffsets.size(); }
|
||||
inline u32 NumScriptInstances() const { return mScriptInstances.size(); }
|
||||
inline u32 ScriptLayerOffset(u32 LayerIdx) const { return mLayerOffsets[LayerIdx]; }
|
||||
};
|
||||
|
||||
|
|
|
@ -503,10 +503,10 @@ void CGameExporter::ExportWorlds()
|
|||
SetResourcePath(AreaID, AreaDir, GameAreaName);
|
||||
ExportResource(*pInst);
|
||||
}
|
||||
|
||||
mStore.DestroyUnreferencedResources();
|
||||
}
|
||||
}
|
||||
|
||||
mStore.DestroyUnreferencedResources();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -233,7 +233,12 @@ void CPackage::Cook()
|
|||
{
|
||||
u32 CompressedSize;
|
||||
std::vector<u8> CompressedData(ResourceData.size() * 2);
|
||||
bool Success = CompressionUtil::CompressZlib(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedData.size(), CompressedSize);
|
||||
bool Success = false;
|
||||
|
||||
if (mpProject->Game() <= eEchoesDemo)
|
||||
Success = CompressionUtil::CompressZlib(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedData.size(), CompressedSize);
|
||||
else
|
||||
Success = CompressionUtil::CompressLZOSegmented(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedSize);
|
||||
|
||||
// Make sure that the compressed data is actually smaller, accounting for padding + uncompressed size value
|
||||
if (Success)
|
||||
|
|
|
@ -31,6 +31,7 @@ CResourceEntry::CResourceEntry(CResourceStore *pStore, const CAssetID& rkID,
|
|||
CResourceEntry::~CResourceEntry()
|
||||
{
|
||||
if (mpResource) delete mpResource;
|
||||
if (mpDependencies) delete mpDependencies;
|
||||
}
|
||||
|
||||
bool CResourceEntry::LoadCacheData()
|
||||
|
@ -102,7 +103,7 @@ bool CResourceEntry::SaveCacheData()
|
|||
File.WriteLong(0);
|
||||
|
||||
u32 DepsStart = File.Tell();
|
||||
if (mpDependencies) mpDependencies->Write(File, Game() <= eEchoes ? e32Bit : e64Bit);
|
||||
if (mpDependencies) mpDependencies->Write(File);
|
||||
u32 DepsEnd = File.Tell();
|
||||
u32 DepsSize = DepsEnd- DepsStart;
|
||||
|
||||
|
@ -137,7 +138,7 @@ void CResourceEntry::UpdateDependencies()
|
|||
|
||||
mpDependencies = mpResource->BuildDependencyTree();
|
||||
if (!WasLoaded)
|
||||
gpResourceStore->DestroyUnreferencedResources();
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
}
|
||||
|
||||
TWideString CResourceEntry::CacheDataPath(bool Relative) const
|
||||
|
@ -253,7 +254,7 @@ bool CResourceEntry::Save()
|
|||
SaveCacheData();
|
||||
|
||||
if (ShouldCollectGarbage)
|
||||
gpResourceStore->DestroyUnreferencedResources();
|
||||
mpStore->DestroyUnreferencedResources();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ CResourceStore::CResourceStore(CGameExporter *pExporter)
|
|||
CResourceStore::~CResourceStore()
|
||||
{
|
||||
CloseActiveProject();
|
||||
DestroyUnreferencedResources();
|
||||
|
||||
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
|
||||
delete It->second;
|
||||
|
@ -139,15 +140,31 @@ void CResourceStore::SetActiveProject(CGameProject *pProj)
|
|||
|
||||
void CResourceStore::CloseActiveProject()
|
||||
{
|
||||
// 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.)
|
||||
DestroyUnreferencedResources();
|
||||
|
||||
// Delete all entries from old project
|
||||
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
|
||||
{
|
||||
CResourceEntry *pEntry = It->second;
|
||||
|
||||
if (!pEntry->IsTransient())
|
||||
{
|
||||
delete pEntry;
|
||||
if (pEntry->IsLoaded())
|
||||
{
|
||||
bool UnloadSuccess = pEntry->Unload();
|
||||
ASSERT(UnloadSuccess);
|
||||
|
||||
auto LoadIt = mLoadedResources.find(pEntry->ID());
|
||||
ASSERT(LoadIt != mLoadedResources.end());
|
||||
mLoadedResources.erase(LoadIt);
|
||||
}
|
||||
|
||||
It = mResourceEntries.erase(It);
|
||||
if (It == mResourceEntries.end()) break;
|
||||
|
||||
delete pEntry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
#include "DependencyListBuilders.h"
|
||||
|
||||
// ************ CCharacterUsageMap ************
|
||||
bool CCharacterUsageMap::IsCharacterUsed(const CAssetID& rkID, u32 CharacterIndex) const
|
||||
{
|
||||
auto Find = mUsageMap.find(rkID);
|
||||
if (Find == mUsageMap.end()) return false;
|
||||
|
||||
const std::vector<bool>& rkUsageList = Find->second;
|
||||
if (CharacterIndex >= rkUsageList.size()) return false;
|
||||
else return rkUsageList[CharacterIndex];
|
||||
}
|
||||
|
||||
void CCharacterUsageMap::FindUsagesForArea(CWorld *pWorld, CResourceEntry *pEntry)
|
||||
{
|
||||
ASSERT(pEntry->ResourceType() == eArea);
|
||||
|
||||
for (u32 iArea = 0; iArea < pWorld->NumAreas(); iArea++)
|
||||
{
|
||||
if (pWorld->AreaResourceID(iArea) == pEntry->ID())
|
||||
{
|
||||
FindUsagesForArea(pWorld, iArea);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCharacterUsageMap::FindUsagesForArea(CWorld *pWorld, u32 AreaIndex)
|
||||
{
|
||||
// We only need to search forward from this area to other areas that both use the same character(s) + have duplicates enabled
|
||||
mUsageMap.clear();
|
||||
mStillLookingIDs.clear();
|
||||
mLayerIndex = -1;
|
||||
mIsInitialArea = true;
|
||||
|
||||
for (u32 iArea = AreaIndex; iArea < pWorld->NumAreas(); iArea++)
|
||||
{
|
||||
if (!mIsInitialArea && mStillLookingIDs.empty()) break;
|
||||
mCurrentAreaAllowsDupes = pWorld->DoesAreaAllowPakDuplicates(iArea);
|
||||
|
||||
CAssetID AreaID = pWorld->AreaResourceID(iArea);
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(AreaID);
|
||||
ASSERT(pEntry && pEntry->ResourceType() == eArea);
|
||||
|
||||
ParseDependencyNode(pEntry->Dependencies());
|
||||
mIsInitialArea = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CCharacterUsageMap::FindUsagesForLayer(CResourceEntry *pAreaEntry, u32 LayerIndex)
|
||||
{
|
||||
mUsageMap.clear();
|
||||
mStillLookingIDs.clear();
|
||||
mLayerIndex = LayerIndex;
|
||||
mIsInitialArea = true;
|
||||
|
||||
CAreaDependencyTree *pTree = static_cast<CAreaDependencyTree*>(pAreaEntry->Dependencies());
|
||||
ASSERT(pTree->Type() == eDNT_Area);
|
||||
|
||||
// Only examine dependencies of the particular layer specified by the caller
|
||||
bool IsLastLayer = (mLayerIndex == pTree->NumScriptLayers() - 1);
|
||||
u32 StartIdx = pTree->ScriptLayerOffset(mLayerIndex);
|
||||
u32 EndIdx = (IsLastLayer ? pTree->NumChildren() : pTree->ScriptLayerOffset(mLayerIndex + 1));
|
||||
|
||||
for (u32 iInst = StartIdx; iInst < EndIdx; iInst++)
|
||||
ParseDependencyNode(pTree->ChildByIndex(iInst));
|
||||
}
|
||||
|
||||
#include "Core/Resource/CAnimSet.h"
|
||||
|
||||
void CCharacterUsageMap::DebugPrintContents()
|
||||
{
|
||||
for (auto Iter = mUsageMap.begin(); Iter != mUsageMap.end(); Iter++)
|
||||
{
|
||||
CAssetID ID = Iter->first;
|
||||
std::vector<bool>& rUsedList = Iter->second;
|
||||
CAnimSet *pSet = (CAnimSet*) gpResourceStore->LoadResource(ID, "ANCS");
|
||||
|
||||
for (u32 iChar = 0; iChar < pSet->NumNodes(); iChar++)
|
||||
{
|
||||
bool Used = (rUsedList.size() > iChar && rUsedList[iChar]);
|
||||
TString CharName = pSet->NodeName(iChar);
|
||||
Log::Write(ID.ToString() + " : Char " + TString::FromInt32(iChar, 0, 10) + " : " + CharName + " : " + (Used ? "USED" : "UNUSED"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ************ PROTECTED ************
|
||||
void CCharacterUsageMap::ParseDependencyNode(IDependencyNode *pNode)
|
||||
{
|
||||
EDependencyNodeType Type = pNode->Type();
|
||||
|
||||
if (Type == eDNT_CharacterProperty)
|
||||
{
|
||||
CCharPropertyDependency *pDep = static_cast<CCharPropertyDependency*>(pNode);
|
||||
CAssetID ResID = pDep->ID();
|
||||
auto Find = mUsageMap.find(ResID);
|
||||
|
||||
if (!mIsInitialArea && mStillLookingIDs.find(ResID) == mStillLookingIDs.end())
|
||||
return;
|
||||
|
||||
if (Find != mUsageMap.end())
|
||||
{
|
||||
if (!mIsInitialArea && mCurrentAreaAllowsDupes)
|
||||
{
|
||||
mStillLookingIDs.erase( mStillLookingIDs.find(ResID) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (!mIsInitialArea) return;
|
||||
mUsageMap[ResID] = std::vector<bool>();
|
||||
mStillLookingIDs.insert(ResID);
|
||||
}
|
||||
|
||||
std::vector<bool>& rUsageList = mUsageMap[ResID];
|
||||
u32 UsedChar = pDep->UsedChar();
|
||||
|
||||
if (rUsageList.size() <= UsedChar)
|
||||
rUsageList.resize(UsedChar + 1, false);
|
||||
|
||||
rUsageList[UsedChar] = true;
|
||||
}
|
||||
|
||||
// Look for sub-dependencies of the current node
|
||||
else
|
||||
{
|
||||
for (u32 iChild = 0; iChild < pNode->NumChildren(); iChild++)
|
||||
ParseDependencyNode(pNode->ChildByIndex(iChild));
|
||||
}
|
||||
}
|
||||
|
||||
// ************ CPackageDependencyListBuilder ************
|
||||
void CPackageDependencyListBuilder::BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut)
|
||||
{
|
||||
mEnableDuplicates = AllowDuplicates;
|
||||
|
||||
// Iterate over all resource collections and resources and parse their dependencies
|
||||
for (u32 iCol = 0; iCol < mpPackage->NumCollections(); iCol++)
|
||||
{
|
||||
CResourceCollection *pCollection = mpPackage->CollectionByIndex(iCol);
|
||||
|
||||
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
|
||||
{
|
||||
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkRes.ID);
|
||||
if (!pEntry) continue;
|
||||
|
||||
if (rkRes.Name.EndsWith("NODEPEND"))
|
||||
{
|
||||
rOut.push_back(rkRes.ID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rkRes.Type == "MLVL")
|
||||
{
|
||||
CResourceEntry *pWorldEntry = gpResourceStore->FindEntry(rkRes.ID);
|
||||
ASSERT(pWorldEntry);
|
||||
mpWorld = (CWorld*) pWorldEntry->Load();
|
||||
ASSERT(mpWorld);
|
||||
}
|
||||
|
||||
AddDependency(nullptr, rkRes.ID, rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPackageDependencyListBuilder::AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut)
|
||||
{
|
||||
if (pCurEntry && pCurEntry->ResourceType() == eDependencyGroup) return;
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
|
||||
if (!pEntry) return;
|
||||
|
||||
EResType ResType = pEntry->ResourceType();
|
||||
|
||||
// Is this entry valid?
|
||||
bool IsValid = ResType != eMidi &&
|
||||
(ResType != eAudioGroupSet || mGame >= eEchoesDemo) &&
|
||||
(ResType != eWorld || !pCurEntry) &&
|
||||
(ResType != eArea || !pCurEntry || pCurEntry->ResourceType() == eWorld);
|
||||
|
||||
if (!IsValid) return;
|
||||
|
||||
if ( ( mCurrentAreaHasDuplicates && mAreaUsedAssets.find(rkID) != mAreaUsedAssets.end()) ||
|
||||
(!mCurrentAreaHasDuplicates && mPackageUsedAssets.find(rkID) != mPackageUsedAssets.end()) )
|
||||
return;
|
||||
|
||||
// Entry is valid, parse its sub-dependencies
|
||||
mPackageUsedAssets.insert(rkID);
|
||||
mAreaUsedAssets.insert(rkID);
|
||||
|
||||
// New area - toggle duplicates and find character usages
|
||||
if (ResType == eArea)
|
||||
{
|
||||
if (mGame <= eEchoes)
|
||||
mCharacterUsageMap.FindUsagesForArea(mpWorld, pEntry);
|
||||
|
||||
mAreaUsedAssets.clear();
|
||||
mCurrentAreaHasDuplicates = false;
|
||||
|
||||
if (!mEnableDuplicates)
|
||||
mCurrentAreaHasDuplicates = false;
|
||||
|
||||
else
|
||||
{
|
||||
for (u32 iArea = 0; iArea < mpWorld->NumAreas(); iArea++)
|
||||
{
|
||||
if (mpWorld->AreaResourceID(iArea) == rkID)
|
||||
{
|
||||
mCurrentAreaHasDuplicates = mpWorld->DoesAreaAllowPakDuplicates(iArea);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate dependencies of this entry
|
||||
CDependencyTree *pTree = pEntry->Dependencies();
|
||||
EvaluateDependencyNode(pEntry, pTree, rOut);
|
||||
rOut.push_back(rkID);
|
||||
}
|
||||
|
||||
void CPackageDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut)
|
||||
{
|
||||
EDependencyNodeType Type = pNode->Type();
|
||||
|
||||
if (Type == eDNT_AnimSet)
|
||||
{
|
||||
// Add base dependencies, then only add dependencies from used characters
|
||||
CAnimSetDependencyTree *pTree = static_cast<CAnimSetDependencyTree*>(pNode);
|
||||
u32 BaseEnd = (pTree->NumCharacters() > 0 ? pTree->CharacterOffset(0) : pTree->NumChildren());
|
||||
|
||||
for (u32 iDep = 0; iDep < BaseEnd; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pDep->Type() == eDNT_ResourceDependency);
|
||||
AddDependency(pCurEntry, pDep->ID(), rOut);
|
||||
}
|
||||
|
||||
for (u32 iChar = 0; iChar < pTree->NumCharacters(); iChar++)
|
||||
{
|
||||
if (mCharacterUsageMap.IsCharacterUsed(pCurEntry->ID(), iChar) || mGame > eEchoes || mIsPlayerActor)
|
||||
{
|
||||
u32 StartIdx = pTree->CharacterOffset(iChar);
|
||||
u32 EndIdx = (iChar == pTree->NumCharacters() - 1 ? pTree->NumChildren() : pTree->CharacterOffset(iChar + 1));
|
||||
|
||||
for (u32 iDep = StartIdx; iDep < EndIdx; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pDep->Type() == eDNT_ResourceDependency);
|
||||
AddDependency(pCurEntry, pDep->ID(), rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (Type == eDNT_ResourceDependency || Type == eDNT_ScriptProperty || Type == eDNT_CharacterProperty)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pNode);
|
||||
AddDependency(pCurEntry, pDep->ID(), rOut);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (Type == eDNT_ScriptInstance)
|
||||
{
|
||||
u32 ObjType = static_cast<CScriptInstanceDependency*>(pNode)->ObjectType();
|
||||
mIsPlayerActor = (ObjType == 0x4C || ObjType == FOURCC("PLAC"));
|
||||
}
|
||||
|
||||
for (u32 iChild = 0; iChild < pNode->NumChildren(); iChild++)
|
||||
EvaluateDependencyNode(pCurEntry, pNode->ChildByIndex(iChild), rOut);
|
||||
|
||||
|
||||
if (Type == eDNT_ScriptInstance)
|
||||
mIsPlayerActor = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ************ CAreaDependencyListBuilder ************
|
||||
void CAreaDependencyListBuilder::BuildDependencyList(std::list<CAssetID>& rAssetsOut, std::list<u32>& rLayerOffsetsOut)
|
||||
{
|
||||
CAreaDependencyTree *pTree = static_cast<CAreaDependencyTree*>(mpAreaEntry->Dependencies());
|
||||
|
||||
// Fill area base used assets set (don't actually add to list yet)
|
||||
u32 BaseEndIndex = (pTree->NumScriptLayers() > 0 ? pTree->ScriptLayerOffset(0) : pTree->NumChildren());
|
||||
|
||||
for (u32 iDep = 0; iDep < BaseEndIndex; iDep++)
|
||||
{
|
||||
CResourceDependency *pRes = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pRes->Type() == eDNT_ResourceDependency);
|
||||
mBaseUsedAssets.insert(pRes->ID());
|
||||
}
|
||||
|
||||
// Get dependencies of each layer
|
||||
for (u32 iLyr = 0; iLyr < pTree->NumScriptLayers(); iLyr++)
|
||||
{
|
||||
mLayerUsedAssets.clear();
|
||||
mCharacterUsageMap.FindUsagesForLayer(mpAreaEntry, iLyr);
|
||||
rLayerOffsetsOut.push_back(rAssetsOut.size());
|
||||
|
||||
bool IsLastLayer = (iLyr == pTree->NumScriptLayers() - 1);
|
||||
u32 StartIdx = pTree->ScriptLayerOffset(iLyr);
|
||||
u32 EndIdx = (IsLastLayer ? pTree->NumChildren() : pTree->ScriptLayerOffset(iLyr + 1));
|
||||
|
||||
for (u32 iChild = StartIdx; iChild < EndIdx; iChild++)
|
||||
{
|
||||
CScriptInstanceDependency *pInst = static_cast<CScriptInstanceDependency*>(pTree->ChildByIndex(iChild));
|
||||
ASSERT(pInst->Type() == eDNT_ScriptInstance);
|
||||
mIsPlayerActor = (pInst->ObjectType() == 0x4C || pInst->ObjectType() == FOURCC("PLAC"));
|
||||
|
||||
for (u32 iDep = 0; iDep < pInst->NumChildren(); iDep++)
|
||||
{
|
||||
CPropertyDependency *pDep = static_cast<CPropertyDependency*>(pInst->ChildByIndex(iDep));
|
||||
|
||||
// For MP3, exclude the CMDL/CSKR properties for the suit assets - only include default character assets
|
||||
if (mGame == eCorruption && mIsPlayerActor)
|
||||
{
|
||||
TString PropID = pDep->PropertyID();
|
||||
|
||||
if ( PropID == "0x846397A8" || PropID == "0x685A4C01" ||
|
||||
PropID == "0x9834ECC9" || PropID == "0x188B8960" ||
|
||||
PropID == "0x134A81E3" || PropID == "0x4ABF030C" ||
|
||||
PropID == "0x9BF030DC" || PropID == "0x981263D3" ||
|
||||
PropID == "0x8A8D5AA5" || PropID == "0xE4734608" ||
|
||||
PropID == "0x3376814D" || PropID == "0x797CA77E" ||
|
||||
PropID == "0x0EBEC440" || PropID == "0xBC0952D8" ||
|
||||
PropID == "0xA8778E57" || PropID == "0x1CB10DBE" )
|
||||
continue;
|
||||
}
|
||||
|
||||
AddDependency(pDep->ID(), rAssetsOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add base assets
|
||||
mBaseUsedAssets.clear();
|
||||
mLayerUsedAssets.clear();
|
||||
rLayerOffsetsOut.push_back(rAssetsOut.size());
|
||||
|
||||
for (u32 iDep = 0; iDep < BaseEndIndex; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
AddDependency(pDep->ID(), rAssetsOut);
|
||||
}
|
||||
}
|
||||
|
||||
void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<CAssetID>& rOut)
|
||||
{
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
|
||||
if (!pEntry) return;
|
||||
|
||||
EResType ResType = pEntry->ResourceType();
|
||||
|
||||
// Check if this is a valid dependency
|
||||
bool IsValid = ResType != eMidi &&
|
||||
ResType != eWorld &&
|
||||
ResType != eArea &&
|
||||
(ResType != eAudioGroupSet || mGame >= eEchoesDemo);
|
||||
|
||||
if (!IsValid) return;
|
||||
|
||||
if (mBaseUsedAssets.find(rkID) != mBaseUsedAssets.end() || mLayerUsedAssets.find(rkID) != mLayerUsedAssets.end())
|
||||
return;
|
||||
|
||||
// Dependency is valid! Evaluate sub-dependencies
|
||||
// For animsets, only add used character indices
|
||||
if (ResType == eAnimSet && mGame <= eEchoes)
|
||||
{
|
||||
// Add base dependencies first, then character-specific ones
|
||||
CAnimSetDependencyTree *pTree = static_cast<CAnimSetDependencyTree*>(pEntry->Dependencies());
|
||||
u32 BaseEndIdx = (pTree->NumCharacters() > 0 ? pTree->CharacterOffset(0) : pTree->NumChildren());
|
||||
|
||||
for (u32 iDep = 0; iDep < BaseEndIdx; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pDep->Type() == eDNT_ResourceDependency);
|
||||
AddDependency(pDep->ID(), rOut);
|
||||
}
|
||||
|
||||
for (u32 iChar = 0; iChar < pTree->NumCharacters(); iChar++)
|
||||
{
|
||||
// Note: For MP1/2 PlayerActor, always treat as if Empty Suit is the only used one
|
||||
const u32 kEmptySuitIndex = (mGame >= eEchoesDemo ? 3 : 5);
|
||||
bool IsUsed = (mIsPlayerActor ? iChar == kEmptySuitIndex : mCharacterUsageMap.IsCharacterUsed(rkID, iChar));
|
||||
if (!IsUsed) continue;
|
||||
|
||||
u32 StartIdx = pTree->CharacterOffset(iChar);
|
||||
u32 EndIdx = (iChar == pTree->NumCharacters() - 1 ? pTree->NumChildren() : pTree->CharacterOffset(iChar + 1));
|
||||
|
||||
for (u32 iDep = StartIdx; iDep < EndIdx; iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pDep->Type() == eDNT_ResourceDependency);
|
||||
AddDependency(pDep->ID(), rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For other resource types (except SCAN and DGRP), evaluate all sub-dependencies
|
||||
else if (ResType != eScan && ResType != eDependencyGroup)
|
||||
{
|
||||
CDependencyTree *pTree = pEntry->Dependencies();
|
||||
|
||||
for (u32 iDep = 0; iDep < pTree->NumChildren(); iDep++)
|
||||
{
|
||||
CResourceDependency *pDep = static_cast<CResourceDependency*>(pTree->ChildByIndex(iDep));
|
||||
ASSERT(pDep->Type() == eDNT_ResourceDependency);
|
||||
AddDependency(pDep->ID(), rOut);
|
||||
}
|
||||
}
|
||||
|
||||
rOut.push_back(rkID);
|
||||
}
|
|
@ -7,259 +7,72 @@
|
|||
#include "Core/Resource/CDependencyGroup.h"
|
||||
#include "Core/Resource/CWorld.h"
|
||||
|
||||
class CCharacterUsageMap
|
||||
{
|
||||
std::map<CAssetID, std::vector<bool>> mUsageMap;
|
||||
std::set<CAssetID> mStillLookingIDs;
|
||||
u32 mLayerIndex;
|
||||
bool mIsInitialArea;
|
||||
bool mCurrentAreaAllowsDupes;
|
||||
|
||||
public:
|
||||
CCharacterUsageMap() : mLayerIndex(-1), mIsInitialArea(true), mCurrentAreaAllowsDupes(false) {}
|
||||
bool IsCharacterUsed(const CAssetID& rkID, u32 CharacterIndex) const;
|
||||
void FindUsagesForArea(CWorld *pWorld, CResourceEntry *pEntry);
|
||||
void FindUsagesForArea(CWorld *pWorld, u32 AreaIndex);
|
||||
void FindUsagesForLayer(CResourceEntry *pAreaEntry, u32 LayerIndex);
|
||||
void DebugPrintContents();
|
||||
|
||||
protected:
|
||||
void ParseDependencyNode(IDependencyNode *pNode);
|
||||
};
|
||||
|
||||
// ************ CPackageDependencyListBuilder ************
|
||||
class CPackageDependencyListBuilder
|
||||
{
|
||||
CPackage *mpPackage;
|
||||
EGame mGame;
|
||||
TResPtr<CWorld> mpWorld;
|
||||
CCharacterUsageMap mCharacterUsageMap;
|
||||
std::set<CAssetID> mPackageUsedAssets;
|
||||
std::set<CAssetID> mAreaUsedAssets;
|
||||
std::list<CAssetID> mScanIDs;
|
||||
bool mEnableDuplicates;
|
||||
bool mCurrentAreaHasDuplicates;
|
||||
bool mAddingScans;
|
||||
bool mIsPlayerActor;
|
||||
|
||||
public:
|
||||
CPackageDependencyListBuilder(CPackage *pPackage)
|
||||
: mpPackage(pPackage)
|
||||
, mGame(pPackage->Project()->Game())
|
||||
, mCurrentAreaHasDuplicates(false)
|
||||
, mAddingScans(false)
|
||||
, mIsPlayerActor(false)
|
||||
{}
|
||||
|
||||
virtual void BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut)
|
||||
{
|
||||
mEnableDuplicates = AllowDuplicates;
|
||||
|
||||
for (u32 iCol = 0; iCol < mpPackage->NumCollections(); iCol++)
|
||||
{
|
||||
CResourceCollection *pCollection = mpPackage->CollectionByIndex(iCol);
|
||||
|
||||
for (u32 iRes = 0; iRes < pCollection->NumResources(); iRes++)
|
||||
{
|
||||
const SNamedResource& rkRes = pCollection->ResourceByIndex(iRes);
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkRes.ID);
|
||||
|
||||
if (pEntry)
|
||||
{
|
||||
if (rkRes.Name.EndsWith("NODEPEND"))
|
||||
rOut.push_back(rkRes.ID);
|
||||
|
||||
else
|
||||
{
|
||||
mScanIDs.clear();
|
||||
mAddingScans = false;
|
||||
|
||||
if (rkRes.Type == "MLVL")
|
||||
{
|
||||
CResourceEntry *pWorldEntry = gpResourceStore->FindEntry(rkRes.ID);
|
||||
ASSERT(pWorldEntry);
|
||||
mpWorld = (CWorld*) pWorldEntry->Load();
|
||||
ASSERT(mpWorld);
|
||||
}
|
||||
|
||||
EvaluateDependencies(pEntry, rOut);
|
||||
|
||||
mAddingScans = true;
|
||||
for (auto Iter = mScanIDs.begin(); Iter != mScanIDs.end(); Iter++)
|
||||
AddDependency(pEntry, *Iter, rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut)
|
||||
{
|
||||
if (pCurEntry->ResourceType() == eDependencyGroup) return;
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
|
||||
EResType Type = (pEntry ? pEntry->ResourceType() : eResource);
|
||||
|
||||
// Defer scans to the end of the list. This is to accomodate the way the game loads SCANs; they are
|
||||
// loaded separately from everything else after the area itself is done loading.
|
||||
if (Type == eScan && !mAddingScans)
|
||||
{
|
||||
mScanIDs.push_back(rkID);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure entry exists + is valid
|
||||
if (pEntry && Type != eMidi && Type != eAudioGroupSet && Type != eWorld && (Type != eArea || pCurEntry->ResourceType() == eWorld))
|
||||
{
|
||||
if ( ( mCurrentAreaHasDuplicates && mAreaUsedAssets.find(rkID) == mAreaUsedAssets.end()) ||
|
||||
(!mCurrentAreaHasDuplicates && mPackageUsedAssets.find(rkID) == mPackageUsedAssets.end()) )
|
||||
EvaluateDependencies(pEntry, rOut);
|
||||
}
|
||||
}
|
||||
|
||||
void EvaluateDependencies(CResourceEntry *pEntry, std::list<CAssetID>& rOut)
|
||||
{
|
||||
// Toggle duplicates
|
||||
if (pEntry->ResourceType() == eArea && mEnableDuplicates)
|
||||
{
|
||||
mAreaUsedAssets.clear();
|
||||
|
||||
for (u32 iArea = 0; iArea < mpWorld->NumAreas(); iArea++)
|
||||
{
|
||||
if (mpWorld->AreaResourceID(iArea) == pEntry->ID())
|
||||
{
|
||||
mCurrentAreaHasDuplicates = mpWorld->DoesAreaAllowPakDuplicates(iArea);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependencies
|
||||
CDependencyTree *pTree = pEntry->Dependencies();
|
||||
mPackageUsedAssets.insert(pTree->ID());
|
||||
mAreaUsedAssets.insert(pTree->ID());
|
||||
|
||||
for (u32 iDep = 0; iDep < pTree->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pTree->DependencyByIndex(iDep);
|
||||
AddDependency(pEntry, ID, rOut);
|
||||
}
|
||||
|
||||
// Add area script dependencies
|
||||
if (pEntry->ResourceType() == eArea)
|
||||
{
|
||||
CAreaDependencyTree *pAreaTree = static_cast<CAreaDependencyTree*>(pTree);
|
||||
|
||||
for (u32 iInst = 0; iInst < pAreaTree->NumScriptInstances(); iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pInstTree = pAreaTree->ScriptInstanceByIndex(iInst);
|
||||
|
||||
for (u32 iDep = 0; iDep < pInstTree->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pInstTree->DependencyByIndex(iDep);
|
||||
AddDependency(pEntry, ID, rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rOut.push_back(pTree->ID());
|
||||
}
|
||||
void BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut);
|
||||
void AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut);
|
||||
void EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut);
|
||||
};
|
||||
|
||||
// ************ CAreaDependencyListBuilder ************
|
||||
class CAreaDependencyListBuilder
|
||||
{
|
||||
CResourceEntry *mpAreaEntry;
|
||||
EGame mGame;
|
||||
CCharacterUsageMap mCharacterUsageMap;
|
||||
std::set<CAssetID> mBaseUsedAssets;
|
||||
std::set<CAssetID> mLayerUsedAssets;
|
||||
bool mIsPlayerActor;
|
||||
|
||||
public:
|
||||
CAreaDependencyListBuilder(CResourceEntry *pAreaEntry)
|
||||
: mpAreaEntry(pAreaEntry) {}
|
||||
|
||||
virtual void BuildDependencyList(std::list<CAssetID>& rAssetsOut, std::list<u32>& rLayerOffsetsOut)
|
||||
: mpAreaEntry(pAreaEntry)
|
||||
, mGame(pAreaEntry->Game())
|
||||
{
|
||||
ASSERT(mpAreaEntry->ResourceType() == eArea);
|
||||
CAreaDependencyTree *pTree = static_cast<CAreaDependencyTree*>(mpAreaEntry->Dependencies());
|
||||
|
||||
// Fill area base used assets set (don't actually add to list yet)
|
||||
for (u32 iDep = 0; iDep < pTree->NumDependencies(); iDep++)
|
||||
mBaseUsedAssets.insert(pTree->DependencyByIndex(iDep));
|
||||
|
||||
// Get dependencies of each layer
|
||||
for (u32 iLyr = 0; iLyr < pTree->NumScriptLayers(); iLyr++)
|
||||
{
|
||||
mLayerUsedAssets.clear();
|
||||
rLayerOffsetsOut.push_back(rAssetsOut.size());
|
||||
|
||||
bool IsLastLayer = (iLyr == pTree->NumScriptLayers() - 1);
|
||||
u32 StartIndex = pTree->ScriptLayerOffset(iLyr);
|
||||
u32 EndIndex = (IsLastLayer ? pTree->NumScriptInstances() : pTree->ScriptLayerOffset(iLyr + 1));
|
||||
|
||||
for (u32 iInst = StartIndex; iInst < EndIndex; iInst++)
|
||||
{
|
||||
CScriptInstanceDependencyTree *pInstTree = pTree->ScriptInstanceByIndex(iInst);
|
||||
mIsPlayerActor = (pInstTree->ObjectType() == 0x4C);
|
||||
|
||||
for (u32 iDep = 0; iDep < pInstTree->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pInstTree->DependencyByIndex(iDep);
|
||||
AddDependency(mpAreaEntry, ID, rAssetsOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add base dependencies
|
||||
mBaseUsedAssets.clear();
|
||||
mLayerUsedAssets.clear();
|
||||
rLayerOffsetsOut.push_back(rAssetsOut.size());
|
||||
|
||||
for (u32 iDep = 0; iDep < pTree->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pTree->DependencyByIndex(iDep);
|
||||
AddDependency(mpAreaEntry, ID, rAssetsOut);
|
||||
}
|
||||
}
|
||||
|
||||
void AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut)
|
||||
{
|
||||
if (pCurEntry->ResourceType() == eDependencyGroup) return;
|
||||
CResourceEntry *pEntry = gpResourceStore->FindEntry(rkID);
|
||||
EResType Type = (pEntry ? pEntry->ResourceType() : eResource);
|
||||
|
||||
// Make sure entry exists + is valid
|
||||
if (pEntry && Type != eMidi && Type != eAudioGroupSet && Type != eScan && Type != eWorld && Type != eArea)
|
||||
{
|
||||
if ( mBaseUsedAssets.find(rkID) == mBaseUsedAssets.end() && mLayerUsedAssets.find(rkID) == mLayerUsedAssets.end())
|
||||
{
|
||||
if (mIsPlayerActor && pEntry->ResourceType() == eAnimSet)
|
||||
EvaluatePlayerActorAnimSet(pEntry, rOut);
|
||||
else
|
||||
EvaluateDependencies(pEntry, rOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EvaluateDependencies(CResourceEntry *pEntry, std::list<CAssetID>& rOut)
|
||||
{
|
||||
CDependencyTree *pTree = pEntry->Dependencies();
|
||||
mLayerUsedAssets.insert(pTree->ID());
|
||||
|
||||
for (u32 iDep = 0; iDep < pTree->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pTree->DependencyByIndex(iDep);
|
||||
AddDependency(pEntry, ID, rOut);
|
||||
}
|
||||
|
||||
rOut.push_back(pTree->ID());
|
||||
}
|
||||
|
||||
void EvaluatePlayerActorAnimSet(CResourceEntry *pEntry, std::list<CAssetID>& rOut)
|
||||
{
|
||||
// For PlayerActor animsets we want to include only the empty suit (char 5) in the dependency list. This is to
|
||||
// accomodate the dynamic loading the game does for PlayerActors to avoid having assets for suits the player
|
||||
// doesn't have in memory. We want common assets (animations, etc) in the list but not per-character assets.
|
||||
// The reason to include empty suit is to make sure resources that are stored as per-character data but are
|
||||
// actually common to every character, such as particle effects, are still included in the list.
|
||||
ASSERT(pEntry->ResourceType() == eAnimSet);
|
||||
CAnimSetDependencyTree *pTree = static_cast<CAnimSetDependencyTree*>(pEntry->Dependencies());
|
||||
mLayerUsedAssets.insert(pTree->ID());
|
||||
|
||||
// Add empty suit dependencies
|
||||
ASSERT(pTree->NumCharacters() >= 7);
|
||||
u32 StartIdx = pTree->CharacterOffset(5);
|
||||
u32 EndIdx = pTree->CharacterOffset(6);
|
||||
|
||||
for (u32 iDep = StartIdx; iDep < EndIdx; iDep++)
|
||||
{
|
||||
CAssetID ID = pTree->DependencyByIndex(iDep);
|
||||
AddDependency(pEntry, ID, rOut);
|
||||
}
|
||||
|
||||
// Add common dependencies
|
||||
for (u32 iDep = 0; iDep < pTree->CharacterOffset(0); iDep++)
|
||||
{
|
||||
CAssetID ID = pTree->DependencyByIndex(iDep);
|
||||
AddDependency(pEntry, ID, rOut);
|
||||
}
|
||||
|
||||
rOut.push_back(pTree->ID());
|
||||
}
|
||||
void BuildDependencyList(std::list<CAssetID>& rAssetsOut, std::list<u32>& rLayerOffsetsOut);
|
||||
void AddDependency(const CAssetID& rkID, std::list<CAssetID>& rOut);
|
||||
};
|
||||
|
||||
#endif // DEPENDENCYLISTBUILDERS
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
for (u32 iDep = 0; iDep < pGroup->NumDependencies(); iDep++)
|
||||
{
|
||||
CAssetID ID = pGroup->DependencyByIndex(iDep);
|
||||
pTree->AddDependency(iDep);
|
||||
pTree->AddDependency(ID);
|
||||
BaseUsedSet.insert(ID);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ public:
|
|||
|
||||
CDependencyTree* BuildDependencyTree() const
|
||||
{
|
||||
if (Game() >= eEchoesDemo)
|
||||
Log::Warning("CScan::BuildDependencyTree not handling Echoes/Corruption dependencies");
|
||||
if (Game() >= eCorruptionProto)
|
||||
Log::Warning("CScan::BuildDependencyTree not handling Corruption dependencies");
|
||||
|
||||
CDependencyTree *pTree = new CDependencyTree(ID());
|
||||
pTree->AddDependency(mFrameID);
|
||||
|
|
|
@ -97,6 +97,9 @@ public:
|
|||
else if (ImageType == L"SA")
|
||||
TexturesStart = 4;
|
||||
|
||||
else if (ImageType.IsHexString(false, IDLength * 2))
|
||||
TexturesStart = 0;
|
||||
|
||||
else
|
||||
{
|
||||
Log::Error("Unrecognized image type: " + ImageType.ToUTF8());
|
||||
|
|
|
@ -381,7 +381,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
|
|||
|
||||
for (u32 iEvnt = 0; iEvnt < EventDataCount; iEvnt++)
|
||||
{
|
||||
CDependencyGroup *pGrp = CUnsupportedFormatLoader::LoadEVNT(rANCS, nullptr);
|
||||
CDependencyGroup *pGrp = CUnsupportedFormatLoader::LoadEVNT(rANCS, nullptr, true);
|
||||
Loader.pSet->mEventDependencies.push_back(pGrp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ public:
|
|||
case eScan: pRes = CScanLoader::LoadSCAN(rInput, pEntry); break;
|
||||
case eSkeleton: pRes = CSkeletonLoader::LoadCINF(rInput, pEntry); break;
|
||||
case eSkin: pRes = CSkinLoader::LoadCSKR(rInput, pEntry); break;
|
||||
case eStateMachine2: pRes = CUnsupportedFormatLoader::LoadFSM2(rInput, pEntry); break;
|
||||
case eStaticGeometryMap: pRes = CPoiToWorldLoader::LoadEGMC(rInput, pEntry); break;
|
||||
case eStringTable: pRes = CStringLoader::LoadSTRG(rInput, pEntry); break;
|
||||
case eTexture: pRes = CTextureDecoder::LoadTXTR(rInput, pEntry); break;
|
||||
|
@ -83,6 +84,8 @@ public:
|
|||
|
||||
case eParticle:
|
||||
case eParticleElectric:
|
||||
case eParticleSorted:
|
||||
case eParticleSpawn:
|
||||
case eParticleSwoosh:
|
||||
case eParticleDecal:
|
||||
case eParticleWeapon:
|
||||
|
|
|
@ -12,13 +12,11 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadCSNG(IInputStream& rCSNG, CResou
|
|||
return pGroup;
|
||||
}
|
||||
|
||||
CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry)
|
||||
CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry, bool IsEchoes /*= false*/)
|
||||
{
|
||||
u32 Version = rEVNT.ReadLong();
|
||||
ASSERT(Version == 1 || Version == 2);
|
||||
|
||||
// kinda hack - check if we're reading an Echoes ANCS
|
||||
bool IsEchoes = (TString(rEVNT.GetSourceString()).GetFileExtension() == "ANCS");
|
||||
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
|
||||
|
||||
// Loop Events
|
||||
|
@ -61,14 +59,17 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
|
|||
}
|
||||
|
||||
// Sound Events
|
||||
u32 NumSoundEvents = rEVNT.ReadLong();
|
||||
|
||||
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
|
||||
if (Version == 2)
|
||||
{
|
||||
rEVNT.Seek(0x2, SEEK_CUR);
|
||||
rEVNT.ReadString();
|
||||
rEVNT.Seek(0x27, SEEK_CUR);
|
||||
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
|
||||
u32 NumSoundEvents = rEVNT.ReadLong();
|
||||
|
||||
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
|
||||
{
|
||||
rEVNT.Seek(0x2, SEEK_CUR);
|
||||
rEVNT.ReadString();
|
||||
rEVNT.Seek(0x27, SEEK_CUR);
|
||||
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
return pGroup;
|
||||
|
@ -76,15 +77,12 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
|
|||
|
||||
CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry)
|
||||
{
|
||||
if (pEntry->Game() >= eEchoesDemo) return nullptr;
|
||||
|
||||
u32 Version = rFRME.ReadLong();
|
||||
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
|
||||
|
||||
// Prime 1
|
||||
if (Version == 0 || Version == 1)
|
||||
{
|
||||
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
|
||||
rFRME.Seek(0xC, SEEK_CUR);
|
||||
u32 NumWidgets = rFRME.ReadLong();
|
||||
|
||||
|
@ -218,6 +216,75 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResou
|
|||
return pGroup;
|
||||
}
|
||||
|
||||
CDependencyGroup* CUnsupportedFormatLoader::LoadFSM2(IInputStream& rFSM2, CResourceEntry *pEntry)
|
||||
{
|
||||
u32 Magic = rFSM2.ReadLong();
|
||||
ASSERT(Magic == FOURCC("FSM2"));
|
||||
|
||||
CDependencyGroup *pOut = new CDependencyGroup(pEntry);
|
||||
u32 Version = rFSM2.ReadLong();
|
||||
u32 NumStates = rFSM2.ReadLong();
|
||||
u32 NumUnkA = rFSM2.ReadLong();
|
||||
u32 NumUnkB = rFSM2.ReadLong();
|
||||
u32 NumUnkC = rFSM2.ReadLong();
|
||||
ASSERT(Version == 1);
|
||||
|
||||
for (u32 iState = 0; iState < NumStates; iState++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
u32 UnkCount = rFSM2.ReadLong();
|
||||
|
||||
for (u32 iUnk = 0; iUnk < UnkCount; iUnk++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
rFSM2.Seek(0x4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 iUnkA = 0; iUnkA < NumUnkA; iUnkA++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
rFSM2.Seek(0x4, SEEK_CUR);
|
||||
u32 UnkCount = rFSM2.ReadLong();
|
||||
|
||||
for (u32 iUnkA2 = 0; iUnkA2 < UnkCount; iUnkA2++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
rFSM2.Seek(0x4, SEEK_CUR);
|
||||
}
|
||||
|
||||
rFSM2.Seek(0x1, SEEK_CUR);
|
||||
}
|
||||
|
||||
for (u32 iUnkB = 0; iUnkB < NumUnkB; iUnkB++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
u32 UnkCount = rFSM2.ReadLong();
|
||||
|
||||
for (u32 iUnkB2 = 0; iUnkB2 < UnkCount; iUnkB2++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
rFSM2.Seek(0x4, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 iUnkC = 0; iUnkC < NumUnkC; iUnkC++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
u32 UnkCount = rFSM2.ReadLong();
|
||||
|
||||
for (u32 iUnkC2 = 0; iUnkC2 < UnkCount; iUnkC2++)
|
||||
{
|
||||
rFSM2.ReadString();
|
||||
rFSM2.Seek(0x4, SEEK_CUR);
|
||||
}
|
||||
|
||||
pOut->AddDependency( CAssetID(rFSM2, eEchoes) );
|
||||
}
|
||||
|
||||
return pOut;
|
||||
}
|
||||
|
||||
CDependencyGroup* CUnsupportedFormatLoader::LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry)
|
||||
{
|
||||
u32 Magic = rHINT.ReadLong();
|
||||
|
@ -323,7 +390,7 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadRULE(IInputStream& rRULE, CResou
|
|||
// Version test
|
||||
u32 IDOffset = rRULE.Tell();
|
||||
rRULE.Seek(0x4, SEEK_CUR);
|
||||
u32 RuleSetCount = rRULE.ReadLong();
|
||||
u32 RuleSetCount = rRULE.ReadShort();
|
||||
EIDLength IDLength = (RuleSetCount > 0xFF ? e64Bit : e32Bit);
|
||||
rRULE.Seek(IDOffset, SEEK_SET);
|
||||
|
||||
|
|
|
@ -12,8 +12,9 @@ class CUnsupportedFormatLoader
|
|||
|
||||
public:
|
||||
static CDependencyGroup* LoadCSNG(IInputStream& rCSNG, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry, bool IsEchoes = false);
|
||||
static CDependencyGroup* LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadFSM2(IInputStream& rFSM2, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadMAPW(IInputStream& rMAPW, CResourceEntry *pEntry);
|
||||
static CDependencyGroup* LoadMAPU(IInputStream& rMAPU, CResourceEntry *pEntry);
|
||||
|
|
Loading…
Reference in New Issue