You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
225 lines
12 KiB
C++
225 lines
12 KiB
C++
#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)
|
|
: ver(0)
|
|
, filename(name)
|
|
, mUseSkinning(false)
|
|
{
|
|
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("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("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("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("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("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 ));
|
|
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)
|
|
{
|
|
union ver_quad
|
|
{
|
|
uint32_t full;
|
|
uint8_t quad[4];
|
|
} version_out;
|
|
|
|
version_out.full = version;
|
|
|
|
std::stringstream stream;
|
|
stream << version_out.quad[3] << "."
|
|
<< version_out.quad[2] << "."
|
|
<< version_out.quad[1] << "."
|
|
<< version_out.quad[0];
|
|
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();
|
|
if(ver != VER_MW)
|
|
fail("Unsupported NIF version: " + printVersion(ver));
|
|
// 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())
|
|
{
|
|
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 != 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);
|
|
|
|
//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] = NULL;
|
|
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;
|
|
}
|
|
|
|
}
|