Implemented SCAN cooker

This commit is contained in:
Aruki 2019-01-30 13:48:05 -07:00
parent e6702d09e8
commit 1e997dac46
9 changed files with 206 additions and 18 deletions

View File

@ -256,6 +256,7 @@ HEADERS += \
Resource/Scan/CScan.h \
Resource/Scan/SScanParametersMP1.h \
Resource/Scan/ELogbookCategory.h \
Resource/Cooker/CScanCooker.h \
NCoreTests.h
# Source Files
@ -374,6 +375,7 @@ SOURCES += \
Tweaks/CTweakCooker.cpp \
Resource/Cooker/CStringCooker.cpp \
Resource/Scan/CScan.cpp \
Resource/Cooker/CScanCooker.cpp \
NCoreTests.cpp
# Codegen

View File

@ -559,3 +559,82 @@ void CAreaDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntr
EvaluateDependencyNode(pCurEntry, pNode->ChildByIndex(iChild), rOut, pAudioGroupsOut);
}
}
// ************ CAssetDependencyListBuilder ************
void CAssetDependencyListBuilder::BuildDependencyList(std::vector<CAssetID>& OutAssets)
{
mCharacterUsageMap.FindUsagesForAsset(mpResourceEntry);
EvaluateDependencyNode(mpResourceEntry, mpResourceEntry->Dependencies(), OutAssets);
}
void CAssetDependencyListBuilder::AddDependency(const CAssetID& kID, std::vector<CAssetID>& Out)
{
CResourceEntry *pEntry = mpResourceEntry->ResourceStore()->FindEntry(kID);
if (!pEntry) return;
EResourceType ResType = pEntry->ResourceType();
if (mUsedAssets.find(kID) != mUsedAssets.end())
return;
// Dependency is valid! Evaluate the node tree
if (ResType == EResourceType::AnimSet)
{
ASSERT(!mCurrentAnimSetID.IsValid());
mCurrentAnimSetID = pEntry->ID();
}
EvaluateDependencyNode(pEntry, pEntry->Dependencies(), Out);
if (ResType == EResourceType::AnimSet)
{
ASSERT(mCurrentAnimSetID.IsValid());
mCurrentAnimSetID = CAssetID::InvalidID(mpResourceEntry->Game());
}
Out.push_back(kID);
mUsedAssets.insert(kID);
}
void CAssetDependencyListBuilder::EvaluateDependencyNode(CResourceEntry* pCurEntry, IDependencyNode* pNode, std::vector<CAssetID>& Out)
{
if (!pNode) return;
EDependencyNodeType Type = pNode->Type();
bool ParseChildren = false;
if (Type == EDependencyNodeType::Resource || Type == EDependencyNodeType::ScriptProperty || Type == EDependencyNodeType::CharacterProperty)
{
CResourceDependency* pDep = static_cast<CResourceDependency*>(pNode);
AddDependency(pDep->ID(), Out);
}
else if (Type == EDependencyNodeType::AnimEvent)
{
CAnimEventDependency* pDep = static_cast<CAnimEventDependency*>(pNode);
uint32 CharIndex = pDep->CharIndex();
if (CharIndex == -1 || mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, CharIndex))
AddDependency(pDep->ID(), Out);
}
else if (Type == EDependencyNodeType::SetCharacter)
{
CSetCharacterDependency* pChar = static_cast<CSetCharacterDependency*>(pNode);
ParseChildren = mCharacterUsageMap.IsCharacterUsed(mCurrentAnimSetID, pChar->CharSetIndex());
}
else if (Type == EDependencyNodeType::SetAnimation)
{
CSetAnimationDependency* pAnim = static_cast<CSetAnimationDependency*>(pNode);
ParseChildren = mCharacterUsageMap.IsAnimationUsed(mCurrentAnimSetID, pAnim);
}
else
ParseChildren = true;
if (ParseChildren)
{
for (uint32 iChild = 0; iChild < pNode->NumChildren(); iChild++)
EvaluateDependencyNode(pCurEntry, pNode->ChildByIndex(iChild), Out);
}
}

View File

@ -96,5 +96,25 @@ public:
void EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut, std::set<CAssetID> *pAudioGroupsOut);
};
// ************ CAssetDependencyListBuilder ************
//@todo merge with CAreaDependencyListBuilder; code is very similar
class CAssetDependencyListBuilder
{
CResourceEntry* mpResourceEntry;
CCharacterUsageMap mCharacterUsageMap;
std::set<CAssetID> mUsedAssets;
CAssetID mCurrentAnimSetID;
public:
CAssetDependencyListBuilder(CResourceEntry* pEntry)
: mpResourceEntry(pEntry)
, mCharacterUsageMap(pEntry->ResourceStore())
{}
void BuildDependencyList(std::vector<CAssetID>& OutAssets);
void AddDependency(const CAssetID& kID, std::vector<CAssetID>& Out);
void EvaluateDependencyNode(CResourceEntry* pCurEntry, IDependencyNode* pNode, std::vector<CAssetID>& Out);
};
#endif // DEPENDENCYLISTBUILDERS

