mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 13:26:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			384 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			384 lines
		
	
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#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 "controlled.hpp"
 | 
						|
#include "controller.hpp"
 | 
						|
#include "data.hpp"
 | 
						|
#include "effect.hpp"
 | 
						|
#include "extra.hpp"
 | 
						|
#include "physics.hpp"
 | 
						|
#include "property.hpp"
 | 
						|
 | 
						|
namespace Nif
 | 
						|
{
 | 
						|
 | 
						|
/// Open a NIF stream. The name is used for error messages.
 | 
						|
NIFFile::NIFFile(Files::IStreamPtr&& stream, const std::string &name)
 | 
						|
    : filename(name)
 | 
						|
{
 | 
						|
    parse(std::move(stream));
 | 
						|
}
 | 
						|
 | 
						|
template <typename NodeType, RecordType recordType>
 | 
						|
static std::unique_ptr<Record> construct()
 | 
						|
{
 | 
						|
    auto result = std::make_unique<NodeType>();
 | 
						|
    result->recType = 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 
 | 
						|
    {
 | 
						|
        {"NiNode"                           , &construct <NiNode                            , RC_NiNode                             >},
 | 
						|
        {"NiSwitchNode"                     , &construct <NiSwitchNode                      , RC_NiSwitchNode                       >},
 | 
						|
        {"NiLODNode"                        , &construct <NiLODNode                         , RC_NiLODNode                          >},
 | 
						|
        {"NiFltAnimationNode"               , &construct <NiFltAnimationNode                , RC_NiFltAnimationNode                 >},
 | 
						|
        {"AvoidNode"                        , &construct <NiNode                            , RC_AvoidNode                          >},
 | 
						|
        {"NiCollisionSwitch"                , &construct <NiNode                            , RC_NiCollisionSwitch                  >},
 | 
						|
        {"NiBSParticleNode"                 , &construct <NiNode                            , RC_NiBSParticleNode                   >},
 | 
						|
        {"NiBSAnimationNode"                , &construct <NiNode                            , RC_NiBSAnimationNode                  >},
 | 
						|
        {"NiBillboardNode"                  , &construct <NiBillboardNode                   , RC_NiBillboardNode                    >},
 | 
						|
        {"NiTriShape"                       , &construct <NiTriShape                        , RC_NiTriShape                         >},
 | 
						|
        {"NiTriStrips"                      , &construct <NiTriStrips                       , RC_NiTriStrips                        >},
 | 
						|
        {"NiLines"                          , &construct <NiLines                           , RC_NiLines                            >},
 | 
						|
        {"NiParticles"                      , &construct <NiParticles                       , RC_NiParticles                        >},
 | 
						|
        {"NiRotatingParticles"              , &construct <NiParticles                       , RC_NiParticles                        >},
 | 
						|
        {"NiAutoNormalParticles"            , &construct <NiParticles                       , RC_NiParticles                        >},
 | 
						|
        {"NiCamera"                         , &construct <NiCamera                          , RC_NiCamera                           >},
 | 
						|
        {"RootCollisionNode"                , &construct <NiNode                            , RC_RootCollisionNode                  >},
 | 
						|
        {"NiTexturingProperty"              , &construct <NiTexturingProperty               , RC_NiTexturingProperty                >},
 | 
						|
        {"NiFogProperty"                    , &construct <NiFogProperty                     , RC_NiFogProperty                      >},
 | 
						|
        {"NiMaterialProperty"               , &construct <NiMaterialProperty                , RC_NiMaterialProperty                 >},
 | 
						|
        {"NiZBufferProperty"                , &construct <NiZBufferProperty                 , RC_NiZBufferProperty                  >},
 | 
						|
        {"NiAlphaProperty"                  , &construct <NiAlphaProperty                   , RC_NiAlphaProperty                    >},
 | 
						|
        {"NiVertexColorProperty"            , &construct <NiVertexColorProperty             , RC_NiVertexColorProperty              >},
 | 
						|
        {"NiShadeProperty"                  , &construct <NiShadeProperty                   , RC_NiShadeProperty                    >},
 | 
						|
        {"NiDitherProperty"                 , &construct <NiDitherProperty                  , RC_NiDitherProperty                   >},
 | 
						|
        {"NiWireframeProperty"              , &construct <NiWireframeProperty               , RC_NiWireframeProperty                >},
 | 
						|
        {"NiSpecularProperty"               , &construct <NiSpecularProperty                , RC_NiSpecularProperty                 >},
 | 
						|
        {"NiStencilProperty"                , &construct <NiStencilProperty                 , RC_NiStencilProperty                  >},
 | 
						|
        {"NiVisController"                  , &construct <NiVisController                   , RC_NiVisController                    >},
 | 
						|
        {"NiGeomMorpherController"          , &construct <NiGeomMorpherController           , RC_NiGeomMorpherController            >},
 | 
						|
        {"NiKeyframeController"             , &construct <NiKeyframeController              , RC_NiKeyframeController               >},
 | 
						|
        {"NiAlphaController"                , &construct <NiAlphaController                 , RC_NiAlphaController                  >},
 | 
						|
        {"NiRollController"                 , &construct <NiRollController                  , RC_NiRollController                   >},
 | 
						|
        {"NiUVController"                   , &construct <NiUVController                    , RC_NiUVController                     >},
 | 
						|
        {"NiPathController"                 , &construct <NiPathController                  , RC_NiPathController                   >},
 | 
						|
        {"NiMaterialColorController"        , &construct <NiMaterialColorController         , RC_NiMaterialColorController          >},
 | 
						|
        {"NiBSPArrayController"             , &construct <NiBSPArrayController              , RC_NiBSPArrayController               >},
 | 
						|
        {"NiParticleSystemController"       , &construct <NiParticleSystemController        , RC_NiParticleSystemController         >},
 | 
						|
        {"NiFlipController"                 , &construct <NiFlipController                  , RC_NiFlipController                   >},
 | 
						|
        {"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                    >},
 | 
						|
        {"NiExtraData"                      , &construct <NiExtraData                       , RC_NiExtraData                        >},
 | 
						|
        {"NiVertWeightsExtraData"           , &construct <NiVertWeightsExtraData            , RC_NiVertWeightsExtraData             >},
 | 
						|
        {"NiTextKeyExtraData"               , &construct <NiTextKeyExtraData                , RC_NiTextKeyExtraData                 >},
 | 
						|
        {"NiStringExtraData"                , &construct <NiStringExtraData                 , RC_NiStringExtraData                  >},
 | 
						|
        {"NiGravity"                        , &construct <NiGravity                         , RC_NiGravity                          >},
 | 
						|
        {"NiPlanarCollider"                 , &construct <NiPlanarCollider                  , RC_NiPlanarCollider                   >},
 | 
						|
        {"NiSphericalCollider"              , &construct <NiSphericalCollider               , RC_NiSphericalCollider                >},
 | 
						|
        {"NiParticleGrowFade"               , &construct <NiParticleGrowFade                , RC_NiParticleGrowFade                 >},
 | 
						|
        {"NiParticleColorModifier"          , &construct <NiParticleColorModifier           , RC_NiParticleColorModifier            >},
 | 
						|
        {"NiParticleRotation"               , &construct <NiParticleRotation                , RC_NiParticleRotation                 >},
 | 
						|
        {"NiFloatData"                      , &construct <NiFloatData                       , RC_NiFloatData                        >},
 | 
						|
        {"NiTriShapeData"                   , &construct <NiTriShapeData                    , RC_NiTriShapeData                     >},
 | 
						|
        {"NiTriStripsData"                  , &construct <NiTriStripsData                   , RC_NiTriStripsData                    >},
 | 
						|
        {"NiLinesData"                      , &construct <NiLinesData                       , RC_NiLinesData                        >},
 | 
						|
        {"NiVisData"                        , &construct <NiVisData                         , RC_NiVisData                          >},
 | 
						|
        {"NiColorData"                      , &construct <NiColorData                       , RC_NiColorData                        >},
 | 
						|
        {"NiPixelData"                      , &construct <NiPixelData                       , RC_NiPixelData                        >},
 | 
						|
        {"NiMorphData"                      , &construct <NiMorphData                       , RC_NiMorphData                        >},
 | 
						|
        {"NiKeyframeData"                   , &construct <NiKeyframeData                    , RC_NiKeyframeData                     >},
 | 
						|
        {"NiSkinData"                       , &construct <NiSkinData                        , RC_NiSkinData                         >},
 | 
						|
        {"NiUVData"                         , &construct <NiUVData                          , RC_NiUVData                           >},
 | 
						|
        {"NiPosData"                        , &construct <NiPosData                         , RC_NiPosData                          >},
 | 
						|
        {"NiParticlesData"                  , &construct <NiParticlesData                   , RC_NiParticlesData                    >},
 | 
						|
        {"NiRotatingParticlesData"          , &construct <NiRotatingParticlesData           , RC_NiParticlesData                    >},
 | 
						|
        {"NiAutoNormalParticlesData"        , &construct <NiParticlesData                   , RC_NiParticlesData                    >},
 | 
						|
        {"NiSequenceStreamHelper"           , &construct <NiSequenceStreamHelper            , RC_NiSequenceStreamHelper             >},
 | 
						|
        {"NiSourceTexture"                  , &construct <NiSourceTexture                   , RC_NiSourceTexture                    >},
 | 
						|
        {"NiSkinInstance"                   , &construct <NiSkinInstance                    , RC_NiSkinInstance                     >},
 | 
						|
        {"NiLookAtController"               , &construct <NiLookAtController                , RC_NiLookAtController                 >},
 | 
						|
        {"NiPalette"                        , &construct <NiPalette                         , RC_NiPalette                          >},
 | 
						|
        {"NiIntegerExtraData"               , &construct <NiIntegerExtraData                , RC_NiIntegerExtraData                 >},
 | 
						|
        {"NiIntegersExtraData"              , &construct <NiIntegersExtraData               , RC_NiIntegersExtraData                >},
 | 
						|
        {"NiBinaryExtraData"                , &construct <NiBinaryExtraData                 , RC_NiBinaryExtraData                  >},
 | 
						|
        {"NiBooleanExtraData"               , &construct <NiBooleanExtraData                , RC_NiBooleanExtraData                 >},
 | 
						|
        {"NiVectorExtraData"                , &construct <NiVectorExtraData                 , RC_NiVectorExtraData                  >},
 | 
						|
        {"NiColorExtraData"                 , &construct <NiVectorExtraData                 , RC_NiColorExtraData                   >},
 | 
						|
        {"NiFloatExtraData"                 , &construct <NiFloatExtraData                  , RC_NiFloatExtraData                   >},
 | 
						|
        {"NiFloatsExtraData"                , &construct <NiFloatsExtraData                 , RC_NiFloatsExtraData                  >},
 | 
						|
        {"NiStringPalette"                  , &construct <NiStringPalette                   , RC_NiStringPalette                    >},
 | 
						|
        {"NiBoolData"                       , &construct <NiBoolData                        , RC_NiBoolData                         >},
 | 
						|
        {"NiSkinPartition"                  , &construct <NiSkinPartition                   , RC_NiSkinPartition                    >},
 | 
						|
        {"BSXFlags"                         , &construct <NiIntegerExtraData                , RC_BSXFlags                           >},
 | 
						|
        {"BSBound"                          , &construct <BSBound                           , RC_BSBound                            >},
 | 
						|
        {"NiTransformData"                  , &construct <NiKeyframeData                    , RC_NiKeyframeData                     >},
 | 
						|
        {"BSFadeNode"                       , &construct <NiNode                            , RC_NiNode                             >},
 | 
						|
        {"bhkBlendController"               , &construct <bhkBlendController                , RC_bhkBlendController                 >},
 | 
						|
        {"NiFloatInterpolator"              , &construct <NiFloatInterpolator               , RC_NiFloatInterpolator                >},
 | 
						|
        {"NiBoolInterpolator"               , &construct <NiBoolInterpolator                , RC_NiBoolInterpolator                 >},
 | 
						|
        {"NiPoint3Interpolator"             , &construct <NiPoint3Interpolator              , RC_NiPoint3Interpolator               >},
 | 
						|
        {"NiTransformController"            , &construct <NiKeyframeController              , RC_NiKeyframeController               >},
 | 
						|
        {"NiMultiTargetTransformController" , &construct <NiMultiTargetTransformController  , RC_NiMultiTargetTransformController   >},
 | 
						|
        {"NiTransformInterpolator"          , &construct <NiTransformInterpolator           , RC_NiTransformInterpolator            >},
 | 
						|
        {"NiColorInterpolator"              , &construct <NiColorInterpolator               , RC_NiColorInterpolator                >},
 | 
						|
        {"BSShaderTextureSet"               , &construct <BSShaderTextureSet                , RC_BSShaderTextureSet                 >},
 | 
						|
        {"BSLODTriShape"                    , &construct <BSLODTriShape                     , RC_BSLODTriShape                      >},
 | 
						|
        {"BSShaderProperty"                 , &construct <BSShaderProperty                  , RC_BSShaderProperty                   >},
 | 
						|
        {"BSShaderPPLightingProperty"       , &construct <BSShaderPPLightingProperty        , RC_BSShaderPPLightingProperty         >},
 | 
						|
        {"BSShaderNoLightingProperty"       , &construct <BSShaderNoLightingProperty        , RC_BSShaderNoLightingProperty         >},
 | 
						|
        {"BSFurnitureMarker"                , &construct <BSFurnitureMarker                 , RC_BSFurnitureMarker                  >},
 | 
						|
        {"NiCollisionObject"                , &construct <NiCollisionObject                 , RC_NiCollisionObject                  >},
 | 
						|
        {"bhkCollisionObject"               , &construct <bhkCollisionObject                , RC_bhkCollisionObject                 >},
 | 
						|
        {"BSDismemberSkinInstance"          , &construct <BSDismemberSkinInstance           , RC_BSDismemberSkinInstance            >},
 | 
						|
        {"NiControllerManager"              , &construct <NiControllerManager               , RC_NiControllerManager                >},
 | 
						|
        {"bhkMoppBvTreeShape"               , &construct <bhkMoppBvTreeShape                , RC_bhkMoppBvTreeShape                 >},
 | 
						|
        {"bhkNiTriStripsShape"              , &construct <bhkNiTriStripsShape               , RC_bhkNiTriStripsShape                >},
 | 
						|
        {"bhkPackedNiTriStripsShape"        , &construct <bhkPackedNiTriStripsShape         , RC_bhkPackedNiTriStripsShape          >},
 | 
						|
        {"hkPackedNiTriStripsData"          , &construct <hkPackedNiTriStripsData           , RC_hkPackedNiTriStripsData            >},
 | 
						|
        {"bhkConvexVerticesShape"           , &construct <bhkConvexVerticesShape            , RC_bhkConvexVerticesShape             >},
 | 
						|
        {"bhkBoxShape"                      , &construct <bhkBoxShape                       , RC_bhkBoxShape                        >},
 | 
						|
        {"bhkListShape"                     , &construct <bhkListShape                      , RC_bhkListShape                       >},
 | 
						|
        {"bhkRigidBody"                     , &construct <bhkRigidBody                      , RC_bhkRigidBody                       >},
 | 
						|
        {"bhkRigidBodyT"                    , &construct <bhkRigidBody                      , RC_bhkRigidBodyT                      >},
 | 
						|
        {"BSLightingShaderProperty"         , &construct <BSLightingShaderProperty          , RC_BSLightingShaderProperty           >},
 | 
						|
        {"NiSortAdjustNode"                 , &construct <NiSortAdjustNode                  , RC_NiSortAdjustNode                   >},
 | 
						|
        {"NiClusterAccumulator"             , &construct <NiClusterAccumulator              , RC_NiClusterAccumulator               >},
 | 
						|
        {"NiAlphaAccumulator"               , &construct <NiAlphaAccumulator                , RC_NiAlphaAccumulator                 >},
 | 
						|
    };
 | 
						|
}
 | 
						|
 | 
						|
///Make the factory map used for parsing the file
 | 
						|
static const std::map<std::string, CreateRecord> factories = makeFactory();
 | 
						|
 | 
						|
std::string NIFFile::printVersion(unsigned int 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 NIFFile::parse(Files::IStreamPtr&& stream)
 | 
						|
{
 | 
						|
    const std::array<std::uint64_t, 2> fileHash = Files::getHash(filename, *stream);
 | 
						|
    hash.append(reinterpret_cast<const char*>(fileHash.data()), fileHash.size() * sizeof(std::uint64_t));
 | 
						|
 | 
						|
    NIFStream nif (this, std::move(stream));
 | 
						|
 | 
						|
    // 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.compare(0, verString.size(), verString) == 0; });
 | 
						|
    if (!supportedHeader)
 | 
						|
        fail("Invalid NIF header: " + head);
 | 
						|
 | 
						|
    // Get BCD version
 | 
						|
    ver = nif.getUInt();
 | 
						|
    // 4.0.0.0 is an older, practically identical version of the format.
 | 
						|
    // It's not used by Morrowind assets but Morrowind supports it.
 | 
						|
    static const std::array<uint32_t, 2> supportedVers =
 | 
						|
    {
 | 
						|
        NIFStream::generateVersion(4,0,0,0),
 | 
						|
        VER_MW
 | 
						|
    };
 | 
						|
    const bool supportedVersion = std::find(supportedVers.begin(), supportedVers.end(), ver) != supportedVers.end();
 | 
						|
    if (!supportedVersion)
 | 
						|
    {
 | 
						|
        if (sLoadUnsupportedFiles)
 | 
						|
            warn("Unsupported NIF version: " + printVersion(ver) + ". Proceed with caution!");
 | 
						|
        else
 | 
						|
            fail("Unsupported NIF version: " + printVersion(ver));
 | 
						|
    }
 | 
						|
 | 
						|
    // NIF data endianness
 | 
						|
    if (ver >= NIFStream::generateVersion(20,0,0,4))
 | 
						|
    {
 | 
						|
        unsigned char endianness = nif.getChar();
 | 
						|
        if (endianness == 0)
 | 
						|
            fail("Big endian NIF files are unsupported");
 | 
						|
    }
 | 
						|
 | 
						|
    // User version
 | 
						|
    if (ver > NIFStream::generateVersion(10,0,1,8))
 | 
						|
        userVer = nif.getUInt();
 | 
						|
 | 
						|
    // Number of records
 | 
						|
    const std::size_t recNum = nif.getUInt();
 | 
						|
    records.resize(recNum);
 | 
						|
 | 
						|
    // Bethesda stream header
 | 
						|
    // It contains Bethesda format version and (useless) export information
 | 
						|
    if (ver == VER_OB_OLD ||
 | 
						|
       (userVer >= 3 && ((ver == VER_OB || ver == VER_BGS)
 | 
						|
    || (ver >= NIFStream::generateVersion(10,1,0,0) && ver <= NIFStream::generateVersion(20,0,0,4) && userVer <= 11))))
 | 
						|
    {
 | 
						|
        bethVer = nif.getUInt();
 | 
						|
        nif.getExportString(); // Author
 | 
						|
        if (bethVer > BETHVER_FO4)
 | 
						|
            nif.getUInt(); // Unknown
 | 
						|
        nif.getExportString(); // Process script
 | 
						|
        nif.getExportString(); // Export script
 | 
						|
        if (bethVer == BETHVER_FO4)
 | 
						|
            nif.getExportString(); // Max file path
 | 
						|
    }
 | 
						|
 | 
						|
    std::vector<std::string> recTypes;
 | 
						|
    std::vector<unsigned short> recTypeIndices;
 | 
						|
 | 
						|
    const bool hasRecTypeListings = ver >= NIFStream::generateVersion(5,0,0,1);
 | 
						|
    if (hasRecTypeListings)
 | 
						|
    {
 | 
						|
        unsigned short recTypeNum = nif.getUShort();
 | 
						|
        if (recTypeNum) // Record type list
 | 
						|
            nif.getSizedStrings(recTypes, recTypeNum);
 | 
						|
        if (recNum) // Record type mapping for each record
 | 
						|
            nif.getUShorts(recTypeIndices, recNum);
 | 
						|
        if (ver >= NIFStream::generateVersion(5,0,0,6)) // Groups
 | 
						|
        {
 | 
						|
            if (ver >= NIFStream::generateVersion(20,1,0,1)) // String table
 | 
						|
            {
 | 
						|
                if (ver >= NIFStream::generateVersion(20,2,0,5) && recNum) // Record sizes
 | 
						|
                {
 | 
						|
                    std::vector<unsigned int> recSizes; // Currently unused
 | 
						|
                    nif.getUInts(recSizes, recNum);
 | 
						|
                }
 | 
						|
                const std::size_t stringNum = nif.getUInt();
 | 
						|
                nif.getUInt(); // Max string length
 | 
						|
                if (stringNum)
 | 
						|
                    nif.getSizedStrings(strings, stringNum);
 | 
						|
            }
 | 
						|
            std::vector<unsigned int> groups; // Currently unused
 | 
						|
            unsigned int groupNum = nif.getUInt();
 | 
						|
            if (groupNum)
 | 
						|
                nif.getUInts(groups, groupNum);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    const bool hasRecordSeparators = ver >= NIFStream::generateVersion(10,0,0,0) && ver < NIFStream::generateVersion(10,2,0,0);
 | 
						|
    for (std::size_t i = 0; i < recNum; i++)
 | 
						|
    {
 | 
						|
        std::unique_ptr<Record> r;
 | 
						|
 | 
						|
        std::string rec = hasRecTypeListings ? recTypes[recTypeIndices[i]] : nif.getString();
 | 
						|
        if(rec.empty())
 | 
						|
        {
 | 
						|
            std::stringstream error;
 | 
						|
            error << "Record number " << i << " out of " << recNum << " is blank.";
 | 
						|
            fail(error.str());
 | 
						|
        }
 | 
						|
 | 
						|
        // Record separator. Some Havok records in Oblivion do not have it.
 | 
						|
        if (hasRecordSeparators && rec.compare(0, 3, "bhk"))
 | 
						|
        {
 | 
						|
            if (nif.getInt())
 | 
						|
            {
 | 
						|
                std::stringstream warning;
 | 
						|
                warning << "Record number " << i << " out of " << recNum << " is preceded by a non-zero separator.";
 | 
						|
                warn(warning.str());
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        const auto entry = factories.find(rec);
 | 
						|
 | 
						|
        if (entry == factories.end())
 | 
						|
            fail("Unknown record type " + rec);
 | 
						|
 | 
						|
        r = entry->second();
 | 
						|
 | 
						|
        if (!supportedVersion)
 | 
						|
            Log(Debug::Verbose) << "NIF Debug: Reading record of type " << rec << ", index " << i << " (" << filename << ")";
 | 
						|
 | 
						|
        assert(r != nullptr);
 | 
						|
        assert(r->recType != RC_MISSING);
 | 
						|
        r->recName = rec;
 | 
						|
        r->recIndex = i;
 | 
						|
        r->read(&nif);
 | 
						|
        records[i] = std::move(r);
 | 
						|
    }
 | 
						|
 | 
						|
    const std::size_t rootNum = nif.getUInt();
 | 
						|
    roots.resize(rootNum);
 | 
						|
 | 
						|
    //Determine which records are roots
 | 
						|
    for (std::size_t i = 0; i < rootNum; i++)
 | 
						|
    {
 | 
						|
        int idx = nif.getInt();
 | 
						|
        if (idx >= 0 && static_cast<std::size_t>(idx) < records.size())
 | 
						|
        {
 | 
						|
            roots[i] = records[idx].get();
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            roots[i] = nullptr;
 | 
						|
            warn("Root " + std::to_string(i + 1) + " does not point to a record: index " + std::to_string(idx));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // Once parsing is done, do post-processing.
 | 
						|
    for (const auto& record : records)
 | 
						|
        record->post(this);
 | 
						|
}
 | 
						|
 | 
						|
void NIFFile::setUseSkinning(bool skinning)
 | 
						|
{
 | 
						|
    mUseSkinning = skinning;
 | 
						|
}
 | 
						|
 | 
						|
bool NIFFile::getUseSkinning() const
 | 
						|
{
 | 
						|
    return mUseSkinning;
 | 
						|
}
 | 
						|
 | 
						|
std::atomic_bool NIFFile::sLoadUnsupportedFiles = false;
 | 
						|
 | 
						|
void NIFFile::setLoadUnsupportedFiles(bool load)
 | 
						|
{
 | 
						|
    sLoadUnsupportedFiles = load;
 | 
						|
}
 | 
						|
 | 
						|
void NIFFile::warn(const std::string &msg) const
 | 
						|
{
 | 
						|
    Log(Debug::Warning) << " NIFFile Warning: " << msg << "\nFile: " << filename;
 | 
						|
}
 | 
						|
 | 
						|
[[noreturn]] void NIFFile::fail(const std::string &msg) const
 | 
						|
{
 | 
						|
    throw std::runtime_error(" NIFFile Error: " + msg + "\nFile: " + filename);
 | 
						|
}
 | 
						|
 | 
						|
std::string NIFFile::getString(uint32_t index) const
 | 
						|
{
 | 
						|
    if (index == std::numeric_limits<uint32_t>::max())
 | 
						|
        return std::string();
 | 
						|
    return strings.at(index);
 | 
						|
}
 | 
						|
 | 
						|
}
 |