Lots of work to get MP2 repacking functional

This commit is contained in:
parax0
2016-11-07 03:07:00 -07:00
parent 040caca896
commit f6fd78af14
135 changed files with 677 additions and 1239 deletions

View File

@@ -55,6 +55,15 @@ void CDependencyTree::AddDependency(CResource *pRes, bool AvoidDuplicates /*= tr
AddDependency(pRes->ID(), AvoidDuplicates);
}
void CDependencyTree::AddCharacterDependency(const CAnimationParameters& rkAnimParams)
{
// This is for formats other than MREA that use AnimationParameters (such as SCAN).
CAnimSet *pSet = rkAnimParams.AnimSet();
if (!pSet || rkAnimParams.CharacterIndex() == -1) return;
CCharPropertyDependency *pChar = new CCharPropertyDependency("NULL", pSet->ID(), rkAnimParams.CharacterIndex());
mChildren.push_back(pChar);
}
// ************ CResourceDependency ************
EDependencyNodeType CResourceDependency::Type() const
{
@@ -215,6 +224,7 @@ CSetCharacterDependency* CSetCharacterDependency::BuildTree(const CAnimSet *pkOw
pTree->AddDependency(pkChar->IceModel);
pTree->AddDependency(pkChar->IceSkin);
pTree->AddDependency(pkChar->SpatialPrimitives);
}
return pTree;
@@ -300,7 +310,8 @@ void CAreaDependencyTree::AddScriptLayer(CScriptLayer *pLayer)
CScriptInstanceDependency *pTree = CScriptInstanceDependency::BuildTree( pLayer->InstanceByIndex(iInst) );
ASSERT(pTree != nullptr);
if (pTree->NumChildren() > 0)
// 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);
else
delete pTree;

View File

@@ -10,6 +10,7 @@ class CScriptLayer;
class CScriptObject;
class CPropertyStruct;
class CAnimSet;
class CAnimationParameters;
struct SSetCharacter;
// Group of node classes forming a tree of cached resource dependencies.
@@ -39,8 +40,8 @@ public:
virtual bool HasDependency(const CAssetID& rkID) const;
// Accessors
u32 NumChildren() const { return mChildren.size(); }
IDependencyNode* ChildByIndex(u32 Index) const { return mChildren[Index]; }
inline u32 NumChildren() const { return mChildren.size(); }
inline IDependencyNode* ChildByIndex(u32 Index) const { return mChildren[Index]; }
};
// Basic dependency tree; this class is sufficient for most resource types.
@@ -59,6 +60,7 @@ public:
void AddChild(IDependencyNode *pNode);
void AddDependency(const CAssetID& rkID, bool AvoidDuplicates = true);
void AddDependency(CResource *pRes, bool AvoidDuplicates = true);
void AddCharacterDependency(const CAnimationParameters& rkAnimParams);
// Accessors
inline void SetID(const CAssetID& rkID) { mRootID = rkID; }
@@ -183,6 +185,7 @@ public:
// Accessors
inline bool IsUsedByCharacter(u32 CharIdx) const { return mCharacterIndices.find(CharIdx) != mCharacterIndices.end(); }
inline bool IsUsedByAnyCharacter() const { return !mCharacterIndices.empty(); }
// Static
static CSetAnimationDependency* BuildTree(const CAnimSet *pkOwnerSet, u32 AnimIndex);

View File

@@ -1,4 +1,5 @@
#include "CGameProject.h"
#include "Core/Resource/Factory/CTemplateLoader.h"
#include "Core/Resource/Script/CMasterTemplate.h"
#include <Common/Serialization/XML.h>
@@ -21,6 +22,7 @@ bool CGameProject::Load(const TWideString& rkPath)
TString ProjPath = rkPath.ToUTF8();
CXMLReader Reader(ProjPath);
Serialize(Reader);
CTemplateLoader::LoadGameTemplates(mGame);
mpResourceStore->LoadResourceDatabase();
mpAudioManager->LoadAssets();

View File

@@ -159,7 +159,7 @@ void CPackage::Cook()
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);
Success = CompressionUtil::CompressLZOSegmented(ResourceData.data(), ResourceData.size(), CompressedData.data(), CompressedSize, false);
// Make sure that the compressed data is actually smaller, accounting for padding + uncompressed size value
if (Success)
@@ -217,7 +217,7 @@ void CPackage::CompareOriginalAssetList(const std::list<CAssetID>& rkNewList)
TWideString CookedPath = CookedPackagePath(false);
CFileInStream Pak(CookedPath.ToUTF8().ToStdString(), IOUtil::eBigEndian);
if (!Pak.IsValid())
if (!Pak.IsValid() || Pak.Size() == 0)
{
Log::Error("Failed to compare to original asset list; couldn't open the original pak");
return;

View File

@@ -139,6 +139,21 @@ void CCharacterUsageMap::ParseDependencyNode(IDependencyNode *pNode)
rUsageList[UsedChar] = true;
}
// Parse dependencies of the referenced resource if it's a type that can reference animsets
else if (Type == eDNT_ResourceDependency || Type == eDNT_ScriptProperty)
{
CResourceDependency *pDep = static_cast<CResourceDependency*>(pNode);
CResourceEntry *pEntry = gpResourceStore->FindEntry(pDep->ID());
if (pEntry)
{
EResType ResType = pEntry->ResourceType();
if (ResType == eScan)
ParseDependencyNode(pEntry->Dependencies());
}
}
// Look for sub-dependencies of the current node
else
{
@@ -275,7 +290,7 @@ void CPackageDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurE
else if (Type == eDNT_SetAnimation)
{
CSetAnimationDependency *pAnim = static_cast<CSetAnimationDependency*>(pNode);
ParseChildren = mCharacterUsageMap.IsAnimationUsed(mCurrentAnimSetID, pAnim) || mIsPlayerActor; // todo - should maybe omit completely unused animations on PlayerActors?
ParseChildren = mCharacterUsageMap.IsAnimationUsed(mCurrentAnimSetID, pAnim) || (mIsPlayerActor && pAnim->IsUsedByAnyCharacter());
}
else
@@ -409,7 +424,10 @@ void CAreaDependencyListBuilder::AddDependency(const CAssetID& rkID, std::list<C
// Don't add CSNGs to the output dependency list (we parse them because we need their AGSC dependencies in the output AudioGroup set)
if (ResType != eMidi)
{
rOut.push_back(rkID);
mLayerUsedAssets.insert(rkID);
}
}
void CAreaDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntry, IDependencyNode *pNode, std::list<CAssetID>& rOut, std::set<CAssetID> *pAudioGroupsOut)
@@ -445,7 +463,7 @@ void CAreaDependencyListBuilder::EvaluateDependencyNode(CResourceEntry *pCurEntr
else if (Type == eDNT_SetAnimation)
{
CSetAnimationDependency *pAnim = static_cast<CSetAnimationDependency*>(pNode);
ParseChildren = mCharacterUsageMap.IsAnimationUsed(mCurrentAnimSetID, pAnim);
ParseChildren = mCharacterUsageMap.IsAnimationUsed(mCurrentAnimSetID, pAnim) || (mIsPlayerActor && pAnim->IsUsedByAnyCharacter());
}
else

View File

@@ -57,6 +57,7 @@ struct SSetCharacter
std::vector<CAssetID> EffectParticles;
CAssetID IceModel;
CAssetID IceSkin;
CAssetID SpatialPrimitives;
std::set<u32> UsedAnimationIndices;
};

View File

@@ -11,7 +11,6 @@ CGameArea::CGameArea(CResourceEntry *pEntry /*= 0*/)
, mOriginalWorldMeshCount(0)
, mUsesCompression(false)
, mpMaterialSet(nullptr)
, mpGeneratorLayer(nullptr)
, mpCollision(nullptr)
{
}
@@ -21,7 +20,6 @@ CGameArea::~CGameArea()
ClearTerrain();
delete mpCollision;
delete mpGeneratorLayer;
for (u32 iSCLY = 0; iSCLY < mScriptLayers.size(); iSCLY++)
delete mScriptLayers[iSCLY];
@@ -54,8 +52,6 @@ CDependencyTree* CGameArea::BuildDependencyTree() const
for (u32 iLayer = 0; iLayer < mScriptLayers.size(); iLayer++)
pTree->AddScriptLayer(mScriptLayers[iLayer]);
pTree->AddScriptLayer(mpGeneratorLayer);
return pTree;
}
@@ -134,8 +130,6 @@ void CGameArea::ClearScriptLayers()
for (auto it = mScriptLayers.begin(); it != mScriptLayers.end(); it++)
delete *it;
mScriptLayers.clear();
delete mpGeneratorLayer;
mpGeneratorLayer = nullptr;
}
u32 CGameArea::TotalInstanceCount() const

