From f980bc7536e63af30837119d3e8a313a201ee1f0 Mon Sep 17 00:00:00 2001 From: Aruki Date: Wed, 26 Jul 2017 01:30:52 -0600 Subject: [PATCH] Added support for tracking extra dependencies in Corruption areas (necessary to avoid crashes) --- src/Core/GameProject/CDependencyTree.cpp | 15 ++++-- src/Core/GameProject/CDependencyTree.h | 2 +- .../GameProject/DependencyListBuilders.cpp | 51 ++++++++++++------- .../Animation/CAnimationParameters.cpp | 2 +- src/Core/Resource/Area/CGameArea.cpp | 21 +++++++- src/Core/Resource/Area/CGameArea.h | 4 ++ src/Core/Resource/Factory/CAreaLoader.cpp | 48 +++++++++++++++++ src/Core/Resource/Factory/CAreaLoader.h | 1 + src/Editor/WorldEditor/CWorldEditor.cpp | 3 ++ 9 files changed, 122 insertions(+), 25 deletions(-) diff --git a/src/Core/GameProject/CDependencyTree.cpp b/src/Core/GameProject/CDependencyTree.cpp index 43774dd9..44644800 100644 --- a/src/Core/GameProject/CDependencyTree.cpp +++ b/src/Core/GameProject/CDependencyTree.cpp @@ -314,10 +314,11 @@ void CAreaDependencyTree::Serialize(IArchive& rArc) rArc << SERIAL_CONTAINER("LayerOffsets", mLayerOffsets, "Offset"); } -void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer) +void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer, const std::vector& rkExtraDeps) { if (!pLayer) return; mLayerOffsets.push_back(mChildren.size()); + std::set UsedIDs; for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) { @@ -326,10 +327,16 @@ void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer) // Note: MP2+ need to track all instances (not just instances with dependencies) to be able to build the layer module list if (pTree->NumChildren() > 0 || pLayer->Area()->Game() >= eEchoesDemo) + { mChildren.push_back(pTree); + pTree->GetAllResourceReferences(UsedIDs); + } else delete pTree; } + + for (u32 iDep = 0; iDep < rkExtraDeps.size(); iDep++) + AddDependency(rkExtraDeps[iDep]); } void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector& rModuleDepsOut, std::vector& rModuleLayerOffsetsOut) const @@ -351,8 +358,10 @@ void CAreaDependencyTree::GetModuleDependencies(EGame Game, std::vector for (u32 iInst = StartIdx; iInst < EndIdx; iInst++) { - CScriptInstanceDependency *pInst = static_cast(mChildren[iInst]); - ASSERT(pInst->Type() == eDNT_ScriptInstance); + IDependencyNode *pNode = mChildren[iInst]; + if (pNode->Type() != eDNT_ScriptInstance) continue; + + CScriptInstanceDependency *pInst = static_cast(pNode); u32 ObjType = pInst->ObjectType(); if (UsedObjectTypes.find(ObjType) == UsedObjectTypes.end()) diff --git a/src/Core/GameProject/CDependencyTree.h b/src/Core/GameProject/CDependencyTree.h index 987f92b3..2e3b8911 100644 --- a/src/Core/GameProject/CDependencyTree.h +++ b/src/Core/GameProject/CDependencyTree.h @@ -215,7 +215,7 @@ public: virtual EDependencyNodeType Type() const; virtual void Serialize(IArchive& rArc); - void AddScriptLayer(CScriptLayer *pLayer); + void AddScriptLayer(CScriptLayer *pLayer, const std::vector& rkExtraDeps); void GetModuleDependencies(EGame Game, std::vector& rModuleDepsOut, std::vector& rModuleLayerOffsetsOut) const; // Accessors diff --git a/src/Core/GameProject/DependencyListBuilders.cpp b/src/Core/GameProject/DependencyListBuilders.cpp index f15a0d3f..4756e246 100644 --- a/src/Core/GameProject/DependencyListBuilders.cpp +++ b/src/Core/GameProject/DependencyListBuilders.cpp @@ -405,31 +405,44 @@ void CAreaDependencyListBuilder::BuildDependencyList(std::list& rAsset for (u32 iChild = StartIdx; iChild < EndIdx; iChild++) { - CScriptInstanceDependency *pInst = static_cast(pTree->ChildByIndex(iChild)); - ASSERT(pInst->Type() == eDNT_ScriptInstance); - mIsPlayerActor = (pInst->ObjectType() == 0x4C || pInst->ObjectType() == FOURCC('PLAC')); + IDependencyNode *pNode = pTree->ChildByIndex(iChild); - for (u32 iDep = 0; iDep < pInst->NumChildren(); iDep++) + if (pNode->Type() == eDNT_ScriptInstance) { - CPropertyDependency *pDep = static_cast(pInst->ChildByIndex(iDep)); + CScriptInstanceDependency *pInst = static_cast(pNode); + mIsPlayerActor = (pInst->ObjectType() == 0x4C || pInst->ObjectType() == FOURCC('PLAC')); - // For MP3, exclude the CMDL/CSKR properties for the suit assets - only include default character assets - if (mGame == eCorruption && mIsPlayerActor) + for (u32 iDep = 0; iDep < pInst->NumChildren(); iDep++) { - TString PropID = pDep->PropertyID(); + CPropertyDependency *pDep = static_cast(pInst->ChildByIndex(iDep)); - 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; + // 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, pAudioGroupsOut); } - - AddDependency(pDep->ID(), rAssetsOut, pAudioGroupsOut); + } + else if (pNode->Type() == eDNT_ResourceDependency) + { + CResourceDependency *pResDep = static_cast(pNode); + AddDependency(pResDep->ID(), rAssetsOut, pAudioGroupsOut); + } + else + { + ASSERT(false); // unhandled case! } } } diff --git a/src/Core/Resource/Animation/CAnimationParameters.cpp b/src/Core/Resource/Animation/CAnimationParameters.cpp index 9518d766..31ee155c 100644 --- a/src/Core/Resource/Animation/CAnimationParameters.cpp +++ b/src/Core/Resource/Animation/CAnimationParameters.cpp @@ -195,7 +195,7 @@ void CAnimationParameters::SetResource(const CAssetID& rkID) if (!pEntry) Log::Error("Invalid resource ID passed to CAnimationParameters: " + rkID.ToString()); - else if (pEntry->ResourceType() != eAnimSet) + else if (pEntry->ResourceType() != eAnimSet && pEntry->ResourceType() != eCharacter) Log::Error("Resource with invalid type passed to CAnimationParameters: " + pEntry->CookedAssetPath().GetFileName()); } } diff --git a/src/Core/Resource/Area/CGameArea.cpp b/src/Core/Resource/Area/CGameArea.cpp index 4a3a7575..b17dc2dc 100644 --- a/src/Core/Resource/Area/CGameArea.cpp +++ b/src/Core/Resource/Area/CGameArea.cpp @@ -48,9 +48,18 @@ CDependencyTree* CGameArea::BuildDependencyTree() const pTree->AddDependency(mpPoiToWorldMap); } + // Extra deps + for (u32 iDep = 0; iDep < mExtraAreaDeps.size(); iDep++) + pTree->AddDependency(mExtraAreaDeps[iDep]); + // Layer dependencies + std::vector DummyDeps; + for (u32 iLayer = 0; iLayer < mScriptLayers.size(); iLayer++) - pTree->AddScriptLayer(mScriptLayers[iLayer]); + { + const std::vector& rkExtras = (mExtraLayerDeps.size() > iLayer ? mExtraLayerDeps[iLayer] : DummyDeps); + pTree->AddScriptLayer(mScriptLayers[iLayer], rkExtras); + } return pTree; } @@ -242,3 +251,13 @@ void CGameArea::DeleteInstance(CScriptObject *pInstance) delete pInstance; } + +void CGameArea::ClearExtraDependencies() +{ + if (mExtraAreaDeps.empty() || !mExtraLayerDeps.empty()) + { + mExtraAreaDeps.clear(); + mExtraLayerDeps.clear(); + Entry()->UpdateDependencies(); + } +} diff --git a/src/Core/Resource/Area/CGameArea.h b/src/Core/Resource/Area/CGameArea.h index 0696f90c..6372dc4e 100644 --- a/src/Core/Resource/Area/CGameArea.h +++ b/src/Core/Resource/Area/CGameArea.h @@ -60,6 +60,9 @@ class CGameArea : public CResource CAssetID mPortalAreaID; // Object to Static Geometry Map TResPtr mpPoiToWorldMap; + // Dependencies + std::vector mExtraAreaDeps; + std::vector< std::vector > mExtraLayerDeps; public: CGameArea(CResourceEntry *pEntry = 0); @@ -80,6 +83,7 @@ public: u32 SuggestedID = -1, u32 SuggestedLayerIndex = -1); void AddInstanceToArea(CScriptObject *pInstance); void DeleteInstance(CScriptObject *pInstance); + void ClearExtraDependencies(); // Inline Accessors inline u32 WorldIndex() const { return mWorldIndex; } diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index ab12dc9d..fdfbea1d 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -412,6 +412,52 @@ void CAreaLoader::ReadGeometryCorruption() mpArea->MergeTerrain(); } +void CAreaLoader::ReadDependenciesCorruption() +{ + mpSectionMgr->ToSection(mDependenciesBlockNum); + + // Read the offsets first so we can read the deps directly into their corresponding arrays + u32 NumDeps = mpMREA->ReadLong(); + u32 DepsStart = mpMREA->Tell(); + mpMREA->Skip(NumDeps * 0xC); + + u32 NumOffsets = mpMREA->ReadLong(); + std::vector Offsets(NumOffsets); + + for (u32 OffsetIdx = 0; OffsetIdx < NumOffsets; OffsetIdx++) + Offsets[OffsetIdx] = mpMREA->ReadLong(); + + mpMREA->GoTo(DepsStart); + + // Read layer dependencies + u32 NumLayers = NumOffsets - 1; + mpArea->mExtraLayerDeps.resize(NumLayers); + + for (u32 LayerIdx = 0; LayerIdx < NumLayers; LayerIdx++) + { + u32 NumLayerDeps = Offsets[LayerIdx+1] - Offsets[LayerIdx]; + mpArea->mExtraLayerDeps[LayerIdx].reserve(NumLayerDeps); + + for (u32 DepIdx = 0; DepIdx < NumLayerDeps; DepIdx++) + { + CAssetID AssetID(*mpMREA, eCorruption); + mpMREA->Skip(4); + mpArea->mExtraLayerDeps[LayerIdx].push_back(AssetID); + } + } + + // Read area dependencies + u32 NumAreaDeps = NumDeps - Offsets[NumLayers]; + mpArea->mExtraAreaDeps.reserve(NumAreaDeps); + + for (u32 DepIdx = 0; DepIdx < NumAreaDeps; DepIdx++) + { + CAssetID AssetID(*mpMREA, eCorruption); + mpMREA->Skip(4); + mpArea->mExtraAreaDeps.push_back(AssetID); + } +} + void CAreaLoader::ReadLightsCorruption() { mpSectionMgr->ToSection(mLightsBlockNum); @@ -733,6 +779,7 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA, CResourceEntry *pEntry) case eCorruptionProto: Loader.ReadHeaderCorruption(); Loader.ReadGeometryPrime(); + Loader.ReadDependenciesCorruption(); Loader.ReadSCLYEchoes(); Loader.ReadCollision(); Loader.ReadLightsCorruption(); @@ -744,6 +791,7 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA, CResourceEntry *pEntry) case eReturns: Loader.ReadHeaderCorruption(); Loader.ReadGeometryCorruption(); + Loader.ReadDependenciesCorruption(); Loader.ReadSCLYEchoes(); Loader.ReadCollision(); if (Loader.mVersion == eCorruption) diff --git a/src/Core/Resource/Factory/CAreaLoader.h b/src/Core/Resource/Factory/CAreaLoader.h index 539d1f34..9f5c1539 100644 --- a/src/Core/Resource/Factory/CAreaLoader.h +++ b/src/Core/Resource/Factory/CAreaLoader.h @@ -67,6 +67,7 @@ class CAreaLoader // Corruption void ReadHeaderCorruption(); void ReadGeometryCorruption(); + void ReadDependenciesCorruption(); void ReadLightsCorruption(); // Common diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 858fb820..8cb41f6a 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -439,6 +439,9 @@ bool CWorldEditor::Save() bool SaveEGMCSuccess = mpArea->PoiToWorldMap() ? mpArea->PoiToWorldMap()->Entry()->Save() : true; bool SaveWorldSuccess = mpWorld->Entry()->Save(); + if (SaveAreaSuccess) + mpArea->ClearExtraDependencies(); + if (SaveAreaSuccess || SaveEGMCSuccess || SaveWorldSuccess) gpEdApp->NotifyAssetsModified();