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:
parax0 2016-08-19 02:13:02 -06:00
parent 881bb28d84
commit 9a243f94ac
18 changed files with 884 additions and 553 deletions

View File

@ -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;

View File

@ -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:

View File

@ -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

View File

@ -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())

View File

@ -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]; }
};

View File

@ -503,11 +503,11 @@ void CGameExporter::ExportWorlds()
SetResourcePath(AreaID, AreaDir, GameAreaName);
ExportResource(*pInst);
}
}
}
mStore.DestroyUnreferencedResources();
}
}
}
#endif
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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,6 +59,8 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
}
// Sound Events
if (Version == 2)
{
u32 NumSoundEvents = rEVNT.ReadLong();
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
@ -70,21 +70,19 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
rEVNT.Seek(0x27, SEEK_CUR);
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
}
}
return pGroup;
}
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);

View File

@ -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);