View File

@ -186,6 +186,13 @@ bool ValidateCooker(EResourceType ResourceType, bool DumpInvalidFileContents)
DumpFile.WriteBytes( NewData.data(), NewData.size() );
DumpFile.Close();
}
if( NumInvalid >= 100 )
{
debugf( "Test aborted; at least 100 invalid resources. Checked %d resources, %d passed, %d failed",
NumValid + NumInvalid, NumValid, NumInvalid );
return false;
}
}
// Test complete

View File

@ -92,7 +92,7 @@ void CAnimationParameters::Write(IOutputStream& rSCLY)
{
CAssetID::skInvalidID32.Write(rSCLY);
rSCLY.WriteLong(0);
rSCLY.WriteLong(0xFFFFFFFF);
rSCLY.WriteLong(0);
}
}

View File

@ -4,6 +4,7 @@
#include "CAreaCooker.h"
#include "CModelCooker.h"
#include "CPoiToWorldCooker.h"
#include "CScanCooker.h"
#include "CStringCooker.h"
#include "CWorldCooker.h"
@ -25,6 +26,7 @@ public:
{
case EResourceType::Area: return CAreaCooker::CookMREA((CGameArea*) pRes, rOutput);
case EResourceType::Model: return CModelCooker::CookCMDL((CModel*) pRes, rOutput);
case EResourceType::Scan: return CScanCooker::CookSCAN((CScan*) pRes, rOutput);
case EResourceType::StaticGeometryMap: return CPoiToWorldCooker::CookEGMC((CPoiToWorld*) pRes, rOutput);
case EResourceType::StringTable: return CStringCooker::CookSTRG((CStringTable*) pRes, rOutput);
case EResourceType::Tweaks: return CTweakCooker::CookCTWK((CTweakData*) pRes, rOutput);

View File

@ -0,0 +1,63 @@
#include "CScanCooker.h"
#include "Core/Resource/Cooker/CScriptCooker.h"
#include "Core/GameProject/DependencyListBuilders.h"
bool CScanCooker::CookSCAN(CScan* pScan, IOutputStream& SCAN)
{
// File header
if (pScan->Game() <= EGame::Prime)
{
// We currently do not support cooking for the MP1 demo build
ASSERT( pScan->Game() != EGame::PrimeDemo );
SCAN.WriteLong( 5 ); // Version number; must be 5
SCAN.WriteLong( 0x0BADBEEF ); // SCAN magic
CStructRef ScanProperties = pScan->ScanData();
CScriptCooker Cooker(pScan->Game());
Cooker.WriteProperty(SCAN, ScanProperties.Property(), ScanProperties.DataPointer(), true);
}
else
{
SCAN.WriteFourCC( FOURCC('SCAN') ); // SCAN magic
SCAN.WriteLong( 2 ); // Version number; must be 2
SCAN.WriteByte( 1 ); // Layer version number; must be 1
SCAN.WriteLong( 1 ); // Instance count
// Scans in MP2/3 are saved with the script object data format
// Write a dummy script object header here
SCAN.WriteLong( FOURCC('SNFO') ); // ScannableObjectInfo object ID
uint ScanInstanceSizeOffset = SCAN.Tell();
SCAN.WriteShort( 0 ); // Object size
SCAN.WriteLong( 0 ); // Instance ID
SCAN.WriteShort( 0 ); // Link count
CStructRef ScanProperties = pScan->ScanData();
CScriptCooker Cooker(pScan->Game());
Cooker.WriteProperty(SCAN, ScanProperties.Property(), ScanProperties.DataPointer(), false);
uint ScanInstanceEnd = SCAN.Tell();
uint ScanInstanceSize = ScanInstanceEnd - ScanInstanceSizeOffset - 2;
SCAN.GoTo(ScanInstanceSizeOffset);
SCAN.WriteShort( (uint16) ScanInstanceSize );
SCAN.GoTo(ScanInstanceEnd);
// Write dependency list
// @todo this output may not be 100% correct. Some dependencies seem to be conditionally excluded in base game.
// This may cause some assets to be unnecessarily loaded into memory ingame.
std::vector<CAssetID> Dependencies;
CAssetDependencyListBuilder Builder(pScan->Entry());
Builder.BuildDependencyList(Dependencies);
SCAN.WriteLong(Dependencies.size());
for (const CAssetID& kID : Dependencies)
{
CResourceEntry* pEntry = pScan->Entry()->ResourceStore()->FindEntry(kID);
ASSERT( pEntry != nullptr );
pEntry->CookedExtension().Write(SCAN);
kID.Write(SCAN);
}
}
return true;
}

View File

@ -0,0 +1,15 @@
#ifndef CSCANCOOKER_H
#define CSCANCOOKER_H
#include "Core/Resource/Scan/CScan.h"
/** Cooker class for writing game-compatible SCAN assets */
class CScanCooker
{
CScanCooker() {}
public:
static bool CookSCAN(CScan* pScan, IOutputStream& SCAN);
};
#endif // CSCANCOOKER_H

View File

@ -2383,7 +2383,7 @@
</Element>
<Element>
<Key ID="0xDFF9B92" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelWater"/>
</Element>
<Element>
<Key ID="0xE07B299" Type="GhorStructB"/>
@ -4383,7 +4383,7 @@
</Element>
<Element>
<Key ID="0x191F4F62" Type="float"/>
<Value Name="Unknown"/>
<Value Name="MarbleToTireThresholdSpeed"/>
</Element>
<Element>
<Key ID="0x19275A97" Type="float"/>
@ -10323,7 +10323,7 @@
</Element>
<Element>
<Key ID="0x38F84011" Type="float"/>
<Value Name="Unknown"/>
<Value Name="ScrewAttackWallJumpGravity"/>
</Element>
<Element>
<Key ID="0x38FABDE9" Type="asset"/>
@ -14491,7 +14491,7 @@
</Element>
<Element>
<Key ID="0x4ED87EBD" Type="float"/>
<Value Name="Unknown"/>
<Value Name="Tireness"/>
</Element>
<Element>
<Key ID="0x4EDB1D0E" Type="bool"/>
@ -14895,7 +14895,7 @@
</Element>
<Element>
<Key ID="0x50F2A114" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelPhazon"/>
</Element>
<Element>
<Key ID="0x50F77DF0" Type="float"/>
@ -15579,7 +15579,7 @@
</Element>
<Element>
<Key ID="0x54C20211" Type="float"/>
<Value Name="Unknown"/>
<Value Name="ForceToLeanGain"/>
</Element>
<Element>
<Key ID="0x54CD1874" Type="asset"/>
@ -16367,7 +16367,7 @@
</Element>
<Element>
<Key ID="0x591DA5B4" Type="float"/>
<Value Name="Unknown"/>
<Value Name="ScrewAttackVerticalJumpVelocity"/>
</Element>
<Element>
<Key ID="0x59276E14" Type="float"/>
@ -16671,7 +16671,7 @@
</Element>
<Element>
<Key ID="0x5B3C4299" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelShrubbery"/>
</Element>
<Element>
<Key ID="0x5B3C92BC" Type="UnknownStruct55"/>
@ -17611,7 +17611,7 @@
</Element>
<Element>
<Key ID="0x60520222" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelOrganic"/>
</Element>
<Element>
<Key ID="0x6057938A" Type="spline"/>
@ -23063,7 +23063,7 @@
</Element>
<Element>
<Key ID="0x7EAB4BAB" Type="float"/>
<Value Name="Unknown"/>
<Value Name="ScrewAttackGravity"/>
</Element>
<Element>
<Key ID="0x7EAF8D70" Type="float"/>
@ -27411,7 +27411,7 @@
</Element>
<Element>
<Key ID="0x96226D18" Type="float"/>
<Value Name="Unknown"/>
<Value Name="TireToMarbleThresholdSpeed"/>
</Element>
<Element>
<Key ID="0x9623E44B" Type="float"/>
@ -32239,7 +32239,7 @@
</Element>
<Element>
<Key ID="0xB06AC970" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelNormal"/>
</Element>
<Element>
<Key ID="0xB06D1B60" Type="choice"/>
@ -33775,7 +33775,7 @@
</Element>
<Element>
<Key ID="0xB90A721E" Type="float"/>
<Value Name="Unknown"/>
<Value Name="LeanTrackingGain"/>
</Element>
<Element>
<Key ID="0xB90DFF44" Type="float"/>
@ -34171,7 +34171,7 @@
</Element>
<Element>
<Key ID="0xBB3C2629" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelAir"/>
</Element>
<Element>
<Key ID="0xBB3C732E" Type="bool"/>
@ -34255,7 +34255,7 @@
</Element>
<Element>
<Key ID="0xBBAF8577" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelLava"/>
</Element>
<Element>
<Key ID="0xBBB2D1E6" Type="SurfacePosition"/>
@ -38571,7 +38571,7 @@
</Element>
<Element>
<Key ID="0xD27A50F1" Type="float"/>
<Value Name="Unknown"/>
<Value Name="BallForwardBrakingAccelIce"/>
</Element>
<Element>
<Key ID="0xD27E8786" Type="asset"/>
@ -41695,7 +41695,7 @@
</Element>
<Element>
<Key ID="0xE428F231" Type="float"/>
<Value Name="Unknown"/>
<Value Name="ScrewAttackHorizontalJumpVelocity"/>
</Element>
<Element>
<Key ID="0xE42BAB04" Type="bool"/>