diff --git a/src/Core/Resource/CAnimationParameters.cpp b/src/Core/Resource/CAnimationParameters.cpp index fac2accf..ab7f8558 100644 --- a/src/Core/Resource/CAnimationParameters.cpp +++ b/src/Core/Resource/CAnimationParameters.cpp @@ -1,71 +1,71 @@ #include "CAnimationParameters.h" #include "CAnimSet.h" #include "CResCache.h" +#include "CResourceInfo.h" #include #include CAnimationParameters::CAnimationParameters() { mGame = ePrime; - mpCharSet = nullptr; mNodeIndex = 0; mUnknown1 = 0; mUnknown2 = 0; mUnknown3 = 0; - mUnknown4 = 0; } CAnimationParameters::CAnimationParameters(IInputStream& SCLY, EGame Game) { mGame = Game; - mpCharSet = nullptr; mNodeIndex = 0; mUnknown1 = 0; mUnknown2 = 0; mUnknown3 = 0; - mUnknown4 = 0; if (Game <= eEchoes) { - u32 AnimSetID = SCLY.ReadLong(); + mCharacter = CResourceInfo(SCLY.ReadLong(), "ANCS"); mNodeIndex = SCLY.ReadLong(); mUnknown1 = SCLY.ReadLong(); - - mpCharSet = gResCache.GetResource(AnimSetID, "ANCS"); } else if (Game <= eCorruption) { - u64 CharID = SCLY.ReadLongLong(); + mCharacter = CResourceInfo(SCLY.ReadLongLong(), "CHAR"); mUnknown1 = SCLY.ReadLong(); - - mpCharSet = gResCache.GetResource(CharID, "CHAR"); } else if (Game == eReturns) { - SCLY.Seek(-6, SEEK_CUR); - u32 Offset = SCLY.Tell(); - u32 PropID = SCLY.ReadLong(); - SCLY.Seek(2, SEEK_CUR); + u8 Flags = SCLY.ReadByte(); - mUnknown1 = (u32) SCLY.ReadByte(); - mUnknown1 &= 0xFF; - - if (mUnknown1 == 0x60) + // 0x80 - CharacterAnimationSet is empty. + if (Flags & 0x80) { - u64 charID = SCLY.ReadLongLong(); - mUnknown2 = SCLY.ReadLong(); - mUnknown3 = SCLY.ReadLong(); - mUnknown4 = SCLY.ReadLong(); - - mpCharSet = gResCache.GetResource(charID, "CHAR"); + mUnknown1 = -1; + mUnknown2 = 0; + mUnknown3 = 0; + return; } - else if (mUnknown1 != 0x80) + mCharacter = CResourceInfo(SCLY.ReadLongLong(), "CHAR"); + + // 0x20 - Default Anim is present + if (Flags & 0x20) + mUnknown1 = SCLY.ReadLong(); + else + mUnknown1 = -1; + + // 0x40 - Two-value struct is present + if (Flags & 0x40) { - Log::FileError(SCLY.GetSourceString(), Offset, - "Unexpected AnimationParameters byte: " + TString::HexString(mUnknown1, true, true, 2) + " (property " + TString::HexString(PropID, true, true, 8) + ")"); + mUnknown2 = SCLY.ReadLong(); + mUnknown3 = SCLY.ReadLong(); + } + else + { + mUnknown2 = 0; + mUnknown3 = 0; } } } @@ -74,9 +74,9 @@ void CAnimationParameters::Write(IOutputStream& rSCLY) { if (mGame <= eEchoes) { - if (mpCharSet) + if (mCharacter.IsValid()) { - rSCLY.WriteLong(AnimSet()->ResID().ToLong()); + rSCLY.WriteLong(mCharacter.ID().ToLong()); rSCLY.WriteLong(mNodeIndex); rSCLY.WriteLong(mUnknown1); } @@ -87,26 +87,72 @@ void CAnimationParameters::Write(IOutputStream& rSCLY) rSCLY.WriteLong(0xFFFFFFFF); } } + + else if (mGame <= eCorruption) + { + if (mCharacter.IsValid()) + { + rSCLY.WriteLongLong(mCharacter.ID().ToLongLong()); + rSCLY.WriteLong(mUnknown1); + } + + else + { + rSCLY.WriteLongLong(CUniqueID::skInvalidID64.ToLongLong()); + rSCLY.WriteLong(0xFFFFFFFF); + } + } + + else + { + if (!mCharacter.IsValid()) + rSCLY.WriteByte((u8) 0x80); + + else + { + u8 Flag = 0; + if (mUnknown1 != -1) Flag |= 0x20; + if (mUnknown2 != 0 || mUnknown3 != 0) Flag |= 0x40; + + rSCLY.WriteByte(Flag); + rSCLY.WriteLongLong(mCharacter.ID().ToLongLong()); + + if (Flag & 0x20) + rSCLY.WriteLong(mUnknown1); + + if (Flag & 0x40) + { + rSCLY.WriteLong(mUnknown2); + rSCLY.WriteLong(mUnknown3); + } + } + } } CModel* CAnimationParameters::GetCurrentModel(s32 NodeIndex /*= -1*/) { - if (!mpCharSet) return nullptr; - if (mpCharSet->Type() != eAnimSet) return nullptr; + if (!mCharacter.IsValid()) return nullptr; + + CAnimSet *pSet = (CAnimSet*) mCharacter.Load(); + if (!pSet) return nullptr; + if (pSet->Type() != eAnimSet) return nullptr; if (NodeIndex == -1) NodeIndex = mNodeIndex; - if (mpCharSet->getNodeCount() <= (u32) NodeIndex) return nullptr; - return mpCharSet->getNodeModel(NodeIndex); + if (pSet->getNodeCount() <= (u32) NodeIndex) return nullptr; + return pSet->getNodeModel(NodeIndex); } TString CAnimationParameters::GetCurrentCharacterName(s32 NodeIndex /*= -1*/) { - if (!mpCharSet) return ""; - if (mpCharSet->Type() != eAnimSet) return ""; + if (!mCharacter.IsValid()) return ""; + + CAnimSet *pSet = (CAnimSet*) mCharacter.Load(); + if (!pSet) return ""; + if (pSet->Type() != eAnimSet) return ""; if (NodeIndex == -1) NodeIndex = mNodeIndex; - if (mpCharSet->getNodeCount() <= (u32) NodeIndex) return ""; - return mpCharSet->getNodeName((u32) NodeIndex); + if (pSet->getNodeCount() <= (u32) NodeIndex) return ""; + return pSet->getNodeName((u32) NodeIndex); } // ************ GETTERS ************ @@ -117,7 +163,7 @@ EGame CAnimationParameters::Version() CAnimSet* CAnimationParameters::AnimSet() { - return mpCharSet; + return (CAnimSet*) mCharacter.Load(); } u32 CAnimationParameters::CharacterIndex() @@ -132,21 +178,20 @@ u32 CAnimationParameters::Unknown(u32 Index) case 0: return mUnknown1; case 1: return mUnknown2; case 2: return mUnknown3; - case 3: return mUnknown4; default: return 0; } } // ************ SETTERS ************ -void CAnimationParameters::SetResource(CResource *pRes) +void CAnimationParameters::SetResource(CResourceInfo Res) { - if (!pRes || (pRes->Type() == eAnimSet) || (pRes->Type() == eCharacter)) + if (Res.Type() == "ANCS" || Res.Type() == "CHAR") { - mpCharSet = pRes; + mCharacter = Res; mNodeIndex = 0; } else - Log::Error("Resource with invalid type passed to CAnimationParameters: " + pRes->Source()); + Log::Error("Resource with invalid type passed to CAnimationParameters: " + Res.ToString()); } void CAnimationParameters::SetNodeIndex(u32 Index) @@ -161,6 +206,5 @@ void CAnimationParameters::SetUnknown(u32 Index, u32 Value) case 0: mUnknown1 = Value; case 1: mUnknown2 = Value; case 2: mUnknown3 = Value; - case 3: mUnknown4 = Value; } } diff --git a/src/Core/Resource/CAnimationParameters.h b/src/Core/Resource/CAnimationParameters.h index 6ffaf5f3..97ba3877 100644 --- a/src/Core/Resource/CAnimationParameters.h +++ b/src/Core/Resource/CAnimationParameters.h @@ -2,6 +2,7 @@ #define CANIMATIONPARAMETERS_H #include "CAnimSet.h" +#include "CResourceInfo.h" #include "EGame.h" #include "TResPtr.h" #include "Core/Resource/Model/CModel.h" @@ -9,13 +10,12 @@ class CAnimationParameters { EGame mGame; - TResPtr mpCharSet; + CResourceInfo mCharacter; u32 mNodeIndex; u32 mUnknown1; u32 mUnknown2; u32 mUnknown3; - u32 mUnknown4; public: CAnimationParameters(); @@ -32,7 +32,7 @@ public: u32 Unknown(u32 index); // Setters - void SetResource(CResource *pRes); + void SetResource(CResourceInfo Res); void SetNodeIndex(u32 Index); void SetUnknown(u32 Index, u32 Value); @@ -40,12 +40,11 @@ public: inline bool operator==(const CAnimationParameters& rkOther) const { return ( (mGame == rkOther.mGame) && - (mpCharSet == rkOther.mpCharSet) && + (mCharacter == rkOther.mCharacter) && (mNodeIndex == rkOther.mNodeIndex) && (mUnknown1 == rkOther.mUnknown1) && (mUnknown2 == rkOther.mUnknown2) && - (mUnknown3 == rkOther.mUnknown3) && - (mUnknown4 == rkOther.mUnknown4) ); + (mUnknown3 == rkOther.mUnknown3) ); } }; diff --git a/src/Core/Resource/CGameArea.cpp b/src/Core/Resource/CGameArea.cpp index f6d22aaa..047ee4fe 100644 --- a/src/Core/Resource/CGameArea.cpp +++ b/src/Core/Resource/CGameArea.cpp @@ -7,6 +7,8 @@ CGameArea::CGameArea() : CResource() mVertexCount = 0; mTriangleCount = 0; mTerrainMerged = false; + mOriginalWorldMeshCount = 0; + mUsesCompression = false; mMaterialSet = nullptr; mpGeneratorLayer = nullptr; mCollision = nullptr; diff --git a/src/Core/Resource/CGameArea.h b/src/Core/Resource/CGameArea.h index c6bdffe3..81c68864 100644 --- a/src/Core/Resource/CGameArea.h +++ b/src/Core/Resource/CGameArea.h @@ -29,8 +29,18 @@ 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 + // Data saved from the original file to help on recook std::vector> mSectionDataBuffers; + u32 mOriginalWorldMeshCount; + bool mUsesCompression; + + struct SSectionNumber + { + CFourCC SectionID; + u32 Index; + }; + std::vector mSectionNumbers; + // Geometry CMaterialSet *mMaterialSet; std::vector mTerrainModels; // TerrainModels is the original version of each model; this is currently mainly used in the POI map editor @@ -39,7 +49,6 @@ class CGameArea : public CResource std::vector mScriptLayers; CScriptLayer *mpGeneratorLayer; std::unordered_map mObjectMap; - // Collision CCollisionMeshGroup *mCollision; // Lights diff --git a/src/Core/Resource/CWorld.cpp b/src/Core/Resource/CWorld.cpp index a60b669b..52366b3e 100644 --- a/src/Core/Resource/CWorld.cpp +++ b/src/Core/Resource/CWorld.cpp @@ -25,6 +25,7 @@ void CWorld::SetAreaLayerInfo(CGameArea *pArea, u32 AreaIndex) for (u32 iLyr = 0; iLyr < pArea->GetScriptLayerCount(); iLyr++) { + if (AreaInfo.Layers.size() <= iLyr) break; CScriptLayer *pLayer = pArea->GetScriptLayer(iLyr); SArea::SLayer& LayerInfo = AreaInfo.Layers[iLyr]; diff --git a/src/Core/Resource/Cooker/CAreaCooker.cpp b/src/Core/Resource/Cooker/CAreaCooker.cpp index 266b1168..932a676d 100644 --- a/src/Core/Resource/Cooker/CAreaCooker.cpp +++ b/src/Core/Resource/Cooker/CAreaCooker.cpp @@ -1,80 +1,190 @@ #include "CAreaCooker.h" #include "CScriptCooker.h" +#include #include +const bool gkForceDisableCompression = false; + CAreaCooker::CAreaCooker() { } -void CAreaCooker::DetermineSectionNumbers() +void CAreaCooker::DetermineSectionNumbersPrime() { 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-9 sections (depending on game) plus one section per surface. + u32 GeometrySections = 0; + u32 OriginalMeshCount = mpArea->mOriginalWorldMeshCount; - // 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++) + switch (mVersion) { - CModel *pModel = mpArea->mTerrainModels[iMesh]; - GeometrySections += (7 + pModel->GetSurfaceCount()); + case ePrimeDemo: + case ePrime: + GeometrySections = 1 + (7 * OriginalMeshCount); // Accounting for materials + break; + case eEchoesDemo: + GeometrySections = 2 + (9 * OriginalMeshCount); // Account for materials + AROT + break; + case eEchoes: + GeometrySections = 3 + (9 * OriginalMeshCount); // Acount for materials + AROT + an unknown section + break; } + for (u32 iMesh = 0; iMesh < mpArea->mTerrainModels.size(); iMesh++) + GeometrySections += mpArea->mTerrainModels[iMesh]->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; + u32 SecNum = GeometrySections; + if (mVersion <= ePrime) mAROTSecNum = SecNum++; + if (mVersion >= eEchoesDemo) mFFFFSecNum = SecNum++; + + if (mVersion >= eEchoesDemo) + { + mSCLYSecNum = SecNum; + SecNum += (mVersion >= eEchoes ? mpArea->mScriptLayers.size() : 1); + mSCGNSecNum = SecNum++; + } + else + mSCLYSecNum = SecNum++; + + mCollisionSecNum = SecNum++; + mUnknownSecNum = SecNum++; + mLightsSecNum = SecNum++; + mVISISecNum = SecNum++; + mPATHSecNum = SecNum++; + + if (mVersion >= eEchoesDemo) + { + mPTLASecNum = SecNum++; + mEGMCSecNum = SecNum++; + } } +void CAreaCooker::DetermineSectionNumbersCorruption() +{ + // Because we're copying these from the original file (because not all the numbers + // are present in every file), we don't care about any of these except SCLY and SCGN. + for (u32 iNum = 0; iNum < mpArea->mSectionNumbers.size(); iNum++) + { + CGameArea::SSectionNumber& rNum = mpArea->mSectionNumbers[iNum]; + if (rNum.SectionID == "SOBJ") mSCLYSecNum = rNum.Index; + else if (rNum.SectionID == "SGEN") mSCGNSecNum = rNum.Index; + } +} + +// ************ HEADER ************ void CAreaCooker::WritePrimeHeader(IOutputStream& rOut) { rOut.WriteLong(0xDEADBEEF); rOut.WriteLong(GetMREAVersion(mVersion)); mpArea->mTransform.Write(rOut); - rOut.WriteLong(mpArea->mTerrainModels.size()); + rOut.WriteLong(mpArea->mOriginalWorldMeshCount); + if (mVersion >= eEchoes) rOut.WriteLong(mpArea->mScriptLayers.size()); rOut.WriteLong(mpArea->mSectionDataBuffers.size()); rOut.WriteLong(mGeometrySecNum); - rOut.WriteLong(mSclySecNum); + rOut.WriteLong(mSCLYSecNum); + if (mVersion >= eEchoesDemo) rOut.WriteLong(mSCGNSecNum); rOut.WriteLong(mCollisionSecNum); rOut.WriteLong(mUnknownSecNum); rOut.WriteLong(mLightsSecNum); - rOut.WriteLong(mVisiSecNum); - rOut.WriteLong(mPathSecNum); - rOut.WriteLong(mArotSecNum); + rOut.WriteLong(mVISISecNum); + rOut.WriteLong(mPATHSecNum); + if (mVersion <= ePrime) rOut.WriteLong(mAROTSecNum); - mSectionSizesOffset = rOut.Tell(); - for (u32 iSec = 0; iSec < mpArea->mSectionDataBuffers.size(); iSec++) - rOut.WriteLong(0); + else + { + rOut.WriteLong(mFFFFSecNum); + rOut.WriteLong(mPTLASecNum); + rOut.WriteLong(mEGMCSecNum); + } + + if (mVersion >= eEchoesDemo) + { + if (mVersion >= eEchoes) rOut.WriteLong(mCompressedBlocks.size()); + rOut.WriteToBoundary(32, 0); + } + + for (u32 iSec = 0; iSec < mSectionSizes.size(); iSec++) + rOut.WriteLong(mSectionSizes[iSec]); + rOut.WriteToBoundary(32, 0); + + if (mVersion >= eEchoes) + WriteCompressionHeader(rOut); +} + +void CAreaCooker::WriteCorruptionHeader(IOutputStream& rOut) +{ + rOut.WriteLong(0xDEADBEEF); + rOut.WriteLong(GetMREAVersion(mVersion)); + mpArea->mTransform.Write(rOut); + rOut.WriteLong(mpArea->mOriginalWorldMeshCount); + rOut.WriteLong(mpArea->mScriptLayers.size()); + rOut.WriteLong(mpArea->mSectionDataBuffers.size()); + rOut.WriteLong(mCompressedBlocks.size()); + rOut.WriteLong(mpArea->mSectionNumbers.size()); + rOut.WriteToBoundary(32, 0); + + for (u32 iSec = 0; iSec < mSectionSizes.size(); iSec++) + rOut.WriteLong(mSectionSizes[iSec]); rOut.WriteToBoundary(32, 0); - mSectionMgr.SetSectionCount(mpArea->mSectionDataBuffers.size()); - mSectionMgr.Init(rOut); + WriteCompressionHeader(rOut); + + for (u32 iNum = 0; iNum < mpArea->mSectionNumbers.size(); iNum++) + { + CGameArea::SSectionNumber& rNum = mpArea->mSectionNumbers[iNum]; + rOut.WriteLong(rNum.SectionID.ToLong()); + rOut.WriteLong(rNum.Index); + } + rOut.WriteToBoundary(32, 0); } +void CAreaCooker::WriteCompressionHeader(IOutputStream& rOut) +{ + for (u32 iCmp = 0; iCmp < mCompressedBlocks.size(); iCmp++) + { + SCompressedBlock& rBlock = mCompressedBlocks[iCmp]; + bool IsCompressed = (rBlock.CompressedSize != 0); + + rOut.WriteLong(IsCompressed ? rBlock.DecompressedSize + 0x120 : rBlock.DecompressedSize); + rOut.WriteLong(rBlock.DecompressedSize); + rOut.WriteLong(rBlock.CompressedSize); + rOut.WriteLong(rBlock.NumSections); + } + + rOut.WriteToBoundary(32, 0); +} + +void CAreaCooker::WriteAreaData(IOutputStream& rOut) +{ + rOut.WriteBytes(mAreaData.Data(), mAreaData.Size()); + rOut.WriteToBoundary(32, 0); +} + +// ************ SCLY ************ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) { - u32 NumLayers = mpArea->mScriptLayers.size(); rOut.WriteString("SCLY", 4); - rOut.WriteLong(0x1); // Unknown value, but it's always 1 + mVersion <= ePrime ? rOut.WriteLong(1) : rOut.WriteByte(1); + + u32 NumLayers = mpArea->mScriptLayers.size(); rOut.WriteLong(NumLayers); u32 LayerSizesStart = rOut.Tell(); for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) rOut.WriteLong(0); + // SCLY std::vector LayerSizes(NumLayers); - for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++) + for (u32 iLyr = 0; iLyr < NumLayers; iLyr++) { u32 LayerStart = rOut.Tell(); - CScriptCooker::WriteLayer(mpArea->Version(), mpArea->mScriptLayers[iLyr], rOut); + CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); LayerSizes[iLyr] = rOut.Tell() - LayerStart; } @@ -85,7 +195,114 @@ void CAreaCooker::WritePrimeSCLY(IOutputStream& rOut) rOut.WriteLong(LayerSizes[iLyr]); rOut.Seek(LayersEnd, SEEK_SET); - rOut.WriteToBoundary(32, 0); + FinishSection(false); + + // SCGN + if (mVersion == eEchoesDemo) + { + rOut.WriteString("SCGN", 4); + rOut.WriteByte(1); + CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut); + FinishSection(false); + } +} + +void CAreaCooker::WriteEchoesSCLY(IOutputStream& rOut) +{ + // SCLY + for (u32 iLyr = 0; iLyr < mpArea->mScriptLayers.size(); iLyr++) + { + rOut.WriteString("SCLY", 4); + rOut.WriteByte(0x1); + rOut.WriteLong(iLyr); + CScriptCooker::WriteLayer(mVersion, mpArea->mScriptLayers[iLyr], rOut); + FinishSection(true); + } + + // SCGN + rOut.WriteString("SCGN", 4); + rOut.WriteByte(0x1); + CScriptCooker::WriteLayer(mVersion, mpArea->mpGeneratorLayer, rOut); + FinishSection(true); +} + +// ************ SECTION MANAGEMENT ************ +void CAreaCooker::AddSectionToBlock() +{ + mCompressedData.WriteBytes(mSectionData.Data(), mSectionData.Size()); + mCompressedData.WriteToBoundary(32, 0); + mCurBlock.DecompressedSize += mSectionData.Size(); + mCurBlock.NumSections++; +} + +void CAreaCooker::FinishSection(bool SingleSectionBlock) +{ + // Our section data is now finished in mSection... + const u32 kSizeThreshold = 0x20000; + mSectionData.WriteToBoundary(32, 0); + + u32 SecSize = mSectionData.Size(); + mSectionSizes.push_back(SecSize); + + // Only track compressed blocks for MP2+. Write everything to one block for MP1. + if (mVersion >= eEchoes) + { + // Finish the current block if this is a single section block OR if the new section would push the block over the size limit. + if (mCurBlock.NumSections > 0 && (mCurBlock.DecompressedSize + SecSize > kSizeThreshold || SingleSectionBlock)) + FinishBlock(); + + AddSectionToBlock(); + + // And finally for a single section block, finish the new block. + if (SingleSectionBlock) + FinishBlock(); + } + + else AddSectionToBlock(); + + mSectionData.Clear(); +} + +void CAreaCooker::FinishBlock() +{ + if (mCurBlock.NumSections == 0) return; + + std::vector CompressedBuf(mCompressedData.Size() * 2); + bool EnableCompression = (mVersion >= eEchoes) && mpArea->mUsesCompression && !gkForceDisableCompression; + bool UseZlib = (mVersion == eReturns); + + u32 CompressedSize = 0; + bool WriteCompressedData = false; + + if (EnableCompression) + { + bool Success = CompressionUtil::CompressSegmentedData((u8*) mCompressedData.Data(), mCompressedData.Size(), CompressedBuf.data(), CompressedSize, UseZlib); + u32 PadBytes = (32 - (CompressedSize % 32)) & 0x3F; + WriteCompressedData = Success && (CompressedSize + PadBytes < (u32) mCompressedData.Size()); + } + + if (WriteCompressedData) + { + u32 PadBytes = 32 - (CompressedSize % 32); + PadBytes &= 0x3F; + + for (u32 iPad = 0; iPad < PadBytes; iPad++) + mAreaData.WriteByte(0); + + mAreaData.WriteBytes(CompressedBuf.data(), CompressedSize); + mCurBlock.CompressedSize = CompressedSize; + } + + else + { + mAreaData.WriteBytes(mCompressedData.Data(), mCompressedData.Size()); + mAreaData.WriteToBoundary(32, 0); + mCurBlock.CompressedSize = 0; + } + + mCompressedData.Clear(); + mCompressedBlocks.push_back(mCurBlock); + mCurBlock = SCompressedBlock(); } // ************ STATIC ************ @@ -95,39 +312,41 @@ void CAreaCooker::WriteCookedArea(CGameArea *pArea, IOutputStream& rOut) 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); + if (Cooker.mVersion <= eEchoes) + Cooker.DetermineSectionNumbersPrime(); + else + Cooker.DetermineSectionNumbersCorruption(); // Write pre-SCLY data sections - for (u32 iSec = 0; iSec < Cooker.mSclySecNum; iSec++) + for (u32 iSec = 0; iSec < Cooker.mSCLYSecNum; iSec++) { - rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); - Cooker.mSectionMgr.AddSize(rOut); + Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); + Cooker.FinishSection(false); } // Write SCLY - Cooker.WritePrimeSCLY(rOut); - Cooker.mSectionMgr.AddSize(rOut); + if (Cooker.mVersion <= eEchoesDemo) + Cooker.WritePrimeSCLY(Cooker.mSectionData); + else + Cooker.WriteEchoesSCLY(Cooker.mSectionData); // Write post-SCLY data sections - for (u32 iSec = Cooker.mSclySecNum + 1; iSec < pArea->mSectionDataBuffers.size(); iSec++) + u32 PostSCLY = (Cooker.mVersion <= ePrime ? Cooker.mSCLYSecNum + 1 : Cooker.mSCGNSecNum + 1); + for (u32 iSec = PostSCLY; iSec < pArea->mSectionDataBuffers.size(); iSec++) { - rOut.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); - Cooker.mSectionMgr.AddSize(rOut); + Cooker.mSectionData.WriteBytes(pArea->mSectionDataBuffers[iSec].data(), pArea->mSectionDataBuffers[iSec].size()); + Cooker.FinishSection(false); } - // Write section sizes - u32 AreaEnd = rOut.Tell(); - rOut.Seek(Cooker.mSectionSizesOffset, SEEK_SET); - Cooker.mSectionMgr.WriteSizes(rOut); - rOut.Seek(AreaEnd, SEEK_SET); + Cooker.FinishBlock(); + + // Write to actual file + if (Cooker.mVersion <= eEchoes) + Cooker.WritePrimeHeader(rOut); + else + Cooker.WriteCorruptionHeader(rOut); + + Cooker.WriteAreaData(rOut); } u32 CAreaCooker::GetMREAVersion(EGame version) diff --git a/src/Core/Resource/Cooker/CAreaCooker.h b/src/Core/Resource/Cooker/CAreaCooker.h index e6c6e371..3c773735 100644 --- a/src/Core/Resource/Cooker/CAreaCooker.h +++ b/src/Core/Resource/Cooker/CAreaCooker.h @@ -11,22 +11,56 @@ class CAreaCooker TResPtr mpArea; EGame mVersion; - CSectionMgrOut mSectionMgr; - u32 mSectionSizesOffset; + std::vector mSectionSizes; u32 mGeometrySecNum; - u32 mSclySecNum; + u32 mSCLYSecNum; + u32 mSCGNSecNum; u32 mCollisionSecNum; u32 mUnknownSecNum; u32 mLightsSecNum; - u32 mVisiSecNum; - u32 mPathSecNum; - u32 mArotSecNum; + u32 mVISISecNum; + u32 mPATHSecNum; + u32 mAROTSecNum; + u32 mFFFFSecNum; + u32 mPTLASecNum; + u32 mEGMCSecNum; + + struct SCompressedBlock + { + u32 CompressedSize; + u32 DecompressedSize; + u32 NumSections; + + SCompressedBlock() + : CompressedSize(0), DecompressedSize(0), NumSections(0) {} + }; + + SCompressedBlock mCurBlock; + CVectorOutStream mSectionData; + CVectorOutStream mCompressedData; + CVectorOutStream mAreaData; + + std::vector mCompressedBlocks; CAreaCooker(); - void DetermineSectionNumbers(); + void DetermineSectionNumbersPrime(); + void DetermineSectionNumbersCorruption(); + + // Header void WritePrimeHeader(IOutputStream& rOut); + void WriteCorruptionHeader(IOutputStream& rOut); + void WriteCompressionHeader(IOutputStream& rOut); + void WriteAreaData(IOutputStream& rOut); + + // SCLY void WritePrimeSCLY(IOutputStream& rOut); + void WriteEchoesSCLY(IOutputStream& rOut); + + // Section Management + void AddSectionToBlock(); + void FinishSection(bool ForceFinishBlock); + void FinishBlock(); public: static void WriteCookedArea(CGameArea *pArea, IOutputStream& rOut); diff --git a/src/Core/Resource/Cooker/CScriptCooker.cpp b/src/Core/Resource/Cooker/CScriptCooker.cpp index 878d6254..a07be1bb 100644 --- a/src/Core/Resource/Cooker/CScriptCooker.cpp +++ b/src/Core/Resource/Cooker/CScriptCooker.cpp @@ -1,7 +1,17 @@ #include "CScriptCooker.h" -void CScriptCooker::WriteProperty(IProperty *pProp) +void CScriptCooker::WriteProperty(IProperty *pProp, bool InSingleStruct) { + u32 SizeOffset = 0, PropStart = 0; + + if (mVersion >= eEchoesDemo && !InSingleStruct) + { + mpSCLY->WriteLong(pProp->ID()); + SizeOffset = mpSCLY->Tell(); + mpSCLY->WriteShort(0x0); + PropStart = mpSCLY->Tell(); + } + switch (pProp->Type()) { @@ -96,25 +106,81 @@ void CScriptCooker::WriteProperty(IProperty *pProp) { TMayaSplineProperty *pSplineCast = static_cast(pProp); std::vector Buffer = pSplineCast->Get(); - mpSCLY->WriteBytes(Buffer.data(), Buffer.size()); + if (!Buffer.empty()) mpSCLY->WriteBytes(Buffer.data(), Buffer.size()); + + else + { + if (mVersion < eReturns) + { + mpSCLY->WriteShort(0); + mpSCLY->WriteLong(0); + mpSCLY->WriteByte(1); + mpSCLY->WriteFloat(0); + mpSCLY->WriteFloat(1); + } + else + { + mpSCLY->WriteLong(0); + mpSCLY->WriteFloat(0); + mpSCLY->WriteFloat(1); + mpSCLY->WriteShort(0); + mpSCLY->WriteByte(1); + } + } + 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()); + std::vector PropertiesToWrite; for (u32 iProp = 0; iProp < pStruct->Count(); iProp++) - WriteProperty(pStruct->PropertyByIndex(iProp)); + { + IProperty *pSubProp = pStruct->PropertyByIndex(iProp); + ECookPreference Pref = pSubProp->Template()->CookPreference(); + if (Pref == eNeverCook) continue; + + if (mVersion < eReturns || pTemp->IsSingleProperty() || Pref == eAlwaysCook || !pSubProp->MatchesDefault()) + PropertiesToWrite.push_back(pSubProp); + } + + if (!pTemp->IsSingleProperty()) + { + if (mVersion <= ePrime) + mpSCLY->WriteLong(PropertiesToWrite.size()); + else + mpSCLY->WriteShort((u16) PropertiesToWrite.size()); + } + + for (u32 iProp = 0; iProp < PropertiesToWrite.size(); iProp++) + WriteProperty(PropertiesToWrite[iProp], pTemp->IsSingleProperty()); break; } + case eArrayProperty: + { + CArrayProperty *pArray = static_cast(pProp); + mpSCLY->WriteLong(pArray->Count()); + + for (u32 iProp = 0; iProp < pArray->Count(); iProp++) + WriteProperty(pArray->PropertyByIndex(iProp), true); + + break; + } + + } + + if (SizeOffset != 0) + { + u32 PropEnd = mpSCLY->Tell(); + mpSCLY->Seek(SizeOffset, SEEK_SET); + mpSCLY->WriteShort((u16) (PropEnd - PropStart)); + mpSCLY->Seek(PropEnd, SEEK_SET); } } @@ -157,12 +223,61 @@ void CScriptCooker::WriteInstanceMP1(CScriptObject *pInstance) mpSCLY->WriteLong(rkLink.ObjectID); } - WriteProperty(pInstance->Properties()); + WriteProperty(pInstance->Properties(), false); u32 InstanceEnd = mpSCLY->Tell(); - u32 InstanceSize = InstanceEnd - InstanceStart; mpSCLY->Seek(SizeOffset, SEEK_SET); - mpSCLY->WriteLong(InstanceSize); + mpSCLY->WriteLong(InstanceEnd - InstanceStart); + mpSCLY->Seek(InstanceEnd, SEEK_SET); +} + +void CScriptCooker::WriteLayerMP2(CScriptLayer *pLayer) +{ + u32 LayerStart = mpSCLY->Tell(); + mpSCLY->WriteByte(0x1); + mpSCLY->WriteLong(pLayer->NumInstances()); + + for (u32 iInst = 0; iInst < pLayer->NumInstances(); iInst++) + { + CScriptObject *pInstance = pLayer->InstanceByIndex(iInst); + WriteInstanceMP2(pInstance); + } + + if (mVersion == eEchoesDemo) + { + 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::WriteInstanceMP2(CScriptObject *pInstance) +{ + mpSCLY->WriteLong(pInstance->ObjectTypeID()); + + u32 SizeOffset = mpSCLY->Tell(); + mpSCLY->WriteShort(0); + u32 InstanceStart = mpSCLY->Tell(); + + mpSCLY->WriteLong(pInstance->InstanceID()); + mpSCLY->WriteShort((u16) 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(), false); + u32 InstanceEnd = mpSCLY->Tell(); + + mpSCLY->Seek(SizeOffset, SEEK_SET); + mpSCLY->WriteShort((u16) (InstanceEnd - InstanceStart)); mpSCLY->Seek(InstanceEnd, SEEK_SET); } @@ -172,5 +287,9 @@ void CScriptCooker::WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& CScriptCooker Cooker; Cooker.mpSCLY = &rOut; Cooker.mVersion = Game; - Cooker.WriteLayerMP1(pLayer); + + if (Game <= ePrime) + Cooker.WriteLayerMP1(pLayer); + else + Cooker.WriteLayerMP2(pLayer); } diff --git a/src/Core/Resource/Cooker/CScriptCooker.h b/src/Core/Resource/Cooker/CScriptCooker.h index 9e7e5b85..fd8fbdec 100644 --- a/src/Core/Resource/Cooker/CScriptCooker.h +++ b/src/Core/Resource/Cooker/CScriptCooker.h @@ -12,9 +12,11 @@ class CScriptCooker EGame mVersion; CScriptCooker() {} - void WriteProperty(IProperty *pProp); + void WriteProperty(IProperty *pProp, bool InSingleStruct); void WriteLayerMP1(CScriptLayer *pLayer); void WriteInstanceMP1(CScriptObject *pInstance); + void WriteLayerMP2(CScriptLayer *pLayer); + void WriteInstanceMP2(CScriptObject *pInstance); public: static void WriteLayer(EGame Game, CScriptLayer *pLayer, IOutputStream& rOut); diff --git a/src/Core/Resource/Factory/CAreaLoader.cpp b/src/Core/Resource/Factory/CAreaLoader.cpp index f9143db9..1119bb99 100644 --- a/src/Core/Resource/Factory/CAreaLoader.cpp +++ b/src/Core/Resource/Factory/CAreaLoader.cpp @@ -63,6 +63,8 @@ void CAreaLoader::ReadHeaderPrime() mpMREA->SeekToBoundary(32); mpSectionMgr->Init(); LoadSectionDataBuffers(); + + mpArea->mOriginalWorldMeshCount = mNumMeshes; } void CAreaLoader::ReadGeometryPrime() @@ -116,39 +118,57 @@ void CAreaLoader::ReadGeometryPrime() void CAreaLoader::ReadSCLYPrime() { + // Prime, Echoes Demo Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP1)"); mpSectionMgr->ToSection(mScriptLayerBlockNum); CFourCC SCLY(*mpMREA); if (SCLY != "SCLY") { - Log::Error(mpMREA->GetSourceString() + " - Invalid SCLY magic: " + SCLY.ToString()); + Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCLY magic: " + SCLY.ToString()); return; } + mpMREA->Seek(0x4, SEEK_CUR); // Skipping unknown value which is always 4 - if (mVersion <= ePrime) - mpMREA->Seek(0x4, SEEK_CUR); - else - mpMREA->Seek(0x1, SEEK_CUR); - + // Read layer sizes mNumLayers = mpMREA->ReadLong(); - mpArea->mScriptLayers.reserve(mNumLayers); - + mpArea->mScriptLayers.resize(mNumLayers); std::vector LayerSizes(mNumLayers); - for (u32 iLayer = 0; iLayer < mNumLayers; iLayer++) - LayerSizes[iLayer] = mpMREA->ReadLong(); - for (u32 iLayer = 0; iLayer < mNumLayers; iLayer++) + for (u32 iLyr = 0; iLyr < mNumLayers; iLyr++) + LayerSizes[iLyr] = mpMREA->ReadLong(); + + // SCLY + for (u32 iLyr = 0; iLyr < mNumLayers; iLyr++) { - u32 Next = mpMREA->Tell() + LayerSizes[iLayer]; - - CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); - if (pLayer) - mpArea->mScriptLayers.push_back(pLayer); - + u32 Next = mpMREA->Tell() + LayerSizes[iLyr]; + mpArea->mScriptLayers[iLyr] = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); mpMREA->Seek(Next, SEEK_SET); } + // SCGN + if (mVersion == eEchoesDemo) + { + mpSectionMgr->ToSection(mScriptGeneratorBlockNum); + + CFourCC SCGN(*mpMREA); + if (SCGN != "SCGN") + Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCGN magic: " + SCGN.ToString()); + + else + { + mpMREA->Seek(0x1, SEEK_CUR); + CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); + + if (pLayer) + { + mpArea->mpGeneratorLayer = pLayer; + pLayer->SetName("Generated Objects"); + pLayer->SetActive(true); + } + } + } + SetUpObjects(); } @@ -270,30 +290,49 @@ void CAreaLoader::ReadHeaderEchoes() mpSectionMgr->Init(); LoadSectionDataBuffers(); + + mpArea->mOriginalWorldMeshCount = mNumMeshes; } void CAreaLoader::ReadSCLYEchoes() { + // MP2, MP3 Proto, MP3, DKCR Log::FileWrite(mpMREA->GetSourceString(), "Reading MREA script layers (MP2/MP3/DKCR)"); mpSectionMgr->ToSection(mScriptLayerBlockNum); + mpArea->mScriptLayers.resize(mNumLayers); // SCLY - for (u32 l = 0; l < mNumLayers; l++) + for (u32 iLyr = 0; iLyr < mNumLayers; iLyr++) { - CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); - - if (pLayer) - mpArea->mScriptLayers.push_back(pLayer); + CFourCC SCLY(*mpMREA); + if (SCLY != "SCLY") + { + Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Layer " + TString::FromInt32(iLyr, 0, 10) + " - Invalid SCLY magic: " + SCLY.ToString()); + mpSectionMgr->ToNextSection(); + continue; + } + mpMREA->Seek(0x5, SEEK_CUR); // Skipping unknown + layer index + mpArea->mScriptLayers[iLyr] = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); mpSectionMgr->ToNextSection(); } // SCGN - mpSectionMgr->ToSection(mScriptGeneratorBlockNum); - CScriptLayer *pLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); + CFourCC SCGN(*mpMREA); + if (SCGN != "SCGN") + { + Log::FileError(mpMREA->GetSourceString(), mpMREA->Tell() - 4, "Invalid SCGN magic: " + SCGN.ToString()); + return; + } - if (pLayer) - mpArea->mpGeneratorLayer = pLayer; + mpMREA->Seek(0x1, SEEK_CUR); // Skipping unknown + mpArea->mpGeneratorLayer = CScriptLoader::LoadLayer(*mpMREA, mpArea, mVersion); + + if (mpArea->mpGeneratorLayer) + { + mpArea->mpGeneratorLayer->SetName("Generated Objects"); + mpArea->mpGeneratorLayer->SetActive(true); + } SetUpObjects(); } @@ -334,12 +373,19 @@ void CAreaLoader::ReadHeaderCorruption() else if (Type == "SOBJ") mScriptLayerBlockNum = Num; else if (Type == "SGEN") mScriptGeneratorBlockNum = Num; else if (Type == "WOBJ") mGeometryBlockNum = Num; // note WOBJ can show up multiple times, but is always 0 + + CGameArea::SSectionNumber SecNum; + SecNum.SectionID = Type; + SecNum.Index = Num; + mpArea->mSectionNumbers.push_back(SecNum); } mpMREA->SeekToBoundary(32); Decompress(); mpSectionMgr->Init(); LoadSectionDataBuffers(); + + mpArea->mOriginalWorldMeshCount = mNumMeshes; } void CAreaLoader::ReadGeometryCorruption() @@ -478,6 +524,8 @@ void CAreaLoader::ReadCompressedBlocks() mClusters[c].CompressedSize = mpMREA->ReadLong(); mClusters[c].NumSections = mpMREA->ReadLong(); mTotalDecmpSize += mClusters[c].DecompressedSize; + + if (mClusters[c].CompressedSize != 0) mpArea->mUsesCompression = true; } mpMREA->SeekToBoundary(32); diff --git a/src/Core/Resource/Factory/CScriptLoader.cpp b/src/Core/Resource/Factory/CScriptLoader.cpp index 5abb60c7..d23e16aa 100644 --- a/src/Core/Resource/Factory/CScriptLoader.cpp +++ b/src/Core/Resource/Factory/CScriptLoader.cpp @@ -367,32 +367,7 @@ CScriptObject* CScriptLoader::LoadObjectMP2(IInputStream& SCLY) CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY) { - bool IsSCGN = false; - - if (mVersion >= eEchoes) - { - CFourCC SCLY_Magic(SCLY); - - if (SCLY_Magic == "SCLY") - { - SCLY.Seek(0x6, SEEK_CUR); - } - else if (SCLY_Magic == "SCGN") - { - SCLY.Seek(0x2, SEEK_CUR); - IsSCGN = true; - } - else - { - Log::FileError(SCLY.GetSourceString(), SCLY.Tell() - 4, "Invalid script layer magic: " + TString::HexString((u32) SCLY_Magic.ToLong())); - return nullptr; - } - } - else - { - SCLY.Seek(0x1, SEEK_CUR); - } - + SCLY.Seek(0x1, SEEK_CUR); // Skipping version. todo: verify this? u32 NumObjects = SCLY.ReadLong(); mpLayer = new CScriptLayer(); @@ -405,11 +380,6 @@ CScriptLayer* CScriptLoader::LoadLayerMP2(IInputStream& SCLY) mpLayer->AddInstance(pObj); } - if (IsSCGN) - { - mpLayer->SetName("Generated"); - mpLayer->SetActive(true); - } return mpLayer; } diff --git a/src/Core/Resource/Script/IProperty.h b/src/Core/Resource/Script/IProperty.h index 1eceab16..f8b39d7f 100644 --- a/src/Core/Resource/Script/IProperty.h +++ b/src/Core/Resource/Script/IProperty.h @@ -160,7 +160,7 @@ public: TMayaSplineProperty(IPropertyTemplate *pTemp, CPropertyStruct *pParent, const std::vector& v) : TTypedProperty(pTemp, pParent, v) {} - virtual bool MatchesDefault() { return true; } + virtual bool MatchesDefault() { return Get().empty(); } }; /* diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 5acecdf7..2013c766 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -608,7 +608,7 @@ void CPropertyDelegate::SetCharacterEditorData(QWidget *pEditor, const QModelInd if (Type == eFileProperty) { - static_cast(pEditor)->SetResource(Params.AnimSet()); + static_cast(pEditor)->SetResource(Params.AnimSet()); } else if (Type == eEnumProperty) @@ -632,10 +632,10 @@ void CPropertyDelegate::SetCharacterModelData(QWidget *pEditor, const QModelInde if (Type == eFileProperty) { - Params.SetResource( static_cast(pEditor)->GetResource() ); + Params.SetResource( static_cast(pEditor)->GetResourceInfo() ); // Reset all other parameters to 0 Params.SetNodeIndex(0); - for (u32 iUnk = 0; iUnk < 4; iUnk++) + for (u32 iUnk = 0; iUnk < 3; iUnk++) Params.SetUnknown(iUnk, 0); } @@ -677,7 +677,7 @@ EPropertyType CPropertyDelegate::DetermineCharacterPropType(EGame Game, const QM else { if (rkIndex.row() == 0) return eFileProperty; - else if (rkIndex.row() <= 3) return eLongProperty; + else if (rkIndex.row() <= 2) return eLongProperty; } return eUnknownProperty; } diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index c3a89e90..1917fc93 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -113,7 +113,7 @@ int CPropertyModel::rowCount(const QModelIndex& rkParent) const CAnimationParameters Params = static_cast(pProp)->Get(); if (Params.Version() <= eEchoes) return 3; if (Params.Version() <= eCorruption) return 2; - return 5; + return 4; } default: @@ -241,7 +241,8 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const if (rkIndex.column() == 0) { if (rkIndex.row() == 0) return "Character"; - else return "Unknown " + QString::number(rkIndex.row()); + else if (rkIndex.row() == 1) return "Default Anim"; + else return "Unknown " + QString::number(rkIndex.row() - 1); } if (rkIndex.column() == 1 && rkIndex.row() > 0) diff --git a/src/Editor/Widgets/WAnimParamsEditor.cpp b/src/Editor/Widgets/WAnimParamsEditor.cpp index 2bf74f6c..7f8c88ad 100644 --- a/src/Editor/Widgets/WAnimParamsEditor.cpp +++ b/src/Editor/Widgets/WAnimParamsEditor.cpp @@ -2,6 +2,7 @@ #include "Editor/UICommon.h" #include #include +#include WAnimParamsEditor::WAnimParamsEditor(QWidget *pParent) : QWidget(pParent), @@ -66,10 +67,10 @@ void WAnimParamsEditor::SetParameters(const CAnimationParameters& params) // ************ PRIVATE SLOTS ************ void WAnimParamsEditor::OnResourceChanged(QString path) { - CResource *pRes = gResCache.GetResource(path.toStdString()); - if (pRes && pRes->Type() != eAnimSet) pRes = nullptr; + CResourceInfo ResInfo(path.toStdString()); + if (ResInfo.Type() != "ANCS" && ResInfo.Type() != "CHAR") ResInfo = CResourceInfo(); - mParams.SetResource(pRes); + mParams.SetResource(ResInfo); emit ParametersChanged(mParams); } @@ -217,7 +218,7 @@ void WAnimParamsEditor::SetupUI() connect(mpSpinBoxes[0], SIGNAL(valueChanged(int)), this, SLOT(OnUnknownChanged())); // Create unknown spin box B/C/D - for (u32 iBox = 1; iBox < 4; iBox++) + for (u32 iBox = 1; iBox < 3; iBox++) { mpSpinBoxes[iBox] = new WIntegralSpinBox(this); mpSpinBoxes[iBox]->setRange(INT32_MIN, INT32_MAX);