reworked NIFStream to use a type-handler

Reworked NIFStream to use a type-handler system to decide how to extract
data from the NIF. It also has the capability to perform bulk reads on
compatible platforms, thus improving cell-load performance.
This commit is contained in:
Nathan Jeffords 2013-01-05 17:33:28 -08:00
parent 44ac0a7c18
commit 03ee7663a3

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
#define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
#include <boost/static_assert.hpp>
namespace Nif namespace Nif
{ {
@ -11,168 +13,278 @@ class NIFStream {
/// Input stream /// Input stream
Ogre::DataStreamPtr inp; Ogre::DataStreamPtr inp;
uint8_t read_byte() template <typename value_t>
value_t read_le ()
{ {
uint8_t byte; uint8_t buffer [sizeof (value_t)];
if(inp->read(&byte, 1) != 1) return 0;
return byte; if (inp->read (buffer, sizeof (buffer)) != sizeof (buffer))
} throw std::runtime_error ("unexpected");
uint16_t read_le16()
{ value_t Value = 0;
uint8_t buffer[2]; value_t Shift = 0;
if(inp->read(buffer, 2) != 2) return 0;
return buffer[0] | (buffer[1]<<8); for (size_t i = 0; i < sizeof (value_t); ++i)
} {
uint32_t read_le32() Value |= value_t (buffer[i]) << Shift;
{ Shift += 8;
uint8_t buffer[4]; }
if(inp->read(buffer, 4) != 4) return 0;
return buffer[0] | (buffer[1]<<8) | (buffer[2]<<16) | (buffer[3]<<24); return Value;
}
float read_le32f()
{
union {
uint32_t i;
float f;
} u = { read_le32() };
return u.f;
} }
public: public:
/*
* This should be true for any processor/platform whose endianess, alignment
* and packing rules would be compatible with x86 and not fault or degrade
* with misaligned reads. This enables some pretty big savings when reading in
* animations and meshes.
*/
#if defined (__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
static const bool FileCompatiblePlatform = true;
#else
static const bool FileCompatiblePlatform = false;
#endif
template <typename type>
struct handler;
template <typename presentation_type, typename backing_type>
struct le_handler;
template <typename presentation_type, typename backing_type>
friend struct le_handler;
template <typename store_type, typename final_type, bool SameLayout>
struct conversion_handler;
NIFFile * const file; NIFFile * const file;
NIFStream (NIFFile * file, Ogre::DataStreamPtr inp): file (file), inp (inp) {} NIFStream (NIFFile * file, Ogre::DataStreamPtr inp) : file (file), inp (inp) {}
/************************************************* /*************************************************
Parser functions Parser functions
****************************************************/ ****************************************************/
template <typename T> void skip(size_t size) { return inp->skip (size); }
struct GetHandler size_t read (void * data, size_t size) { return inp->read (data, size); }
{
typedef T (NIFStream::*fn_t)();
static const fn_t sValue; // this is specialized per supported type in the .cpp file
static T read (NIFStream* nif)
{
return (nif->*sValue) ();
}
};
template <typename T> template <typename T>
void read (NIFStream* nif, T & Value) void uncheckedRead (T & Value)
{ {
Value = GetHandler <T>::read (nif); typedef handler <T> handler_t;
handler_t::extract (*this, Value);
} }
void skip(size_t size) { inp->skip(size); } template <typename T>
void read (void * data, size_t size) { inp->read (data, size); } T getValue ()
{
T Value;
getValue (Value);
return Value;
}
char getChar() { return read_byte(); } template <typename T>
short getShort() { return read_le16(); } void getValue (T & Value)
unsigned short getUShort() { return read_le16(); }
int getInt() { return read_le32(); }
int getUInt() { return read_le32(); }
float getFloat() { return read_le32f(); }
Ogre::Vector2 getVector2()
{ {
float a[2]; typedef handler <T> handler_t;
for(size_t i = 0;i < 2;i++) if (FileCompatiblePlatform && handler_t::FileCompatibleLayout)
a[i] = getFloat();
return Ogre::Vector2(a);
}
Ogre::Vector3 getVector3()
{
float a[3];
for(size_t i = 0;i < 3;i++)
a[i] = getFloat();
return Ogre::Vector3(a);
}
Ogre::Vector4 getVector4()
{
float a[4];
for(size_t i = 0;i < 4;i++)
a[i] = getFloat();
return Ogre::Vector4(a);
}
Ogre::Matrix3 getMatrix3()
{
Ogre::Real a[3][3];
for(size_t i = 0;i < 3;i++)
{ {
for(size_t j = 0;j < 3;j++) BOOST_STATIC_ASSERT_MSG (handler_t::FixedLength, "non-fixed length encoding not supported...");
a[i][j] = Ogre::Real(getFloat()); BOOST_STATIC_ASSERT_MSG (handler_t::EncodedLength == sizeof (T), "unexpected structure size");
inp->read (&Value, handler_t::EncodedLength);
}
else
{
handler_t::extract (*this, Value);
} }
return Ogre::Matrix3(a);
} }
Ogre::Quaternion getQuaternion()
template <typename element_type>
void getArray (element_type * Array, size_t Size)
{ {
float a[4]; typedef handler <element_type> handler_t;
for(size_t i = 0;i < 4;i++)
a[i] = getFloat(); if (FileCompatiblePlatform && handler_t::FileCompatibleLayout)
return Ogre::Quaternion(a); {
BOOST_STATIC_ASSERT_MSG (handler_t::FixedLength, "non-fixed length encoding not supported...");
BOOST_STATIC_ASSERT_MSG (handler_t::EncodedLength == sizeof (element_type), "unexpected structure size");
inp->read (Array, handler_t::EncodedLength * Size);
}
else
{
for(size_t i = 0; i < Size; i++)
handler_t::extract (*this, Array[i]);
}
} }
Transformation getTrafo()
template <typename element_type, typename allocator_type>
void getStdVector (std::vector <element_type, allocator_type> & Vector, size_t Size)
{ {
Transformation t; Vector.resize(Size);
t.pos = getVector3();
t.rotation = getMatrix3(); getArray (&Vector.front (), Vector.size ());
t.scale = getFloat();
return t;
} }
template <typename length_type, typename element_type, typename allocator_type>
void getStdVector (std::vector <element_type, allocator_type> & Vector)
{
length_type Length;
getValue (Length);
getStdVector (Vector, Length);
}
char getChar() { return getValue <char> (); }
signed int getInt() { return getValue <int32_t> (); }
unsigned int getUInt() { return getValue <uint32_t> (); }
signed short getShort() { return getValue <int16_t> (); }
unsigned short getUShort() { return getValue <uint16_t> (); }
//signed long getLong() { return getValue <signed long> (); }
//unsigned long getULong() { return getValue <unsigned long> (); }
float getFloat() { return getValue <float> (); }
Ogre::Vector2 getVector2() { return getValue <Ogre::Vector2> (); }
Ogre::Vector3 getVector3() { return getValue <Ogre::Vector3> (); }
Ogre::Vector4 getVector4() { return getValue <Ogre::Vector4> (); }
Ogre::Matrix3 getMatrix3() { return getValue <Ogre::Matrix3> (); }
Ogre::Quaternion getQuaternion() { return getValue <Ogre::Quaternion> (); }
Transformation getTrafo() { return getValue <Transformation> (); }
void getShorts(std::vector<short> &vec, size_t size) { return getStdVector (vec, size); }
void getFloats(std::vector<float> &vec, size_t size) { return getStdVector (vec, size); }
void getVector2s(std::vector<Ogre::Vector2> &vec, size_t size) { return getStdVector (vec, size); }
void getVector3s(std::vector<Ogre::Vector3> &vec, size_t size) { return getStdVector (vec, size); }
void getVector4s(std::vector<Ogre::Vector4> &vec, size_t size) { return getStdVector (vec, size); }
void getQuaternions(std::vector<Ogre::Quaternion> &vec, size_t size) { return getStdVector (vec, size); }
std::string getString(size_t length) std::string getString(size_t length)
{ {
std::vector<char> str (length+1, 0); std::vector<char> str (length+1, 0);
if(inp->read(&str[0], length) != length) if(read(&str[0], length) != length)
throw std::runtime_error ("string length in NIF file does not match"); throw std::runtime_error ("string length in NIF file does not match");
return &str[0]; return &str[0];
} }
std::string getString() std::string getString()
{ {
size_t size = read_le32(); size_t size = getValue <uint32_t> ();
return getString(size); return getString(size);
} }
};
void getShorts(std::vector<short> &vec, size_t size) /*
* generic type handlers
*/
template <typename type, size_t Size>
struct NIFStream::handler < type [Size] >
{
typedef handler <type> inner_handler;
static const bool FixedLength = inner_handler::FixedLength;
static const size_t EncodedLength = inner_handler::EncodedLength * Size;
static const bool FileCompatibleLayout = inner_handler::FileCompatibleLayout;
static void extract (NIFStream & Stream, type (&Value) [Size])
{ {
vec.resize(size); for (size_t i = 0; i < Size; ++i)
for(size_t i = 0;i < vec.size();i++) inner_handler::extract (Stream, Value [i]);
vec[i] = getShort();
} }
void getFloats(std::vector<float> &vec, size_t size) };
template <typename presentation_type, typename backing_type>
struct NIFStream::le_handler
{
static const bool FixedLength = true;
static const size_t EncodedLength = sizeof (backing_type);
static const bool FileCompatibleLayout = true;
static void extract (NIFStream & Stream, presentation_type & Value)
{ {
vec.resize(size); BOOST_STATIC_ASSERT_MSG(sizeof (presentation_type) == sizeof (backing_type), "Invalid use of NIFile::le_handler template");
for(size_t i = 0;i < vec.size();i++)
vec[i] = getFloat(); union {
backing_type Backing;
presentation_type Presentation;
} u;
u.Backing = Stream.read_le <backing_type> ();
Value = u.Presentation;
} }
void getVector2s(std::vector<Ogre::Vector2> &vec, size_t size) };
template <typename store_type, typename final_type>
struct NIFStream::conversion_handler <store_type, final_type, false>
{
typedef handler <store_type> store_handler;
static const bool FixedLength = store_handler::FixedLength;
static const size_t EncodedLength = store_handler::EncodedLength;
static const bool FileCompatibleLayout = false;
static void extract (NIFStream & Stream, final_type & Value)
{ {
vec.resize(size); store_type StoreValue;
for(size_t i = 0;i < vec.size();i++) store_handler::extract (Stream, StoreValue);
vec[i] = getVector2(); Value = final_type (StoreValue);
} }
void getVector3s(std::vector<Ogre::Vector3> &vec, size_t size) };
template <typename store_type, typename final_type>
struct NIFStream::conversion_handler <store_type, final_type, true>
{
typedef handler <store_type> store_handler;
static const bool FixedLength = store_handler::FixedLength;
static const size_t EncodedLength = store_handler::EncodedLength;
static const bool FileCompatibleLayout = store_handler::FileCompatibleLayout;
static void extract (NIFStream & Stream, final_type & FinalValue)
{ {
vec.resize(size); store_handler::extract (Stream, reinterpret_cast <store_type &> (FinalValue));
for(size_t i = 0;i < vec.size();i++)
vec[i] = getVector3();
} }
void getVector4s(std::vector<Ogre::Vector4> &vec, size_t size) };
template <> struct NIFStream::handler <int8_t> : NIFStream::le_handler <int8_t, uint8_t> {};
template <> struct NIFStream::handler <uint8_t> : NIFStream::le_handler <uint8_t, uint8_t> {};
template <> struct NIFStream::handler <int16_t> : NIFStream::le_handler <int16_t, uint16_t> {};
template <> struct NIFStream::handler <uint16_t> : NIFStream::le_handler <uint16_t, uint16_t> {};
template <> struct NIFStream::handler <int32_t> : NIFStream::le_handler <int32_t, uint32_t> {};
template <> struct NIFStream::handler <uint32_t> : NIFStream::le_handler <uint32_t, uint32_t> {};
template <> struct NIFStream::handler <char> : NIFStream::le_handler <char, uint8_t> {};
template <> struct NIFStream::handler <float> : NIFStream::le_handler <float, uint32_t> {};
template <> struct NIFStream::handler <Ogre::Vector2> : NIFStream::conversion_handler <float[2], Ogre::Vector2, true> {};
template <> struct NIFStream::handler <Ogre::Vector3> : NIFStream::conversion_handler <float[3], Ogre::Vector3, true> {};
template <> struct NIFStream::handler <Ogre::Vector4> : NIFStream::conversion_handler <float[4], Ogre::Vector4, true> {};
template <> struct NIFStream::handler <Ogre::Matrix3> : NIFStream::conversion_handler <float[3][3], Ogre::Matrix3, true> {};
template <> struct NIFStream::handler <Ogre::Quaternion> : NIFStream::conversion_handler <float[4], Ogre::Quaternion, true> {};
template <> struct NIFStream::handler <Transformation>
{
static const bool FixedLength = true;
static const size_t EncodedLength =
handler <Ogre::Vector3>::EncodedLength +
handler <Ogre::Matrix3>::EncodedLength +
handler <Ogre::Real>::EncodedLength;
static const bool FileCompatibleLayout = true;
static void extract (NIFStream & stream, Transformation & value)
{ {
vec.resize(size); stream.uncheckedRead (value.pos);
for(size_t i = 0;i < vec.size();i++) stream.uncheckedRead (value.rotation);
vec[i] = getVector4(); stream.uncheckedRead (value.scale);
}
void getQuaternions(std::vector<Ogre::Quaternion> &quat, size_t size)
{
quat.resize(size);
for(size_t i = 0;i < quat.size();i++)
quat[i] = getQuaternion();
} }
}; };