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) switch (Game)
{ {
case ePrimeDemo: return "MP1D"; case ePrimeDemo: return "MP1D";
case ePrime: return "MP1 "; case ePrime: return "MPRM";
case eEchoesDemo: return "MP2D"; case eEchoesDemo: return "MP2D";
case eEchoes: return "MP2E"; case eEchoes: return "MP2E";
case eCorruptionProto: return "MP3P"; case eCorruptionProto: return "MP3P";
@ -19,7 +19,7 @@ CFourCC GetGameID(EGame Game)
EGame GetGameForID(const CFourCC& rkID) EGame GetGameForID(const CFourCC& rkID)
{ {
if (rkID == "MP1D") return ePrimeDemo; if (rkID == "MP1D") return ePrimeDemo;
if (rkID == "MP1 ") return ePrime; if (rkID == "MPRM") return ePrime;
if (rkID == "MP2D") return eEchoesDemo; if (rkID == "MP2D") return eEchoesDemo;
if (rkID == "MP2E") return eEchoes; if (rkID == "MP2E") return eEchoes;
if (rkID == "MP3P") return eCorruptionProto; if (rkID == "MP3P") return eCorruptionProto;

View File

@ -23,7 +23,7 @@ public:
mFileVersion = TString( mpCurElem->Attribute("FileVer") ).ToInt32(10); mFileVersion = TString( mpCurElem->Attribute("FileVer") ).ToInt32(10);
mArchiveVersion = TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10); mArchiveVersion = TString( mpCurElem->Attribute("ArchiveVer") ).ToInt32(10);
const char *pkGameAttr = mpCurElem->Attribute("Game"); const char *pkGameAttr = mpCurElem->Attribute("Game");
mGame = pkGameAttr ? eUnknownGame : GetGameForID( CFourCC(pkGameAttr) ); mGame = pkGameAttr ? GetGameForID( CFourCC(pkGameAttr) ) : eUnknownGame;
} }
protected: protected:

View File

@ -299,4 +299,5 @@ SOURCES += \
Resource/Factory/CDependencyGroupLoader.cpp \ Resource/Factory/CDependencyGroupLoader.cpp \
GameProject/CDependencyTree.cpp \ GameProject/CDependencyTree.cpp \
Resource/Factory/CUnsupportedFormatLoader.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/CScriptLayer.h"
#include "Core/Resource/Script/CScriptObject.h" #include "Core/Resource/Script/CScriptObject.h"
// ************ CResourceDependency ************ // ************ IDependencyNode ************
EDependencyNodeType CResourceDependency::Type() const 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); for (u32 iChild = 0; iChild < mChildren.size(); iChild++)
}
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++)
{ {
CResourceDependency *pDepend = new CResourceDependency; if (mChildren[iChild]->HasDependency(rkID))
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)
return true; return true;
} }
return false; return false;
} }
CAssetID CDependencyTree::DependencyByIndex(u32 Index) const // ************ CDependencyTree ************
EDependencyNodeType CDependencyTree::Type() const
{ {
ASSERT(Index >= 0 && Index < mReferencedResources.size()); return eDNT_DependencyTree;
return mReferencedResources[Index]->ID(); }
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*/) 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; if (!rkID.IsValid() || (AvoidDuplicates && HasDependency(rkID))) return;
CResourceDependency *pDepend = new CResourceDependency(rkID); 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 ************ // ************ CAnimSetDependencyTree ************
@ -145,9 +226,9 @@ void CAnimSetDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
mCharacterOffsets.push_back( rFile.ReadLong() ); 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()); rFile.WriteLong(mCharacterOffsets.size());
for (u32 iChar = 0; iChar < mCharacterOffsets.size(); iChar++) 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) void CAnimSetDependencyTree::AddCharacter(const SSetCharacter *pkChar, const std::set<CAssetID>& rkBaseUsedSet)
{ {
mCharacterOffsets.push_back( NumDependencies() ); mCharacterOffsets.push_back( NumChildren() );
if (!pkChar) return; if (!pkChar) return;
std::set<CAssetID> UsedSet = rkBaseUsedSet; std::set<CAssetID> UsedSet = rkBaseUsedSet;
@ -195,120 +276,7 @@ void CAnimSetDependencyTree::AddCharDependency(CResource *pRes, std::set<CAssetI
AddCharDependency(pRes->ID(), rUsedSet); 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::~CAreaDependencyTree()
{
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++)
delete mScriptInstances[iInst];
}
EDependencyNodeType CAreaDependencyTree::Type() const EDependencyNodeType CAreaDependencyTree::Type() const
{ {
return eDNT_Area; return eDNT_Area;
@ -316,16 +284,27 @@ EDependencyNodeType CAreaDependencyTree::Type() const
void CAreaDependencyTree::Read(IInputStream& rFile, EIDLength IDLength) void CAreaDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
{ {
mRootID = CAssetID(rFile, IDLength);
// Base dependency list contains non-script dependencies (world geometry textures + PATH/PTLA/EGMC) // 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(); u32 NumScriptInstances = rFile.ReadLong();
mScriptInstances.reserve(NumScriptInstances); mChildren.reserve(mChildren.size() + NumScriptInstances);
for (u32 iInst = 0; iInst < NumScriptInstances; iInst++) for (u32 iInst = 0; iInst < NumScriptInstances; iInst++)
{ {
CScriptInstanceDependencyTree *pInst = new CScriptInstanceDependencyTree; CScriptInstanceDependency *pInst = new CScriptInstanceDependency;
pInst->Read(rFile, IDLength); pInst->Read(rFile, IDLength);
mScriptInstances.push_back(pInst); mChildren.push_back(pInst);
} }
u32 NumLayers = rFile.ReadLong(); u32 NumLayers = rFile.ReadLong();
@ -335,13 +314,20 @@ void CAreaDependencyTree::Read(IInputStream& rFile, EIDLength IDLength)
mLayerOffsets.push_back( rFile.ReadLong() ); mLayerOffsets.push_back( rFile.ReadLong() );
} }
void CAreaDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) const void CAreaDependencyTree::Write(IOutputStream& rFile) const
{ {
CDependencyTree::Write(rFile, IDLength); mRootID.Write(rFile);
rFile.WriteLong(mScriptInstances.size());
for (u32 iInst = 0; iInst < mScriptInstances.size(); iInst++) u32 NumBaseDependencies = (mLayerOffsets.empty() ? mChildren.size() : mLayerOffsets.front());
mScriptInstances[iInst]->Write(rFile, IDLength); 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()); rFile.WriteLong(mLayerOffsets.size());
@ -352,26 +338,20 @@ void CAreaDependencyTree::Write(IOutputStream& rFile, EIDLength IDLength) const
void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer) void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer)
{ {
if (!pLayer) return; if (!pLayer) return;
mLayerOffsets.push_back(mScriptInstances.size()); mLayerOffsets.push_back(mChildren.size());
for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) 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); ASSERT(pTree != nullptr);
if (pTree->NumDependencies() > 0) if (pTree->NumChildren() > 0)
mScriptInstances.push_back(pTree); mChildren.push_back(pTree);
else else
delete pTree; 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 void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector<TString>& rModuleDepsOut, std::vector<u32>& rModuleLayerOffsetsOut) const
{ {
CMasterTemplate *pMaster = CMasterTemplate::MasterForGame(Game); 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++) for (u32 iLayer = 0; iLayer < mLayerOffsets.size(); iLayer++)
{ {
u32 StartIdx = mLayerOffsets[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(); u32 ModuleStartIdx = rModuleDepsOut.size();
rModuleLayerOffsetsOut.push_back(ModuleStartIdx); rModuleLayerOffsetsOut.push_back(ModuleStartIdx);
@ -391,7 +371,8 @@ void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector<TString>
for (u32 iInst = StartIdx; iInst < EndIdx; iInst++) 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(); u32 ObjType = pInst->ObjectType();
if (UsedObjectTypes.find(ObjType) == UsedObjectTypes.end()) if (UsedObjectTypes.find(ObjType) == UsedObjectTypes.end())

View File

@ -9,33 +9,63 @@
class CScriptLayer; class CScriptLayer;
class CScriptObject; class CScriptObject;
class CPropertyStruct; class CPropertyStruct;
class TCharacterProperty;
struct SSetCharacter; struct SSetCharacter;
// Group of node classes forming a tree of cached resource dependencies. // Group of node classes forming a tree of cached resource dependencies.
enum EDependencyNodeType enum EDependencyNodeType
{ {
eDNT_Root, eDNT_DependencyTree,
eDNT_AnimSet,
eDNT_ScriptInstance,
eDNT_Area,
eDNT_ResourceDependency, 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 class IDependencyNode
{ {
protected:
std::vector<IDependencyNode*> mChildren;
public: public:
virtual ~IDependencyNode() {} virtual ~IDependencyNode();
virtual EDependencyNodeType Type() const = 0; virtual EDependencyNodeType Type() const = 0;
virtual void Read(IInputStream& rFile, EIDLength IDLength) = 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. // Node representing a single resource dependency.
class CResourceDependency : public IDependencyNode class CResourceDependency : public IDependencyNode
{ {
protected:
CAssetID mID; CAssetID mID;
public: public:
@ -44,59 +74,80 @@ public:
virtual EDependencyNodeType Type() const; virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EIDLength IDLength); 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 // Accessors
inline CAssetID ID() const { return mID; } inline CAssetID ID() const { return mID; }
inline void SetID(const CAssetID& rkID) { mID = rkID; } 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. // Node representing a single resource dependency referenced by a script property.
class CAnimSetDependency : public CResourceDependency 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: protected:
u32 mUsedChar; u32 mUsedChar;
public: 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 EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EIDLength IDLength); virtual void Read(IInputStream& rFile, EIDLength IDLength);
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const; virtual void Write(IOutputStream& rFile) const;
// Accessors // Accessors
inline u32 UsedChar() const { return mUsedChar; } 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. // Node representing a script object. Indicates the type of object.
class CDependencyTree : public IDependencyNode class CScriptInstanceDependency : public IDependencyNode
{ {
protected: protected:
CAssetID mID; u32 mObjectType;
std::vector<CResourceDependency*> mReferencedResources;
public: public:
CDependencyTree(const CAssetID& rkID) : mID(rkID) {}
~CDependencyTree();
virtual EDependencyNodeType Type() const; virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EIDLength IDLength); virtual void Read(IInputStream& rFile, EIDLength IDLength);
virtual void Write(IOutputStream& rFile, EIDLength IDLength) const; virtual void Write(IOutputStream& rFile) 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);
// Accessors // Accessors
inline void SetID(const CAssetID& rkID) { mID = rkID; } inline u32 ObjectType() const { return mObjectType; }
inline CAssetID ID() const { return mID; }
// 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. // 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) {} CAnimSetDependencyTree(const CAssetID& rkID) : CDependencyTree(rkID) {}
virtual EDependencyNodeType Type() const; virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EIDLength IDLength); 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 AddCharacter(const SSetCharacter *pkChar, const std::set<CAssetID>& rkBaseUsedSet);
void AddCharDependency(const CAssetID& rkID, std::set<CAssetID>& rUsedSet); void AddCharDependency(const CAssetID& rkID, std::set<CAssetID>& rUsedSet);
@ -120,53 +171,24 @@ public:
inline u32 CharacterOffset(u32 CharIdx) const { return mCharacterOffsets[CharIdx]; } 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. // Node representing an area. Tracks dependencies on a per-instance basis and can separate dependencies of different script layers.
class CAreaDependencyTree : public CDependencyTree class CAreaDependencyTree : public CDependencyTree
{ {
protected: protected:
std::vector<CScriptInstanceDependencyTree*> mScriptInstances;
std::vector<u32> mLayerOffsets; std::vector<u32> mLayerOffsets;
public: public:
CAreaDependencyTree(const CAssetID& rkID) : CDependencyTree(rkID) {} CAreaDependencyTree(const CAssetID& rkID) : CDependencyTree(rkID) {}
~CAreaDependencyTree();
virtual EDependencyNodeType Type() const; virtual EDependencyNodeType Type() const;
virtual void Read(IInputStream& rFile, EIDLength IDLength); 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); void AddScriptLayer(CScriptLayer *pLayer);
CScriptInstanceDependencyTree* ScriptInstanceByIndex(u32 Index) const;
void GetModuleDependencies(EGame Game, std::vector<TString>& rModuleDepsOut, std::vector<u32>& rModuleLayerOffsetsOut) const; void GetModuleDependencies(EGame Game, std::vector<TString>& rModuleDepsOut, std::vector<u32>& rModuleLayerOffsetsOut) const;
// Accessors // Accessors
inline u32 NumScriptLayers() const { return mLayerOffsets.size(); } inline u32 NumScriptLayers() const { return mLayerOffsets.size(); }
inline u32 NumScriptInstances() const { return mScriptInstances.size(); }
inline u32 ScriptLayerOffset(u32 LayerIdx) const { return mLayerOffsets[LayerIdx]; } inline u32 ScriptLayerOffset(u32 LayerIdx) const { return mLayerOffsets[LayerIdx]; }
}; };

View File

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

View File

@ -233,7 +233,12 @@ void CPackage::Cook()
{ {
u32 CompressedSize; u32 CompressedSize;
std::vector<u8> CompressedData(ResourceData.size() * 2); 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 // Make sure that the compressed data is actually smaller, accounting for padding + uncompressed size value
if (Success) if (Success)

View File

@ -31,6 +31,7 @@ CResourceEntry::CResourceEntry(CResourceStore *pStore, const CAssetID& rkID,
CResourceEntry::~CResourceEntry() CResourceEntry::~CResourceEntry()
{ {
if (mpResource) delete mpResource; if (mpResource) delete mpResource;
if (mpDependencies) delete mpDependencies;
} }
bool CResourceEntry::LoadCacheData() bool CResourceEntry::LoadCacheData()
@ -102,7 +103,7 @@ bool CResourceEntry::SaveCacheData()
File.WriteLong(0); File.WriteLong(0);
u32 DepsStart = File.Tell(); u32 DepsStart = File.Tell();
if (mpDependencies) mpDependencies->Write(File, Game() <= eEchoes ? e32Bit : e64Bit); if (mpDependencies) mpDependencies->Write(File);
u32 DepsEnd = File.Tell(); u32 DepsEnd = File.Tell();
u32 DepsSize = DepsEnd- DepsStart; u32 DepsSize = DepsEnd- DepsStart;
@ -137,7 +138,7 @@ void CResourceEntry::UpdateDependencies()
mpDependencies = mpResource->BuildDependencyTree(); mpDependencies = mpResource->BuildDependencyTree();
if (!WasLoaded) if (!WasLoaded)
gpResourceStore->DestroyUnreferencedResources(); mpStore->DestroyUnreferencedResources();
} }
TWideString CResourceEntry::CacheDataPath(bool Relative) const TWideString CResourceEntry::CacheDataPath(bool Relative) const
@ -253,7 +254,7 @@ bool CResourceEntry::Save()
SaveCacheData(); SaveCacheData();
if (ShouldCollectGarbage) if (ShouldCollectGarbage)
gpResourceStore->DestroyUnreferencedResources(); mpStore->DestroyUnreferencedResources();
return true; return true;
} }

View File

@ -25,6 +25,7 @@ CResourceStore::CResourceStore(CGameExporter *pExporter)
CResourceStore::~CResourceStore() CResourceStore::~CResourceStore()
{ {
CloseActiveProject(); CloseActiveProject();
DestroyUnreferencedResources();
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++) for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
delete It->second; delete It->second;
@ -139,15 +140,31 @@ void CResourceStore::SetActiveProject(CGameProject *pProj)
void CResourceStore::CloseActiveProject() 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 // Delete all entries from old project
for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++) for (auto It = mResourceEntries.begin(); It != mResourceEntries.end(); It++)
{ {
CResourceEntry *pEntry = It->second; CResourceEntry *pEntry = It->second;
if (!pEntry->IsTransient()) 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); It = mResourceEntries.erase(It);
if (It == mResourceEntries.end()) break; 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/CDependencyGroup.h"
#include "Core/Resource/CWorld.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 ************ // ************ CPackageDependencyListBuilder ************
class CPackageDependencyListBuilder class CPackageDependencyListBuilder
{ {
CPackage *mpPackage; CPackage *mpPackage;
EGame mGame;
TResPtr<CWorld> mpWorld; TResPtr<CWorld> mpWorld;
CCharacterUsageMap mCharacterUsageMap;
std::set<CAssetID> mPackageUsedAssets; std::set<CAssetID> mPackageUsedAssets;
std::set<CAssetID> mAreaUsedAssets; std::set<CAssetID> mAreaUsedAssets;
std::list<CAssetID> mScanIDs;
bool mEnableDuplicates; bool mEnableDuplicates;
bool mCurrentAreaHasDuplicates; bool mCurrentAreaHasDuplicates;
bool mAddingScans; bool mIsPlayerActor;
public: public:
CPackageDependencyListBuilder(CPackage *pPackage) CPackageDependencyListBuilder(CPackage *pPackage)
: mpPackage(pPackage) : mpPackage(pPackage)
, mGame(pPackage->Project()->Game())
, mCurrentAreaHasDuplicates(false) , mCurrentAreaHasDuplicates(false)
, mAddingScans(false) , mIsPlayerActor(false)
{} {}
virtual void BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut) void BuildDependencyList(bool AllowDuplicates, std::list<CAssetID>& rOut);
{ void AddDependency(CResourceEntry *pCurEntry, const CAssetID& rkID, std::list<CAssetID>& rOut);
mEnableDuplicates = AllowDuplicates; void EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut);
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());
}
}; };
// ************ CAreaDependencyListBuilder ************ // ************ CAreaDependencyListBuilder ************
class CAreaDependencyListBuilder class CAreaDependencyListBuilder
{ {
CResourceEntry *mpAreaEntry; CResourceEntry *mpAreaEntry;
EGame mGame;
CCharacterUsageMap mCharacterUsageMap;
std::set<CAssetID> mBaseUsedAssets; std::set<CAssetID> mBaseUsedAssets;
std::set<CAssetID> mLayerUsedAssets; std::set<CAssetID> mLayerUsedAssets;
bool mIsPlayerActor; bool mIsPlayerActor;
public: public:
CAreaDependencyListBuilder(CResourceEntry *pAreaEntry) CAreaDependencyListBuilder(CResourceEntry *pAreaEntry)
: mpAreaEntry(pAreaEntry) {} : mpAreaEntry(pAreaEntry)
, mGame(pAreaEntry->Game())
virtual void BuildDependencyList(std::list<CAssetID>& rAssetsOut, std::list<u32>& rLayerOffsetsOut)
{ {
ASSERT(mpAreaEntry->ResourceType() == eArea); 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) void BuildDependencyList(std::list<CAssetID>& rAssetsOut, std::list<u32>& rLayerOffsetsOut);
{ void AddDependency(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());
}
}; };
#endif // DEPENDENCYLISTBUILDERS #endif // DEPENDENCYLISTBUILDERS

View File

@ -90,7 +90,7 @@ public:
for (u32 iDep = 0; iDep < pGroup->NumDependencies(); iDep++) for (u32 iDep = 0; iDep < pGroup->NumDependencies(); iDep++)
{ {
CAssetID ID = pGroup->DependencyByIndex(iDep); CAssetID ID = pGroup->DependencyByIndex(iDep);
pTree->AddDependency(iDep); pTree->AddDependency(ID);
BaseUsedSet.insert(ID); BaseUsedSet.insert(ID);
} }
} }

View File

@ -43,8 +43,8 @@ public:
CDependencyTree* BuildDependencyTree() const CDependencyTree* BuildDependencyTree() const
{ {
if (Game() >= eEchoesDemo) if (Game() >= eCorruptionProto)
Log::Warning("CScan::BuildDependencyTree not handling Echoes/Corruption dependencies"); Log::Warning("CScan::BuildDependencyTree not handling Corruption dependencies");
CDependencyTree *pTree = new CDependencyTree(ID()); CDependencyTree *pTree = new CDependencyTree(ID());
pTree->AddDependency(mFrameID); pTree->AddDependency(mFrameID);

View File

@ -97,6 +97,9 @@ public:
else if (ImageType == L"SA") else if (ImageType == L"SA")
TexturesStart = 4; TexturesStart = 4;
else if (ImageType.IsHexString(false, IDLength * 2))
TexturesStart = 0;
else else
{ {
Log::Error("Unrecognized image type: " + ImageType.ToUTF8()); 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++) 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); Loader.pSet->mEventDependencies.push_back(pGrp);
} }
} }

View File

@ -76,6 +76,7 @@ public:
case eScan: pRes = CScanLoader::LoadSCAN(rInput, pEntry); break; case eScan: pRes = CScanLoader::LoadSCAN(rInput, pEntry); break;
case eSkeleton: pRes = CSkeletonLoader::LoadCINF(rInput, pEntry); break; case eSkeleton: pRes = CSkeletonLoader::LoadCINF(rInput, pEntry); break;
case eSkin: pRes = CSkinLoader::LoadCSKR(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 eStaticGeometryMap: pRes = CPoiToWorldLoader::LoadEGMC(rInput, pEntry); break;
case eStringTable: pRes = CStringLoader::LoadSTRG(rInput, pEntry); break; case eStringTable: pRes = CStringLoader::LoadSTRG(rInput, pEntry); break;
case eTexture: pRes = CTextureDecoder::LoadTXTR(rInput, pEntry); break; case eTexture: pRes = CTextureDecoder::LoadTXTR(rInput, pEntry); break;
@ -83,6 +84,8 @@ public:
case eParticle: case eParticle:
case eParticleElectric: case eParticleElectric:
case eParticleSorted:
case eParticleSpawn:
case eParticleSwoosh: case eParticleSwoosh:
case eParticleDecal: case eParticleDecal:
case eParticleWeapon: case eParticleWeapon:

View File

@ -12,13 +12,11 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadCSNG(IInputStream& rCSNG, CResou
return pGroup; return pGroup;
} }
CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry) CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResourceEntry *pEntry, bool IsEchoes /*= false*/)
{ {
u32 Version = rEVNT.ReadLong(); u32 Version = rEVNT.ReadLong();
ASSERT(Version == 1 || Version == 2); 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); CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
// Loop Events // Loop Events
@ -61,14 +59,17 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
} }
// Sound Events // Sound Events
u32 NumSoundEvents = rEVNT.ReadLong(); if (Version == 2)
for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
{ {
rEVNT.Seek(0x2, SEEK_CUR); u32 NumSoundEvents = rEVNT.ReadLong();
rEVNT.ReadString();
rEVNT.Seek(0x27, SEEK_CUR); for (u32 iSound = 0; iSound < NumSoundEvents; iSound++)
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR); {
rEVNT.Seek(0x2, SEEK_CUR);
rEVNT.ReadString();
rEVNT.Seek(0x27, SEEK_CUR);
if (IsEchoes) rEVNT.Seek(0xC, SEEK_CUR);
}
} }
return pGroup; return pGroup;
@ -76,15 +77,12 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadEVNT(IInputStream& rEVNT, CResou
CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry) CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry)
{ {
if (pEntry->Game() >= eEchoesDemo) return nullptr;
u32 Version = rFRME.ReadLong(); u32 Version = rFRME.ReadLong();
CDependencyGroup *pGroup = new CDependencyGroup(pEntry); CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
// Prime 1 // Prime 1
if (Version == 0 || Version == 1) if (Version == 0 || Version == 1)
{ {
CDependencyGroup *pGroup = new CDependencyGroup(pEntry);
rFRME.Seek(0xC, SEEK_CUR); rFRME.Seek(0xC, SEEK_CUR);
u32 NumWidgets = rFRME.ReadLong(); u32 NumWidgets = rFRME.ReadLong();
@ -218,6 +216,75 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadFRME(IInputStream& rFRME, CResou
return pGroup; 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) CDependencyGroup* CUnsupportedFormatLoader::LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry)
{ {
u32 Magic = rHINT.ReadLong(); u32 Magic = rHINT.ReadLong();
@ -323,7 +390,7 @@ CDependencyGroup* CUnsupportedFormatLoader::LoadRULE(IInputStream& rRULE, CResou
// Version test // Version test
u32 IDOffset = rRULE.Tell(); u32 IDOffset = rRULE.Tell();
rRULE.Seek(0x4, SEEK_CUR); rRULE.Seek(0x4, SEEK_CUR);
u32 RuleSetCount = rRULE.ReadLong(); u32 RuleSetCount = rRULE.ReadShort();
EIDLength IDLength = (RuleSetCount > 0xFF ? e64Bit : e32Bit); EIDLength IDLength = (RuleSetCount > 0xFF ? e64Bit : e32Bit);
rRULE.Seek(IDOffset, SEEK_SET); rRULE.Seek(IDOffset, SEEK_SET);

View File

@ -12,8 +12,9 @@ class CUnsupportedFormatLoader
public: public:
static CDependencyGroup* LoadCSNG(IInputStream& rCSNG, CResourceEntry *pEntry); 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* LoadFRME(IInputStream& rFRME, CResourceEntry *pEntry);
static CDependencyGroup* LoadFSM2(IInputStream& rFSM2, CResourceEntry *pEntry);
static CDependencyGroup* LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry); static CDependencyGroup* LoadHINT(IInputStream& rHINT, CResourceEntry *pEntry);
static CDependencyGroup* LoadMAPW(IInputStream& rMAPW, CResourceEntry *pEntry); static CDependencyGroup* LoadMAPW(IInputStream& rMAPW, CResourceEntry *pEntry);
static CDependencyGroup* LoadMAPU(IInputStream& rMAPU, CResourceEntry *pEntry); static CDependencyGroup* LoadMAPU(IInputStream& rMAPU, CResourceEntry *pEntry);