From e430cbfb7382f04855017111e6198a09adf47644 Mon Sep 17 00:00:00 2001 From: parax0 Date: Sun, 31 Jan 2016 19:29:10 -0700 Subject: [PATCH] Implemented area cooker, added the ability to resave Prime 1 MREAs from the World Editor --- src/Core/Core.pro | 9 +- src/Core/Resource/CAnimationParameters.cpp | 19 +++ src/Core/Resource/CAnimationParameters.h | 2 + src/Core/Resource/CGameArea.h | 5 +- src/Core/Resource/Cooker/CAreaCooker.cpp | 126 +++++++++++++++ src/Core/Resource/Cooker/CAreaCooker.h | 24 ++- src/Core/Resource/Cooker/CScriptCooker.cpp | 170 +++++++++++++++++++++ src/Core/Resource/Cooker/CScriptCooker.h | 23 +++ src/Core/Resource/Factory/CAreaLoader.cpp | 69 +++++---- src/Core/Resource/Factory/CAreaLoader.h | 5 +- src/Core/Resource/Factory/CBlockMgr.cpp | 58 ------- src/Core/Resource/Factory/CBlockMgrIn.h | 28 ---- src/Core/Resource/Factory/CModelLoader.cpp | 54 +++---- src/Core/Resource/Factory/CModelLoader.h | 8 +- src/Core/Resource/Factory/CSectionMgrIn.h | 60 ++++++++ src/Editor/WorldEditor/CWorldEditor.cpp | 14 +- src/Editor/WorldEditor/CWorldEditor.h | 1 + src/Math/CTransform4f.cpp | 6 + src/Math/CTransform4f.h | 1 + 19 files changed, 529 insertions(+), 153 deletions(-) create mode 100644 src/Core/Resource/Cooker/CScriptCooker.cpp create mode 100644 src/Core/Resource/Cooker/CScriptCooker.h delete mode 100644 src/Core/Resource/Factory/CBlockMgr.cpp delete mode 100644 src/Core/Resource/Factory/CBlockMgrIn.h create mode 100644 src/Core/Resource/Factory/CSectionMgrIn.h diff --git a/src/Core/Core.pro b/src/Core/Core.pro index 1806147a..1832fe9a 100644 --- a/src/Core/Core.pro +++ b/src/Core/Core.pro @@ -94,7 +94,6 @@ HEADERS += \ Resource/Cooker/CWorldCooker.h \ Resource/Factory/CAnimSetLoader.h \ Resource/Factory/CAreaLoader.h \ - Resource/Factory/CBlockMgrIn.h \ Resource/Factory/CCollisionLoader.h \ Resource/Factory/CFontLoader.h \ Resource/Factory/CMaterialLoader.h \ @@ -185,7 +184,9 @@ HEADERS += \ Resource/CResourceInfo.h \ Resource/CPoiToWorld.h \ Resource/Factory/CPoiToWorldLoader.h \ - Resource/Cooker/CPoiToWorldCooker.h + Resource/Cooker/CPoiToWorldCooker.h \ + Resource/Factory/CSectionMgrIn.h \ + Resource/Cooker/CScriptCooker.h # Source Files SOURCES += \ @@ -202,7 +203,6 @@ SOURCES += \ Resource/Cooker/CWorldCooker.cpp \ Resource/Factory/CAnimSetLoader.cpp \ Resource/Factory/CAreaLoader.cpp \ - Resource/Factory/CBlockMgr.cpp \ Resource/Factory/CCollisionLoader.cpp \ Resource/Factory/CFontLoader.cpp \ Resource/Factory/CMaterialLoader.cpp \ @@ -274,4 +274,5 @@ SOURCES += \ Scene/CSceneIterator.cpp \ Resource/CPoiToWorld.cpp \ Resource/Factory/CPoiToWorldLoader.cpp \ - Resource/Cooker/CPoiToWorldCooker.cpp + Resource/Cooker/CPoiToWorldCooker.cpp \ + Resource/Cooker/CScriptCooker.cpp diff --git a/src/Core/Resource/CAnimationParameters.cpp b/src/Core/Resource/CAnimationParameters.cpp index 20b389a8..c32ff015 100644 --- a/src/Core/Resource/CAnimationParameters.cpp +++ b/src/Core/Resource/CAnimationParameters.cpp @@ -70,6 +70,25 @@ CAnimationParameters::CAnimationParameters(IInputStream& SCLY, EGame Game) } } +void CAnimationParameters::Write(IOutputStream& rSCLY) +{ + if (mGame <= eEchoes) + { + if (mpCharSet) + { + rSCLY.WriteLong(AnimSet()->ResID().ToLong()); + rSCLY.WriteLong(mNodeIndex); + rSCLY.WriteLong(mUnknown1); + } + else + { + rSCLY.WriteLong(0xFFFFFFFF); + rSCLY.WriteLong(0); + rSCLY.WriteLong(0xFFFFFFFF); + } + } +} + CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/) { if (!mpCharSet) return nullptr; diff --git a/src/Core/Resource/CAnimationParameters.h b/src/Core/Resource/CAnimationParameters.h index 762022b4..6ffaf5f3 100644 --- a/src/Core/Resource/CAnimationParameters.h +++ b/src/Core/Resource/CAnimationParameters.h @@ -20,6 +20,8 @@ class CAnimationParameters public: CAnimationParameters(); CAnimationParameters(IInputStream& SCLY, EGame Game); + void Write(IOutputStream& rSCLY); + CModel* GetCurrentModel(s32 NodeIndex = -1); TString GetCurrentCharacterName(s32 NodeIndex = -1); diff --git a/src/Core/Resource/CGameArea.h b/src/Core/Resource/CGameArea.h index f5112c21..c6bdffe3 100644 --- a/src/Core/Resource/CGameArea.h +++ b/src/Core/Resource/CGameArea.h @@ -20,6 +20,7 @@ class CGameArea : public CResource { DECLARE_RESOURCE_TYPE(eArea) friend class CAreaLoader; + friend class CAreaCooker; EGame mVersion; u32 mVertexCount; @@ -28,9 +29,11 @@ class CGameArea : public CResource CTransform4f mTransform; CAABox mAABox; + // Section data buffers; this is used to avoid having to regenerate the entire contents of the file on cook + std::vector> mSectionDataBuffers; // Geometry CMaterialSet *mMaterialSet; - std::vector mTerrainModels; // TerrainModels is the original version of each model; this is used by the editor (bounding box checks, material editing, etc) + std::vector mTerrainModels; // TerrainModels is the original version of each model; this is currently mainly used in the POI map editor std::vector mStaticTerrainModels; // StaticTerrainModels is the merged terrain for faster rendering in the world editor // Script std::vector mScriptLayers; diff --git a/src/Core/Resource/Cooker/CAreaCooker.cpp b/src/Core/Resource/Cooker/CAreaCooker.cpp index c3809c72..22257448 100644 --- a/src/Core/Resource/Cooker/CAreaCooker.cpp +++ b/src/Core/Resource/Cooker/CAreaCooker.cpp @@ -1,9 +1,135 @@ #include "CAreaCooker.h" +#include "CScriptCooker.h" +#include CAreaCooker::CAreaCooker() { } +void CAreaCooker::DetermineSectionNumbers() +{ + mGeometrySecNum = 0; + + // Determine how many sections are taken up by geometry... + u32 GeometrySections = 1; // Starting at 1 to account for materials + + // Each world mesh has (7 + surface count) sections + // header, verts, normals, colors, float UVs, short UVs, surface offsets + surfaces + for (u32 iMesh = 0; iMesh < mpArea->mTerrainModels.size(); iMesh++) + { + CModel *pModel = mpArea->mTerrainModels[iMesh]; + GeometrySections += (7 + pModel->GetSurfaceCount()); + } + + // Set section numbers + mArotSecNum = mGeometrySecNum + GeometrySections; + mSclySecNum = mArotSecNum + 1; + mCollisionSecNum = mSclySecNum + 1; + mUnknownSecNum = mCollisionSecNum + 1; + mLightsSecNum = mUnknownSecNum + 1; + mVisiSecNum = mLightsSecNum + 1; + mPathSecNum = mVisiSecNum + 1; +} + +void CAreaCooker::WritePrimeHeader(IOutputStream& rOut) +{ + rOut.WriteLong(0xDEADBEEF); + rOut.WriteLong(GetMREAVersion(mVersion)); + mpArea->mTransform.Write(rOut); + rOut.WriteLong(mpArea->mTerrainModels.size()); + rOut.WriteLong(mpArea->mSectionDataBuffers.size()); + + rOut.WriteLong(mGeometrySecNum); + rOut.WriteLong(mSclySecNum); + rOut.WriteLong(mCollisionSecNum); + rOut.WriteLong(mUnknownSecNum); + rOut.WriteLong(mLightsSecNum); + rOut.WriteLong(mVisiSecNum); + rOut.WriteLong(mPathSecNum); + rOut.WriteLong(mArotSecNum); + + mSectionSizesOffset = rOut.Tell(); + for (u32 iSec = 0; iSec < mpArea->mSectionDataBuffers.size(); iSec++) + rOut.WriteLong(0); + + rOut.WriteToBoundary(32, 0); + + mSectionMgr.SetSectionCount(mpArea->mSectionDataBuffers.size()); + mSectionMgr.Init(rOut); +} + +void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) +{ + u32 NumLayers = mpArea->mScriptLayers.size(); + rOut.WriteString("SCLY", 4); + rOut.WriteLong(0x1); // Unknown value, but it's always 1 + rOut.WriteLong(NumLayers); + + u32 LayerSizesStart = rOut.Tell(); + for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) + rOut.WriteLong(0); + + std::vector LayerSizes(NumLayers); + + for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++) + { + u32 LayerStart = rOut.Tell(); + CScriptCooker::WriteLayer(mpArea->Version(), mpArea->mScriptLayers[iLyr], rOut); + LayerSizes[iLyr] = rOut.Tell() - LayerStart; + } + + u32 LayersEnd = rOut.Tell(); + rOut.Seek(LayerSizesStart, SEEK_SET); + + for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) + rOut.WriteLong(LayerSizes[iLyr]); + + rOut.Seek(LayersEnd, SEEK_SET); + rOut.WriteToBoundary(32, 0); +} + +// ************ STATIC ************ +void CAreaCooker::WriteCookedArea(CGameArea *pArea, IOutputStream& rOut) +{ + CAreaCooker Cooker; + Cooker.mpArea = pArea; + Cooker.mVersion = pArea->Version(); + + if (Cooker.mVersion > ePrime) + { + Log::Error("Area cooking is not supported for games other than Metroid Prime"); + return; + } + + // Write header + Cooker.DetermineSectionNumbers(); + Cooker.WritePrimeHeader(rOut); + + // Write pre-SCLY data sections + for (u32 iSec = 0; iSec < Cooker.mSclySecNum; iSec++) + { + rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); + Cooker.mSectionMgr.AddSize(rOut); + } + + // Write SCLY + Cooker.WritePrimeSCLY(rOut); + Cooker.mSectionMgr.AddSize(rOut); + + // Write post-SCLY data sections + for (u32 iSec = Cooker.mSclySecNum + 1; iSec < pArea->mSectionDataBuffers.size(); iSec++) + { + rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); + Cooker.mSectionMgr.AddSize(rOut); + } + + // Write section sizes + u32 AreaEnd = rOut.Tell(); + rOut.Seek(Cooker.mSectionSizesOffset, SEEK_SET); + Cooker.mSectionMgr.WriteSizes(rOut); + rOut.Seek(AreaEnd, SEEK_SET); +} + u32 CAreaCooker::GetMREAVersion(EGame version) { switch (version) diff --git a/src/Core/Resource/Cooker/CAreaCooker.h b/src/Core/Resource/Cooker/CAreaCooker.h index 1cb3d3d8..e6c6e371 100644 --- a/src/Core/Resource/Cooker/CAreaCooker.h +++ b/src/Core/Resource/Cooker/CAreaCooker.h @@ -1,13 +1,35 @@ #ifndef CAREACOOKER_H #define CAREACOOKER_H +#include "CSectionMgrOut.h" +#include "Core/Resource/CGameArea.h" #include "Core/Resource/EGame.h" -#include +#include class CAreaCooker { + TResPtr mpArea; + EGame mVersion; + + CSectionMgrOut mSectionMgr; + u32 mSectionSizesOffset; + + u32 mGeometrySecNum; + u32 mSclySecNum; + u32 mCollisionSecNum; + u32 mUnknownSecNum; + u32 mLightsSecNum; + u32 mVisiSecNum; + u32 mPathSecNum; + u32 mArotSecNum; + CAreaCooker(); + void DetermineSectionNumbers(); + void WritePrimeHeader(IOutputStream& rOut); + void WritePrimeSCLY(IOutputStream& rOut); + public: + static void WriteCookedArea(CGameArea *pArea, IOutputStream& rOut); static u32 GetMREAVersion(EGame version); }; diff --git a/src/Core/Resource/Cooker/CScriptCooker.cpp b/src/Core/Resource/Cooker/CScriptCooker.cpp new file mode 100644 index 00000000..ed952ea9 --- /dev/null +++ b/src/Core/Resource/Cooker/CScriptCooker.cpp @@ -0,0 +1,170 @@ +#include "CScriptCooker.h" + +void CScriptCooker::WriteProperty(IProperty *pProp) +{ + switch (pProp->Type()) + { + + case eBoolProperty: + { + TBoolProperty *pBoolCast = static_cast(pProp); + mpSCLY->WriteByte(pBoolCast->Get() ? 1 : 0); + break; + } + + case eByteProperty: + { + TByteProperty *pByteCast = static_cast(pProp); + mpSCLY->WriteByte(pByteCast->Get()); + break; + } + + case eShortProperty: + { + TShortProperty *pShortCast = static_cast(pProp); + mpSCLY->WriteShort(pShortCast->Get()); + break; + } + + case eLongProperty: + { + TLongProperty *pLongCast = static_cast(pProp); + mpSCLY->WriteLong(pLongCast->Get()); + break; + } + + case eEnumProperty: + { + TEnumProperty *pEnumCast = static_cast(pProp); + CEnumTemplate *pEnumTemp = static_cast(pEnumCast->Template()); + u32 ID = pEnumTemp->EnumeratorID(pEnumCast->Get()); + mpSCLY->WriteLong(ID); + break; + } + + case eBitfieldProperty: + { + TBitfieldProperty *pBitfieldCast = static_cast(pProp); + mpSCLY->WriteLong(pBitfieldCast->Get()); + break; + } + + case eFloatProperty: + { + TFloatProperty *pFloatCast = static_cast(pProp); + mpSCLY->WriteFloat(pFloatCast->Get()); + break; + } + + case eStringProperty: + { + TStringProperty *pStringCast = static_cast(pProp); + mpSCLY->WriteString(pStringCast->Get().ToStdString()); + break; + } + + case eVector3Property: + { + TVector3Property *pVectorCast = static_cast(pProp); + pVectorCast->Get().Write(*mpSCLY); + break; + } + + case eColorProperty: + { + TColorProperty *pColorCast = static_cast(pProp); + pColorCast->Get().Write(*mpSCLY, false); + break; + } + + case eFileProperty: + { + TFileProperty *pFileCast = static_cast(pProp); + if (mVersion <= eEchoes) + mpSCLY->WriteLong(pFileCast->Get().ID().ToLong()); + else + mpSCLY->WriteLongLong(pFileCast->Get().ID().ToLongLong()); + break; + } + + case eCharacterProperty: + { + TCharacterProperty *pCharCast = static_cast(pProp); + pCharCast->Get().Write(*mpSCLY); + break; + } + + case eStructProperty: + case eArrayProperty: + { + CPropertyStruct *pStruct = static_cast(pProp); + CStructTemplate *pTemp = static_cast(pStruct->Template()); + + if (!pTemp->IsSingleProperty() || pProp->Type() == eArrayProperty) + mpSCLY->WriteLong(pStruct->Count()); + + for (u32 iProp = 0; iProp < pStruct->Count(); iProp++) + WriteProperty(pStruct->PropertyByIndex(iProp)); + + break; + } + + } +} + +void CScriptCooker::WriteLayerMP1(CScriptLayer *pLayer) +{ + u32 LayerStart = mpSCLY->Tell(); + mpSCLY->WriteByte(0); // Unknown value + mpSCLY->WriteLong(pLayer->GetNumObjects()); + + for (u32 iInst = 0; iInst < pLayer->GetNumObjects(); iInst++) + { + CScriptObject *pInstance = pLayer->ObjectByIndex(iInst); + WriteInstanceMP1(pInstance); + } + + u32 LayerSize = mpSCLY->Tell() - LayerStart; + u32 NumPadBytes = 32 - (LayerSize % 32); + if (NumPadBytes == 32) NumPadBytes = 0; + + for (u32 iPad = 0; iPad < NumPadBytes; iPad++) + mpSCLY->WriteByte(0); +} + +void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance) +{ + mpSCLY->WriteByte((u8) pInstance->ObjectTypeID()); + + u32 SizeOffset = mpSCLY->Tell(); + mpSCLY->WriteLong(0); + u32 InstanceStart = mpSCLY->Tell(); + + mpSCLY->WriteLong(pInstance->InstanceID()); + mpSCLY->WriteLong(pInstance->NumOutLinks()); + + for (u32 iLink = 0; iLink < pInstance->NumOutLinks(); iLink++) + { + const SLink& rkLink = pInstance->OutLink(iLink); + mpSCLY->WriteLong(rkLink.State); + mpSCLY->WriteLong(rkLink.Message); + mpSCLY->WriteLong(rkLink.ObjectID); + } + + WriteProperty(pInstance->Properties()); + u32 InstanceEnd = mpSCLY->Tell(); + u32 InstanceSize = InstanceEnd - InstanceStart; + + mpSCLY->Seek(SizeOffset, SEEK_SET); + mpSCLY->WriteLong(InstanceSize); + mpSCLY->Seek(InstanceEnd, SEEK_SET); +} + +// ************ STATIC ************ +void CScriptCooker::WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut) +{ + CScriptCooker Cooker; + Cooker.mpSCLY = &rOut; + Cooker.mVersion = Game; + Cooker.WriteLayerMP1(pLayer); +} diff --git a/src/Core/Resource/Cooker/CScriptCooker.h b/src/Core/Resource/Cooker/CScriptCooker.h new file mode 100644 index 00000000..9e7e5b85 --- /dev/null +++ b/src/Core/Resource/Cooker/CScriptCooker.h @@ -0,0 +1,23 @@ +#ifndef CSCRIPTCOOKER_H +#define CSCRIPTCOOKER_H + +#include "Core/Resource/EGame.h" +#include "Core/Resource/Script/CScriptLayer.h" +#include "Core/Resource/Script/CScriptObject.h" +#include + +class CScriptCooker +{ + IOutputStream *mpSCLY; + EGame mVersion; + + CScriptCooker() {} + void WriteProperty(IProperty *pProp); + void WriteLayerMP1(CScriptLayer *pLayer); + void WriteInstanceMP1(CScriptObject *pInstance); + +public: + static void WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut); +}; + +#endif // CSCRIPTCOOKER_H diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index 8ad592c0..4ddd48e1 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -59,26 +59,27 @@ void CAreaLoader::ReadHeaderPrime() mPathBlockNum = mpMREA->ReadLong(); mOctreeBlockNum = mpMREA->ReadLong(); - mBlockMgr = new CBlockMgrIn(mNumBlocks, mpMREA); + mpSectionMgr = new CSectionMgrIn(mNumBlocks, mpMREA); mpMREA->SeekToBoundary(32); - mBlockMgr->Init(); + mpSectionMgr->Init(); + LoadSectionDataBuffers(); } void CAreaLoader::ReadGeometryPrime() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP1/MP2)"); - mBlockMgr->ToBlock(mGeometryBlockNum); + mpSectionMgr->ToSection(mGeometryBlockNum); // Materials mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion); - mBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Geometry std::vector FileModels; for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++) { - CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, mVersion); + CModel *pModel = CModelLoader::LoadWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, mVersion); FileModels.push_back(pModel); if (mVersion <= ePrime) @@ -95,8 +96,8 @@ void CAreaLoader::ReadGeometryPrime() pModel->GetSurface(iSurf)->MeshID = mpMREA->ReadShort(); } - mBlockMgr->ToNextBlock(); - mBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); + mpSectionMgr->ToNextSection(); } } @@ -116,7 +117,7 @@ void CAreaLoader::ReadGeometryPrime() void CAreaLoader::ReadSCLYPrime() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)"); - mBlockMgr->ToBlock(mScriptLayerBlockNum); + mpSectionMgr->ToSection(mScriptLayerBlockNum); CFourCC SCLY(*mpMREA); if (SCLY != "SCLY") @@ -154,7 +155,7 @@ void CAreaLoader::ReadSCLYPrime() void CAreaLoader::ReadLightsPrime() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP1/MP2)"); - mBlockMgr->ToBlock(mLightsBlockNum); + mpSectionMgr->ToSection(mLightsBlockNum); u32 babedead = mpMREA->ReadLong(); if (babedead != 0xbabedead) return; @@ -258,7 +259,7 @@ void CAreaLoader::ReadHeaderEchoes() if (mVersion == eEchoes) mClusters.resize(mpMREA->ReadLong()); mpMREA->SeekToBoundary(32); - mBlockMgr = new CBlockMgrIn(numBlocks, mpMREA); + mpSectionMgr = new CSectionMgrIn(numBlocks, mpMREA); mpMREA->SeekToBoundary(32); if (mVersion == eEchoes) @@ -267,13 +268,14 @@ void CAreaLoader::ReadHeaderEchoes() Decompress(); } - mBlockMgr->Init(); + mpSectionMgr->Init(); + LoadSectionDataBuffers(); } void CAreaLoader::ReadSCLYEchoes() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)"); - mBlockMgr->ToBlock(mScriptLayerBlockNum); + mpSectionMgr->ToSection(mScriptLayerBlockNum); // SCLY for (u32 l = 0; l < mNumLayers; l++) @@ -283,11 +285,11 @@ void CAreaLoader::ReadSCLYEchoes() if (pLayer) mpArea->mScriptLayers.push_back(pLayer); - mBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); } // SCGN - mBlockMgr->ToBlock(mScriptGeneratorBlockNum); + mpSectionMgr->ToSection(mScriptGeneratorBlockNum); CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); if (pLayer) @@ -309,7 +311,7 @@ void CAreaLoader::ReadHeaderCorruption() u32 SectionNumberCount = mpMREA->ReadLong(); mpMREA->SeekToBoundary(32); - mBlockMgr = new CBlockMgrIn(NumSections, mpMREA); + mpSectionMgr = new CSectionMgrIn(NumSections, mpMREA); mpMREA->SeekToBoundary(32); ReadCompressedBlocks(); @@ -336,17 +338,18 @@ void CAreaLoader::ReadHeaderCorruption() mpMREA->SeekToBoundary(32); Decompress(); - mBlockMgr->Init(); + mpSectionMgr->Init(); + LoadSectionDataBuffers(); } void CAreaLoader::ReadGeometryCorruption() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA world geometry (MP3)"); - mBlockMgr->ToBlock(mGeometryBlockNum); + mpSectionMgr->ToSection(mGeometryBlockNum); // Materials mpArea->mMaterialSet = CMaterialLoader::LoadMaterialSet(*mpMREA, mVersion); - mBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Geometry std::vector FileModels; @@ -355,14 +358,14 @@ void CAreaLoader::ReadGeometryCorruption() for (u32 iMesh = 0; iMesh < mNumMeshes; iMesh++) { - CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mBlockMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion); + CModel *pWorldModel = CModelLoader::LoadCorruptionWorldModel(*mpMREA, *mpSectionMgr, *mpArea->mMaterialSet, CurWOBJSection, CurGPUSection, mVersion); FileModels.push_back(pWorldModel); CurWOBJSection += 4; - CurGPUSection = mBlockMgr->CurrentBlock(); + CurGPUSection = mpSectionMgr->CurrentSection(); // Load surface mesh IDs - mBlockMgr->ToBlock(CurWOBJSection - 2); + mpSectionMgr->ToSection(CurWOBJSection - 2); u16 NumSurfaces = mpMREA->ReadShort(); for (u32 iSurf = 0; iSurf < NumSurfaces; iSurf++) @@ -384,7 +387,7 @@ void CAreaLoader::ReadGeometryCorruption() void CAreaLoader::ReadLightsCorruption() { Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA dynamic lights (MP3)"); - mBlockMgr->ToBlock(mLightsBlockNum); + mpSectionMgr->ToSection(mLightsBlockNum); u32 babedead = mpMREA->ReadLong(); if (babedead != 0xbabedead) return; @@ -522,21 +525,35 @@ void CAreaLoader::Decompress() TString Source = mpMREA->GetSourceString(); mpMREA = new CMemoryInStream(mDecmpBuffer, mTotalDecmpSize, IOUtil::eBigEndian); mpMREA->SetSourceString(Source.ToStdString()); - mBlockMgr->SetInputStream(mpMREA); + mpSectionMgr->SetInputStream(mpMREA); mHasDecompressedBuffer = true; } +void CAreaLoader::LoadSectionDataBuffers() +{ + mpArea->mSectionDataBuffers.resize(mpSectionMgr->NumSections()); + mpSectionMgr->ToSection(0); + + for (u32 iSec = 0; iSec < mpSectionMgr->NumSections(); iSec++) + { + u32 Size = mpSectionMgr->CurrentSectionSize(); + mpArea->mSectionDataBuffers[iSec].resize(Size); + mpMREA->ReadBytes(mpArea->mSectionDataBuffers[iSec].data(), mpArea->mSectionDataBuffers[iSec].size()); + mpSectionMgr->ToNextSection(); + } +} + void CAreaLoader::ReadCollision() { Log::FileWrite(mpMREA->GetSourceString(), "Reading collision (MP1/MP2/MP3)"); - mBlockMgr->ToBlock(mCollisionBlockNum); + mpSectionMgr->ToSection(mCollisionBlockNum); mpArea->mCollision = CCollisionLoader::LoadAreaCollision(*mpMREA); } void CAreaLoader::ReadEGMC() { Log::FileWrite(mpMREA->GetSourceString(), "Reading EGMC"); - mBlockMgr->ToBlock(mEGMCBlockNum); + mpSectionMgr->ToSection(mEGMCBlockNum); CUniqueID EGMC(*mpMREA, (mVersion <= eEchoes ? e32Bit : e64Bit)); mpArea->mpPoiToWorldMap = gResCache.GetResource(EGMC, "EGMC"); } @@ -665,7 +682,7 @@ CGameArea* CAreaLoader::LoadMREA(IInputStream& MREA) } // Cleanup - delete Loader.mBlockMgr; + delete Loader.mpSectionMgr; return Loader.mpArea; } diff --git a/src/Core/Resource/Factory/CAreaLoader.h b/src/Core/Resource/Factory/CAreaLoader.h index 7312161c..15f69a28 100644 --- a/src/Core/Resource/Factory/CAreaLoader.h +++ b/src/Core/Resource/Factory/CAreaLoader.h @@ -1,7 +1,7 @@ #ifndef CAREALOADER_H #define CAREALOADER_H -#include "CBlockMgrIn.h" +#include "CSectionMgrIn.h" #include "Core/Resource/Script/SConnection.h" #include "Core/Resource/CGameArea.h" #include "Core/Resource/EGame.h" @@ -16,7 +16,7 @@ class CAreaLoader // Area data TResPtr mpArea; IInputStream *mpMREA; - CBlockMgrIn *mBlockMgr; + CSectionMgrIn *mpSectionMgr; EGame mVersion; u32 mNumMeshes; u32 mNumLayers; @@ -74,6 +74,7 @@ class CAreaLoader // Common void ReadCompressedBlocks(); void Decompress(); + void LoadSectionDataBuffers(); void ReadCollision(); void ReadEGMC(); void SetUpObjects(); diff --git a/src/Core/Resource/Factory/CBlockMgr.cpp b/src/Core/Resource/Factory/CBlockMgr.cpp deleted file mode 100644 index 5431c1ce..00000000 --- a/src/Core/Resource/Factory/CBlockMgr.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "CBlockMgrIn.h" - -CBlockMgrIn::CBlockMgrIn(unsigned long count, IInputStream* src) -{ - mpInputStream = src; - mBlockCount = count; - mBlockSizes.resize(count); - - for (unsigned long b = 0; b < count; b++) - mBlockSizes[b] = src->ReadLong(); -} - -void CBlockMgrIn::Init() -{ - // Initialize the block manager; this marks the start of the first block - mCurBlock = 0; - mCurBlockStart = mpInputStream->Tell(); - mBlocksStart = mpInputStream->Tell(); -} - -void CBlockMgrIn::ToBlock(unsigned long block) -{ - unsigned long offset = mBlocksStart; - for (unsigned long b = 0; b < block; b++) - offset += mBlockSizes[b]; - - mpInputStream->Seek(offset, SEEK_SET); - - mCurBlock = block; - mCurBlockStart = mpInputStream->Tell(); -} - -void CBlockMgrIn::ToNextBlock() -{ - mpInputStream->Seek(mCurBlockStart + mBlockSizes[mCurBlock], SEEK_SET); - mCurBlock++; - mCurBlockStart = mpInputStream->Tell(); -} - -long CBlockMgrIn::NextOffset() -{ - return mCurBlockStart + mBlockSizes[mCurBlock]; -} - -long CBlockMgrIn::CurrentBlock() -{ - return mCurBlock; -} - -long CBlockMgrIn::CurrentBlockSize() -{ - return mBlockSizes[mCurBlock]; -} - -void CBlockMgrIn::SetInputStream(IInputStream *in) -{ - mpInputStream = in; -} diff --git a/src/Core/Resource/Factory/CBlockMgrIn.h b/src/Core/Resource/Factory/CBlockMgrIn.h deleted file mode 100644 index 6ffecab4..00000000 --- a/src/Core/Resource/Factory/CBlockMgrIn.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CBLOCKMGRIN_H -#define CBLOCKMGRIN_H - -#include -#include - -// The purpose of this class is to keep track of data block navigation - required to read CMDL and MREA files correctly -class CBlockMgrIn -{ - IInputStream *mpInputStream; - unsigned long mBlockCount; - std::vector mBlockSizes; - unsigned long mCurBlock; - unsigned long mCurBlockStart; - unsigned long mBlocksStart; - -public: - CBlockMgrIn(unsigned long count, IInputStream* src); - void Init(); - void ToBlock(unsigned long block); - void ToNextBlock(); - long NextOffset(); - long CurrentBlock(); - long CurrentBlockSize(); - void SetInputStream(IInputStream *in); -}; - -#endif // CBLOCKMGRIN_H diff --git a/src/Core/Resource/Factory/CModelLoader.cpp b/src/Core/Resource/Factory/CModelLoader.cpp index 6f6c557e..5715c07b 100644 --- a/src/Core/Resource/Factory/CModelLoader.cpp +++ b/src/Core/Resource/Factory/CModelLoader.cpp @@ -18,7 +18,7 @@ void CModelLoader::LoadWorldMeshHeader(IInputStream &Model) // I don't really have any need for most of this data, so Model.Seek(0x34, SEEK_CUR); mAABox = CAABox(Model); - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); } void CModelLoader::LoadAttribArrays(IInputStream& Model) @@ -26,7 +26,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model) // Positions if (mFlags & eShortPositions) // Shorts (DKCR only) { - mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0x6); + mPositions.resize(mpSectionMgr->CurrentSectionSize() / 0x6); float Divisor = 8192.f; // Might be incorrect! Needs verification via size comparison. for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++) @@ -40,18 +40,18 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model) else // Floats { - mPositions.resize(mpBlockMgr->CurrentBlockSize() / 0xC); + mPositions.resize(mpSectionMgr->CurrentSectionSize() / 0xC); for (u32 iVtx = 0; iVtx < mPositions.size(); iVtx++) mPositions[iVtx] = CVector3f(Model); } - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Normals if (mFlags & eShortNormals) // Shorts { - mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0x6); + mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0x6); float Divisor = (mVersion < eReturns) ? 32768.f : 16384.f; for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++) @@ -64,35 +64,35 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model) } else // Floats { - mNormals.resize(mpBlockMgr->CurrentBlockSize() / 0xC); + mNormals.resize(mpSectionMgr->CurrentSectionSize() / 0xC); for (u32 iVtx = 0; iVtx < mNormals.size(); iVtx++) mNormals[iVtx] = CVector3f(Model); } - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Colors - mColors.resize(mpBlockMgr->CurrentBlockSize() / 4); + mColors.resize(mpSectionMgr->CurrentSectionSize() / 4); for (u32 iVtx = 0; iVtx < mColors.size(); iVtx++) mColors[iVtx] = CColor(Model); - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Float UVs - mTex0.resize(mpBlockMgr->CurrentBlockSize() / 0x8); + mTex0.resize(mpSectionMgr->CurrentSectionSize() / 0x8); for (u32 iVtx = 0; iVtx < mTex0.size(); iVtx++) mTex0[iVtx] = CVector2f(Model); - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); // Short UVs if (mFlags & eHasTex1) { - mTex1.resize(mpBlockMgr->CurrentBlockSize() / 0x4); + mTex1.resize(mpSectionMgr->CurrentSectionSize() / 0x4); float Divisor = (mVersion < eReturns) ? 32768.f : 8192.f; for (u32 iVtx = 0; iVtx < mTex1.size(); iVtx++) @@ -102,7 +102,7 @@ void CModelLoader::LoadAttribArrays(IInputStream& Model) mTex1[iVtx] = CVector2f(x, y); } - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); } } @@ -114,7 +114,7 @@ void CModelLoader::LoadSurfaceOffsets(IInputStream& Model) for (u32 iSurf = 0; iSurf < mSurfaceCount; iSurf++) mSurfaceOffsets[iSurf] = Model.ReadLong(); - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); } SSurface* CModelLoader::LoadSurface(IInputStream& Model) @@ -132,7 +132,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model) // Primitive table u8 Flag = Model.ReadByte(); - u32 NextSurface = mpBlockMgr->NextOffset(); + u32 NextSurface = mpSectionMgr->NextOffset(); while ((Flag != 0) && ((u32) Model.Tell() < NextSurface)) { @@ -225,7 +225,7 @@ SSurface* CModelLoader::LoadSurface(IInputStream& Model) Flag = Model.ReadByte(); } // Primitive table end - mpBlockMgr->ToNextBlock(); + mpSectionMgr->ToNextSection(); return pSurf; } @@ -451,9 +451,9 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL) CModel *pModel = new CModel(); Loader.mpModel = pModel; - Loader.mpBlockMgr = new CBlockMgrIn(BlockCount, &CMDL); + Loader.mpSectionMgr = new CSectionMgrIn(BlockCount, &CMDL); CMDL.SeekToBoundary(32); - Loader.mpBlockMgr->Init(); + Loader.mpSectionMgr->Init(); // Materials Loader.mMaterials.resize(MatSetCount); @@ -462,12 +462,12 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL) Loader.mMaterials[iMat] = CMaterialLoader::LoadMaterialSet(CMDL, Loader.mVersion); if (Loader.mVersion < eCorruptionProto) - Loader.mpBlockMgr->ToNextBlock(); + Loader.mpSectionMgr->ToNextSection(); } pModel->mMaterialSets = Loader.mMaterials; pModel->mHasOwnMaterials = true; - if (Loader.mVersion >= eCorruptionProto) Loader.mpBlockMgr->ToNextBlock(); + if (Loader.mVersion >= eCorruptionProto) Loader.mpSectionMgr->ToNextSection(); // Mesh Loader.LoadAttribArrays(CMDL); @@ -486,14 +486,14 @@ CModel* CModelLoader::LoadCMDL(IInputStream& CMDL) pModel->mHasOwnSurfaces = true; // Cleanup - delete Loader.mpBlockMgr; + delete Loader.mpSectionMgr; return pModel; } -CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version) +CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version) { CModelLoader Loader; - Loader.mpBlockMgr = &BlockMgr; + Loader.mpSectionMgr = &BlockMgr; Loader.mVersion = Version; Loader.mFlags = eShortNormals; if (Version != eCorruptionProto) Loader.mFlags |= eHasTex1; @@ -523,10 +523,10 @@ CModel* CModelLoader::LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, return pModel; } -CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CBlockMgrIn &BlockMgr, CMaterialSet &MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version) +CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CSectionMgrIn &BlockMgr, CMaterialSet &MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version) { CModelLoader Loader; - Loader.mpBlockMgr = &BlockMgr; + Loader.mpSectionMgr = &BlockMgr; Loader.mVersion = Version; Loader.mFlags = eShortNormals; Loader.mMaterials.resize(1); @@ -534,10 +534,10 @@ CModel* CModelLoader::LoadCorruptionWorldModel(IInputStream &MREA, CBlockMgrIn & if (Version == eReturns) Loader.mFlags |= eHasTex1; // Corruption/DKCR MREAs split the mesh header and surface offsets away from the actual geometry data so I need two section numbers to read it - BlockMgr.ToBlock(HeaderSecNum); + BlockMgr.ToSection(HeaderSecNum); Loader.LoadWorldMeshHeader(MREA); Loader.LoadSurfaceOffsets(MREA); - BlockMgr.ToBlock(GPUSecNum); + BlockMgr.ToSection(GPUSecNum); Loader.LoadAttribArrays(MREA); CModel *pModel = new CModel(); diff --git a/src/Core/Resource/Factory/CModelLoader.h b/src/Core/Resource/Factory/CModelLoader.h index f5ee132a..b9fea32c 100644 --- a/src/Core/Resource/Factory/CModelLoader.h +++ b/src/Core/Resource/Factory/CModelLoader.h @@ -1,7 +1,7 @@ #ifndef CMODELLOADER_H #define CMODELLOADER_H -#include "CBlockMgrIn.h" +#include "CSectionMgrIn.h" #include "Core/Resource/Model/CBasicModel.h" #include "Core/Resource/Model/CModel.h" #include "Core/Resource/CResCache.h" @@ -27,7 +27,7 @@ public: private: TResPtr mpModel; std::vector mMaterials; - CBlockMgrIn *mpBlockMgr; + CSectionMgrIn *mpSectionMgr; CAABox mAABox; EGame mVersion; @@ -57,8 +57,8 @@ private: public: static CModel* LoadCMDL(IInputStream& CMDL); - static CModel* LoadWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version); - static CModel* LoadCorruptionWorldModel(IInputStream& MREA, CBlockMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version); + static CModel* LoadWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, EGame Version); + static CModel* LoadCorruptionWorldModel(IInputStream& MREA, CSectionMgrIn& BlockMgr, CMaterialSet& MatSet, u32 HeaderSecNum, u32 GPUSecNum, EGame Version); static void BuildWorldMeshes(const std::vector& rkIn, std::vector& rOut, bool DeleteInputModels); static CModel* ImportAssimpNode(const aiNode *pNode, const aiScene *pScene, CMaterialSet& matSet); static EGame GetFormatVersion(u32 Version); diff --git a/src/Core/Resource/Factory/CSectionMgrIn.h b/src/Core/Resource/Factory/CSectionMgrIn.h new file mode 100644 index 00000000..b52cb774 --- /dev/null +++ b/src/Core/Resource/Factory/CSectionMgrIn.h @@ -0,0 +1,60 @@ +#ifndef CSECTIONMGRIN_H +#define CSECTIONMGRIN_H + +#include +#include +#include + +// The purpose of this class is to keep track of data block navigation - required to read CMDL and MREA files correctly +class CSectionMgrIn +{ + IInputStream *mpInputStream; + std::vector mSectionSizes; + u32 mCurSec; + u32 mCurSecStart; + u32 mSecsStart; + +public: + CSectionMgrIn(u32 Count, IInputStream* pSrc) + : mpInputStream(pSrc) + { + mSectionSizes.resize(Count); + + for (u32 iSec = 0; iSec < Count; iSec++) + mSectionSizes[iSec] = pSrc->ReadLong(); + } + + inline void Init() + { + // Initializes the block manager and marks the start of the first block + mCurSec = 0; + mCurSecStart = mpInputStream->Tell(); + mSecsStart = mCurSecStart; + } + + void ToSection(u32 SecNum) + { + u32 Offset = mSecsStart; + for (u32 iSec = 0; iSec < SecNum; iSec++) + Offset += mSectionSizes[iSec]; + + mpInputStream->Seek(Offset, SEEK_SET); + mCurSec = SecNum; + mCurSecStart = mpInputStream->Tell(); + } + + inline void ToNextSection() + { + mCurSecStart += mSectionSizes[mCurSec]; + mpInputStream->Seek(mCurSecStart, SEEK_SET); + mCurSec++; + } + + inline u32 NextOffset() { return mCurSecStart + mSectionSizes[mCurSec]; } + inline u32 CurrentSection() { return mCurSec; } + inline u32 CurrentSectionSize() { return mSectionSizes[mCurSec]; } + inline u32 NumSections() { return mSectionSizes.size(); } + inline void SetInputStream(IInputStream *pIn) { mpInputStream = pIn; } +}; + +#endif // CSECTIONMGRIN_H diff --git a/src/Editor/WorldEditor/CWorldEditor.cpp b/src/Editor/WorldEditor/CWorldEditor.cpp index 3a7d21b0..b9335e64 100644 --- a/src/Editor/WorldEditor/CWorldEditor.cpp +++ b/src/Editor/WorldEditor/CWorldEditor.cpp @@ -11,13 +11,15 @@ #include "Editor/UICommon.h" #include +#include #include #include #include -#include -#include #include +#include +#include +#include CWorldEditor::CWorldEditor(QWidget *parent) : INodeEditor(parent), @@ -528,3 +530,11 @@ void CWorldEditor::on_ActionEditPoiToWorldMap_triggered() mpPoiDialog->show(); } } + +void CWorldEditor::on_ActionSave_triggered() +{ + TString Out = mpArea->FullSource(); + CFileOutStream MREA(Out.ToStdString(), IOUtil::eBigEndian); + CAreaCooker::WriteCookedArea(mpArea, MREA); + QMessageBox::information(this, "Success", "Successfully saved area to " + TO_QSTRING(Out)); +} diff --git a/src/Editor/WorldEditor/CWorldEditor.h b/src/Editor/WorldEditor/CWorldEditor.h index ca33eb11..598f8d99 100644 --- a/src/Editor/WorldEditor/CWorldEditor.h +++ b/src/Editor/WorldEditor/CWorldEditor.h @@ -85,6 +85,7 @@ private slots: void on_ActionSelectAll_triggered(); void on_ActionInvertSelection_triggered(); void on_ActionEditPoiToWorldMap_triggered(); + void on_ActionSave_triggered(); }; #endif // CWORLDEDITOR_H diff --git a/src/Math/CTransform4f.cpp b/src/Math/CTransform4f.cpp index 97d00e91..fbb6f7e6 100644 --- a/src/Math/CTransform4f.cpp +++ b/src/Math/CTransform4f.cpp @@ -54,6 +54,12 @@ CTransform4f::CTransform4f(CVector3f, CVector3f, CVector3f) { } +void CTransform4f::Write(IOutputStream& rOut) +{ + for (int iFlt = 0; iFlt < 12; iFlt++) + rOut.WriteFloat(_m[iFlt]); +} + // ************ MATH ************ void CTransform4f::Translate(CVector3f Translation) { diff --git a/src/Math/CTransform4f.h b/src/Math/CTransform4f.h index 6e9e33a0..dbf73fd7 100644 --- a/src/Math/CTransform4f.h +++ b/src/Math/CTransform4f.h @@ -27,6 +27,7 @@ public: float m20, float m21, float m22, float m23); CTransform4f(CVector3f Position, CQuaternion Rotation, CVector3f Scale); CTransform4f(CVector3f Position, CVector3f Rotation, CVector3f Scale); + void Write(IOutputStream& rOut); // Math void Translate(CVector3f Translation);