View File

@@ -49,7 +49,6 @@ class CGameArea : public CResource
std::vector<CStaticModel*> mStaticWorldModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor
// Script
std::vector<CScriptLayer*> mScriptLayers;
CScriptLayer *mpGeneratorLayer;
std::unordered_map<u32, CScriptObject*> mObjectMap;
// Collision
CCollisionMeshGroup *mpCollision;
@@ -92,7 +91,6 @@ public:
inline CCollisionMeshGroup* Collision() const { return mpCollision; }
inline u32 NumScriptLayers() const { return mScriptLayers.size(); }
inline CScriptLayer* ScriptLayer(u32 Index) const { return mScriptLayers[Index]; }
inline CScriptLayer* GeneratedObjectsLayer() const { return mpGeneratorLayer; }
inline u32 NumLightLayers() const { return mLightLayers.size(); }
inline u32 NumLights(u32 LayerIndex) const { return (LayerIndex < mLightLayers.size() ? mLightLayers[LayerIndex].size() : 0); }
inline CLight* Light(u32 LayerIndex, u32 LightIndex) const { return mLightLayers[LayerIndex][LightIndex]; }

View File

@@ -4,6 +4,7 @@
#include "CResource.h"
#include "CStringTable.h"
#include "TResPtr.h"
#include "Core/Resource/Animation/CAnimationParameters.h"
#include <Common/EGame.h>
class CScan : public CResource
@@ -22,15 +23,34 @@ public:
eResearch
};
struct SScanInfoSecondaryModel
{
CAssetID ModelID;
CAnimationParameters AnimParams;
TString AttachBoneName;
};
private:
EGame mVersion;
CAssetID mFrameID;
// Common
TResPtr<CStringTable> mpStringTable;
bool mIsSlow;
bool mIsImportant;
ELogbookCategory mCategory;
// MP1
CAssetID mFrameID;
CAssetID mScanImageTextures[4];
std::vector<CAssetID> mLogbookAssets;
// MP2/3
bool mUseLogbookModelPostScan;
CAssetID mPostOverrideTexture;
float mLogbookDefaultRotX;
float mLogbookDefaultRotZ;
float mLogbookScale;
CAssetID mLogbookModel;
CAnimationParameters mLogbookAnimParams;
CAnimationParameters mUnknownAnimParams;
std::vector<SScanInfoSecondaryModel> mSecondaryModels;
public:
CScan(CResourceEntry *pEntry = 0)
@@ -43,23 +63,41 @@ public:
CDependencyTree* BuildDependencyTree() const
{
// note: not handling Corruption yet
if (Game() >= eCorruptionProto)
Log::Warning("CScan::BuildDependencyTree not handling Corruption dependencies");
CDependencyTree *pTree = new CDependencyTree(ID());
pTree->AddDependency(mFrameID);
if (Game() <= ePrime)
pTree->AddDependency(mFrameID);
pTree->AddDependency(mpStringTable);
for (u32 iImg = 0; iImg < 4; iImg++)
pTree->AddDependency(mScanImageTextures[iImg]);
if (Game() <= ePrime)
{
for (u32 iImg = 0; iImg < 4; iImg++)
pTree->AddDependency(mScanImageTextures[iImg]);
}
for (u32 iLog = 0; iLog < mLogbookAssets.size(); iLog++)
pTree->AddDependency(mLogbookAssets[iLog]);
else if (Game() <= eEchoes)
{
pTree->AddDependency(mPostOverrideTexture);
pTree->AddDependency(mLogbookModel);
pTree->AddCharacterDependency(mLogbookAnimParams);
pTree->AddCharacterDependency(mUnknownAnimParams);
for (u32 iSec = 0; iSec < mSecondaryModels.size(); iSec++)
{
const SScanInfoSecondaryModel& rkSecModel = mSecondaryModels[iSec];
pTree->AddDependency(rkSecModel.ModelID);
pTree->AddCharacterDependency(rkSecModel.AnimParams);
}
}
return pTree;
}
EGame Version() const { return mVersion; }
CStringTable* ScanText() const { return mpStringTable; }
bool IsImportant() const { return mIsImportant; }
bool IsSlow() const { return mIsSlow; }

