mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
Merge branch 'nifstreamingservice' into 'master'
NIFStream rewrite/refactoring/rewhatever See merge request OpenMW/openmw!3281
This commit is contained in:
commit
11ae1a1fcb
8 changed files with 381 additions and 361 deletions
|
@ -55,7 +55,7 @@ namespace Nif
|
|||
nif->skip(2); // Keep flags and compress flags
|
||||
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(vertices, verts);
|
||||
nif->readVector(vertices, verts);
|
||||
|
||||
unsigned int dataFlags = 0;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0))
|
||||
|
@ -67,11 +67,11 @@ namespace Nif
|
|||
|
||||
if (nif->getBoolean())
|
||||
{
|
||||
nif->getVector3s(normals, verts);
|
||||
nif->readVector(normals, verts);
|
||||
if (dataFlags & 0x1000)
|
||||
{
|
||||
nif->getVector3s(tangents, verts);
|
||||
nif->getVector3s(bitangents, verts);
|
||||
nif->readVector(tangents, verts);
|
||||
nif->readVector(bitangents, verts);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ namespace Nif
|
|||
radius = nif->getFloat();
|
||||
|
||||
if (nif->getBoolean())
|
||||
nif->getVector4s(colors, verts);
|
||||
nif->readVector(colors, verts);
|
||||
|
||||
unsigned int numUVs = dataFlags;
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0))
|
||||
|
@ -102,7 +102,7 @@ namespace Nif
|
|||
uvlist.resize(numUVs);
|
||||
for (unsigned int i = 0; i < numUVs; i++)
|
||||
{
|
||||
nif->getVector2s(uvlist[i], verts);
|
||||
nif->readVector(uvlist[i], verts);
|
||||
// flip the texture coordinates to convert them to the OpenGL convention of bottom-left image origin
|
||||
for (unsigned int uv = 0; uv < uvlist[i].size(); ++uv)
|
||||
{
|
||||
|
@ -135,7 +135,7 @@ namespace Nif
|
|||
if (nif->getVersion() > NIFFile::NIFVersion::VER_OB_OLD)
|
||||
hasTriangles = nif->getBoolean();
|
||||
if (hasTriangles)
|
||||
nif->getUShorts(triangles, cnt);
|
||||
nif->readVector(triangles, cnt);
|
||||
|
||||
// Read the match list, which lists the vertices that are equal to
|
||||
// vertices. We don't actually need need this for anything, so
|
||||
|
@ -157,7 +157,7 @@ namespace Nif
|
|||
int numStrips = nif->getUShort();
|
||||
|
||||
std::vector<unsigned short> lengths;
|
||||
nif->getUShorts(lengths, numStrips);
|
||||
nif->readVector(lengths, numStrips);
|
||||
|
||||
// "Has Strips" flag. Exceptionally useful.
|
||||
bool hasStrips = true;
|
||||
|
@ -168,15 +168,15 @@ namespace Nif
|
|||
|
||||
strips.resize(numStrips);
|
||||
for (int i = 0; i < numStrips; i++)
|
||||
nif->getUShorts(strips[i], lengths[i]);
|
||||
nif->readVector(strips[i], lengths[i]);
|
||||
}
|
||||
|
||||
void NiLinesData::read(NIFStream* nif)
|
||||
{
|
||||
NiGeometryData::read(nif);
|
||||
size_t num = vertices.size();
|
||||
std::vector<char> flags;
|
||||
nif->getChars(flags, num);
|
||||
std::vector<uint8_t> flags;
|
||||
nif->readVector(flags, num);
|
||||
// Can't construct a line from a single vertex.
|
||||
if (num < 2)
|
||||
return;
|
||||
|
@ -208,21 +208,21 @@ namespace Nif
|
|||
if (nif->getVersion() <= NIFStream::generateVersion(10, 0, 1, 0))
|
||||
std::fill(particleRadii.begin(), particleRadii.end(), nif->getFloat());
|
||||
else if (nif->getBoolean())
|
||||
nif->getFloats(particleRadii, vertices.size());
|
||||
nif->readVector(particleRadii, vertices.size());
|
||||
activeCount = nif->getUShort();
|
||||
|
||||
// Particle sizes
|
||||
if (nif->getBoolean())
|
||||
nif->getFloats(sizes, vertices.size());
|
||||
nif->readVector(sizes, vertices.size());
|
||||
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 0, 1, 0) && nif->getBoolean())
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
nif->readVector(rotations, vertices.size());
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20, 0, 0, 4))
|
||||
{
|
||||
if (nif->getBoolean())
|
||||
nif->getFloats(rotationAngles, vertices.size());
|
||||
nif->readVector(rotationAngles, vertices.size());
|
||||
if (nif->getBoolean())
|
||||
nif->getVector3s(rotationAxes, vertices.size());
|
||||
nif->readVector(rotationAxes, vertices.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -231,7 +231,7 @@ namespace Nif
|
|||
NiParticlesData::read(nif);
|
||||
|
||||
if (nif->getVersion() <= NIFStream::generateVersion(4, 2, 2, 0) && nif->getBoolean())
|
||||
nif->getQuaternions(rotations, vertices.size());
|
||||
nif->readVector(rotations, vertices.size());
|
||||
}
|
||||
|
||||
void NiPosData::read(NIFStream* nif)
|
||||
|
@ -301,8 +301,7 @@ namespace Nif
|
|||
unsigned int numPixels = nif->getUInt();
|
||||
bool hasFaces = nif->getVersion() >= NIFStream::generateVersion(10, 4, 0, 2);
|
||||
unsigned int numFaces = hasFaces ? nif->getUInt() : 1;
|
||||
if (numPixels && numFaces)
|
||||
nif->getUChars(data, numPixels * numFaces);
|
||||
nif->readVector(data, numPixels * numFaces);
|
||||
}
|
||||
|
||||
void NiPixelData::post(Reader& nif)
|
||||
|
@ -397,24 +396,22 @@ namespace Nif
|
|||
size_t numBones = nif->getUShort();
|
||||
size_t numStrips = nif->getUShort();
|
||||
size_t bonesPerVertex = nif->getUShort();
|
||||
if (numBones)
|
||||
nif->getUShorts(bones, numBones);
|
||||
nif->readVector(bones, numBones);
|
||||
|
||||
bool hasVertexMap = true;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
hasVertexMap = nif->getBoolean();
|
||||
if (hasVertexMap && numVertices)
|
||||
nif->getUShorts(vertexMap, numVertices);
|
||||
if (hasVertexMap)
|
||||
nif->readVector(vertexMap, numVertices);
|
||||
|
||||
bool hasVertexWeights = true;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
hasVertexWeights = nif->getBoolean();
|
||||
if (hasVertexWeights && numVertices && bonesPerVertex)
|
||||
nif->getFloats(weights, numVertices * bonesPerVertex);
|
||||
if (hasVertexWeights)
|
||||
nif->readVector(weights, numVertices * bonesPerVertex);
|
||||
|
||||
std::vector<unsigned short> stripLengths;
|
||||
if (numStrips)
|
||||
nif->getUShorts(stripLengths, numStrips);
|
||||
nif->readVector(stripLengths, numStrips);
|
||||
|
||||
bool hasFaces = true;
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
|
@ -425,14 +422,14 @@ namespace Nif
|
|||
{
|
||||
strips.resize(numStrips);
|
||||
for (size_t i = 0; i < numStrips; i++)
|
||||
nif->getUShorts(strips[i], stripLengths[i]);
|
||||
nif->readVector(strips[i], stripLengths[i]);
|
||||
}
|
||||
else if (numTriangles)
|
||||
nif->getUShorts(triangles, numTriangles * 3);
|
||||
else
|
||||
nif->readVector(triangles, numTriangles * 3);
|
||||
}
|
||||
bool hasBoneIndices = nif->getChar() != 0;
|
||||
if (hasBoneIndices && numVertices && bonesPerVertex)
|
||||
nif->getChars(boneIndices, numVertices * bonesPerVertex);
|
||||
if (hasBoneIndices)
|
||||
nif->readVector(boneIndices, numVertices * bonesPerVertex);
|
||||
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||
{
|
||||
nif->getChar(); // LOD level
|
||||
|
@ -490,7 +487,7 @@ namespace Nif
|
|||
{
|
||||
mMorphs[i].mKeyFrames = std::make_shared<FloatKeyMap>();
|
||||
mMorphs[i].mKeyFrames->read(nif, /*morph*/ true);
|
||||
nif->getVector3s(mMorphs[i].mVertices, vertCount);
|
||||
nif->readVector(mMorphs[i].mVertices, vertCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ namespace Nif
|
|||
void NiExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
if (recordSize)
|
||||
nif->getChars(data, recordSize);
|
||||
nif->readVector(data, recordSize);
|
||||
}
|
||||
|
||||
void NiStringExtraData::read(NIFStream* nif)
|
||||
|
@ -47,17 +46,13 @@ namespace Nif
|
|||
{
|
||||
Extra::read(nif);
|
||||
|
||||
unsigned int num = nif->getUInt();
|
||||
if (num)
|
||||
nif->getUInts(data, num);
|
||||
nif->readVector(data, nif->getUInt());
|
||||
}
|
||||
|
||||
void NiBinaryExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
unsigned int size = nif->getUInt();
|
||||
if (size)
|
||||
nif->getChars(data, size);
|
||||
nif->readVector(data, nif->getUInt());
|
||||
}
|
||||
|
||||
void NiBooleanExtraData::read(NIFStream* nif)
|
||||
|
@ -82,9 +77,7 @@ namespace Nif
|
|||
void NiFloatsExtraData::read(NIFStream* nif)
|
||||
{
|
||||
Extra::read(nif);
|
||||
unsigned int num = nif->getUInt();
|
||||
if (num)
|
||||
nif->getFloats(data, num);
|
||||
nif->readVector(data, nif->getUInt());
|
||||
}
|
||||
|
||||
void BSBound::read(NIFStream* nif)
|
||||
|
|
|
@ -319,28 +319,26 @@ namespace Nif
|
|||
if (hasRecTypeListings)
|
||||
{
|
||||
unsigned short recTypeNum = nif.getUShort();
|
||||
if (recTypeNum) // Record type list
|
||||
nif.getSizedStrings(recTypes, recTypeNum);
|
||||
if (recNum) // Record type mapping for each record
|
||||
nif.getUShorts(recTypeIndices, recNum);
|
||||
// Record type list
|
||||
nif.getSizedStrings(recTypes, recTypeNum);
|
||||
// Record type mapping for each record
|
||||
nif.readVector(recTypeIndices, recNum);
|
||||
if (ver >= NIFStream::generateVersion(5, 0, 0, 6)) // Groups
|
||||
{
|
||||
if (ver >= NIFStream::generateVersion(20, 1, 0, 1)) // String table
|
||||
{
|
||||
if (ver >= NIFStream::generateVersion(20, 2, 0, 5) && recNum) // Record sizes
|
||||
if (ver >= NIFStream::generateVersion(20, 2, 0, 5)) // Record sizes
|
||||
{
|
||||
std::vector<unsigned int> recSizes; // Currently unused
|
||||
nif.getUInts(recSizes, recNum);
|
||||
nif.readVector(recSizes, recNum);
|
||||
}
|
||||
const std::size_t stringNum = nif.getUInt();
|
||||
nif.getUInt(); // Max string length
|
||||
if (stringNum)
|
||||
nif.getSizedStrings(strings, stringNum);
|
||||
nif.getSizedStrings(strings, stringNum);
|
||||
}
|
||||
std::vector<unsigned int> groups; // Currently unused
|
||||
unsigned int groupNum = nif.getUInt();
|
||||
if (groupNum)
|
||||
nif.getUInts(groups, groupNum);
|
||||
nif.readVector(groups, groupNum);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,53 +1,217 @@
|
|||
#include "nifstream.hpp"
|
||||
// For error reporting
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "niffile.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Read a range of elements into a dynamic buffer per-element
|
||||
// This one should be used if the type cannot be read contiguously
|
||||
// (e.g. quaternions)
|
||||
template <class T>
|
||||
void readRange(Nif::NIFStream& stream, T* dest, size_t size)
|
||||
{
|
||||
for (T& value : std::span(dest, size))
|
||||
stream.read(value);
|
||||
}
|
||||
|
||||
// Read a range of elements into a dynamic buffer
|
||||
// This one should be used if the type can be read contiguously as an array of a different type
|
||||
// (e.g. osg::VecXf can be read as a float array of X elements)
|
||||
template <class elementType, size_t numElements, class T>
|
||||
void readAlignedRange(Files::IStreamPtr& stream, T* dest, size_t size)
|
||||
{
|
||||
static_assert(std::is_standard_layout_v<T>);
|
||||
static_assert(std::alignment_of_v<T> == std::alignment_of_v<elementType>);
|
||||
static_assert(sizeof(T) == sizeof(elementType) * numElements);
|
||||
Nif::readDynamicBufferOfType(stream, reinterpret_cast<elementType*>(dest), size * numElements);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Nif
|
||||
{
|
||||
osg::Quat NIFStream::getQuaternion()
|
||||
{
|
||||
float f[4];
|
||||
readLittleEndianBufferOfType<4, float>(inp, f);
|
||||
osg::Quat quat;
|
||||
quat.w() = f[0];
|
||||
quat.x() = f[1];
|
||||
quat.y() = f[2];
|
||||
quat.z() = f[3];
|
||||
return quat;
|
||||
}
|
||||
|
||||
Transformation NIFStream::getTrafo()
|
||||
{
|
||||
Transformation t;
|
||||
t.pos = getVector3();
|
||||
t.rotation = getMatrix3();
|
||||
t.scale = getFloat();
|
||||
return t;
|
||||
}
|
||||
|
||||
/// Booleans in 4.0.0.2 (Morrowind format) and earlier are 4 byte, while in 4.1.0.0+ they're 1 byte.
|
||||
bool NIFStream::getBoolean()
|
||||
{
|
||||
return getVersion() < generateVersion(4, 1, 0, 0) ? getInt() != 0 : getChar() != 0;
|
||||
}
|
||||
|
||||
/// Read in a string, either from the string table using the index or from the stream using the specified length
|
||||
std::string NIFStream::getString()
|
||||
{
|
||||
return getVersion() < generateVersion(20, 1, 0, 1) ? getSizedString() : file.getString(getUInt());
|
||||
}
|
||||
|
||||
// Convenience utility functions: get the versions of the currently read file
|
||||
unsigned int NIFStream::getVersion() const
|
||||
{
|
||||
return file.getVersion();
|
||||
return mReader.getVersion();
|
||||
}
|
||||
|
||||
unsigned int NIFStream::getUserVersion() const
|
||||
{
|
||||
return file.getBethVersion();
|
||||
return mReader.getUserVersion();
|
||||
}
|
||||
|
||||
unsigned int NIFStream::getBethVersion() const
|
||||
{
|
||||
return file.getBethVersion();
|
||||
return mReader.getBethVersion();
|
||||
}
|
||||
|
||||
std::string NIFStream::getSizedString(size_t length)
|
||||
{
|
||||
std::string str(length, '\0');
|
||||
mStream->read(str.data(), length);
|
||||
if (mStream->bad())
|
||||
throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars");
|
||||
size_t end = str.find('\0');
|
||||
if (end != std::string::npos)
|
||||
str.erase(end);
|
||||
return str;
|
||||
}
|
||||
|
||||
void NIFStream::getSizedStrings(std::vector<std::string>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
vec[i] = getSizedString();
|
||||
}
|
||||
|
||||
std::string NIFStream::getVersionString()
|
||||
{
|
||||
std::string result;
|
||||
std::getline(*mStream, result);
|
||||
if (mStream->bad())
|
||||
throw std::runtime_error("Failed to read version string");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string NIFStream::getStringPalette()
|
||||
{
|
||||
size_t size = get<uint32_t>();
|
||||
std::string str(size, '\0');
|
||||
mStream->read(str.data(), size);
|
||||
if (mStream->bad())
|
||||
throw std::runtime_error("Failed to read string palette of " + std::to_string(size) + " chars");
|
||||
return str;
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec2f>(osg::Vec2f& vec)
|
||||
{
|
||||
readBufferOfType(mStream, vec._v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec3f>(osg::Vec3f& vec)
|
||||
{
|
||||
readBufferOfType(mStream, vec._v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec4f>(osg::Vec4f& vec)
|
||||
{
|
||||
readBufferOfType(mStream, vec._v);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<Matrix3>(Matrix3& mat)
|
||||
{
|
||||
readBufferOfType<9>(mStream, reinterpret_cast<float*>(&mat.mValues));
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Quat>(osg::Quat& quat)
|
||||
{
|
||||
std::array<float, 4> data;
|
||||
readArray(data);
|
||||
quat.w() = data[0];
|
||||
quat.x() = data[1];
|
||||
quat.y() = data[2];
|
||||
quat.z() = data[3];
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<Transformation>(Transformation& t)
|
||||
{
|
||||
read(t.pos);
|
||||
read(t.rotation);
|
||||
read(t.scale);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<bool>(bool& data)
|
||||
{
|
||||
if (getVersion() < generateVersion(4, 1, 0, 0))
|
||||
data = get<int32_t>() != 0;
|
||||
else
|
||||
data = get<int8_t>() != 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<std::string>(std::string& str)
|
||||
{
|
||||
if (getVersion() < generateVersion(20, 1, 0, 1))
|
||||
str = getSizedString();
|
||||
else
|
||||
str = mReader.getString(get<uint32_t>());
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec2f>(osg::Vec2f* dest, size_t size)
|
||||
{
|
||||
readAlignedRange<float, 2>(mStream, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec3f>(osg::Vec3f* dest, size_t size)
|
||||
{
|
||||
readAlignedRange<float, 3>(mStream, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec4f>(osg::Vec4f* dest, size_t size)
|
||||
{
|
||||
readAlignedRange<float, 4>(mStream, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<Matrix3>(Matrix3* dest, size_t size)
|
||||
{
|
||||
readAlignedRange<float, 9>(mStream, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Quat>(osg::Quat* dest, size_t size)
|
||||
{
|
||||
readRange(*this, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<Transformation>(Transformation* dest, size_t size)
|
||||
{
|
||||
readRange(*this, dest, size);
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<bool>(bool* dest, size_t size)
|
||||
{
|
||||
if (getVersion() < generateVersion(4, 1, 0, 0))
|
||||
{
|
||||
for (bool& value : std::span(dest, size))
|
||||
value = get<int32_t>() != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (bool& value : std::span(dest, size))
|
||||
value = get<int8_t>() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void NIFStream::read<std::string>(std::string* dest, size_t size)
|
||||
{
|
||||
if (getVersion() < generateVersion(20, 1, 0, 1))
|
||||
{
|
||||
for (std::string& value : std::span(dest, size))
|
||||
value = getSizedString();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (std::string& value : std::span(dest, size))
|
||||
value = mReader.getString(get<uint32_t>());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,288 +26,177 @@ namespace Nif
|
|||
|
||||
class Reader;
|
||||
|
||||
template <class T>
|
||||
struct is_arithmetic
|
||||
: std::integral_constant<bool, std::is_arithmetic_v<T> || std::is_same<T, Misc::float16_t>::value>
|
||||
{
|
||||
};
|
||||
|
||||
template <std::size_t numInstances, typename T>
|
||||
inline void readLittleEndianBufferOfType(Files::IStreamPtr& pIStream, T* dest)
|
||||
inline void readBufferOfType(Files::IStreamPtr& pIStream, T* dest)
|
||||
{
|
||||
static_assert(is_arithmetic<T>(), "Buffer element type is not arithmetic");
|
||||
static_assert(
|
||||
std::is_arithmetic_v<T> || std::is_same_v<T, Misc::float16_t>, "Buffer element type is not arithmetic");
|
||||
static_assert(!std::is_same_v<T, bool>, "Buffer element type is boolean");
|
||||
pIStream->read((char*)dest, numInstances * sizeof(T));
|
||||
if (pIStream->bad())
|
||||
throw std::runtime_error("Failed to read little endian typed (" + std::string(typeid(T).name())
|
||||
+ ") buffer of " + std::to_string(numInstances) + " instances");
|
||||
throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") buffer of "
|
||||
+ std::to_string(numInstances) + " instances");
|
||||
if constexpr (Misc::IS_BIG_ENDIAN)
|
||||
for (std::size_t i = 0; i < numInstances; i++)
|
||||
Misc::swapEndiannessInplace(dest[i]);
|
||||
}
|
||||
|
||||
template <std::size_t numInstances, typename T>
|
||||
inline void readBufferOfType(Files::IStreamPtr& pIStream, T (&dest)[numInstances])
|
||||
{
|
||||
readBufferOfType<numInstances>(pIStream, static_cast<T*>(dest));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances)
|
||||
inline void readDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances)
|
||||
{
|
||||
static_assert(is_arithmetic<T>(), "Buffer element type is not arithmetic");
|
||||
static_assert(
|
||||
std::is_arithmetic_v<T> || std::is_same_v<T, Misc::float16_t>, "Buffer element type is not arithmetic");
|
||||
static_assert(!std::is_same_v<T, bool>, "Buffer element type is boolean");
|
||||
pIStream->read((char*)dest, numInstances * sizeof(T));
|
||||
if (pIStream->bad())
|
||||
throw std::runtime_error(
|
||||
"Failed to read little endian dynamic buffer of " + std::to_string(numInstances) + " instances");
|
||||
throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") dynamic buffer of "
|
||||
+ std::to_string(numInstances) + " instances");
|
||||
if constexpr (Misc::IS_BIG_ENDIAN)
|
||||
for (std::size_t i = 0; i < numInstances; i++)
|
||||
Misc::swapEndiannessInplace(dest[i]);
|
||||
}
|
||||
template <typename type>
|
||||
type inline readLittleEndianType(Files::IStreamPtr& pIStream)
|
||||
{
|
||||
type val;
|
||||
readLittleEndianBufferOfType<1, type>(pIStream, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
class NIFStream
|
||||
{
|
||||
const Reader& file;
|
||||
|
||||
/// Input stream
|
||||
Files::IStreamPtr inp;
|
||||
const Reader& mReader;
|
||||
Files::IStreamPtr mStream;
|
||||
|
||||
public:
|
||||
explicit NIFStream(const Reader& file, Files::IStreamPtr&& inp)
|
||||
: file(file)
|
||||
, inp(std::move(inp))
|
||||
explicit NIFStream(const Reader& reader, Files::IStreamPtr&& stream)
|
||||
: mReader(reader)
|
||||
, mStream(std::move(stream))
|
||||
{
|
||||
}
|
||||
|
||||
const Reader& getFile() const { return file; }
|
||||
|
||||
void skip(size_t size) { inp->ignore(size); }
|
||||
|
||||
template <class T>
|
||||
void read(T& data)
|
||||
{
|
||||
data = readLittleEndianType<T>(inp);
|
||||
}
|
||||
|
||||
void read(osg::Vec3f& data) { readLittleEndianBufferOfType<3, float>(inp, data._v); }
|
||||
void read(osg::Vec4f& data) { readLittleEndianBufferOfType<4, float>(inp, data._v); }
|
||||
|
||||
template <class T>
|
||||
T get()
|
||||
{
|
||||
return readLittleEndianType<T>(inp);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void readVector(std::vector<T>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<T>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
template <class T, size_t size>
|
||||
void readArray(std::array<T, size>& arr)
|
||||
{
|
||||
readLittleEndianDynamicBufferOfType<T>(inp, arr.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
char getChar() { return readLittleEndianType<char>(inp); }
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
short getShort() { return readLittleEndianType<short>(inp); }
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
unsigned short getUShort() { return readLittleEndianType<unsigned short>(inp); }
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
int getInt() { return readLittleEndianType<int>(inp); }
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
unsigned int getUInt() { return readLittleEndianType<unsigned int>(inp); }
|
||||
|
||||
// DEPRECATED: Use read() or get() whenever relevant
|
||||
float getFloat() { return readLittleEndianType<float>(inp); }
|
||||
|
||||
osg::Vec2f getVector2()
|
||||
{
|
||||
osg::Vec2f vec;
|
||||
readLittleEndianBufferOfType<2, float>(inp, vec._v);
|
||||
return vec;
|
||||
}
|
||||
|
||||
// DEPRECATED: Use read() whenever relevant
|
||||
osg::Vec3f getVector3()
|
||||
{
|
||||
osg::Vec3f vec;
|
||||
readLittleEndianBufferOfType<3, float>(inp, vec._v);
|
||||
return vec;
|
||||
}
|
||||
|
||||
osg::Vec4f getVector4()
|
||||
{
|
||||
osg::Vec4f vec;
|
||||
readLittleEndianBufferOfType<4, float>(inp, vec._v);
|
||||
return vec;
|
||||
}
|
||||
|
||||
Matrix3 getMatrix3()
|
||||
{
|
||||
Matrix3 mat;
|
||||
readLittleEndianBufferOfType<9, float>(inp, (float*)&mat.mValues);
|
||||
return mat;
|
||||
}
|
||||
|
||||
osg::Quat getQuaternion();
|
||||
|
||||
Transformation getTrafo();
|
||||
|
||||
bool getBoolean();
|
||||
|
||||
std::string getString();
|
||||
const Reader& getFile() const { return mReader; }
|
||||
|
||||
unsigned int getVersion() const;
|
||||
unsigned int getUserVersion() const;
|
||||
unsigned int getBethVersion() const;
|
||||
|
||||
// Convert human-readable version numbers into a number that can be compared.
|
||||
/// Convert human-readable version numbers into a number that can be compared.
|
||||
static constexpr uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev)
|
||||
{
|
||||
return (major << 24) + (minor << 16) + (patch << 8) + rev;
|
||||
}
|
||||
|
||||
/// Read in a string of the given length
|
||||
std::string getSizedString(size_t length)
|
||||
void skip(size_t size) { mStream->ignore(size); }
|
||||
|
||||
/// Read into a single instance of type
|
||||
template <class T>
|
||||
void read(T& data)
|
||||
{
|
||||
std::string str(length, '\0');
|
||||
inp->read(str.data(), length);
|
||||
if (inp->bad())
|
||||
throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars");
|
||||
size_t end = str.find('\0');
|
||||
if (end != std::string::npos)
|
||||
str.erase(end);
|
||||
return str;
|
||||
}
|
||||
/// Read in a string of the length specified in the file
|
||||
std::string getSizedString()
|
||||
{
|
||||
size_t size = readLittleEndianType<uint32_t>(inp);
|
||||
return getSizedString(size);
|
||||
readBufferOfType<1>(mStream, &data);
|
||||
}
|
||||
|
||||
/// Specific to Bethesda headers, uses a byte for length
|
||||
std::string getExportString()
|
||||
/// Read multiple instances of type into an array
|
||||
template <class T, size_t size>
|
||||
void readArray(std::array<T, size>& arr)
|
||||
{
|
||||
size_t size = static_cast<size_t>(readLittleEndianType<uint8_t>(inp));
|
||||
return getSizedString(size);
|
||||
readBufferOfType<size>(mStream, arr.data());
|
||||
}
|
||||
|
||||
/// This is special since the version string doesn't start with a number, and ends with "\n"
|
||||
std::string getVersionString()
|
||||
/// Read instances of type into a dynamic buffer
|
||||
template <class T>
|
||||
void read(T* dest, size_t size)
|
||||
{
|
||||
std::string result;
|
||||
std::getline(*inp, result);
|
||||
if (inp->bad())
|
||||
throw std::runtime_error("Failed to read version string");
|
||||
return result;
|
||||
readDynamicBufferOfType<T>(mStream, dest, size);
|
||||
}
|
||||
|
||||
/// Read multiple instances of type into a vector
|
||||
template <class T>
|
||||
void readVector(std::vector<T>& vec, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
vec.resize(size);
|
||||
read(vec.data(), size);
|
||||
}
|
||||
|
||||
/// Extract an instance of type
|
||||
template <class T>
|
||||
T get()
|
||||
{
|
||||
T data;
|
||||
read(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/// Read a string of the given length
|
||||
std::string getSizedString(size_t length);
|
||||
|
||||
/// Read a string of the length specified in the file
|
||||
std::string getSizedString() { return getSizedString(get<uint32_t>()); }
|
||||
|
||||
/// Read a list of strings without using the string table, e.g. the string table itself
|
||||
void getSizedStrings(std::vector<std::string>& vec, size_t size);
|
||||
|
||||
/// Read a Bethesda header string that uses a byte for length
|
||||
std::string getExportString() { return getSizedString(get<uint8_t>()); }
|
||||
|
||||
/// Read the version string which doesn't start with a number and ends with "\n"
|
||||
std::string getVersionString();
|
||||
|
||||
/// Read a sequence of null-terminated strings
|
||||
std::string getStringPalette()
|
||||
{
|
||||
size_t size = readLittleEndianType<uint32_t>(inp);
|
||||
std::string str(size, '\0');
|
||||
inp->read(str.data(), size);
|
||||
if (inp->bad())
|
||||
throw std::runtime_error("Failed to read string palette of " + std::to_string(size) + " chars");
|
||||
return str;
|
||||
}
|
||||
std::string getStringPalette();
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getChars(std::vector<char>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<char>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getUChars(std::vector<unsigned char>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<unsigned char>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getUShorts(std::vector<unsigned short>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<unsigned short>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getFloats(std::vector<float>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<float>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getInts(std::vector<int>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<int>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
// DEPRECATED: Use readVector()
|
||||
void getUInts(std::vector<unsigned int>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
readLittleEndianDynamicBufferOfType<unsigned int>(inp, vec.data(), size);
|
||||
}
|
||||
|
||||
void getVector2s(std::vector<osg::Vec2f>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec2f is 2 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size * 2);
|
||||
}
|
||||
|
||||
void getVector3s(std::vector<osg::Vec3f>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec3f is 3 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size * 3);
|
||||
}
|
||||
|
||||
void getVector4s(std::vector<osg::Vec4f>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
/* The packed storage of each Vec4f is 4 floats exactly */
|
||||
readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size * 4);
|
||||
}
|
||||
|
||||
void getQuaternions(std::vector<osg::Quat>& quat, size_t size)
|
||||
{
|
||||
quat.resize(size);
|
||||
for (size_t i = 0; i < quat.size(); i++)
|
||||
quat[i] = getQuaternion();
|
||||
}
|
||||
|
||||
void getStrings(std::vector<std::string>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
vec[i] = getString();
|
||||
}
|
||||
/// We need to use this when the string table isn't actually initialized.
|
||||
void getSizedStrings(std::vector<std::string>& vec, size_t size)
|
||||
{
|
||||
vec.resize(size);
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
vec[i] = getSizedString();
|
||||
}
|
||||
/// DEPRECATED: Use read() or get()
|
||||
char getChar() { return get<char>(); }
|
||||
short getShort() { return get<short>(); }
|
||||
unsigned short getUShort() { return get<unsigned short>(); }
|
||||
int getInt() { return get<int>(); }
|
||||
unsigned int getUInt() { return get<unsigned int>(); }
|
||||
float getFloat() { return get<float>(); }
|
||||
osg::Vec2f getVector2() { return get<osg::Vec2f>(); }
|
||||
osg::Vec3f getVector3() { return get<osg::Vec3f>(); }
|
||||
osg::Vec4f getVector4() { return get<osg::Vec4f>(); }
|
||||
Matrix3 getMatrix3() { return get<Matrix3>(); }
|
||||
osg::Quat getQuaternion() { return get<osg::Quat>(); }
|
||||
Transformation getTrafo() { return get<Transformation>(); }
|
||||
bool getBoolean() { return get<bool>(); }
|
||||
std::string getString() { return get<std::string>(); }
|
||||
};
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec2f>(osg::Vec2f& vec);
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec3f>(osg::Vec3f& vec);
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec4f>(osg::Vec4f& vec);
|
||||
template <>
|
||||
void NIFStream::read<Matrix3>(Matrix3& mat);
|
||||
template <>
|
||||
void NIFStream::read<osg::Quat>(osg::Quat& quat);
|
||||
template <>
|
||||
void NIFStream::read<Transformation>(Transformation& t);
|
||||
template <>
|
||||
void NIFStream::read<bool>(bool& data);
|
||||
template <>
|
||||
void NIFStream::read<std::string>(std::string& str);
|
||||
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec2f>(osg::Vec2f* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec3f>(osg::Vec3f* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<osg::Vec4f>(osg::Vec4f* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<Matrix3>(Matrix3* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<osg::Quat>(osg::Quat* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<Transformation>(Transformation* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<bool>(bool* dest, size_t size);
|
||||
template <>
|
||||
void NIFStream::read<std::string>(std::string* dest, size_t size);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -157,11 +157,9 @@ namespace Nif
|
|||
num = nif->getBoolean(); // Has Shader
|
||||
else if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
|
||||
num = nif->getUInt();
|
||||
if (num)
|
||||
{
|
||||
nif->getStrings(names, num);
|
||||
nif->getInts(extra, num);
|
||||
}
|
||||
|
||||
nif->readVector(names, num);
|
||||
nif->readVector(extra, num);
|
||||
if (nif->getVersion() >= NIFStream::generateVersion(20, 2, 0, 5))
|
||||
active = nif->getUInt();
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
|
@ -342,7 +340,7 @@ namespace Nif
|
|||
|
||||
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76)
|
||||
{
|
||||
nif->readVector(mBoundMinMax, 6);
|
||||
nif->readArray(mBoundMinMax);
|
||||
}
|
||||
|
||||
mSkin.read(nif);
|
||||
|
@ -377,10 +375,7 @@ namespace Nif
|
|||
}
|
||||
|
||||
if (mDataSize > 0)
|
||||
{
|
||||
mTriangles.resize(triNum * 3);
|
||||
nif->readVector(mTriangles, triNum * 3);
|
||||
}
|
||||
|
||||
if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_SSE)
|
||||
{
|
||||
|
|
|
@ -356,7 +356,7 @@ namespace Nif
|
|||
struct BSTriShape : Node
|
||||
{
|
||||
NiBoundingVolume::NiSphereBV mBoundingSphere;
|
||||
std::vector<float> mBoundMinMax;
|
||||
std::array<float, 6> mBoundMinMax;
|
||||
|
||||
NiSkinInstancePtr mSkin;
|
||||
BSShaderPropertyPtr mShaderProperty;
|
||||
|
|
|
@ -53,8 +53,7 @@ namespace Nif
|
|||
mOffset = nif->getVector4();
|
||||
if (nif->getBethVersion() > NIFFile::BethVersion::BETHVER_FO3)
|
||||
nif->getChar(); // MOPP data build type
|
||||
if (size)
|
||||
nif->getChars(mData, size);
|
||||
nif->readVector(mData, size);
|
||||
}
|
||||
|
||||
void bhkEntityCInfo::read(NIFStream* nif)
|
||||
|
@ -99,18 +98,10 @@ namespace Nif
|
|||
mMaterialIndex = nif->getUInt();
|
||||
mReference = nif->getUShort();
|
||||
mTransformIndex = nif->getUShort();
|
||||
size_t numVertices = nif->getUInt();
|
||||
if (numVertices)
|
||||
nif->getUShorts(mVertices, numVertices);
|
||||
size_t numIndices = nif->getUInt();
|
||||
if (numIndices)
|
||||
nif->getUShorts(mIndices, numIndices);
|
||||
size_t numStrips = nif->getUInt();
|
||||
if (numStrips)
|
||||
nif->getUShorts(mStrips, numStrips);
|
||||
size_t numInfos = nif->getUInt();
|
||||
if (numInfos)
|
||||
nif->getUShorts(mWeldingInfos, numInfos);
|
||||
nif->readVector(mVertices, nif->getUInt());
|
||||
nif->readVector(mIndices, nif->getUInt());
|
||||
nif->readVector(mStrips, nif->getUInt());
|
||||
nif->readVector(mWeldingInfos, nif->getUInt());
|
||||
}
|
||||
|
||||
void bhkRigidBodyCInfo::read(NIFStream* nif)
|
||||
|
@ -395,8 +386,7 @@ namespace Nif
|
|||
if (nif->getVersion() >= NIFStream::generateVersion(10, 1, 0, 0))
|
||||
mScale = nif->getVector4();
|
||||
readRecordList(nif, mData);
|
||||
unsigned int numFilters = nif->getUInt();
|
||||
nif->getUInts(mFilters, numFilters);
|
||||
nif->readVector(mFilters, nif->getUInt());
|
||||
}
|
||||
|
||||
void bhkNiTriStripsShape::post(Reader& nif)
|
||||
|
@ -438,7 +428,7 @@ namespace Nif
|
|||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
compressed = nif->getBoolean();
|
||||
if (!compressed)
|
||||
nif->getVector3s(mVertices, numVertices);
|
||||
nif->readVector(mVertices, numVertices);
|
||||
else
|
||||
nif->skip(6 * numVertices); // Half-precision vectors are not currently supported
|
||||
if (nif->getVersion() >= NIFFile::NIFVersion::VER_BGS)
|
||||
|
@ -465,12 +455,8 @@ namespace Nif
|
|||
bhkConvexShape::read(nif);
|
||||
mVerticesProperty.read(nif);
|
||||
mNormalsProperty.read(nif);
|
||||
unsigned int numVertices = nif->getUInt();
|
||||
if (numVertices)
|
||||
nif->getVector4s(mVertices, numVertices);
|
||||
unsigned int numNormals = nif->getUInt();
|
||||
if (numNormals)
|
||||
nif->getVector4s(mNormals, numNormals);
|
||||
nif->readVector(mVertices, nif->getUInt());
|
||||
nif->readVector(mNormals, nif->getUInt());
|
||||
}
|
||||
|
||||
void bhkConvexTransformShape::read(NIFStream* nif)
|
||||
|
@ -564,9 +550,7 @@ namespace Nif
|
|||
for (bhkQsTransform& transform : mChunkTransforms)
|
||||
transform.read(nif);
|
||||
|
||||
size_t numBigVertices = nif->getUInt();
|
||||
if (numBigVertices)
|
||||
nif->getVector4s(mBigVerts, numBigVertices);
|
||||
nif->readVector(mBigVerts, nif->getUInt());
|
||||
|
||||
size_t numBigTriangles = nif->getUInt();
|
||||
mBigTris.resize(numBigTriangles);
|
||||
|
|
Loading…
Reference in a new issue