From 5b202d9d62e0df1ef1ecd898b79ed53878c84078 Mon Sep 17 00:00:00 2001 From: ousnius Date: Mon, 16 Sep 2024 22:14:46 +0200 Subject: [PATCH] Fix various Oblivion 20.0.0.5 and 10.1.0.1xx issues Add more fields and version checks. Fix NiDynamicEffect condition. --- include/Animation.hpp | 87 +++++++++++------- include/BasicTypes.hpp | 6 +- include/Object3d.hpp | 6 -- include/Objects.hpp | 2 +- include/Particles.hpp | 28 ++++++ src/Animation.cpp | 203 ++++++++++++++++++++++++++++++----------- src/Objects.cpp | 2 +- src/Particles.cpp | 34 +++++++ src/Skin.cpp | 5 +- 9 files changed, 278 insertions(+), 95 deletions(-) diff --git a/include/Animation.hpp b/include/Animation.hpp index 0894c7e..48863e6 100644 --- a/include/Animation.hpp +++ b/include/Animation.hpp @@ -11,6 +11,22 @@ See the included GPLv3 LICENSE file #include "Keys.hpp" namespace nifly { +struct QuatTransform { + Vector3 translation; + Quaternion rotation; + float scale = 1.0f; + bool trsValid[3]{}; + + void Sync(NiStreamReversible& stream) { + stream.Sync(translation); + stream.Sync(rotation); + stream.Sync(scale); + + if (stream.GetVersion().File() < V10_1_0_110) + stream.Sync(trsValid); + } +}; + class NiKeyframeData : public NiCloneableStreamable { public: NiKeyType rotationType = NO_INTERP; @@ -176,6 +192,7 @@ class InterpBlendItem { NiBlockRef interpolatorRef; float weight = 0.0f; float normalizedWeight = 0.0f; + uint32_t priorityInt = 0; uint8_t priority = 0; float easeSpinner = 0.0f; @@ -185,20 +202,30 @@ class InterpBlendItem { class NiBlendInterpolator : public NiCloneableStreamable { public: InterpBlendFlags flags = INTERP_BLEND_MANAGER_CONTROLLED; - uint8_t arraySize = 0; + uint16_t arraySize = 0; + uint16_t arrayGrowBy = 0; float weightThreshold = 0.0f; - uint8_t interpCount = 0; + uint16_t interpCount = 0; uint8_t singleIndex = NiByteMax; + uint16_t singleIndexShort = NiUShortMax; char highPriority = NiCharMin; + int highPriorityInt = NiIntMin; char nextHighPriority = NiCharMin; + int nextHighPriorityInt = NiIntMin; float singleTime = NiFloatMin; float highWeightsSum = NiFloatMin; float nextHighWeightsSum = NiFloatMin; float highEaseSpinner = NiFloatMin; std::vector interpItems; + bool managerControlled = false; + bool onlyUseHighestWeight = false; + NiBlockRef singleInterpolatorRef; + void Sync(NiStreamReversible& stream); + void GetChildRefs(std::set& refs) override; + void GetChildIndices(std::vector& indices) override; }; class NiBlendBoolInterpolator : public NiCloneableStreamable { @@ -235,10 +262,12 @@ class NiBlendPoint3Interpolator class NiBlendTransformInterpolator : public NiCloneableStreamable { public: + QuatTransform value; + static constexpr const char* BlockName = "NiBlendTransformInterpolator"; const char* GetBlockName() override { return BlockName; } - void Sync(NiStreamReversible&) {} + void Sync(NiStreamReversible& stream); }; class NiKeyBasedInterpolator : public NiInterpolator {}; @@ -570,10 +599,16 @@ class NiBSBoneLODController : public NiCloneable vectors; void Sync(NiStreamReversible& stream, uint32_t numVerts) { - frameName.Sync(stream); + if (stream.GetVersion().File() >= V10_1_0_106) + frameName.Sync(stream); + + if (stream.GetVersion().File() >= V10_1_0_104 && stream.GetVersion().File() < V20_1_0_3 && stream.GetVersion().Stream() < 10) + stream.Sync(legacyWeight); + vectors.resize(numVerts); for (uint32_t i = 0; i < numVerts; i++) stream.Sync(vectors[i]); @@ -601,7 +636,12 @@ class NiMorphData : public NiCloneableStreamable { void SetMorphs(const uint32_t numVerts, const std::vector& m); }; -class NiInterpController : public NiCloneable {}; +class NiInterpController : public NiCloneableStreamable { +public: + bool managerControlled = false; + + void Sync(NiStreamReversible& stream); +}; class MorphWeight { public: @@ -797,10 +837,16 @@ class BSNiAlphaPropertyTestRefController const char* GetBlockName() override { return BlockName; } }; -class NiKeyframeController : public NiCloneable { +class NiKeyframeController : public NiCloneableStreamable { public: + NiBlockRef dataRef; + static constexpr const char* BlockName = "NiKeyframeController"; const char* GetBlockName() override { return BlockName; } + + void Sync(NiStreamReversible& stream); + void GetChildRefs(std::set& refs) override; + void GetChildIndices(std::vector& indices) override; }; class NiTransformController : public NiCloneable { @@ -1027,33 +1073,6 @@ class NiPSysEmitterPlanarAngleVarCtlr const char* GetBlockName() override { return BlockName; } }; -class NiPSysEmitterCtlr : public NiCloneableStreamable { -public: - NiBlockRef visInterpolatorRef; - - static constexpr const char* BlockName = "NiPSysEmitterCtlr"; - const char* GetBlockName() override { return BlockName; } - - void Sync(NiStreamReversible& stream); - void GetChildRefs(std::set& refs) override; - void GetChildIndices(std::vector& indices) override; -}; - -class BSMasterParticleSystem; - -class BSPSysMultiTargetEmitterCtlr - : public NiCloneableStreamable { -public: - uint16_t maxEmitters = 0; - NiBlockPtr masterParticleSystemRef; - - static constexpr const char* BlockName = "BSPSysMultiTargetEmitterCtlr"; - const char* GetBlockName() override { return BlockName; } - - void Sync(NiStreamReversible& stream); - void GetPtrs(std::set& ptrs) override; -}; - class NiStringPalette : public NiCloneableStreamable { public: NiString palette; @@ -1203,8 +1222,10 @@ class NiControllerSequence : public NiCloneableStreamable textKeyRef; CycleType cycleType = CYCLE_LOOP; float frequency = 0.0f; + float phase = 0.0f; float startTime = 0.0f; float stopTime = 0.0f; + bool playBackwards = false; NiBlockPtr managerRef; NiStringRef accumRootName; diff --git a/include/BasicTypes.hpp b/include/BasicTypes.hpp index f30fb88..32e3e0c 100644 --- a/include/BasicTypes.hpp +++ b/include/BasicTypes.hpp @@ -29,6 +29,8 @@ constexpr auto NiByteMin = std::numeric_limits::min(); constexpr auto NiByteMax = std::numeric_limits::max(); constexpr auto NiUShortMin = std::numeric_limits::min(); constexpr auto NiUShortMax = std::numeric_limits::max(); +constexpr auto NiIntMin = std::numeric_limits::min(); +constexpr auto NiIntMax = std::numeric_limits::max(); constexpr auto NiUIntMin = std::numeric_limits::min(); constexpr auto NiUIntMax = std::numeric_limits::max(); constexpr auto NiFloatMin = std::numeric_limits::lowest(); @@ -57,8 +59,10 @@ enum NiFileVersion : uint32_t { V10_1_0_0 = 0x0A010000, V10_1_0_101 = 0x0A010065, V10_1_0_104 = 0x0A010068, - V10_1_0_110 = 0x0A01006E, V10_1_0_106 = 0x0A01006A, + V10_1_0_108 = 0x0A01006C, + V10_1_0_110 = 0x0A01006E, + V10_1_0_112 = 0x0A010070, V10_1_0_113 = 0x0A010071, V10_1_0_114 = 0x0A010072, V10_2_0_0 = 0x0A020000, diff --git a/include/Object3d.hpp b/include/Object3d.hpp index e8f7b96..6314c47 100644 --- a/include/Object3d.hpp +++ b/include/Object3d.hpp @@ -1009,12 +1009,6 @@ struct QuaternionXYZW { }; -struct QuatTransform { - Vector3 translation; - Quaternion rotation; - float scale = 1.0f; -}; - struct MatTransform { /* On MatTransform and coordinate-system (CS) transformations: diff --git a/include/Objects.hpp b/include/Objects.hpp index 689b0c8..5c3b69b 100644 --- a/include/Objects.hpp +++ b/include/Objects.hpp @@ -315,7 +315,7 @@ enum CoordGenType : uint32_t { class NiDynamicEffect : public NiCloneableStreamable { public: - bool switchState = false; + bool switchState = true; NiBlockPtrArray affectedNodes; void Sync(NiStreamReversible& stream); diff --git a/include/Particles.hpp b/include/Particles.hpp index ce811b6..e6ae98c 100644 --- a/include/Particles.hpp +++ b/include/Particles.hpp @@ -179,6 +179,34 @@ class NiPSysEmitterCtlrData : public NiCloneableStreamable { +public: + NiBlockRef dataRef; + NiBlockRef visInterpolatorRef; + + static constexpr const char* BlockName = "NiPSysEmitterCtlr"; + const char* GetBlockName() override { return BlockName; } + + void Sync(NiStreamReversible& stream); + void GetChildRefs(std::set& refs) override; + void GetChildIndices(std::vector& indices) override; +}; + +class BSMasterParticleSystem; + +class BSPSysMultiTargetEmitterCtlr + : public NiCloneableStreamable { +public: + uint16_t maxEmitters = 0; + NiBlockPtr masterParticleSystemRef; + + static constexpr const char* BlockName = "BSPSysMultiTargetEmitterCtlr"; + const char* GetBlockName() override { return BlockName; } + + void Sync(NiStreamReversible& stream); + void GetPtrs(std::set& ptrs) override; +}; + class NiParticleSystem; class NiPSysModifier : public NiCloneableStreamable { diff --git a/src/Animation.cpp b/src/Animation.cpp index d6cfda6..f6ffcd9 100644 --- a/src/Animation.cpp +++ b/src/Animation.cpp @@ -293,6 +293,12 @@ void NiMorphData::SetMorphs(const uint32_t numVerts, const std::vector& m } +void NiInterpController::Sync(NiStreamReversible& stream) { + if (stream.GetVersion().File() >= V10_1_0_104 && stream.GetVersion().File() <= V10_1_0_108) + stream.Sync(managerControlled); +} + + void NiGeomMorpherController::Sync(NiStreamReversible& stream) { stream.Sync(morpherFlags); dataRef.Sync(stream); @@ -412,6 +418,24 @@ void NiTextureTransformController::Sync(NiStreamReversible& stream) { } +void NiKeyframeController::Sync(NiStreamReversible& stream) { + if (stream.GetVersion().File() < V10_1_0_104) + dataRef.Sync(stream); +} + +void NiKeyframeController::GetChildRefs(std::set& refs) { + NiSingleInterpController::GetChildRefs(refs); + + refs.insert(&dataRef); +} + +void NiKeyframeController::GetChildIndices(std::vector& indices) { + NiSingleInterpController::GetChildIndices(indices); + + indices.push_back(dataRef.index); +} + + void BSLightingShaderPropertyColorController::Sync(NiStreamReversible& stream) { stream.Sync(typeOfControlledColor); } @@ -460,35 +484,6 @@ void NiPSysModifierCtlr::GetStringRefs(std::vector& refs) { } -void NiPSysEmitterCtlr::Sync(NiStreamReversible& stream) { - visInterpolatorRef.Sync(stream); -} - -void NiPSysEmitterCtlr::GetChildRefs(std::set& refs) { - NiPSysModifierCtlr::GetChildRefs(refs); - - refs.insert(&visInterpolatorRef); -} - -void NiPSysEmitterCtlr::GetChildIndices(std::vector& indices) { - NiPSysModifierCtlr::GetChildIndices(indices); - - indices.push_back(visInterpolatorRef.index); -} - - -void BSPSysMultiTargetEmitterCtlr::Sync(NiStreamReversible& stream) { - stream.Sync(maxEmitters); - masterParticleSystemRef.Sync(stream); -} - -void BSPSysMultiTargetEmitterCtlr::GetPtrs(std::set& ptrs) { - NiPSysEmitterCtlr::GetPtrs(ptrs); - - ptrs.insert(&masterParticleSystemRef); -} - - void NiBSplineInterpolator::Sync(NiStreamReversible& stream) { stream.Sync(startTime); stream.Sync(stopTime); @@ -556,32 +551,118 @@ void InterpBlendItem::Sync(NiStreamReversible& stream) { interpolatorRef.Sync(stream); stream.Sync(weight); stream.Sync(normalizedWeight); - stream.Sync(priority); + if (stream.GetVersion().File() < V10_1_0_110) + stream.Sync(priorityInt); + else + stream.Sync(priority); stream.Sync(easeSpinner); } void NiBlendInterpolator::Sync(NiStreamReversible& stream) { - stream.Sync(flags); - stream.Sync(arraySize); - stream.Sync(weightThreshold); + if (stream.GetVersion().File() >= V10_1_0_112) + stream.Sync(flags); - if ((flags & INTERP_BLEND_MANAGER_CONTROLLED) == 0) { - stream.Sync(interpCount); - stream.Sync(singleIndex); - stream.Sync(highPriority); - stream.Sync(nextHighPriority); - stream.Sync(singleTime); - stream.Sync(highWeightsSum); - stream.Sync(nextHighWeightsSum); - stream.Sync(highEaseSpinner); + if (stream.GetVersion().File() < V10_1_0_110) { + stream.Sync(arraySize); + } + else { + uint8_t arraySizeByte = 0; + if (stream.GetMode() == NiStreamReversible::Mode::Writing) + arraySizeByte = static_cast(arraySize); + + stream.Sync(arraySizeByte); + + if (stream.GetMode() == NiStreamReversible::Mode::Reading) + arraySize = arraySizeByte; + } + + if (stream.GetVersion().File() < V10_1_0_110) + stream.Sync(arrayGrowBy); + + if (stream.GetVersion().File() >= V10_1_0_112) + stream.Sync(weightThreshold); + + if (stream.GetVersion().File() >= V10_1_0_112) { + if ((flags & INTERP_BLEND_MANAGER_CONTROLLED) == 0) { + uint8_t interpCountByte = 0; + if (stream.GetMode() == NiStreamReversible::Mode::Writing) + interpCountByte = static_cast(interpCount); + + stream.Sync(interpCountByte); + if (stream.GetMode() == NiStreamReversible::Mode::Reading) + interpCount = interpCountByte; + + stream.Sync(singleIndex); + stream.Sync(highPriority); + stream.Sync(nextHighPriority); + stream.Sync(singleTime); + stream.Sync(highWeightsSum); + stream.Sync(nextHighWeightsSum); + stream.Sync(highEaseSpinner); + + interpItems.resize(arraySize); + for (auto& item : interpItems) + item.Sync(stream); + } + } + else { interpItems.resize(arraySize); for (auto& item : interpItems) item.Sync(stream); + + stream.Sync(managerControlled); + stream.Sync(weightThreshold); + stream.Sync(onlyUseHighestWeight); + } + + if (stream.GetVersion().File() < V10_1_0_110) { + stream.Sync(interpCount); + stream.Sync(singleIndexShort); + } + + if (stream.GetVersion().File() >= V10_1_0_110 && stream.GetVersion().File() < V10_1_0_112) { + uint8_t interpCountByte = 0; + if (stream.GetMode() == NiStreamReversible::Mode::Writing) + interpCountByte = static_cast(interpCount); + + stream.Sync(interpCountByte); + + if (stream.GetMode() == NiStreamReversible::Mode::Reading) + interpCount = interpCountByte; + + stream.Sync(singleIndex); + } + + if (stream.GetVersion().File() >= V10_1_0_108 && stream.GetVersion().File() < V10_1_0_112) { + singleInterpolatorRef.Sync(stream); + stream.Sync(singleTime); + } + + if (stream.GetVersion().File() < V10_1_0_110) { + stream.Sync(highPriorityInt); + stream.Sync(nextHighPriorityInt); + } + + if (stream.GetVersion().File() >= V10_1_0_110 && stream.GetVersion().File() < V10_1_0_112) { + stream.Sync(highPriority); + stream.Sync(nextHighPriority); } } +void NiBlendInterpolator::GetChildRefs(std::set& refs) { + NiInterpolator::GetChildRefs(refs); + + refs.insert(&singleInterpolatorRef); +} + +void NiBlendInterpolator::GetChildIndices(std::vector& indices) { + NiInterpolator::GetChildIndices(indices); + + indices.push_back(singleInterpolatorRef.index); +} + void NiBlendBoolInterpolator::Sync(NiStreamReversible& stream) { stream.Sync(value); @@ -598,6 +679,12 @@ void NiBlendPoint3Interpolator::Sync(NiStreamReversible& stream) { } +void NiBlendTransformInterpolator::Sync(NiStreamReversible& stream) { + if (stream.GetVersion().File() < V10_1_0_110) + value.Sync(stream); +} + + void NiBoolInterpolator::Sync(NiStreamReversible& stream) { stream.Sync(boolValue); dataRef.Sync(stream); @@ -701,7 +788,7 @@ void NiLookAtInterpolator::Sync(NiStreamReversible& stream) { stream.Sync(flags); lookAtRef.Sync(stream); lookAtName.Sync(stream); - stream.Sync(transform); + transform.Sync(stream); translateInterpRef.Sync(stream); rollInterpRef.Sync(stream); scaleInterpRef.Sync(stream); @@ -772,7 +859,9 @@ void NiSequence::Sync(NiStreamReversible& stream) { name.Sync(stream); uint32_t sz = controlledBlocks.SyncSize(stream); - stream.Sync(arrayGrowBy); + + if (stream.GetVersion().File() >= V10_1_0_106) + stream.Sync(arrayGrowBy); controlledBlocks.SyncData(stream, sz); } @@ -829,14 +918,24 @@ void BSAnimNotes::GetChildIndices(std::vector& indices) { void NiControllerSequence::Sync(NiStreamReversible& stream) { - stream.Sync(weight); - textKeyRef.Sync(stream); - stream.Sync(cycleType); - stream.Sync(frequency); - stream.Sync(startTime); - stream.Sync(stopTime); - managerRef.Sync(stream); - accumRootName.Sync(stream); + if (stream.GetVersion().File() >= V10_1_0_106) { + stream.Sync(weight); + textKeyRef.Sync(stream); + stream.Sync(cycleType); + stream.Sync(frequency); + + if (stream.GetVersion().File() <= V10_4_0_1) + stream.Sync(phase); + + stream.Sync(startTime); + stream.Sync(stopTime); + + if (stream.GetVersion().File() == V10_1_0_106) + stream.Sync(playBackwards); + + managerRef.Sync(stream); + accumRootName.Sync(stream); + } if (stream.GetVersion().File() >= V10_1_0_113 && stream.GetVersion().File() < V20_1_0_1) stringPaletteRef.Sync(stream); diff --git a/src/Objects.cpp b/src/Objects.cpp index 21d2f54..d453307 100644 --- a/src/Objects.cpp +++ b/src/Objects.cpp @@ -270,7 +270,7 @@ void NiDynamicEffect::Sync(NiStreamReversible& stream) { if (stream.GetVersion().File() > NiFileVersion::V10_1_0_101) stream.Sync(switchState); - if (stream.GetVersion().File() <= NiFileVersion::V4_0_0_2 && stream.GetVersion().File() >= NiFileVersion::V10_1_0_0) + if (stream.GetVersion().File() <= NiFileVersion::V4_0_0_2 || stream.GetVersion().File() >= NiFileVersion::V10_1_0_0) affectedNodes.Sync(stream); } } diff --git a/src/Particles.cpp b/src/Particles.cpp index 95ef0c9..7fa6af8 100644 --- a/src/Particles.cpp +++ b/src/Particles.cpp @@ -162,6 +162,40 @@ void NiPSysEmitterCtlrData::Sync(NiStreamReversible& stream) { } +void NiPSysEmitterCtlr::Sync(NiStreamReversible& stream) { + if (stream.GetVersion().File() < V10_1_0_104) + dataRef.Sync(stream); + else + visInterpolatorRef.Sync(stream); +} + +void NiPSysEmitterCtlr::GetChildRefs(std::set& refs) { + NiPSysModifierCtlr::GetChildRefs(refs); + + refs.insert(&dataRef); + refs.insert(&visInterpolatorRef); +} + +void NiPSysEmitterCtlr::GetChildIndices(std::vector& indices) { + NiPSysModifierCtlr::GetChildIndices(indices); + + indices.push_back(dataRef.index); + indices.push_back(visInterpolatorRef.index); +} + + +void BSPSysMultiTargetEmitterCtlr::Sync(NiStreamReversible& stream) { + stream.Sync(maxEmitters); + masterParticleSystemRef.Sync(stream); +} + +void BSPSysMultiTargetEmitterCtlr::GetPtrs(std::set& ptrs) { + NiPSysEmitterCtlr::GetPtrs(ptrs); + + ptrs.insert(&masterParticleSystemRef); +} + + void NiPSysModifier::Sync(NiStreamReversible& stream) { name.Sync(stream); diff --git a/src/Skin.cpp b/src/Skin.cpp index c102b24..0396eed 100644 --- a/src/Skin.cpp +++ b/src/Skin.cpp @@ -512,7 +512,10 @@ void NiSkinPartition::PrepareTriParts(const std::vector& shapeTris) { void NiSkinInstance::Sync(NiStreamReversible& stream) { dataRef.Sync(stream); - skinPartitionRef.Sync(stream); + + if (stream.GetVersion().File() >= V10_1_0_101) + skinPartitionRef.Sync(stream); + targetRef.Sync(stream); boneRefs.Sync(stream); }