View File

@@ -200,10 +200,11 @@ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut)
// SCGN
if (mVersion == eEchoesDemo)
{
rOut.WriteString("SCGN", 4);
// IMPORTANT TODO - REGENERATE SCGN LAYER
/*rOut.WriteString("SCGN", 4);
rOut.WriteByte(1);
CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut);
FinishSection(false);
FinishSection(false);*/
}
}
@@ -220,9 +221,10 @@ void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut)
}
// SCGN
// IMPORTANT TODO - REGENERATE SCGN
rOut.WriteString("SCGN", 4);
rOut.WriteByte(0x1);
CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut);
//CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut);
FinishSection(true);
}
@@ -276,7 +278,7 @@ void CAreaCooker::FinishBlock()
if (EnableCompression)
{
bool Success = CompressionUtil::CompressSegmentedData((u8*) mCompressedData.Data(), mCompressedData.Size(), CompressedBuf.data(), CompressedSize, UseZlib);
bool Success = CompressionUtil::CompressSegmentedData((u8*) mCompressedData.Data(), mCompressedData.Size(), CompressedBuf.data(), CompressedSize, UseZlib, true);
u32 PadBytes = (32 - (CompressedSize % 32)) & 0x1F;
WriteCompressedData = Success && (CompressedSize + PadBytes < (u32) mCompressedData.Size());
}

