#include "nifstream.hpp" #include #include "niffile.hpp" #include "../to_utf8/to_utf8.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 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 void readAlignedRange(Files::IStreamPtr& stream, T* dest, size_t size) { static_assert(std::is_standard_layout_v); static_assert(std::alignment_of_v == std::alignment_of_v); static_assert(sizeof(T) == sizeof(elementType) * numElements); Nif::readDynamicBufferOfType(stream, reinterpret_cast(dest), size * numElements); } } namespace Nif { unsigned int NIFStream::getVersion() const { return mReader.getVersion(); } unsigned int NIFStream::getUserVersion() const { return mReader.getUserVersion(); } unsigned int NIFStream::getBethVersion() const { 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); if (mEncoder) str = mEncoder->getUtf8(str, ToUTF8::BufferAllocationPolicy::UseGrowFactor, mBuffer); return str; } void NIFStream::getSizedStrings(std::vector& 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(); 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& vec) { readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(osg::Vec3f& vec) { readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(osg::Vec4f& vec) { readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(Matrix3& mat) { readBufferOfType<9>(mStream, reinterpret_cast(&mat.mValues)); } template <> void NIFStream::read(osg::Quat& quat) { std::array data; readArray(data); quat.w() = data[0]; quat.x() = data[1]; quat.y() = data[2]; quat.z() = data[3]; } template <> void NIFStream::read(osg::BoundingSpheref& sphere) { read(sphere.center()); read(sphere.radius()); } template <> void NIFStream::read(NiTransform& transform) { read(transform.mRotation); read(transform.mTranslation); read(transform.mScale); } template <> void NIFStream::read(NiQuatTransform& transform) { read(transform.mTranslation); read(transform.mRotation); read(transform.mScale); if (getVersion() >= generateVersion(10, 1, 0, 110)) return; if (!get()) transform.mTranslation = osg::Vec3f(); if (!get()) transform.mRotation = osg::Quat(); if (!get()) transform.mScale = 1.f; } template <> void NIFStream::read(bool& data) { if (getVersion() < generateVersion(4, 1, 0, 0)) data = get() != 0; else data = get() != 0; } template <> void NIFStream::read(std::string& str) { if (getVersion() < generateVersion(20, 1, 0, 1)) str = getSizedString(); else str = mReader.getString(get()); } template <> void NIFStream::read(osg::Vec2f* dest, size_t size) { readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Vec3f* dest, size_t size) { readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Vec4f* dest, size_t size) { readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(Matrix3* dest, size_t size) { readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Quat* dest, size_t size) { readRange(*this, dest, size); } template <> void NIFStream::read(osg::BoundingSpheref* dest, size_t size) { readRange(*this, dest, size); } template <> void NIFStream::read(NiTransform* dest, size_t size) { readRange(*this, dest, size); } template <> void NIFStream::read(NiQuatTransform* dest, size_t size) { readRange(*this, dest, size); } template <> void NIFStream::read(bool* dest, size_t size) { if (getVersion() < generateVersion(4, 1, 0, 0)) { for (bool& value : std::span(dest, size)) value = get() != 0; } else { for (bool& value : std::span(dest, size)) value = get() != 0; } } template <> void NIFStream::read(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()); } } }