#include "niffile.hpp" #include "effect.hpp" #include <map> #include <sstream> 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(stream); } NIFFile::~NIFFile() { for (std::vector<Record*>::iterator it = records.begin() ; it != records.end(); ++it) { delete *it; } } template <typename NodeType> static Record* construct() { return new NodeType; } struct RecordFactoryEntry { typedef Record* (*create_t) (); create_t mCreate; RecordType mType; }; ///Helper function for adding records to the factory map static std::pair<std::string,RecordFactoryEntry> makeEntry(std::string recName, Record* (*create_t) (), RecordType type) { RecordFactoryEntry anEntry = {create_t,type}; return std::make_pair(recName, anEntry); } ///These are all the record types we know how to read. static std::map<std::string,RecordFactoryEntry> makeFactory() { std::map<std::string,RecordFactoryEntry> newFactory; newFactory.insert(makeEntry("NiNode", &construct <NiNode> , RC_NiNode )); newFactory.insert(makeEntry("NiSwitchNode", &construct <NiSwitchNode> , RC_NiSwitchNode )); newFactory.insert(makeEntry("NiLODNode", &construct <NiLODNode> , RC_NiLODNode )); newFactory.insert(makeEntry("AvoidNode", &construct <NiNode> , RC_AvoidNode )); newFactory.insert(makeEntry("NiCollisionSwitch", &construct <NiNode> , RC_NiCollisionSwitch )); newFactory.insert(makeEntry("NiBSParticleNode", &construct <NiNode> , RC_NiBSParticleNode )); newFactory.insert(makeEntry("NiBSAnimationNode", &construct <NiNode> , RC_NiBSAnimationNode )); newFactory.insert(makeEntry("NiBillboardNode", &construct <NiNode> , RC_NiBillboardNode )); newFactory.insert(makeEntry("NiTriShape", &construct <NiTriShape> , RC_NiTriShape )); newFactory.insert(makeEntry("NiTriStrips", &construct <NiTriStrips> , RC_NiTriStrips )); newFactory.insert(makeEntry("NiLines", &construct <NiLines> , RC_NiLines )); newFactory.insert(makeEntry("NiRotatingParticles", &construct <NiRotatingParticles> , RC_NiRotatingParticles )); newFactory.insert(makeEntry("NiAutoNormalParticles", &construct <NiAutoNormalParticles> , RC_NiAutoNormalParticles )); newFactory.insert(makeEntry("NiCamera", &construct <NiCamera> , RC_NiCamera )); newFactory.insert(makeEntry("RootCollisionNode", &construct <NiNode> , RC_RootCollisionNode )); newFactory.insert(makeEntry("NiTexturingProperty", &construct <NiTexturingProperty> , RC_NiTexturingProperty )); newFactory.insert(makeEntry("NiFogProperty", &construct <NiFogProperty> , RC_NiFogProperty )); newFactory.insert(makeEntry("NiMaterialProperty", &construct <NiMaterialProperty> , RC_NiMaterialProperty )); newFactory.insert(makeEntry("NiZBufferProperty", &construct <NiZBufferProperty> , RC_NiZBufferProperty )); newFactory.insert(makeEntry("NiAlphaProperty", &construct <NiAlphaProperty> , RC_NiAlphaProperty )); newFactory.insert(makeEntry("NiVertexColorProperty", &construct <NiVertexColorProperty> , RC_NiVertexColorProperty )); newFactory.insert(makeEntry("NiShadeProperty", &construct <NiShadeProperty> , RC_NiShadeProperty )); newFactory.insert(makeEntry("NiDitherProperty", &construct <NiDitherProperty> , RC_NiDitherProperty )); newFactory.insert(makeEntry("NiWireframeProperty", &construct <NiWireframeProperty> , RC_NiWireframeProperty )); newFactory.insert(makeEntry("NiSpecularProperty", &construct <NiSpecularProperty> , RC_NiSpecularProperty )); newFactory.insert(makeEntry("NiStencilProperty", &construct <NiStencilProperty> , RC_NiStencilProperty )); newFactory.insert(makeEntry("NiVisController", &construct <NiVisController> , RC_NiVisController )); newFactory.insert(makeEntry("NiGeomMorpherController", &construct <NiGeomMorpherController> , RC_NiGeomMorpherController )); newFactory.insert(makeEntry("NiKeyframeController", &construct <NiKeyframeController> , RC_NiKeyframeController )); newFactory.insert(makeEntry("NiAlphaController", &construct <NiAlphaController> , RC_NiAlphaController )); newFactory.insert(makeEntry("NiRollController", &construct <NiRollController> , RC_NiRollController )); newFactory.insert(makeEntry("NiUVController", &construct <NiUVController> , RC_NiUVController )); newFactory.insert(makeEntry("NiPathController", &construct <NiPathController> , RC_NiPathController )); newFactory.insert(makeEntry("NiMaterialColorController", &construct <NiMaterialColorController> , RC_NiMaterialColorController )); newFactory.insert(makeEntry("NiBSPArrayController", &construct <NiBSPArrayController> , RC_NiBSPArrayController )); newFactory.insert(makeEntry("NiParticleSystemController", &construct <NiParticleSystemController> , RC_NiParticleSystemController )); newFactory.insert(makeEntry("NiFlipController", &construct <NiFlipController> , RC_NiFlipController )); newFactory.insert(makeEntry("NiAmbientLight", &construct <NiLight> , RC_NiLight )); newFactory.insert(makeEntry("NiDirectionalLight", &construct <NiLight> , RC_NiLight )); newFactory.insert(makeEntry("NiPointLight", &construct <NiPointLight> , RC_NiLight )); newFactory.insert(makeEntry("NiSpotLight", &construct <NiSpotLight> , RC_NiLight )); newFactory.insert(makeEntry("NiTextureEffect", &construct <NiTextureEffect> , RC_NiTextureEffect )); newFactory.insert(makeEntry("NiVertWeightsExtraData", &construct <NiVertWeightsExtraData> , RC_NiVertWeightsExtraData )); newFactory.insert(makeEntry("NiTextKeyExtraData", &construct <NiTextKeyExtraData> , RC_NiTextKeyExtraData )); newFactory.insert(makeEntry("NiStringExtraData", &construct <NiStringExtraData> , RC_NiStringExtraData )); newFactory.insert(makeEntry("NiGravity", &construct <NiGravity> , RC_NiGravity )); newFactory.insert(makeEntry("NiPlanarCollider", &construct <NiPlanarCollider> , RC_NiPlanarCollider )); newFactory.insert(makeEntry("NiSphericalCollider", &construct <NiSphericalCollider> , RC_NiSphericalCollider )); newFactory.insert(makeEntry("NiParticleGrowFade", &construct <NiParticleGrowFade> , RC_NiParticleGrowFade )); newFactory.insert(makeEntry("NiParticleColorModifier", &construct <NiParticleColorModifier> , RC_NiParticleColorModifier )); newFactory.insert(makeEntry("NiParticleRotation", &construct <NiParticleRotation> , RC_NiParticleRotation )); newFactory.insert(makeEntry("NiFloatData", &construct <NiFloatData> , RC_NiFloatData )); newFactory.insert(makeEntry("NiTriShapeData", &construct <NiTriShapeData> , RC_NiTriShapeData )); newFactory.insert(makeEntry("NiTriStripsData", &construct <NiTriStripsData> , RC_NiTriStripsData )); newFactory.insert(makeEntry("NiLinesData", &construct <NiLinesData> , RC_NiLinesData )); newFactory.insert(makeEntry("NiVisData", &construct <NiVisData> , RC_NiVisData )); newFactory.insert(makeEntry("NiColorData", &construct <NiColorData> , RC_NiColorData )); newFactory.insert(makeEntry("NiPixelData", &construct <NiPixelData> , RC_NiPixelData )); newFactory.insert(makeEntry("NiMorphData", &construct <NiMorphData> , RC_NiMorphData )); newFactory.insert(makeEntry("NiKeyframeData", &construct <NiKeyframeData> , RC_NiKeyframeData )); newFactory.insert(makeEntry("NiSkinData", &construct <NiSkinData> , RC_NiSkinData )); newFactory.insert(makeEntry("NiUVData", &construct <NiUVData> , RC_NiUVData )); newFactory.insert(makeEntry("NiPosData", &construct <NiPosData> , RC_NiPosData )); newFactory.insert(makeEntry("NiRotatingParticlesData", &construct <NiRotatingParticlesData> , RC_NiRotatingParticlesData )); newFactory.insert(makeEntry("NiAutoNormalParticlesData", &construct <NiAutoNormalParticlesData> , RC_NiAutoNormalParticlesData )); newFactory.insert(makeEntry("NiSequenceStreamHelper", &construct <NiSequenceStreamHelper> , RC_NiSequenceStreamHelper )); newFactory.insert(makeEntry("NiSourceTexture", &construct <NiSourceTexture> , RC_NiSourceTexture )); newFactory.insert(makeEntry("NiSkinInstance", &construct <NiSkinInstance> , RC_NiSkinInstance )); newFactory.insert(makeEntry("NiLookAtController", &construct <NiLookAtController> , RC_NiLookAtController )); newFactory.insert(makeEntry("NiPalette", &construct <NiPalette> , RC_NiPalette )); return newFactory; } ///Make the factory map used for parsing the file static const std::map<std::string,RecordFactoryEntry> 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) { NIFStream nif (this, stream); // Check the header string std::string head = nif.getVersionString(); if(head.compare(0, 22, "NetImmerse File Format") != 0) 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. if(ver != NIFStream::generateVersion(4,0,0,0) && ver != VER_MW) fail("Unsupported NIF version: " + printVersion(ver)); // Number of records size_t recNum = nif.getInt(); records.resize(recNum); for(size_t i = 0;i < recNum;i++) { Record *r = nullptr; std::string rec = nif.getString(); if(rec.empty()) { std::stringstream error; error << "Record number " << i << " out of " << recNum << " is blank."; fail(error.str()); } std::map<std::string,RecordFactoryEntry>::const_iterator entry = factories.find(rec); if (entry != factories.end()) { r = entry->second.mCreate (); r->recType = entry->second.mType; } else fail("Unknown record type " + rec); assert(r != nullptr); assert(r->recType != RC_MISSING); r->recName = rec; r->recIndex = i; records[i] = r; r->read(&nif); } size_t rootNum = nif.getUInt(); roots.resize(rootNum); //Determine which records are roots for(size_t i = 0;i < rootNum;i++) { int idx = nif.getInt(); if (idx >= 0 && idx < int(records.size())) { roots[i] = records[idx]; } else { roots[i] = nullptr; warn("Null Root found"); } } // Once parsing is done, do post-processing. for(size_t i=0; i<recNum; i++) records[i]->post(this); } void NIFFile::setUseSkinning(bool skinning) { mUseSkinning = skinning; } bool NIFFile::getUseSkinning() const { return mUseSkinning; } }