View File

@@ -141,6 +141,10 @@ bool CWorldCooker::CookMLVL(CWorld *pWorld, IOutputStream& rMLVL)
for (u32 iOff = 0; iOff < ModuleLayerOffsets.size(); iOff++)
rMLVL.WriteLong(ModuleLayerOffsets[iOff]);
}
// Internal Name
if (Game >= eEchoesDemo)
rMLVL.WriteString(rArea.InternalName.ToStdString());
}
if (Game <= eCorruption)

View File

@@ -33,7 +33,7 @@ CAnimSet* CAnimSetLoader::LoadReturnsCHAR(IInputStream& rCHAR)
return pSet;
}
void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4, SSetCharacter *pChar)
void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4)
{
// For now, just parse the data; don't store it
rPAS4.Seek(0x4, SEEK_CUR); // Skipping PAS4 FourCC
@@ -68,11 +68,7 @@ void CAnimSetLoader::LoadPASDatabase(IInputStream& rPAS4, SSetCharacter *pChar)
}
for (u32 iInfo = 0; iInfo < AnimInfoCount; iInfo++)
{
u32 MetaAnimID = rPAS4.ReadLong();
rPAS4.Seek(Skip, SEEK_CUR);
pChar->UsedAnimationIndices.insert(MetaAnimID);
}
rPAS4.Seek(0x4 + Skip, SEEK_CUR);
}
}
@@ -321,7 +317,7 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
}
// PAS Database
Loader.LoadPASDatabase(rANCS, pChar);
Loader.LoadPASDatabase(rANCS);
// Particles
u32 ParticleCount = rANCS.ReadLong();
@@ -394,7 +390,8 @@ CAnimSet* CAnimSetLoader::LoadANCS(IInputStream& rANCS, CResourceEntry *pEntry)
if (Loader.mVersion == eEchoes)
{
rANCS.Seek(0x5, SEEK_CUR);
pChar->SpatialPrimitives = rANCS.ReadLong();
rANCS.Seek(0x1, SEEK_CUR);
u32 UnknownCount2 = rANCS.ReadLong();
rANCS.Seek(UnknownCount2 * 0x1C, SEEK_CUR);
}

View File

@@ -12,7 +12,7 @@ class CAnimSetLoader
CAnimSetLoader();
CAnimSet* LoadCorruptionCHAR(IInputStream& rCHAR);
CAnimSet* LoadReturnsCHAR(IInputStream& rCHAR);
void LoadPASDatabase(IInputStream& rPAS4, SSetCharacter *pChar);
void LoadPASDatabase(IInputStream& rPAS4);
void LoadAnimationSet(IInputStream& rANCS);
void ProcessPrimitives();

View File

