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.
openmw-tes3coop/components/nif/niffile.cpp

227 lines
13 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("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("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 ));
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;
}
}