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

@@ -14,16 +14,17 @@
* Note that in public release builds, asserts are compiled out entirely, so neither log messages nor debug breaks
* will occur.
*
* There are two other macros defined which can be useful for debugging, but shouldn't be used as permanent error
* checks: BREAK_ONLY_ASSERT, which doesn't write the error to the log, and LOG_ONLY_ASSERT, which doesn't trigger
* a debug break.
* LOG_ONLY_ASSERT is similar to a regular assert, but doesn't trigger a debug break. It can be useful for debugging,
* but shouldn't be used as a permanent error check.
*/
#define __FILE_SHORT__ strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__
#if _DEBUG
#define DEBUG_BREAK __debugbreak();
#define CONDITIONAL_BREAK(Condition) if (##Condition) DEBUG_BREAK
#else
#define DEBUG_BREAK {}
#define CONDITIONAL_BREAK(Condition) {}
#endif
#if !PUBLIC_RELEASE
@@ -41,11 +42,6 @@
#define WRITE_FAILURE_TO_LOG(Expression) \
Log::Write(TString(__FILE_SHORT__) + "(" + TString::FromInt32(__LINE__, 0, 10) + "): ASSERT FAILED: " + #Expression);
#define BREAK_ONLY_ASSERT(Expression) \
ASSERT_CHECK_BEGIN(Expression) \
DEBUG_BREAK \
ASSERT_CHECK_END
#define LOG_ONLY_ASSERT(Expression) \
ASSERT_CHECK_BEGIN(Expression) \
WRITE_FAILURE_TO_LOG(Expression) \

View File

@@ -199,7 +199,7 @@ namespace CompressionUtil
return true;
}
bool CompressSegmentedData(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool IsZlib)
bool CompressSegmentedData(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool IsZlib, bool AllowUncompressedSegments)
{
u8 *pSrcEnd = pSrc + SrcLen;
u8 *pDstStart = pDst;
@@ -222,7 +222,7 @@ namespace CompressionUtil
CompressLZO(pSrc, Size, Compressed.data(), TotalOut);
// Verify that the compressed data is actually smaller.
if (TotalOut >= Size)
if (AllowUncompressedSegments && TotalOut >= Size)
{
// Write negative size value to destination (which signifies uncompressed)
*pDst++ = -Size >> 8;
@@ -250,13 +250,13 @@ namespace CompressionUtil
return true;
}
bool CompressZlibSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut)
bool CompressZlibSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool AllowUncompressedSegments)
{
return CompressSegmentedData(pSrc, SrcLen, pDst, rTotalOut, true);
return CompressSegmentedData(pSrc, SrcLen, pDst, rTotalOut, true, AllowUncompressedSegments);
}
bool CompressLZOSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut)
bool CompressLZOSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool AllowUncompressedSegments)
{
return CompressSegmentedData(pSrc, SrcLen, pDst, rTotalOut, false);
return CompressSegmentedData(pSrc, SrcLen, pDst, rTotalOut, false, AllowUncompressedSegments);
}
}

View File

@@ -18,9 +18,9 @@ namespace CompressionUtil
// Compression
bool CompressZlib(u8 *pSrc, u32 SrcLen, u8 *pDst, u32 DstLen, u32& rTotalOut);
bool CompressLZO(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut);
bool CompressSegmentedData(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool IsZlib);
bool CompressZlibSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut);
bool CompressLZOSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut);
bool CompressSegmentedData(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool IsZlib, bool AllowUncompressedSegments);
bool CompressZlibSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool AllowUncompressedSegments);
bool CompressLZOSegmented(u8 *pSrc, u32 SrcLen, u8 *pDst, u32& rTotalOut, bool AllowUncompressedSegments);
}
#endif // COMPRESSIONUTIL_H

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

View File

@@ -12,6 +12,7 @@ CCreateInstanceCommand::CCreateInstanceCommand(CWorldEditor *pEditor, CScriptTem
, mpNewInstance(nullptr)
, mpNewNode(nullptr)
{
ASSERT(mLayerIndex != -1);
}
void CCreateInstanceCommand::undo()
@@ -30,7 +31,7 @@ void CCreateInstanceCommand::redo()
{
mpEditor->NotifyNodeAboutToBeSpawned();
CScriptLayer *pLayer = (mLayerIndex == -1 ? mpArea->GeneratedObjectsLayer() : mpArea->ScriptLayer(mLayerIndex));
CScriptLayer *pLayer = mpArea->ScriptLayer(mLayerIndex);
CScriptObject *pNewInst = mpArea->SpawnInstance(mpTemplate, pLayer, mSpawnPosition);
CScriptNode *pNewNode = mpScene->CreateScriptNode(pNewInst);
pNewNode->SetPosition(mSpawnPosition);

View File

@@ -45,7 +45,7 @@ void WScanPreviewPanel::SetResource(CResource *pRes)
ui->ScanTypeLabel->setText("<b><font color=\"red\">Important</font></b>");
else
{
if (pScan->Version() <= ePrime)
if (pScan->Game() <= ePrime)
ui->ScanTypeLabel->setText("<b><font color=\"#FF9030\">Normal</font></b>");
else
ui->ScanTypeLabel->setText("<b><font color=\"#A0A0FF\">Normal</font></b>");
@@ -81,7 +81,7 @@ void WScanPreviewPanel::SetResource(CResource *pRes)
ui->ScanTextWidget->SetResource(pScan->ScanText());
// Show logbook category? (Yes on MP1, no on MP2+)
if (pScan->Version() <= ePrime)
if (pScan->Game() <= ePrime)
{
ui->CategoryInfoLabel->show();
ui->ScanCategoryLabel->show();

View File

@@ -2,10 +2,10 @@
#include "Editor/UICommon.h"
#include <Core/Resource/Script/CScriptLayer.h>
CLayerModel::CLayerModel(QObject *pParent) : QAbstractListModel(pParent)
CLayerModel::CLayerModel(QObject *pParent)
: QAbstractListModel(pParent)
, mpArea(nullptr)
{
mpArea = nullptr;
mHasGenerateLayer = false;
}
CLayerModel::~CLayerModel()
@@ -14,9 +14,7 @@ CLayerModel::~CLayerModel()
int CLayerModel::rowCount(const QModelIndex& /*parent*/) const
{
if (!mpArea) return 0;
if (mHasGenerateLayer) return mpArea->NumScriptLayers() + 1;
else return mpArea->NumScriptLayers();
return mpArea ? mpArea->NumScriptLayers() : 0;
}
QVariant CLayerModel::data(const QModelIndex &index, int role) const
@@ -30,7 +28,6 @@ QVariant CLayerModel::data(const QModelIndex &index, int role) const
void CLayerModel::SetArea(CGameArea *pArea)
{
mpArea = pArea;
mHasGenerateLayer = (pArea->GeneratedObjectsLayer() != nullptr);
emit layoutChanged();
}
@@ -41,8 +38,6 @@ CScriptLayer* CLayerModel::Layer(const QModelIndex& index) const
if (index.row() < (int) NumLayers)
return mpArea->ScriptLayer(index.row());
if (mHasGenerateLayer && (index.row() == NumLayers))
return mpArea->GeneratedObjectsLayer();
return nullptr;
}

View File

@@ -7,7 +7,6 @@
class CLayerModel : public QAbstractListModel
{
TResPtr<CGameArea> mpArea;
bool mHasGenerateLayer;
public:
explicit CLayerModel(QObject *pParent = 0);