@@ -158,9 +158,8 @@ void CAreaLoader::ReadSCLYPrime()
if (pLayer)
{
mpArea->mpGeneratorLayer = pLayer;
pLayer->SetName("Generated Objects");
pLayer->SetActive(true);
MergeGeneratedLayer(pLayer);
delete pLayer;
}
}
}
@@ -311,6 +310,7 @@ void CAreaLoader::ReadSCLYEchoes()
}
// SCGN
// we want to regenerate the SCGN layer on cook - for now just move everything back to its original layer
CFourCC SCGN(*mpMREA);
if (SCGN != "SCGN")
{
@@ -319,12 +319,12 @@ void CAreaLoader::ReadSCLYEchoes()
}
mpMREA->Seek(0x1, SEEK_CUR); // Skipping unknown
mpArea->mpGeneratorLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
CScriptLayer *pGeneratedLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion);
if (mpArea->mpGeneratorLayer)
if (pGeneratedLayer)
{
mpArea->mpGeneratorLayer->SetName("Generated Objects");
mpArea->mpGeneratorLayer->SetActive(true);
MergeGeneratedLayer(pGeneratedLayer);
delete pGeneratedLayer;
}
SetUpObjects();
@@ -606,19 +606,37 @@ void CAreaLoader::ReadEGMC()
mpArea->mpPoiToWorldMap = gpResourceStore->LoadResource(EGMC, "EGMC");
}
void CAreaLoader::SetUpObjects()
void CAreaLoader::MergeGeneratedLayer(CScriptLayer *pLayer)
{
// Iterate over all objects
for (u32 iLyr = 0; iLyr < mpArea->NumScriptLayers() + 1; iLyr++)
while (pLayer->NumInstances() != 0)
{
CScriptLayer *pLayer;
if (iLyr < mpArea->NumScriptLayers()) pLayer = mpArea->mScriptLayers[iLyr];
CScriptObject *pObj = pLayer->InstanceByIndex(0);
u32 InstanceID = pObj->InstanceID();
// Check if this is a duplicate of an existing instance (this only happens with DKCR GenericCreature as far as I'm aware)
CScriptObject *pDupe = mpArea->InstanceByID(InstanceID);
if (pDupe)
{
Log::Write("Duplicate SCGN object: [" + pObj->Template()->Name() + "] " + pObj->InstanceName() + " (" + TString::HexString(pObj->InstanceID(), 8, false) + ")");
pLayer->RemoveInstance(pObj);
delete pObj;
}
else
{
pLayer = mpArea->GeneratedObjectsLayer();
if (!pLayer) break;
u32 LayerIdx = (InstanceID >> 26) & 0x3F;
pObj->SetLayer( mpArea->ScriptLayer(LayerIdx) );
}
}
}
void CAreaLoader::SetUpObjects()
{
// Iterate over all objects
for (u32 iLyr = 0; iLyr < mpArea->NumScriptLayers(); iLyr++)
{
CScriptLayer *pLayer = mpArea->mScriptLayers[iLyr];
for (u32 iObj = 0; iObj < pLayer->NumInstances(); iObj++)
{

View File

@@ -78,6 +78,7 @@ class CAreaLoader
void ReadPATH();
void ReadPTLA();
void ReadEGMC();
void MergeGeneratedLayer(CScriptLayer *pLayer);
void SetUpObjects();
public:

View File

@@ -27,28 +27,33 @@ CAudioGroup* CAudioGroupLoader::LoadAGSC(IInputStream& rAGSC, CResourceEntry *pE
}
// Read needed data from the Proj chunk
u32 ProjStart = rAGSC.Tell();
rAGSC.Seek(0x4, SEEK_CUR);
u16 GroupID = rAGSC.ReadShort();
u16 GroupType = rAGSC.ReadShort();
rAGSC.Seek(0x14, SEEK_CUR);
u32 SfxTableStart = rAGSC.ReadLong();
u16 Peek = rAGSC.PeekShort();
if (Game == ePrime)
pOut->mGroupID = GroupID;
else
ASSERT(pOut->mGroupID == GroupID);
if (GroupType == 1)
if (Peek != 0xFFFF)
{
rAGSC.Seek(ProjStart + SfxTableStart, SEEK_SET);
u16 NumSounds = rAGSC.ReadShort();
rAGSC.Seek(0x2, SEEK_CUR);
u32 ProjStart = rAGSC.Tell();
rAGSC.Seek(0x4, SEEK_CUR);
u16 GroupID = rAGSC.ReadShort();
u16 GroupType = rAGSC.ReadShort();
rAGSC.Seek(0x14, SEEK_CUR);
u32 SfxTableStart = rAGSC.ReadLong();
for (u32 iSfx = 0; iSfx < NumSounds; iSfx++)
if (Game == ePrime)
pOut->mGroupID = GroupID;
else
ASSERT(pOut->mGroupID == GroupID);
if (GroupType == 1)
{
pOut->mDefineIDs.push_back( rAGSC.ReadShort() );
rAGSC.Seek(0x8, SEEK_CUR);
rAGSC.Seek(ProjStart + SfxTableStart, SEEK_SET);
u16 NumSounds = rAGSC.ReadShort();
rAGSC.Seek(0x2, SEEK_CUR);
for (u32 iSfx = 0; iSfx < NumSounds; iSfx++)
{
pOut->mDefineIDs.push_back( rAGSC.ReadShort() );
rAGSC.Seek(0x8, SEEK_CUR);
}
}
}
@@ -70,9 +75,10 @@ CStringList* CAudioGroupLoader::LoadSTLC(IInputStream& rSTLC, CResourceEntry *pE
{
CStringList *pOut = new CStringList(pEntry);
u32 NumStrings = rSTLC.ReadLong();
pOut->mStringList.reserve(NumStrings);
for (u32 iStr = 0; iStr < NumStrings; iStr++)
pOut->mStringList[iStr] = rSTLC.ReadString();
pOut->mStringList.push_back( rSTLC.ReadString() );
return pOut;
}

View File

@@ -21,7 +21,7 @@ CScan* CScanLoader::LoadScanMP1(IInputStream& rSCAN)
rSCAN.Seek(0x18, SEEK_CUR);
}
mpScan->mVersion = ePrime;
mpScan->SetGame(ePrime);
return mpScan;
}
@@ -67,26 +67,29 @@ CScan* CScanLoader::LoadScanMP2(IInputStream& rSCAN)
case 0xB:
mpScan = new CScan(mpEntry);
mpScan->SetGame(eEchoes);
LoadParamsMP2(rSCAN);
LoadParamsMP2(rSCAN, NumProperties);
break;
case 0x12:
case 0x16:
mpScan = new CScan(mpEntry);
mpScan->SetGame(eCorruption);
LoadParamsMP3(rSCAN);
LoadParamsMP3(rSCAN, NumProperties);
break;
default:
Log::FileError(rSCAN.GetSourceString(), rSCAN.Tell() - 2, "Invalid SNFO property count: " + TString::HexString(NumProperties));
return nullptr;
}
mpScan->SetGame(eEchoes);
return mpScan;
}
void CScanLoader::LoadParamsMP2(IInputStream& rSCAN)
void CScanLoader::LoadParamsMP2(IInputStream& rSCAN, u16 NumProperties)
{
// Function begins after the SNFO property count
for (u32 iProp = 0; iProp < 20; iProp++)
mpScan->mSecondaryModels.resize(9);
for (u32 iProp = 0; iProp < NumProperties; iProp++)
{
u32 PropertyID = rSCAN.ReadLong();
u16 PropertySize = rSCAN.ReadShort();
@@ -103,39 +106,75 @@ void CScanLoader::LoadParamsMP2(IInputStream& rSCAN)
break;
case 0x7B714814:
mpScan->mIsImportant = (rSCAN.ReadByte() != 0);
mpScan->mIsImportant = rSCAN.ReadBool();
break;
case 0x1733B1EC:
mpScan->mUseLogbookModelPostScan = rSCAN.ReadBool();
break;
// Override texture and logbook model/animsets
case 0x53336141:
case 0xB7ADC418:
case 0x15694EE1:
case 0x58F9FE99:
mpScan->mLogbookAssets.push_back( CAssetID(rSCAN, eEchoes) );
mpScan->mPostOverrideTexture = CAssetID(rSCAN, mVersion);
break;
case 0x3DE0BA64:
mpScan->mLogbookDefaultRotX = rSCAN.ReadFloat();
break;
case 0x2ADD6628:
mpScan->mLogbookDefaultRotZ = rSCAN.ReadFloat();
break;
case 0xD0C15066:
mpScan->mLogbookScale = rSCAN.ReadFloat();
break;
case 0xB7ADC418:
mpScan->mLogbookModel = CAssetID(rSCAN, mVersion);
break;
case 0x15694EE1:
mpScan->mLogbookAnimParams = CAnimationParameters(rSCAN, mVersion);
break;
case 0x58F9FE99:
mpScan->mUnknownAnimParams = CAnimationParameters(rSCAN, mVersion);
break;
// ScanInfoSecondaryModels
case 0x1C5B4A3A:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[0] );
break;
case 0x8728A0EE:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[1] );
break;
case 0xF1CD99D3:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[2] );
break;
case 0x6ABE7307:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[3] );
break;
case 0x1C07EBA9:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[4] );
break;
case 0x8774017D:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[5] );
break;
case 0xF1913840:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[6] );
break;
case 0x6AE2D294:
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[7] );
break;
case 0x1CE2091C:
u16 NumSubProps = rSCAN.ReadShort();
for (u32 iSub = 0; iSub < NumSubProps; iSub++)
{
u32 SubPropertyID = rSCAN.ReadLong();
u32 Next = rSCAN.Tell() + rSCAN.ReadShort();
if (SubPropertyID == 0x1F7921BC || SubPropertyID == 0xCDD202D1)
mpScan->mLogbookAssets.push_back( CAssetID(rSCAN, eEchoes) );
rSCAN.Seek(Next, SEEK_SET);
}
LoadScanInfoSecondaryModel( rSCAN, mpScan->mSecondaryModels[8] );
break;
}
@@ -143,15 +182,12 @@ void CScanLoader::LoadParamsMP2(IInputStream& rSCAN)
}
mpScan->mCategory = CScan::eNone;
mpScan->mVersion = eEchoes;
}
void CScanLoader::LoadParamsMP3(IInputStream& rSCAN)
void CScanLoader::LoadParamsMP3(IInputStream& rSCAN, u16 NumProperties)
{
// Function begins after the SNFO property count
// Function is near-identical to the MP2 one, but when I add support
// for the other params, there will be more differences
for (u32 iProp = 0; iProp < 20; iProp++)
for (u32 iProp = 0; iProp < NumProperties; iProp++)
{
u32 PropertyID = rSCAN.ReadLong();
u16 PropertySize = rSCAN.ReadShort();
@@ -176,7 +212,35 @@ void CScanLoader::LoadParamsMP3(IInputStream& rSCAN)
}
mpScan->mCategory = CScan::eNone;
mpScan->mVersion = eCorruption;
}
void CScanLoader::LoadScanInfoSecondaryModel(IInputStream& rSCAN, CScan::SScanInfoSecondaryModel& rSecondaryModel)
{
u16 NumProperties = rSCAN.ReadShort();
for (u32 iProp = 0; iProp < NumProperties; iProp++)
{
u32 PropertyID = rSCAN.ReadLong();
u16 PropertySize = rSCAN.ReadShort();
u32 Next = rSCAN.Tell() + PropertySize;
switch (PropertyID)
{
case 0x1F7921BC:
rSecondaryModel.ModelID = CAssetID(rSCAN, mVersion);
break;
case 0xCDD202D1:
rSecondaryModel.AnimParams = CAnimationParameters(rSCAN, mVersion);
break;
case 0x3EA2BED8:
rSecondaryModel.AttachBoneName = rSCAN.ReadString();
break;
}
rSCAN.Seek(Next, SEEK_SET);
}
}
// ************ STATIC/PUBLIC ************

