Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
7.7 kB · 277 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278#include "nifstream.hpp"
#include <cerrno>#include <format>#include <span>#include <stdexcept>#include <system_error>
#include <components/toutf8/toutf8.hpp>
#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{
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) { checkStreamSize(length); std::string str(length, '\0'); mStream->read(str.data(), length); if (mStream->fail()) throw std::runtime_error(std::format( "Failed to read sized string of {} chars: {}", length, std::generic_category().message(errno))); 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<std::string>& vec, size_t size) { vec.clear(); vec.reserve(size); for (size_t i = 0; i < size; i++) vec.push_back(getSizedString()); }
std::string NIFStream::getVersionString() { std::string result; std::getline(*mStream, result); if (mStream->fail()) throw std::runtime_error( std::format("Failed to read version string: {}", std::generic_category().message(errno))); return result; }
std::string NIFStream::getStringPalette() { size_t size = get<uint32_t>(); checkStreamSize(size); std::string str(size, '\0'); mStream->read(str.data(), size); if (mStream->fail()) throw std::runtime_error(std::format( "Failed to read string palette of {} chars: {}", size, std::generic_category().message(errno))); 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<osg::BoundingSpheref>(osg::BoundingSpheref& sphere) { read(sphere.center()); read(sphere.radius()); }
template <> void NIFStream::read<NiTransform>(NiTransform& transform) { read(transform.mRotation); read(transform.mTranslation); read(transform.mScale); }
template <> void NIFStream::read<NiQuatTransform>(NiQuatTransform& transform) { read(transform.mTranslation); read(transform.mRotation); read(transform.mScale); if (getVersion() >= generateVersion(10, 1, 0, 110)) return; if (!get<bool>()) transform.mTranslation = osg::Vec3f(); if (!get<bool>()) transform.mRotation = osg::Quat(); if (!get<bool>()) transform.mScale = 1.f; }
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<osg::BoundingSpheref>(osg::BoundingSpheref* dest, size_t size) { readRange(*this, dest, size); }
template <> void NIFStream::read<NiTransform>(NiTransform* dest, size_t size) { readRange(*this, dest, size); }
template <> void NIFStream::read<NiQuatTransform>(NiQuatTransform* 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)) { checkStreamSize(size * sizeof(int32_t)); std::vector<int32_t> buf(size); read(buf.data(), size); for (size_t i = 0; i < size; ++i) dest[i] = buf[i] != 0; } else { checkStreamSize(size * sizeof(int8_t)); std::vector<int8_t> buf(size); read(buf.data(), size); for (size_t i = 0; i < size; ++i) dest[i] = buf[i] != 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>()); } }
void NIFStream::checkStreamSize(std::size_t size) { if (size > mStreamSize) throw std::runtime_error(std::format("Trying to read more than stream size: {} max={}", size, mStreamSize)); }}