Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
43 kB · 750 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751#include "niffile.hpp"
#include <components/debug/debuglog.hpp>#include <components/files/hash.hpp>
#include <algorithm>#include <array>#include <limits>#include <map>#include <sstream>#include <stdexcept>
#include "controller.hpp"#include "data.hpp"#include "effect.hpp"#include "exception.hpp"#include "extra.hpp"#include "node.hpp"#include "particle.hpp"#include "physics.hpp"#include "property.hpp"#include "texture.hpp"
namespace Nif{
Reader::Reader(NIFFile& file, const ToUTF8::StatelessUtf8Encoder* encoder) : mVersion(file.mVersion) , mUserVersion(file.mUserVersion) , mBethVersion(file.mBethVersion) , mFilename(file.mPath) , mHash(file.mHash) , mRecords(file.mRecords) , mRoots(file.mRoots) , mUseSkinning(file.mUseSkinning) , mEncoder(encoder) { }
template <typename NodeType, RecordType recordType> static std::unique_ptr<Record> construct() { auto result = std::make_unique<NodeType>(); result->mRecordType = recordType; return result; }
using CreateRecord = std::unique_ptr<Record> (*)();
/// These are all the record types we know how to read. static std::map<std::string, CreateRecord> makeFactory() { return { // 4.0.0.2 refers to Bethesda variant of NetImmerse 4.0.0.2 file format // Gamebryo refers to files newer than 4.0.0.2 // Bethesda refers to custom records Bethesda introduced post-4.0.0.2
// NODES
// NiNode-like nodes, 4.0.0.2 { "NiNode", &construct<NiNode, RC_NiNode> }, { "AvoidNode", &construct<NiNode, RC_AvoidNode> }, { "NiBillboardNode", &construct<NiBillboardNode, RC_NiBillboardNode> }, { "NiBSAnimationNode", &construct<NiNode, RC_NiBSAnimationNode> }, { "NiBSParticleNode", &construct<NiNode, RC_NiBSParticleNode> }, { "NiCollisionSwitch", &construct<NiNode, RC_NiCollisionSwitch> }, { "NiSortAdjustNode", &construct<NiSortAdjustNode, RC_NiSortAdjustNode> }, { "RootCollisionNode", &construct<NiNode, RC_RootCollisionNode> },
// NiNode-like nodes, Bethesda { "BSBlastNode", &construct<BSRangeNode, RC_NiNode> }, { "BSDamageStage", &construct<BSRangeNode, RC_NiNode> }, { "BSDebrisNode", &construct<BSRangeNode, RC_NiNode> }, { "BSDistantObjectInstancedNode", &construct<BSDistantObjectInstancedNode, RC_BSDistantObjectInstancedNode> }, { "BSFadeNode", &construct<NiNode, RC_NiNode> }, { "BSLeafAnimNode", &construct<NiNode, RC_NiNode> }, { "BSMasterParticleSystem", &construct<BSMasterParticleSystem, RC_NiNode> }, { "BSMultiBoundNode", &construct<BSMultiBoundNode, RC_NiNode> }, { "BSOrderedNode", &construct<BSOrderedNode, RC_NiNode> }, { "BSRangeNode", &construct<BSRangeNode, RC_NiNode> }, { "BSTreeNode", &construct<BSTreeNode, RC_NiNode> }, { "BSValueNode", &construct<BSValueNode, RC_NiNode> },
// Switch nodes, 4.0.0.2 { "NiSwitchNode", &construct<NiSwitchNode, RC_NiSwitchNode> }, { "NiFltAnimationNode", &construct<NiFltAnimationNode, RC_NiFltAnimationNode> }, { "NiLODNode", &construct<NiLODNode, RC_NiLODNode> },
// NiSequence nodes, 4.0.0.2 { "NiSequenceStreamHelper", &construct<NiSequenceStreamHelper, RC_NiSequenceStreamHelper> },
// NiSequence nodes, Gamebryo { "NiSequence", &construct<NiSequence, RC_NiSequence> }, { "NiControllerSequence", &construct<NiControllerSequence, RC_NiControllerSequence> },
// Other nodes, 4.0.0.2 { "NiCamera", &construct<NiCamera, RC_NiCamera> },
// ACCUMULATORS
// 4.0.0.2 { "NiAlphaAccumulator", &construct<NiAlphaAccumulator, RC_NiAlphaAccumulator> }, { "NiClusterAccumulator", &construct<NiClusterAccumulator, RC_NiClusterAccumulator> },
// CONTROLLERS
// 4.0.0.2 { "NiAlphaController", &construct<NiAlphaController, RC_NiAlphaController> }, { "NiBSPArrayController", &construct<NiBSPArrayController, RC_NiBSPArrayController> }, { "NiFlipController", &construct<NiFlipController, RC_NiFlipController> }, { "NiGeomMorpherController", &construct<NiGeomMorpherController, RC_NiGeomMorpherController> }, { "NiKeyframeController", &construct<NiKeyframeController, RC_NiKeyframeController> }, { "NiLookAtController", &construct<NiLookAtController, RC_NiLookAtController> }, { "NiLightColorController", &construct<NiLightColorController, RC_NiLightColorController> }, { "NiMaterialColorController", &construct<NiMaterialColorController, RC_NiMaterialColorController> }, { "NiPathController", &construct<NiPathController, RC_NiPathController> }, { "NiRollController", &construct<NiRollController, RC_NiRollController> }, { "NiUVController", &construct<NiUVController, RC_NiUVController> }, { "NiVisController", &construct<NiVisController, RC_NiVisController> },
// Gamebryo { "NiBoneLODController", &construct<NiBoneLODController, RC_NiBoneLODController> }, { "NiControllerManager", &construct<NiControllerManager, RC_NiControllerManager> }, { "NiLightDimmerController", &construct<NiFloatInterpController, RC_NiLightDimmerController> }, { "NiTransformController", &construct<NiKeyframeController, RC_NiKeyframeController> }, { "NiTextureTransformController", &construct<NiTextureTransformController, RC_NiTextureTransformController> }, { "NiMultiTargetTransformController", &construct<NiMultiTargetTransformController, RC_NiMultiTargetTransformController> },
// Extra data controllers, Gamebryo { "NiColorExtraDataController", &construct<NiExtraDataController, RC_NiColorExtraDataController> }, { "NiFloatExtraDataController", &construct<NiFloatExtraDataController, RC_NiFloatExtraDataController> }, { "NiFloatsExtraDataController", &construct<NiFloatsExtraDataController, RC_NiFloatsExtraDataController> }, { "NiFloatsExtraDataPoint3Controller", &construct<NiFloatsExtraDataPoint3Controller, RC_NiFloatsExtraDataPoint3Controller> },
// Bethesda { "BSFrustumFOVController", &construct<NiFloatInterpController, RC_BSFrustumFOVController> }, { "BSKeyframeController", &construct<BSKeyframeController, RC_BSKeyframeController> }, { "BSLagBoneController", &construct<BSLagBoneController, RC_BSLagBoneController> }, { "BSProceduralLightningController", &construct<BSProceduralLightningController, RC_BSProceduralLightningController> }, { "BSMaterialEmittanceMultController", &construct<NiFloatInterpController, RC_BSMaterialEmittanceMultController> }, { "BSNiAlphaPropertyTestRefController", &construct<NiFloatInterpController, RC_BSNiAlphaPropertyTestRefController> }, { "BSRefractionFirePeriodController", &construct<NiSingleInterpController, RC_BSRefractionFirePeriodController> }, { "BSRefractionStrengthController", &construct<NiFloatInterpController, RC_BSRefractionStrengthController> }, { "BSEffectShaderPropertyColorController", &construct<BSEffectShaderPropertyColorController, RC_BSEffectShaderPropertyColorController> }, { "BSEffectShaderPropertyFloatController", &construct<BSEffectShaderPropertyFloatController, RC_BSEffectShaderPropertyFloatController> }, { "BSLightingShaderPropertyColorController", &construct<BSEffectShaderPropertyColorController, RC_BSLightingShaderPropertyColorController> }, { "BSLightingShaderPropertyFloatController", &construct<BSEffectShaderPropertyFloatController, RC_BSLightingShaderPropertyFloatController> }, { "BSLightingShaderPropertyUShortController", &construct<BSEffectShaderPropertyFloatController, RC_BSLightingShaderPropertyUShortController> }, { "bhkBlendController", &construct<bhkBlendController, RC_bhkBlendController> }, { "NiBSBoneLODController", &construct<NiBoneLODController, RC_NiBoneLODController> }, { "NiLightRadiusController", &construct<NiFloatInterpController, RC_NiLightRadiusController> },
// Interpolators, Gamebryo { "NiBSplineCompFloatInterpolator", &construct<NiBSplineCompFloatInterpolator, RC_NiBSplineCompFloatInterpolator> }, { "NiBSplineCompPoint3Interpolator", &construct<NiBSplineCompPoint3Interpolator, RC_NiBSplineCompPoint3Interpolator> }, { "NiBSplineCompTransformInterpolator", &construct<NiBSplineCompTransformInterpolator, RC_NiBSplineCompTransformInterpolator> }, { "NiBSplineTransformInterpolator", &construct<NiBSplineTransformInterpolator, RC_NiBSplineTransformInterpolator> }, { "NiBlendBoolInterpolator", &construct<NiBlendBoolInterpolator, RC_NiBlendBoolInterpolator> }, { "NiBlendFloatInterpolator", &construct<NiBlendFloatInterpolator, RC_NiBlendFloatInterpolator> }, { "NiBlendPoint3Interpolator", &construct<NiBlendPoint3Interpolator, RC_NiBlendPoint3Interpolator> }, { "NiBlendTransformInterpolator", &construct<NiBlendTransformInterpolator, RC_NiBlendTransformInterpolator> }, { "NiBoolInterpolator", &construct<NiBoolInterpolator, RC_NiBoolInterpolator> }, { "NiBoolTimelineInterpolator", &construct<NiBoolInterpolator, RC_NiBoolTimelineInterpolator> }, { "NiColorInterpolator", &construct<NiColorInterpolator, RC_NiColorInterpolator> }, { "NiFloatInterpolator", &construct<NiFloatInterpolator, RC_NiFloatInterpolator> }, { "NiLookAtInterpolator", &construct<NiLookAtInterpolator, RC_NiLookAtInterpolator> }, { "NiPathInterpolator", &construct<NiPathInterpolator, RC_NiPathInterpolator> }, { "NiPoint3Interpolator", &construct<NiPoint3Interpolator, RC_NiPoint3Interpolator> }, { "NiTransformInterpolator", &construct<NiTransformInterpolator, RC_NiTransformInterpolator> },
// Interpolators, Bethesda { "BSRotAccumTransfInterpolator", &construct<NiTransformInterpolator, RC_BSRotAccumTransfInterpolator> }, { "BSTreadTransfInterpolator", &construct<BSTreadTransfInterpolator, RC_BSTreadTransfInterpolator> },
// DATA
// 4.0.0.2 { "NiColorData", &construct<NiColorData, RC_NiColorData> }, { "NiFloatData", &construct<NiFloatData, RC_NiFloatData> }, { "NiKeyframeData", &construct<NiKeyframeData, RC_NiKeyframeData> }, { "NiMorphData", &construct<NiMorphData, RC_NiMorphData> }, { "NiPalette", &construct<NiPalette, RC_NiPalette> }, { "NiPixelData", &construct<NiPixelData, RC_NiPixelData> }, { "NiPosData", &construct<NiPosData, RC_NiPosData> }, { "NiSourceTexture", &construct<NiSourceTexture, RC_NiSourceTexture> }, { "NiUVData", &construct<NiUVData, RC_NiUVData> }, { "NiVisData", &construct<NiVisData, RC_NiVisData> },
// Gamebryo { "NiAdditionalGeometryData", &construct<NiAdditionalGeometryData, RC_NiAdditionalGeometryData> }, { "NiBoolData", &construct<NiBoolData, RC_NiBoolData> }, { "NiBSplineData", &construct<NiBSplineData, RC_NiBSplineData> }, { "NiBSplineBasisData", &construct<NiBSplineBasisData, RC_NiBSplineBasisData> }, { "NiDefaultAVObjectPalette", &construct<NiDefaultAVObjectPalette, RC_NiDefaultAVObjectPalette> }, { "NiTransformData", &construct<NiKeyframeData, RC_NiKeyframeData> },
// Bethesda { "BSAnimNote", &construct<BSAnimNote, RC_BSAnimNote> }, { "BSAnimNotes", &construct<BSAnimNotes, RC_BSAnimNotes> }, { "BSPackedAdditionalGeometryData", &construct<NiAdditionalGeometryData, RC_BSPackedAdditionalGeometryData> }, { "BSShaderTextureSet", &construct<BSShaderTextureSet, RC_BSShaderTextureSet> },
// DYNAMIC EFFECTS
// 4.0.0.2 { "NiAmbientLight", &construct<NiLight, RC_NiLight> }, { "NiDirectionalLight", &construct<NiLight, RC_NiLight> }, { "NiPointLight", &construct<NiPointLight, RC_NiLight> }, { "NiSpotLight", &construct<NiSpotLight, RC_NiLight> }, { "NiTextureEffect", &construct<NiTextureEffect, RC_NiTextureEffect> },
// EXTRA DATA
// 4.0.0.2 { "NiExtraData", &construct<NiExtraData, RC_NiExtraData> }, { "NiStringExtraData", &construct<NiStringExtraData, RC_NiStringExtraData> }, { "NiTextKeyExtraData", &construct<NiTextKeyExtraData, RC_NiTextKeyExtraData> }, { "NiVertWeightsExtraData", &construct<NiVertWeightsExtraData, RC_NiVertWeightsExtraData> },
// Gamebryo { "NiBinaryExtraData", &construct<NiBinaryExtraData, RC_NiBinaryExtraData> }, { "NiBooleanExtraData", &construct<NiBooleanExtraData, RC_NiBooleanExtraData> }, { "NiColorExtraData", &construct<NiVectorExtraData, RC_NiColorExtraData> }, { "NiFloatExtraData", &construct<NiFloatExtraData, RC_NiFloatExtraData> }, { "NiFloatsExtraData", &construct<NiFloatsExtraData, RC_NiFloatsExtraData> }, { "NiIntegerExtraData", &construct<NiIntegerExtraData, RC_NiIntegerExtraData> }, { "NiIntegersExtraData", &construct<NiIntegersExtraData, RC_NiIntegersExtraData> }, { "NiVectorExtraData", &construct<NiVectorExtraData, RC_NiVectorExtraData> }, { "NiStringsExtraData", &construct<NiStringsExtraData, RC_NiStringsExtraData> }, { "NiStringPalette", &construct<NiStringPalette, RC_NiStringPalette> },
// Bethesda bounds { "BSBound", &construct<BSBound, RC_BSBound> }, { "BSMultiBound", &construct<BSMultiBound, RC_BSMultiBound> }, { "BSMultiBoundAABB", &construct<BSMultiBoundAABB, RC_BSMultiBoundAABB> }, { "BSMultiBoundOBB", &construct<BSMultiBoundOBB, RC_BSMultiBoundOBB> }, { "BSMultiBoundSphere", &construct<BSMultiBoundSphere, RC_BSMultiBoundSphere> },
// Bethesda markers { "BSFurnitureMarker", &construct<BSFurnitureMarker, RC_BSFurnitureMarker> }, { "BSFurnitureMarkerNode", &construct<BSFurnitureMarker, RC_BSFurnitureMarker> }, { "BSInvMarker", &construct<BSInvMarker, RC_BSInvMarker> },
// Other Bethesda records { "BSExtraData", &construct<BSExtraData, RC_BSExtraData> }, { "BSBehaviorGraphExtraData", &construct<BSBehaviorGraphExtraData, RC_BSBehaviorGraphExtraData> }, { "BSBoneLODExtraData", &construct<BSBoneLODExtraData, RC_BSBoneLODExtraData> }, { "BSClothExtraData", &construct<BSClothExtraData, RC_BSClothExtraData> }, { "BSCollisionQueryProxyExtraData", &construct<BSCollisionQueryProxyExtraData, RC_BSCollisionQueryProxyExtraData> }, { "BSConnectPoint::Children", &construct<BSConnectPoint::Children, RC_BSConnectPointChildren> }, { "BSConnectPoint::Parents", &construct<BSConnectPoint::Parents, RC_BSConnectPointParents> }, { "BSDecalPlacementVectorExtraData", &construct<BSDecalPlacementVectorExtraData, RC_BSDecalPlacementVectorExtraData> }, { "BSDistantObjectExtraData", &construct<BSDistantObjectExtraData, RC_BSDistantObjectExtraData> }, { "BSDistantObjectLargeRefExtraData", &construct<BSDistantObjectLargeRefExtraData, RC_BSDistantObjectLargeRefExtraData> }, { "BSEyeCenterExtraData", &construct<BSEyeCenterExtraData, RC_BSEyeCenterExtraData> }, { "BSPackedCombinedSharedGeomDataExtra", &construct<BSPackedCombinedSharedGeomDataExtra, RC_BSPackedCombinedSharedGeomDataExtra> }, { "BSPositionData", &construct<BSPositionData, RC_BSPositionData> }, { "BSWArray", &construct<BSWArray, RC_BSWArray> }, { "BSXFlags", &construct<NiIntegerExtraData, RC_BSXFlags> },
// GEOMETRY
// 4.0.0.2 { "NiLines", &construct<NiLines, RC_NiLines> }, { "NiLinesData", &construct<NiLinesData, RC_NiLinesData> }, { "NiSkinData", &construct<NiSkinData, RC_NiSkinData> }, { "NiSkinInstance", &construct<NiSkinInstance, RC_NiSkinInstance> }, { "NiSkinPartition", &construct<NiSkinPartition, RC_NiSkinPartition> }, { "NiTriShape", &construct<NiTriShape, RC_NiTriShape> }, { "NiTriShapeData", &construct<NiTriShapeData, RC_NiTriShapeData> }, { "NiTriStrips", &construct<NiTriStrips, RC_NiTriStrips> }, { "NiTriStripsData", &construct<NiTriStripsData, RC_NiTriStripsData> },
// Bethesda { "BSDismemberSkinInstance", &construct<BSDismemberSkinInstance, RC_BSDismemberSkinInstance> }, { "BSSkin::Instance", &construct<BSSkinInstance, RC_BSSkinInstance> }, { "BSSkin::BoneData", &construct<BSSkinBoneData, RC_BSSkinBoneData> }, { "BSTriShape", &construct<BSTriShape, RC_BSTriShape> }, { "BSDynamicTriShape", &construct<BSDynamicTriShape, RC_BSDynamicTriShape> }, { "BSLODTriShape", &construct<BSLODTriShape, RC_BSLODTriShape> }, { "BSMeshLODTriShape", &construct<BSMeshLODTriShape, RC_BSMeshLODTriShape> }, { "BSSegmentedTriShape", &construct<BSSegmentedTriShape, RC_BSSegmentedTriShape> }, { "BSSubIndexTriShape", &construct<BSSubIndexTriShape, RC_BSSubIndexTriShape> },
// PARTICLES
// Geometry, 4.0.0.2 { "NiAutoNormalParticles", &construct<NiParticles, RC_NiParticles> }, { "NiAutoNormalParticlesData", &construct<NiParticlesData, RC_NiParticlesData> }, { "NiParticles", &construct<NiParticles, RC_NiParticles> }, { "NiParticlesData", &construct<NiParticlesData, RC_NiParticlesData> }, { "NiRotatingParticles", &construct<NiParticles, RC_NiParticles> }, { "NiRotatingParticlesData", &construct<NiRotatingParticlesData, RC_NiParticlesData> },
// Geometry, Gamebryo { "NiParticleSystem", &construct<NiParticleSystem, RC_NiParticleSystem> }, { "NiMeshParticleSystem", &construct<NiParticleSystem, RC_NiParticleSystem> }, { "NiPSysData", &construct<NiPSysData, RC_NiPSysData> }, { "NiMeshPSysData", &construct<NiMeshPSysData, RC_NiMeshPSysData> },
// Geometry, Bethesda { "BSStripParticleSystem", &construct<NiParticleSystem, RC_BSStripParticleSystem> }, { "BSStripPSysData", &construct<BSStripPSysData, RC_BSStripPSysData> },
// Modifiers, 4.0.0.2 { "NiGravity", &construct<NiGravity, RC_NiGravity> }, { "NiParticleBomb", &construct<NiParticleBomb, RC_NiParticleBomb> }, { "NiParticleColorModifier", &construct<NiParticleColorModifier, RC_NiParticleColorModifier> }, { "NiParticleGrowFade", &construct<NiParticleGrowFade, RC_NiParticleGrowFade> }, { "NiParticleRotation", &construct<NiParticleRotation, RC_NiParticleRotation> },
// Modifiers, Gamebryo { "NiPSysAgeDeathModifier", &construct<NiPSysAgeDeathModifier, RC_NiPSysAgeDeathModifier> }, { "NiPSysBombModifier", &construct<NiPSysBombModifier, RC_NiPSysBombModifier> }, { "NiPSysBoundUpdateModifier", &construct<NiPSysBoundUpdateModifier, RC_NiPSysBoundUpdateModifier> }, { "NiPSysColorModifier", &construct<NiPSysColorModifier, RC_NiPSysColorModifier> }, { "NiPSysDragModifier", &construct<NiPSysDragModifier, RC_NiPSysDragModifier> }, { "NiPSysGravityModifier", &construct<NiPSysGravityModifier, RC_NiPSysGravityModifier> }, { "NiPSysGrowFadeModifier", &construct<NiPSysGrowFadeModifier, RC_NiPSysGrowFadeModifier> }, { "NiPSysPositionModifier", &construct<NiPSysModifier, RC_NiPSysPositionModifier> }, { "NiPSysRotationModifier", &construct<NiPSysRotationModifier, RC_NiPSysRotationModifier> }, { "NiPSysSpawnModifier", &construct<NiPSysSpawnModifier, RC_NiPSysSpawnModifier> }, { "NiPSysMeshUpdateModifier", &construct<NiPSysMeshUpdateModifier, RC_NiPSysMeshUpdateModifier> },
// Modifiers, Bethesda { "BSParentVelocityModifier", &construct<BSParentVelocityModifier, RC_BSParentVelocityModifier> }, { "BSPSysHavokUpdateModifier", &construct<BSPSysHavokUpdateModifier, RC_BSPSysHavokUpdateModifier> }, { "BSPSysInheritVelocityModifier", &construct<BSPSysInheritVelocityModifier, RC_BSPSysInheritVelocityModifier> }, { "BSPSysLODModifier", &construct<BSPSysLODModifier, RC_BSPSysLODModifier> }, { "BSPSysRecycleBoundModifier", &construct<BSPSysRecycleBoundModifier, RC_BSPSysRecycleBoundModifier> }, { "BSPSysScaleModifier", &construct<BSPSysScaleModifier, RC_BSPSysScaleModifier> }, { "BSPSysSimpleColorModifier", &construct<BSPSysSimpleColorModifier, RC_BSPSysSimpleColorModifier> }, { "BSPSysStripUpdateModifier", &construct<BSPSysStripUpdateModifier, RC_BSPSysStripUpdateModifier> }, { "BSPSysSubTexModifier", &construct<BSPSysSubTexModifier, RC_BSPSysSubTexModifier> }, { "BSWindModifier", &construct<BSWindModifier, RC_BSWindModifier> },
// Emitters, Gamebryo { "NiPSysBoxEmitter", &construct<NiPSysBoxEmitter, RC_NiPSysBoxEmitter> }, { "NiPSysCylinderEmitter", &construct<NiPSysCylinderEmitter, RC_NiPSysCylinderEmitter> }, { "NiPSysMeshEmitter", &construct<NiPSysMeshEmitter, RC_NiPSysMeshEmitter> }, { "NiPSysSphereEmitter", &construct<NiPSysSphereEmitter, RC_NiPSysSphereEmitter> },
// Emitters, Bethesda { "BSPSysArrayEmitter", &construct<NiPSysVolumeEmitter, RC_BSPSysArrayEmitter> },
// Modifier controllers, Gamebryo { "NiPSysAirFieldAirFrictionCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysAirFieldAirFrictionCtlr> }, { "NiPSysAirFieldInheritVelocityCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysAirFieldInheritVelocityCtlr> }, { "NiPSysAirFieldSpreadCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysAirFieldSpreadCtlr> }, { "NiPSysEmitterCtlr", &construct<NiPSysEmitterCtlr, RC_NiPSysEmitterCtlr> }, { "NiPSysEmitterDeclinationCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterDeclinationCtlr> }, { "NiPSysEmitterDeclinationVarCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterDeclinationVarCtlr> }, { "NiPSysEmitterInitialRadiusCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterInitialRadiusCtlr> }, { "NiPSysEmitterLifeSpanCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterLifeSpanCtlr> }, { "NiPSysEmitterPlanarAngleCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterPlanarAngleCtlr> }, { "NiPSysEmitterPlanarAngleVarCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterPlanarAngleVarCtlr> }, { "NiPSysEmitterSpeedCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysEmitterSpeedCtlr> }, { "NiPSysFieldAttenuationCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysFieldAttenuationCtlr> }, { "NiPSysFieldMagnitudeCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysFieldMagnitudeCtlr> }, { "NiPSysFieldMaxDistanceCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysFieldMaxDistanceCtlr> }, { "NiPSysGravityStrengthCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysGravityStrengthCtlr> }, { "NiPSysInitialRotSpeedCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysInitialRotSpeedCtlr> }, { "NiPSysInitialRotSpeedVarCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysInitialRotSpeedVarCtlr> }, { "NiPSysInitialRotAngleCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysInitialRotAngleCtlr> }, { "NiPSysInitialRotAngleVarCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysInitialRotAngleVarCtlr> }, { "NiPSysModifierActiveCtlr", &construct<NiPSysModifierBoolCtlr, RC_NiPSysModifierActiveCtlr> }, { "NiPSysRotDampeningCtlr", &construct<NiPSysModifierFloatCtlr, RC_NiPSysRotDampeningCtlr> },
// Modifier controllers, Bethesda { "BSPSysMultiTargetEmitterCtlr", &construct<BSPSysMultiTargetEmitterCtlr, RC_BSPSysMultiTargetEmitterCtlr> },
// Modifier controller data, Gamebryo { "NiPSysEmitterCtlrData", &construct<NiPSysEmitterCtlrData, RC_NiPSysEmitterCtlrData> },
// Colliders, 4.0.0.2 { "NiPlanarCollider", &construct<NiPlanarCollider, RC_NiPlanarCollider> }, { "NiSphericalCollider", &construct<NiSphericalCollider, RC_NiSphericalCollider> },
// Colliders, Gamebryo { "NiPSysColliderManager", &construct<NiPSysColliderManager, RC_NiPSysColliderManager> }, { "NiPSysPlanarCollider", &construct<NiPSysPlanarCollider, RC_NiPSysPlanarCollider> }, { "NiPSysSphericalCollider", &construct<NiPSysSphericalCollider, RC_NiPSysSphericalCollider> },
// Particle system controllers, 4.0.0.2 { "NiParticleSystemController", &construct<NiParticleSystemController, RC_NiParticleSystemController> },
// Particle system controllers, Gamebryo { "NiPSysResetOnLoopCtlr", &construct<NiTimeController, RC_NiPSysResetOnLoopCtlr> }, { "NiPSysUpdateCtlr", &construct<NiTimeController, RC_NiPSysUpdateCtlr> },
// PHYSICS
// Collision objects, Gamebryo { "NiCollisionObject", &construct<NiCollisionObject, RC_NiCollisionObject> },
// Collision objects, Bethesda { "bhkCollisionObject", &construct<bhkCollisionObject, RC_bhkCollisionObject> }, { "bhkPCollisionObject", &construct<bhkCollisionObject, RC_bhkCollisionObject> }, { "bhkSPCollisionObject", &construct<bhkCollisionObject, RC_bhkCollisionObject> }, { "bhkNPCollisionObject", &construct<bhkNPCollisionObject, RC_bhkCollisionObject> }, { "bhkBlendCollisionObject", &construct<bhkBlendCollisionObject, RC_bhkBlendCollisionObject> },
// Constraint records, Bethesda { "bhkBallAndSocketConstraint", &construct<bhkBallAndSocketConstraint, RC_bhkBallAndSocketConstraint> }, { "bhkBallSocketConstraintChain", &construct<bhkBallSocketConstraintChain, RC_bhkBallSocketConstraintChain> }, { "bhkHingeConstraint", &construct<bhkHingeConstraint, RC_bhkHingeConstraint> }, { "bhkLimitedHingeConstraint", &construct<bhkLimitedHingeConstraint, RC_bhkLimitedHingeConstraint> }, { "bhkRagdollConstraint", &construct<bhkRagdollConstraint, RC_bhkRagdollConstraint> }, { "bhkStiffSpringConstraint", &construct<bhkStiffSpringConstraint, RC_bhkStiffSpringConstraint> }, { "bhkPrismaticConstraint", &construct<bhkPrismaticConstraint, RC_bhkPrismaticConstraint> }, { "bhkMalleableConstraint", &construct<bhkMalleableConstraint, RC_bhkMalleableConstraint> }, { "bhkBreakableConstraint", &construct<bhkBreakableConstraint, RC_bhkBreakableConstraint> },
// Physics body records, Bethesda { "bhkRigidBody", &construct<bhkRigidBody, RC_bhkRigidBody> }, { "bhkRigidBodyT", &construct<bhkRigidBody, RC_bhkRigidBodyT> },
// Physics geometry records, Bethesda { "bhkBoxShape", &construct<bhkBoxShape, RC_bhkBoxShape> }, { "bhkCapsuleShape", &construct<bhkCapsuleShape, RC_bhkCapsuleShape> }, { "bhkCylinderShape", &construct<bhkCylinderShape, RC_bhkCylinderShape> }, { "bhkCompressedMeshShape", &construct<bhkCompressedMeshShape, RC_bhkCompressedMeshShape> }, { "bhkCompressedMeshShapeData", &construct<bhkCompressedMeshShapeData, RC_bhkCompressedMeshShapeData> }, { "bhkConvexListShape", &construct<bhkConvexListShape, RC_bhkConvexListShape> }, { "bhkConvexSweepShape", &construct<bhkConvexSweepShape, RC_bhkConvexSweepShape> }, { "bhkConvexTransformShape", &construct<bhkConvexTransformShape, RC_bhkConvexTransformShape> }, { "bhkConvexVerticesShape", &construct<bhkConvexVerticesShape, RC_bhkConvexVerticesShape> }, { "bhkListShape", &construct<bhkListShape, RC_bhkListShape> }, { "bhkMoppBvTreeShape", &construct<bhkMoppBvTreeShape, RC_bhkMoppBvTreeShape> }, { "bhkMeshShape", &construct<bhkMeshShape, RC_bhkMeshShape> }, { "bhkMultiSphereShape", &construct<bhkMultiSphereShape, RC_bhkMultiSphereShape> }, { "bhkNiTriStripsShape", &construct<bhkNiTriStripsShape, RC_bhkNiTriStripsShape> }, { "bhkPackedNiTriStripsShape", &construct<bhkPackedNiTriStripsShape, RC_bhkPackedNiTriStripsShape> }, { "hkPackedNiTriStripsData", &construct<hkPackedNiTriStripsData, RC_hkPackedNiTriStripsData> }, { "bhkPlaneShape", &construct<bhkPlaneShape, RC_bhkPlaneShape> }, { "bhkSphereShape", &construct<bhkSphereShape, RC_bhkSphereShape> }, { "bhkTransformShape", &construct<bhkConvexTransformShape, RC_bhkConvexTransformShape> },
// Phantom records, Bethesda { "bhkAabbPhantom", &construct<bhkAabbPhantom, RC_bhkAabbPhantom> }, { "bhkSimpleShapePhantom", &construct<bhkSimpleShapePhantom, RC_bhkSimpleShapePhantom> },
// Physics system records, Bethesda { "bhkPhysicsSystem", &construct<bhkPhysicsSystem, RC_bhkPhysicsSystem> }, { "bhkRagdollSystem", &construct<bhkRagdollSystem, RC_bhkRagdollSystem> },
// Action records { "bhkLiquidAction", &construct<bhkLiquidAction, RC_bhkLiquidAction> }, { "bhkOrientHingedBodyAction", &construct<bhkOrientHingedBodyAction, RC_bhkOrientHingedBodyAction> },
// Ragdoll template records { "bhkRagdollTemplate", &construct<bhkRagdollTemplate, RC_bhkRagdollTemplate> }, { "bhkRagdollTemplateData", &construct<bhkRagdollTemplateData, RC_bhkRagdollTemplateData> },
// Other records { "bhkPoseArray", &construct<bhkPoseArray, RC_bhkPoseArray> },
// PROPERTIES
// 4.0.0.2 { "NiAlphaProperty", &construct<NiAlphaProperty, RC_NiAlphaProperty> }, { "NiDitherProperty", &construct<NiDitherProperty, RC_NiDitherProperty> }, { "NiFogProperty", &construct<NiFogProperty, RC_NiFogProperty> }, { "NiMaterialProperty", &construct<NiMaterialProperty, RC_NiMaterialProperty> }, { "NiShadeProperty", &construct<NiShadeProperty, RC_NiShadeProperty> }, { "NiSpecularProperty", &construct<NiSpecularProperty, RC_NiSpecularProperty> }, { "NiStencilProperty", &construct<NiStencilProperty, RC_NiStencilProperty> }, { "NiTexturingProperty", &construct<NiTexturingProperty, RC_NiTexturingProperty> }, { "NiVertexColorProperty", &construct<NiVertexColorProperty, RC_NiVertexColorProperty> }, { "NiWireframeProperty", &construct<NiWireframeProperty, RC_NiWireframeProperty> }, { "NiZBufferProperty", &construct<NiZBufferProperty, RC_NiZBufferProperty> },
// Shader properties, Bethesda { "BSShaderProperty", &construct<BSShaderProperty, RC_BSShaderProperty> }, { "BSShaderPPLightingProperty", &construct<BSShaderPPLightingProperty, RC_BSShaderPPLightingProperty> }, { "BSShaderNoLightingProperty", &construct<BSShaderNoLightingProperty, RC_BSShaderNoLightingProperty> }, { "BSDistantTreeShaderProperty", &construct<BSShaderProperty, RC_BSDistantTreeShaderProperty> }, { "BSLightingShaderProperty", &construct<BSLightingShaderProperty, RC_BSLightingShaderProperty> }, { "BSEffectShaderProperty", &construct<BSEffectShaderProperty, RC_BSEffectShaderProperty> }, { "BSSkyShaderProperty", &construct<BSSkyShaderProperty, RC_BSSkyShaderProperty> }, { "BSWaterShaderProperty", &construct<BSWaterShaderProperty, RC_BSWaterShaderProperty> }, { "DistantLODShaderProperty", &construct<BSShaderProperty, RC_DistantLODShaderProperty> }, { "HairShaderProperty", &construct<BSShaderProperty, RC_HairShaderProperty> }, { "Lighting30ShaderProperty", &construct<BSShaderPPLightingProperty, RC_BSShaderPPLightingProperty> }, { "SkyShaderProperty", &construct<SkyShaderProperty, RC_SkyShaderProperty> }, { "TallGrassShaderProperty", &construct<TallGrassShaderProperty, RC_TallGrassShaderProperty> }, { "TileShaderProperty", &construct<TileShaderProperty, RC_TileShaderProperty> }, { "VolumetricFogShaderProperty", &construct<BSShaderProperty, RC_VolumetricFogShaderProperty> }, { "WaterShaderProperty", &construct<BSShaderProperty, RC_WaterShaderProperty> }, }; }
/// Make the factory map used for parsing the file static const std::map<std::string, CreateRecord> factories = makeFactory();
std::string Reader::versionToString(std::uint32_t version) { int major = (version >> 24) & 0xFF; int minor = (version >> 16) & 0xFF; int patch = (version >> 8) & 0xFF; int rev = version & 0xFF;
std::stringstream stream; stream << major << "." << minor << "." << patch << "." << rev; return stream.str(); }
void Reader::parse(Files::IStreamPtr&& stream) { const bool writeDebug = sWriteNifDebugLog; if (writeDebug) Log(Debug::Verbose) << "NIF Debug: Reading file: '" << mFilename << "'";
const std::array<std::uint64_t, 2> fileHash = Files::getHash(mFilename, *stream); mHash.append(reinterpret_cast<const char*>(fileHash.data()), fileHash.size() * sizeof(std::uint64_t));
NIFStream nif(*this, std::move(stream), mEncoder);
// Check the header string std::string head = nif.getVersionString(); static const std::array<std::string, 2> verStrings = { "NetImmerse File Format", "Gamebryo File Format", }; const bool supportedHeader = std::any_of(verStrings.begin(), verStrings.end(), [&](const std::string& verString) { return head.starts_with(verString); }); if (!supportedHeader) throw Nif::Exception("Invalid NIF header: " + head, mFilename);
// Get BCD version nif.read(mVersion);
const bool hasEndianness = mVersion >= NIFStream::generateVersion(20, 0, 0, 4); const bool hasUserVersion = mVersion >= NIFStream::generateVersion(10, 0, 1, 8); const bool hasRecTypeListings = mVersion >= NIFStream::generateVersion(5, 0, 0, 1); const bool hasRecTypeHashes = mVersion == NIFStream::generateVersion(20, 3, 1, 2); const bool hasRecordSizes = mVersion >= NIFStream::generateVersion(20, 2, 0, 5); const bool hasGroups = mVersion >= NIFStream::generateVersion(5, 0, 0, 6); const bool hasStringTable = mVersion >= NIFStream::generateVersion(20, 1, 0, 1); const bool hasRecordSeparators = mVersion >= NIFStream::generateVersion(10, 0, 0, 0) && mVersion < NIFStream::generateVersion(10, 2, 0, 0);
// Record type list std::vector<std::string> recTypes; // Record type mapping for each record std::vector<std::uint16_t> recTypeIndices;
{ std::uint8_t endianness = 1; if (hasEndianness) nif.read(endianness);
// TODO: find some big-endian files and investigate the difference if (endianness == 0) throw Nif::Exception("Big endian NIF files are unsupported", mFilename); }
if (hasUserVersion) nif.read(mUserVersion);
const std::uint32_t recordsCount = nif.get<std::uint32_t>();
mRecords.reserve(recordsCount);
// Bethesda stream header { bool hasBSStreamHeader = false; if (mVersion == NIFFile::VER_OB_OLD) hasBSStreamHeader = true; else if (mUserVersion >= 3 && mVersion >= NIFStream::generateVersion(10, 1, 0, 0)) { if (mVersion <= NIFFile::VER_OB || mVersion == NIFFile::VER_BGS) hasBSStreamHeader = mUserVersion <= 11 || mVersion >= NIFFile::VER_OB; }
if (hasBSStreamHeader) { nif.read(mBethVersion); nif.getExportString(); // Author if (mBethVersion >= 131) nif.get<std::uint32_t>(); // Unknown else nif.getExportString(); // Process script nif.getExportString(); // Export script if (mBethVersion >= 103) nif.getExportString(); // Max file path } }
if (writeDebug) { std::stringstream versionInfo; versionInfo << "NIF Debug: Version: " << versionToString(mVersion); if (mUserVersion) versionInfo << "\nUser version: " << mUserVersion; if (mBethVersion) versionInfo << "\nBSStream version: " << mBethVersion; Log(Debug::Verbose) << versionInfo.str(); }
if (hasRecTypeListings) { // TODO: 20.3.1.2 uses DJB hashes instead of strings if (hasRecTypeHashes) throw Nif::Exception("Hashed record types are unsupported", mFilename); else { nif.getSizedStrings(recTypes, nif.get<std::uint16_t>()); nif.readVector(recTypeIndices, recordsCount); } }
if (hasRecordSizes) // Record sizes { std::vector<std::uint32_t> recSizes; // Currently unused nif.readVector(recSizes, recordsCount); }
if (hasStringTable) { std::uint32_t stringNum, maxStringLength; nif.read(stringNum); nif.read(maxStringLength); nif.getSizedStrings(mStrings, stringNum); }
if (hasGroups) { std::vector<std::uint32_t> groups; // Currently unused nif.readVector(groups, nif.get<std::uint32_t>()); }
for (std::size_t i = 0; i < recordsCount; i++) { std::unique_ptr<Record> r;
std::string rec = hasRecTypeListings ? recTypes.at(recTypeIndices[i]) : nif.get<std::string>(); if (rec.empty()) { std::stringstream error; error << "Record type is blank (index " << i << ")"; throw Nif::Exception(error.str(), mFilename); }
// Record separator. Some Havok records in Oblivion do not have it. if (hasRecordSeparators && !rec.starts_with("bhk") && nif.get<int32_t>()) throw Nif::Exception("Non-zero separator precedes " + rec + ", index " + std::to_string(i), mFilename);
const auto entry = factories.find(rec);
if (entry == factories.end()) throw Nif::Exception("Unknown record type " + rec, mFilename);
r = entry->second();
if (writeDebug) Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i;
assert(r != nullptr); assert(r->mRecordType != RC_MISSING); r->mRecordName = std::move(rec); r->mRecordIndex = static_cast<unsigned>(i); r->read(&nif); mRecords.push_back(std::move(r)); }
// Determine which records are roots const std::uint32_t rootsCount = nif.get<uint32_t>(); mRoots.reserve(rootsCount); constexpr std::uint32_t maxDoesNotPointWarnings = 10; std::uint32_t doesNotPointWarnings = 0; for (std::size_t i = 0; i < rootsCount; i++) { std::int32_t idx; nif.read(idx); if (idx >= 0 && static_cast<std::size_t>(idx) < mRecords.size()) { mRoots.push_back(mRecords[idx].get()); } else { mRoots.push_back(nullptr); ++doesNotPointWarnings; if (doesNotPointWarnings <= maxDoesNotPointWarnings) { Log(Debug::Warning) << "NIFFile Warning: Root " << i + 1 << " does not point to a record: index " << idx << ". File: " << mFilename; } } } if (doesNotPointWarnings > maxDoesNotPointWarnings) Log(Debug::Warning) << "NIFFile Warning: " << doesNotPointWarnings - maxDoesNotPointWarnings << " more roots did not point to a record. File: " << mFilename;
// Once parsing is done, do post-processing. for (const auto& record : mRecords) record->post(*this); }
void Reader::setUseSkinning(bool skinning) { mUseSkinning = skinning; }
std::atomic_bool Reader::sWriteNifDebugLog = false;
void Reader::setWriteNifDebugLog(bool value) { sWriteNifDebugLog = value; }
std::string Reader::getString(std::uint32_t index) const { if (index == std::numeric_limits<std::uint32_t>::max()) return std::string(); return mStrings.at(index); }
}