View File

@@ -13,8 +13,9 @@ class CScanLoader
CScanLoader();
CScan* LoadScanMP1(IInputStream& rSCAN);
CScan* LoadScanMP2(IInputStream& rSCAN);
void LoadParamsMP2(IInputStream& rSCAN);
void LoadParamsMP3(IInputStream& rSCAN);
void LoadParamsMP2(IInputStream& rSCAN, u16 NumProperties);
void LoadParamsMP3(IInputStream& rSCAN, u16 NumProperties);
void LoadScanInfoSecondaryModel(IInputStream& rSCAN, CScan::SScanInfoSecondaryModel& rSecondaryModel);
public:
static CScan* LoadSCAN(IInputStream& rSCAN, CResourceEntry *pEntry);

View File

@@ -59,7 +59,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& rSCLY
u32 Check = pBitfieldCast->Get() & ~Mask;
if (Check != 0)
Log::FileWarning(rSCLY.GetSourceString(), rSCLY.Tell() - 4, "Bitfield property \"" + pBitfieldTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has flags set that aren't in the template: " + TString::HexString(Check));
Log::FileWarning(rSCLY.GetSourceString(), rSCLY.Tell() - 4, "Bitfield property \"" + pBitfieldTemp->FullName() + "\" + (" + pBitfieldTemp->IDString(true) + ") has flags set that aren't in the template: " + TString::HexString(Check));
break;
}
@@ -72,7 +72,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& rSCLY
// Validate
u32 Index = pEnumTemp->EnumeratorIndex(ID);
if (Index == -1) Log::FileError(rSCLY.GetSourceString(), rSCLY.Tell() - 4, "Enum property \"" + pEnumTemp->Name() + "\" in struct \"" + pTemp->Name() + "\" has invalid enumerator value: " + TString::HexString(ID));
if (Index == -1) Log::FileError(rSCLY.GetSourceString(), rSCLY.Tell() - 4, "Enum property \"" + pEnumTemp->FullName() + "\" (" + pEnumTemp->IDString(true) + ") has invalid enumerator value: " + TString::HexString(ID));
pEnumCast->Set(ID);
break;
@@ -140,7 +140,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& rSCLY
}
if (!Valid)
Log::FileWarning(rSCLY.GetSourceString(), rSCLY.Tell() - ID.Length(), "Asset property " + pTemp->IDString(true) + " in object " + pTemp->ScriptTemplate()->Name() + " has a reference to an illegal asset type: " + CookedExt);
Log::FileWarning(rSCLY.GetSourceString(), rSCLY.Tell() - ID.Length(), "Asset property \"" + pTemp->FullName() + "\" (" + pTemp->IDString(true) + ") has a reference to an illegal asset type: " + CookedExt);
}
}
@@ -220,7 +220,7 @@ void CScriptLoader::LoadStructMP1(IInputStream& rSCLY, CPropertyStruct *pStruct,
TIDString IDString = pTemp->IDString(true);
if (!IDString.IsEmpty()) IDString = " (" + IDString + ")";
Log::FileWarning(rSCLY.GetSourceString(), StructStart, "Struct \"" + pTemp->Name() + "\"" + IDString + " template prop count doesn't match file; template is " + TString::HexString(PropCount, 2) + ", file is " + TString::HexString(FilePropCount, 2));
Log::FileWarning(rSCLY.GetSourceString(), StructStart, "Struct \"" + pTemp->FullName() + "\" (" + IDString + ") template prop count doesn't match file; template is " + TString::HexString(PropCount, 2) + ", file is " + TString::HexString(FilePropCount, 2));
Version = 0;
}
}

