#include "niffile.hpp" #include "effect.hpp" #include namespace Nif { /// Open a NIF stream. The name is used for error messages. NIFFile::NIFFile(const std::string &name) : ver(0) , filename(name) { parse(); } NIFFile::~NIFFile() { for(std::size_t i=0; i static Record* construct() { return new NodeType; } struct RecordFactoryEntry { typedef Record* (*create_t) (); char const * mName; create_t mCreate; RecordType mType; }; /* These are all the record types we know how to read. This can be heavily optimized later if needed. For example, a hash table or a FSM-based parser could be used to look up node names. */ static const RecordFactoryEntry recordFactories [] = { { "NiNode", &construct , RC_NiNode }, { "AvoidNode", &construct , RC_AvoidNode }, { "NiBSParticleNode", &construct , RC_NiBSParticleNode }, { "NiBSAnimationNode", &construct , RC_NiBSAnimationNode }, { "NiBillboardNode", &construct , RC_NiBillboardNode }, { "NiTriShape", &construct , RC_NiTriShape }, { "NiRotatingParticles", &construct , RC_NiRotatingParticles }, { "NiAutoNormalParticles", &construct , RC_NiAutoNormalParticles }, { "NiCamera", &construct , RC_NiCamera }, { "RootCollisionNode", &construct , RC_RootCollisionNode }, { "NiTexturingProperty", &construct , RC_NiTexturingProperty }, { "NiMaterialProperty", &construct , RC_NiMaterialProperty }, { "NiZBufferProperty", &construct , RC_NiZBufferProperty }, { "NiAlphaProperty", &construct , RC_NiAlphaProperty }, { "NiVertexColorProperty", &construct , RC_NiVertexColorProperty }, { "NiShadeProperty", &construct , RC_NiShadeProperty }, { "NiDitherProperty", &construct , RC_NiDitherProperty }, { "NiWireframeProperty", &construct , RC_NiWireframeProperty }, { "NiSpecularProperty", &construct , RC_NiSpecularProperty }, { "NiStencilProperty", &construct , RC_NiStencilProperty }, { "NiVisController", &construct , RC_NiVisController }, { "NiGeomMorpherController", &construct , RC_NiGeomMorpherController }, { "NiKeyframeController", &construct , RC_NiKeyframeController }, { "NiAlphaController", &construct , RC_NiAlphaController }, { "NiUVController", &construct , RC_NiUVController }, { "NiPathController", &construct , RC_NiPathController }, { "NiMaterialColorController", &construct , RC_NiMaterialColorController }, { "NiBSPArrayController", &construct , RC_NiBSPArrayController }, { "NiParticleSystemController", &construct , RC_NiParticleSystemController }, { "NiFlipController", &construct , RC_NiFlipController }, { "NiAmbientLight", &construct , RC_NiLight }, { "NiDirectionalLight", &construct , RC_NiLight }, { "NiTextureEffect", &construct , RC_NiTextureEffect }, { "NiVertWeightsExtraData", &construct , RC_NiVertWeightsExtraData }, { "NiTextKeyExtraData", &construct , RC_NiTextKeyExtraData }, { "NiStringExtraData", &construct , RC_NiStringExtraData }, { "NiGravity", &construct , RC_NiGravity }, { "NiPlanarCollider", &construct , RC_NiPlanarCollider }, { "NiParticleGrowFade", &construct , RC_NiParticleGrowFade }, { "NiParticleColorModifier", &construct , RC_NiParticleColorModifier }, { "NiParticleRotation", &construct , RC_NiParticleRotation }, { "NiFloatData", &construct , RC_NiFloatData }, { "NiTriShapeData", &construct , RC_NiTriShapeData }, { "NiVisData", &construct , RC_NiVisData }, { "NiColorData", &construct , RC_NiColorData }, { "NiPixelData", &construct , RC_NiPixelData }, { "NiMorphData", &construct , RC_NiMorphData }, { "NiKeyframeData", &construct , RC_NiKeyframeData }, { "NiSkinData", &construct , RC_NiSkinData }, { "NiUVData", &construct , RC_NiUVData }, { "NiPosData", &construct , RC_NiPosData }, { "NiRotatingParticlesData", &construct , RC_NiRotatingParticlesData }, { "NiAutoNormalParticlesData", &construct , RC_NiAutoNormalParticlesData }, { "NiSequenceStreamHelper", &construct , RC_NiSequenceStreamHelper }, { "NiSourceTexture", &construct , RC_NiSourceTexture }, { "NiSkinInstance", &construct , RC_NiSkinInstance }, }; static RecordFactoryEntry const * recordFactories_begin = &recordFactories [0]; static RecordFactoryEntry const * recordFactories_end = &recordFactories [sizeof (recordFactories) / sizeof (recordFactories[0])]; RecordFactoryEntry const * lookupRecordFactory (char const * name) { RecordFactoryEntry const * i; for (i = recordFactories_begin; i != recordFactories_end; ++i) if (strcmp (name, i->mName) == 0) break; if (i == recordFactories_end) return NULL; return i; } /* This file implements functions from the NIFFile class. It is also where we stash all the functions we couldn't add as inline definitions in the record types. */ void NIFFile::parse() { NIFStream nif (this, Ogre::ResourceGroupManager::getSingleton().openResource(filename)); // Check the header string std::string head = nif.getString(40); if(head.compare(0, 22, "NetImmerse File Format") != 0) fail("Invalid NIF header"); // Get BCD version ver = nif.getInt(); if(ver != VER_MW) fail("Unsupported NIF version"); // Number of records size_t recNum = nif.getInt(); records.resize(recNum); /* The format for 10.0.1.0 seems to be a bit different. After the header, it contains the number of records, r (int), just like 4.0.0.2, but following that it contains a short x, followed by x strings. Then again by r shorts, one for each record, giving which of the above strings to use to identify the record. After this follows two ints (zero?) and then the record data. However we do not support or plan to support other versions yet. */ for(size_t i = 0;i < recNum;i++) { Record *r = NULL; std::string rec = nif.getString(); if(rec.empty()) fail("Record number " + Ogre::StringConverter::toString(i) + " out of " + Ogre::StringConverter::toString(recNum) + " is blank."); RecordFactoryEntry const * entry = lookupRecordFactory (rec.c_str ()); if (entry != NULL) { r = entry->mCreate (); r->recType = entry->mType; } else fail("Unknown record type " + rec); assert(r != NULL); 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); for(size_t i = 0;i < rootNum;i++) { intptr_t idx = nif.getInt(); roots[i] = ((idx >= 0) ? records.at(idx) : NULL); } // Once parsing is done, do post-processing. for(size_t i=0; ipost(this); } }