View File

@@ -105,7 +105,7 @@ IPropertyTemplate* CTemplateLoader::LoadProperty(XMLElement *pElem, CScriptTempl
pParams = pParams->NextSiblingElement();
}
// File-specific parameters
// Asset-specific parameters
if (Type == eAssetProperty)
{
TString ExtensionsAttr = pElem->Attribute("extensions");

View File

@@ -2,6 +2,7 @@
#define CSCRIPTLAYER_H
#include "CScriptObject.h"
#include "Core/Resource/CDependencyGroup.h"
#include <Common/types.h>
#include <string>
#include <vector>
@@ -107,9 +108,6 @@ public:
return iLyr;
}
if (mpArea->GeneratedObjectsLayer() == this)
return mpArea->NumScriptLayers();
return -1;
}

View File

@@ -70,11 +70,13 @@ bool CScriptObject::IsEditorProperty(IProperty *pProp)
void CScriptObject::SetLayer(CScriptLayer *pLayer, u32 NewLayerIndex)
{
ASSERT(pLayer != nullptr);
if (pLayer != mpLayer)
{
if (mpLayer) mpLayer->RemoveInstance(this);
mpLayer = pLayer;
if (mpLayer) mpLayer->AddInstance(this, NewLayerIndex);
mpLayer->AddInstance(this, NewLayerIndex);
}
}

View File

@@ -20,20 +20,25 @@ bool IPropertyTemplate::IsInVersion(u32 Version) const
return false;
}
TString IPropertyTemplate::FullName() const
{
return mpParent ? mpParent->FullName() + "::" + Name() : Name();
}
TIDString IPropertyTemplate::IDString(bool FullPath) const
{
if (mID != 0xFFFFFFFF)
{
TIDString out;
TIDString Out;
if (mpParent && FullPath)
{
out = mpParent->IDString(true);
if (!out.IsEmpty()) out += ":";
Out = mpParent->IDString(true);
if (!Out.IsEmpty()) Out += ":";
}
out += TIDString::HexString(mID);
return out;
Out += TIDString::HexString(mID);
return Out;
}
else return "";
}

View File

@@ -113,6 +113,7 @@ public:
EGame Game() const;
bool IsInVersion(u32 Version) const;
TString FullName() const;
TIDString IDString(bool FullPath) const;
bool IsDescendantOf(const CStructTemplate *pStruct) const;
bool IsFromStructTemplate() const;

View File

@@ -217,16 +217,6 @@ void CScene::SetActiveArea(CGameArea *pArea)
}
}
CScriptLayer *pGenLayer = mpArea->GeneratedObjectsLayer();
if (pGenLayer)
{
for (u32 iObj = 0; iObj < pGenLayer->NumInstances(); iObj++)
{
CScriptObject *pObj = pGenLayer->InstanceByIndex(iObj);
CreateScriptNode(pObj);
}
}
// Ensure script nodes have valid positions + build light lists
for (CSceneIterator It(this, eScriptNode, true); It; ++It)
{