From aed440ce6480deac42c763160457adb7603ad1d7 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 03:46:38 +0300 Subject: [PATCH 01/10] Clean up error handling in NIFStream reading No particular reason to introduce is_arithmetic specifically for 16-bit floats, there's no significant code duplication Bypassing getBoolean() could be error-prone for records that are shared between pre-4.2 and post-4.2 formats --- components/nif/nifstream.hpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 92f8ca0f91..c3fc03320d 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -26,20 +26,16 @@ namespace Nif class Reader; - template - struct is_arithmetic - : std::integral_constant || std::is_same::value> - { - }; - template inline void readLittleEndianBufferOfType(Files::IStreamPtr& pIStream, T* dest) { - static_assert(is_arithmetic(), "Buffer element type is not arithmetic"); + static_assert( + std::is_arithmetic_v || std::is_same_v, "Buffer element type is not arithmetic"); + static_assert(!std::is_same_v, "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]); @@ -48,11 +44,13 @@ namespace Nif template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) { - static_assert(is_arithmetic(), "Buffer element type is not arithmetic"); + static_assert( + std::is_arithmetic_v || std::is_same_v, "Buffer element type is not arithmetic"); + static_assert(!std::is_same_v, "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]); From 62d643f668015564dddd0a29eabf4837e636ff39 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 04:10:55 +0300 Subject: [PATCH 02/10] NIFStream: Drop LittleEndian from reading methods This is just noise. We don't clarify reading endianness for any other file format. --- components/nif/nifstream.cpp | 2 +- components/nif/nifstream.hpp | 64 ++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 2bd57d1633..8e7833e127 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -7,7 +7,7 @@ namespace Nif osg::Quat NIFStream::getQuaternion() { float f[4]; - readLittleEndianBufferOfType<4, float>(inp, f); + readBufferOfType<4, float>(inp, f); osg::Quat quat; quat.w() = f[0]; quat.x() = f[1]; diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index c3fc03320d..8cd11b9829 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -27,7 +27,7 @@ namespace Nif class Reader; template - inline void readLittleEndianBufferOfType(Files::IStreamPtr& pIStream, T* dest) + inline void readBufferOfType(Files::IStreamPtr& pIStream, T* dest) { static_assert( std::is_arithmetic_v || std::is_same_v, "Buffer element type is not arithmetic"); @@ -42,7 +42,7 @@ namespace Nif } template - 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( std::is_arithmetic_v || std::is_same_v, "Buffer element type is not arithmetic"); @@ -56,10 +56,10 @@ namespace Nif Misc::swapEndiannessInplace(dest[i]); } template - type inline readLittleEndianType(Files::IStreamPtr& pIStream) + type inline readType(Files::IStreamPtr& pIStream) { type val; - readLittleEndianBufferOfType<1, type>(pIStream, &val); + readBufferOfType<1, type>(pIStream, &val); return val; } @@ -84,53 +84,53 @@ namespace Nif template void read(T& data) { - data = readLittleEndianType(inp); + data = readType(inp); } - void read(osg::Vec3f& data) { readLittleEndianBufferOfType<3, float>(inp, data._v); } - void read(osg::Vec4f& data) { readLittleEndianBufferOfType<4, float>(inp, data._v); } + void read(osg::Vec3f& data) { readBufferOfType<3, float>(inp, data._v); } + void read(osg::Vec4f& data) { readBufferOfType<4, float>(inp, data._v); } template T get() { - return readLittleEndianType(inp); + return readType(inp); } template void readVector(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } template void readArray(std::array& arr) { - readLittleEndianDynamicBufferOfType(inp, arr.data(), size); + readDynamicBufferOfType(inp, arr.data(), size); } // DEPRECATED: Use read() or get() whenever relevant - char getChar() { return readLittleEndianType(inp); } + char getChar() { return readType(inp); } // DEPRECATED: Use read() or get() whenever relevant - short getShort() { return readLittleEndianType(inp); } + short getShort() { return readType(inp); } // DEPRECATED: Use read() or get() whenever relevant - unsigned short getUShort() { return readLittleEndianType(inp); } + unsigned short getUShort() { return readType(inp); } // DEPRECATED: Use read() or get() whenever relevant - int getInt() { return readLittleEndianType(inp); } + int getInt() { return readType(inp); } // DEPRECATED: Use read() or get() whenever relevant - unsigned int getUInt() { return readLittleEndianType(inp); } + unsigned int getUInt() { return readType(inp); } // DEPRECATED: Use read() or get() whenever relevant - float getFloat() { return readLittleEndianType(inp); } + float getFloat() { return readType(inp); } osg::Vec2f getVector2() { osg::Vec2f vec; - readLittleEndianBufferOfType<2, float>(inp, vec._v); + readBufferOfType<2, float>(inp, vec._v); return vec; } @@ -138,21 +138,21 @@ namespace Nif osg::Vec3f getVector3() { osg::Vec3f vec; - readLittleEndianBufferOfType<3, float>(inp, vec._v); + readBufferOfType<3, float>(inp, vec._v); return vec; } osg::Vec4f getVector4() { osg::Vec4f vec; - readLittleEndianBufferOfType<4, float>(inp, vec._v); + readBufferOfType<4, float>(inp, vec._v); return vec; } Matrix3 getMatrix3() { Matrix3 mat; - readLittleEndianBufferOfType<9, float>(inp, (float*)&mat.mValues); + readBufferOfType<9, float>(inp, (float*)&mat.mValues); return mat; } @@ -189,14 +189,14 @@ namespace Nif /// Read in a string of the length specified in the file std::string getSizedString() { - size_t size = readLittleEndianType(inp); + size_t size = readType(inp); return getSizedString(size); } /// Specific to Bethesda headers, uses a byte for length std::string getExportString() { - size_t size = static_cast(readLittleEndianType(inp)); + size_t size = static_cast(readType(inp)); return getSizedString(size); } @@ -213,7 +213,7 @@ namespace Nif /// Read a sequence of null-terminated strings std::string getStringPalette() { - size_t size = readLittleEndianType(inp); + size_t size = readType(inp); std::string str(size, '\0'); inp->read(str.data(), size); if (inp->bad()) @@ -225,63 +225,63 @@ namespace Nif void getChars(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUChars(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUShorts(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getFloats(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getInts(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUInts(std::vector& vec, size_t size) { vec.resize(size); - readLittleEndianDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(inp, vec.data(), size); } void getVector2s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec2f is 2 floats exactly */ - readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size * 2); + readDynamicBufferOfType(inp, (float*)vec.data(), size * 2); } void getVector3s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec3f is 3 floats exactly */ - readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size * 3); + readDynamicBufferOfType(inp, (float*)vec.data(), size * 3); } void getVector4s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec4f is 4 floats exactly */ - readLittleEndianDynamicBufferOfType(inp, (float*)vec.data(), size * 4); + readDynamicBufferOfType(inp, (float*)vec.data(), size * 4); } void getQuaternions(std::vector& quat, size_t size) From 4f549c267f871fe919fb017ca0b5922e2662bc04 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 04:19:18 +0300 Subject: [PATCH 03/10] NIFStream: rename input stream and file methods To conform to modern style conventions --- components/nif/nifstream.cpp | 10 ++--- components/nif/nifstream.hpp | 84 ++++++++++++++++++------------------ 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 8e7833e127..489548f52c 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -7,7 +7,7 @@ namespace Nif osg::Quat NIFStream::getQuaternion() { float f[4]; - readBufferOfType<4, float>(inp, f); + readBufferOfType<4, float>(mStream, f); osg::Quat quat; quat.w() = f[0]; quat.x() = f[1]; @@ -34,20 +34,20 @@ namespace Nif /// 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()); + return getVersion() < generateVersion(20, 1, 0, 1) ? getSizedString() : mReader.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.getBethVersion(); } unsigned int NIFStream::getBethVersion() const { - return file.getBethVersion(); + return mReader.getBethVersion(); } } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 8cd11b9829..9456d2870a 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -65,72 +65,70 @@ namespace Nif 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; } + const Reader& getFile() const { return mReader; } - void skip(size_t size) { inp->ignore(size); } + void skip(size_t size) { mStream->ignore(size); } template void read(T& data) { - data = readType(inp); + data = readType(mStream); } - void read(osg::Vec3f& data) { readBufferOfType<3, float>(inp, data._v); } - void read(osg::Vec4f& data) { readBufferOfType<4, float>(inp, data._v); } + void read(osg::Vec3f& data) { readBufferOfType<3, float>(mStream, data._v); } + void read(osg::Vec4f& data) { readBufferOfType<4, float>(mStream, data._v); } template T get() { - return readType(inp); + return readType(mStream); } template void readVector(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } template void readArray(std::array& arr) { - readDynamicBufferOfType(inp, arr.data(), size); + readDynamicBufferOfType(mStream, arr.data(), size); } // DEPRECATED: Use read() or get() whenever relevant - char getChar() { return readType(inp); } + char getChar() { return readType(mStream); } // DEPRECATED: Use read() or get() whenever relevant - short getShort() { return readType(inp); } + short getShort() { return readType(mStream); } // DEPRECATED: Use read() or get() whenever relevant - unsigned short getUShort() { return readType(inp); } + unsigned short getUShort() { return readType(mStream); } // DEPRECATED: Use read() or get() whenever relevant - int getInt() { return readType(inp); } + int getInt() { return readType(mStream); } // DEPRECATED: Use read() or get() whenever relevant - unsigned int getUInt() { return readType(inp); } + unsigned int getUInt() { return readType(mStream); } // DEPRECATED: Use read() or get() whenever relevant - float getFloat() { return readType(inp); } + float getFloat() { return readType(mStream); } osg::Vec2f getVector2() { osg::Vec2f vec; - readBufferOfType<2, float>(inp, vec._v); + readBufferOfType<2, float>(mStream, vec._v); return vec; } @@ -138,21 +136,21 @@ namespace Nif osg::Vec3f getVector3() { osg::Vec3f vec; - readBufferOfType<3, float>(inp, vec._v); + readBufferOfType<3, float>(mStream, vec._v); return vec; } osg::Vec4f getVector4() { osg::Vec4f vec; - readBufferOfType<4, float>(inp, vec._v); + readBufferOfType<4, float>(mStream, vec._v); return vec; } Matrix3 getMatrix3() { Matrix3 mat; - readBufferOfType<9, float>(inp, (float*)&mat.mValues); + readBufferOfType<9, float>(mStream, (float*)&mat.mValues); return mat; } @@ -178,8 +176,8 @@ namespace Nif std::string getSizedString(size_t length) { std::string str(length, '\0'); - inp->read(str.data(), length); - if (inp->bad()) + 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) @@ -189,14 +187,14 @@ namespace Nif /// Read in a string of the length specified in the file std::string getSizedString() { - size_t size = readType(inp); + size_t size = readType(mStream); return getSizedString(size); } /// Specific to Bethesda headers, uses a byte for length std::string getExportString() { - size_t size = static_cast(readType(inp)); + size_t size = static_cast(readType(mStream)); return getSizedString(size); } @@ -204,8 +202,8 @@ namespace Nif std::string getVersionString() { std::string result; - std::getline(*inp, result); - if (inp->bad()) + std::getline(*mStream, result); + if (mStream->bad()) throw std::runtime_error("Failed to read version string"); return result; } @@ -213,10 +211,10 @@ namespace Nif /// Read a sequence of null-terminated strings std::string getStringPalette() { - size_t size = readType(inp); + size_t size = readType(mStream); std::string str(size, '\0'); - inp->read(str.data(), size); - if (inp->bad()) + 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; } @@ -225,63 +223,63 @@ namespace Nif void getChars(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } // DEPRECATED: Use readVector() void getUChars(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } // DEPRECATED: Use readVector() void getUShorts(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } // DEPRECATED: Use readVector() void getFloats(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } // DEPRECATED: Use readVector() void getInts(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } // DEPRECATED: Use readVector() void getUInts(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(inp, vec.data(), size); + readDynamicBufferOfType(mStream, vec.data(), size); } void getVector2s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec2f is 2 floats exactly */ - readDynamicBufferOfType(inp, (float*)vec.data(), size * 2); + readDynamicBufferOfType(mStream, (float*)vec.data(), size * 2); } void getVector3s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec3f is 3 floats exactly */ - readDynamicBufferOfType(inp, (float*)vec.data(), size * 3); + readDynamicBufferOfType(mStream, (float*)vec.data(), size * 3); } void getVector4s(std::vector& vec, size_t size) { vec.resize(size); /* The packed storage of each Vec4f is 4 floats exactly */ - readDynamicBufferOfType(inp, (float*)vec.data(), size * 4); + readDynamicBufferOfType(mStream, (float*)vec.data(), size * 4); } void getQuaternions(std::vector& quat, size_t size) From e8f9a918ae1fee508f9871c7ceb646e0a0447707 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 04:28:16 +0300 Subject: [PATCH 04/10] NIFStream: use the right function for readArray --- components/nif/nifstream.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 9456d2870a..05cffa8596 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -104,7 +104,7 @@ namespace Nif template void readArray(std::array& arr) { - readDynamicBufferOfType(mStream, arr.data(), size); + readBufferOfType(mStream, arr.data()); } // DEPRECATED: Use read() or get() whenever relevant From ce13f0518798e04ea3fafeedc2cee98f79535ea4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 04:39:32 +0300 Subject: [PATCH 05/10] NIFStream: clean up comments --- components/nif/nifstream.cpp | 5 +---- components/nif/nifstream.hpp | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 489548f52c..55b0cb713a 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -1,5 +1,5 @@ #include "nifstream.hpp" -// For error reporting + #include "niffile.hpp" namespace Nif @@ -25,19 +25,16 @@ namespace Nif 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() : mReader.getString(getUInt()); } - // Convenience utility functions: get the versions of the currently read file unsigned int NIFStream::getVersion() const { return mReader.getVersion(); diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 05cffa8596..1826ae125c 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -79,6 +79,7 @@ namespace Nif void skip(size_t size) { mStream->ignore(size); } + /// Read into a single instance of type template void read(T& data) { @@ -88,12 +89,14 @@ namespace Nif void read(osg::Vec3f& data) { readBufferOfType<3, float>(mStream, data._v); } void read(osg::Vec4f& data) { readBufferOfType<4, float>(mStream, data._v); } + /// Extract an instance of type template T get() { return readType(mStream); } + /// Read multiple instances of type into a vector template void readVector(std::vector& vec, size_t size) { @@ -101,6 +104,7 @@ namespace Nif readDynamicBufferOfType(mStream, vec.data(), size); } + /// Read multiple instances of type into an array template void readArray(std::array& arr) { @@ -158,21 +162,23 @@ namespace Nif Transformation getTrafo(); + /// Read in a boolean. Boolean serialization format differs between versions bool getBoolean(); + /// Read in a string, either from the string table or from the stream depending on the version std::string getString(); 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 + /// Read a string of the given length std::string getSizedString(size_t length) { std::string str(length, '\0'); @@ -184,21 +190,22 @@ namespace Nif str.erase(end); return str; } - /// Read in a string of the length specified in the file + + /// Read a string of the length specified in the file std::string getSizedString() { size_t size = readType(mStream); return getSizedString(size); } - /// Specific to Bethesda headers, uses a byte for length + /// Read a Bethesda header string that uses a byte for length std::string getExportString() { size_t size = static_cast(readType(mStream)); return getSizedString(size); } - /// This is special since the version string doesn't start with a number, and ends with "\n" + /// Read the version string which doesn't start with a number and ends with "\n" std::string getVersionString() { std::string result; @@ -264,21 +271,21 @@ namespace Nif void getVector2s(std::vector& vec, size_t size) { vec.resize(size); - /* The packed storage of each Vec2f is 2 floats exactly */ + // The packed storage of each Vec2f is 2 floats exactly readDynamicBufferOfType(mStream, (float*)vec.data(), size * 2); } void getVector3s(std::vector& vec, size_t size) { vec.resize(size); - /* The packed storage of each Vec3f is 3 floats exactly */ + // The packed storage of each Vec3f is 3 floats exactly readDynamicBufferOfType(mStream, (float*)vec.data(), size * 3); } void getVector4s(std::vector& vec, size_t size) { vec.resize(size); - /* The packed storage of each Vec4f is 4 floats exactly */ + // The packed storage of each Vec4f is 4 floats exactly readDynamicBufferOfType(mStream, (float*)vec.data(), size * 4); } @@ -295,7 +302,8 @@ namespace Nif 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. + + /// Read a list of strings without using the string table, e.g. the string table itself void getSizedStrings(std::vector& vec, size_t size) { vec.resize(size); From d078907dcbb3d22c19144c126852a3a000437ec7 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 04:51:14 +0300 Subject: [PATCH 06/10] NIFStream: rewrite loading for everything Replace overloading with read() template specializations for every type that needs specific handling Make use of the new read() or get() in all methods Move complex string-related methods to implementation --- components/nif/nifstream.cpp | 206 +++++++++++++++++++---- components/nif/nifstream.hpp | 318 ++++++++++++----------------------- 2 files changed, 279 insertions(+), 245 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index 55b0cb713a..d387ae5259 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -4,47 +4,191 @@ namespace Nif { - osg::Quat NIFStream::getQuaternion() - { - float f[4]; - readBufferOfType<4, float>(mStream, 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; - } - - bool NIFStream::getBoolean() - { - return getVersion() < generateVersion(4, 1, 0, 0) ? getInt() != 0 : getChar() != 0; - } - - std::string NIFStream::getString() - { - return getVersion() < generateVersion(20, 1, 0, 1) ? getSizedString() : mReader.getString(getUInt()); - } unsigned int NIFStream::getVersion() const { return mReader.getVersion(); } + unsigned int NIFStream::getUserVersion() const { - return mReader.getBethVersion(); + 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); + 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<2>(mStream, vec._v); + } + + template <> + void NIFStream::read(osg::Vec3f& vec) + { + readBufferOfType<3>(mStream, vec._v); + } + + template <> + void NIFStream::read(osg::Vec4f& vec) + { + readBufferOfType<4>(mStream, vec._v); + } + + template <> + void NIFStream::read(Matrix3& mat) + { + readBufferOfType<9>(mStream, (float*)&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(Transformation& t) + { + read(t.pos); + read(t.rotation); + read(t.scale); + } + + 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) + { + // The packed storage of each Vec2f is 2 floats exactly + readDynamicBufferOfType(mStream, (float*)dest, size * 2); + } + + template <> + void NIFStream::read(osg::Vec3f* dest, size_t size) + { + // The packed storage of each Vec3f is 3 floats exactly + readDynamicBufferOfType(mStream, (float*)dest, size * 3); + } + + template <> + void NIFStream::read(osg::Vec4f* dest, size_t size) + { + // The packed storage of each Vec4f is 4 floats exactly + readDynamicBufferOfType(mStream, (float*)dest, size * 4); + } + + template <> + void NIFStream::read(Matrix3* dest, size_t size) + { + // The packed storage of each Matrix3 is 9 floats exactly + readDynamicBufferOfType(mStream, (float*)dest, size * 9); + } + + template <> + void NIFStream::read(osg::Quat* dest, size_t size) + { + for (size_t i = 0; i < size; i++) + read(dest[i]); + } + + template <> + void NIFStream::read(Transformation* dest, size_t size) + { + for (size_t i = 0; i < size; i++) + read(dest[i]); + } + + template <> + void NIFStream::read(bool* dest, size_t size) + { + if (getVersion() < generateVersion(4, 1, 0, 0)) + { + for (size_t i = 0; i < size; i++) + dest[i] = get() != 0; + } + else + { + for (size_t i = 0; i < size; i++) + dest[i] = get() != 0; + } + } + + template <> + void NIFStream::read(std::string* dest, size_t size) + { + if (getVersion() < generateVersion(20, 1, 0, 1)) + { + for (size_t i = 0; i < size; i++) + dest[i] = getSizedString(); + } + else + { + for (size_t i = 0; i < size; i++) + dest[i] = mReader.getString(get()); + } + } + } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 1826ae125c..b9b2f1923d 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -55,13 +55,6 @@ namespace Nif for (std::size_t i = 0; i < numInstances; i++) Misc::swapEndiannessInplace(dest[i]); } - template - type inline readType(Files::IStreamPtr& pIStream) - { - type val; - readBufferOfType<1, type>(pIStream, &val); - return val; - } class NIFStream { @@ -77,23 +70,37 @@ namespace Nif 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. + 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; + } + void skip(size_t size) { mStream->ignore(size); } /// Read into a single instance of type template void read(T& data) { - data = readType(mStream); + readBufferOfType<1>(mStream, &data); } - void read(osg::Vec3f& data) { readBufferOfType<3, float>(mStream, data._v); } - void read(osg::Vec4f& data) { readBufferOfType<4, float>(mStream, data._v); } - - /// Extract an instance of type - template - T get() + /// Read multiple instances of type into an array + template + void readArray(std::array& arr) { - return readType(mStream); + readBufferOfType(mStream, arr.data()); + } + + /// Read instances of type into a dynamic buffer + template + void read(T* dest, size_t size) + { + readDynamicBufferOfType(mStream, dest, size); } /// Read multiple instances of type into a vector @@ -101,217 +108,100 @@ namespace Nif void readVector(std::vector& vec, size_t size) { vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); + read(vec.data(), size); } - /// Read multiple instances of type into an array - template - void readArray(std::array& arr) + /// Extract an instance of type + template + T get() { - readBufferOfType(mStream, arr.data()); - } - - // DEPRECATED: Use read() or get() whenever relevant - char getChar() { return readType(mStream); } - - // DEPRECATED: Use read() or get() whenever relevant - short getShort() { return readType(mStream); } - - // DEPRECATED: Use read() or get() whenever relevant - unsigned short getUShort() { return readType(mStream); } - - // DEPRECATED: Use read() or get() whenever relevant - int getInt() { return readType(mStream); } - - // DEPRECATED: Use read() or get() whenever relevant - unsigned int getUInt() { return readType(mStream); } - - // DEPRECATED: Use read() or get() whenever relevant - float getFloat() { return readType(mStream); } - - osg::Vec2f getVector2() - { - osg::Vec2f vec; - readBufferOfType<2, float>(mStream, vec._v); - return vec; - } - - // DEPRECATED: Use read() whenever relevant - osg::Vec3f getVector3() - { - osg::Vec3f vec; - readBufferOfType<3, float>(mStream, vec._v); - return vec; - } - - osg::Vec4f getVector4() - { - osg::Vec4f vec; - readBufferOfType<4, float>(mStream, vec._v); - return vec; - } - - Matrix3 getMatrix3() - { - Matrix3 mat; - readBufferOfType<9, float>(mStream, (float*)&mat.mValues); - return mat; - } - - osg::Quat getQuaternion(); - - Transformation getTrafo(); - - /// Read in a boolean. Boolean serialization format differs between versions - bool getBoolean(); - - /// Read in a string, either from the string table or from the stream depending on the version - std::string getString(); - - unsigned int getVersion() const; - unsigned int getUserVersion() const; - unsigned int getBethVersion() const; - - /// 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; + T data; + read(data); + return data; } /// Read a string of the given length - std::string 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; - } + std::string getSizedString(size_t length); /// Read a string of the length specified in the file - std::string getSizedString() - { - size_t size = readType(mStream); - return getSizedString(size); - } - - /// Read a Bethesda header string that uses a byte for length - std::string getExportString() - { - size_t size = static_cast(readType(mStream)); - return getSizedString(size); - } - - /// Read the version string which doesn't start with a number and ends with "\n" - std::string getVersionString() - { - std::string result; - std::getline(*mStream, result); - if (mStream->bad()) - throw std::runtime_error("Failed to read version string"); - return result; - } - - /// Read a sequence of null-terminated strings - std::string getStringPalette() - { - size_t size = readType(mStream); - 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; - } - - // DEPRECATED: Use readVector() - void getChars(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - // DEPRECATED: Use readVector() - void getUChars(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - // DEPRECATED: Use readVector() - void getUShorts(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - // DEPRECATED: Use readVector() - void getFloats(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - // DEPRECATED: Use readVector() - void getInts(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - // DEPRECATED: Use readVector() - void getUInts(std::vector& vec, size_t size) - { - vec.resize(size); - readDynamicBufferOfType(mStream, vec.data(), size); - } - - void getVector2s(std::vector& vec, size_t size) - { - vec.resize(size); - // The packed storage of each Vec2f is 2 floats exactly - readDynamicBufferOfType(mStream, (float*)vec.data(), size * 2); - } - - void getVector3s(std::vector& vec, size_t size) - { - vec.resize(size); - // The packed storage of each Vec3f is 3 floats exactly - readDynamicBufferOfType(mStream, (float*)vec.data(), size * 3); - } - - void getVector4s(std::vector& vec, size_t size) - { - vec.resize(size); - // The packed storage of each Vec4f is 4 floats exactly - readDynamicBufferOfType(mStream, (float*)vec.data(), size * 4); - } - - void getQuaternions(std::vector& quat, size_t size) - { - quat.resize(size); - for (size_t i = 0; i < quat.size(); i++) - quat[i] = getQuaternion(); - } - - void getStrings(std::vector& vec, size_t size) - { - vec.resize(size); - for (size_t i = 0; i < vec.size(); i++) - vec[i] = getString(); - } + std::string getSizedString() { return getSizedString(get()); } /// Read a list of strings without using the string table, e.g. the string table itself - void getSizedStrings(std::vector& vec, size_t size) - { - vec.resize(size); - for (size_t i = 0; i < vec.size(); i++) - vec[i] = getSizedString(); - } + void getSizedStrings(std::vector& vec, size_t size); + + /// Read a Bethesda header string that uses a byte for length + std::string getExportString() { return getSizedString(get()); } + + /// 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(); + + /// DEPRECATED: Use read() or get() + char getChar() { return get(); } + short getShort() { return get(); } + unsigned short getUShort() { return get(); } + int getInt() { return get(); } + unsigned int getUInt() { return get(); } + float getFloat() { return get(); } + osg::Vec2f getVector2() { return get(); } + osg::Vec3f getVector3() { return get(); } + osg::Vec4f getVector4() { return get(); } + Matrix3 getMatrix3() { return get(); } + osg::Quat getQuaternion() { return get(); } + Transformation getTrafo() { return get(); } + bool getBoolean() { return get(); } + std::string getString() { return get(); } + + /// DEPRECATED: Use readVector() + void getChars(std::vector& vec, size_t size) { readVector(vec, size); } + void getUChars(std::vector& vec, size_t size) { readVector(vec, size); } + void getUShorts(std::vector& vec, size_t size) { readVector(vec, size); } + void getFloats(std::vector& vec, size_t size) { readVector(vec, size); } + void getInts(std::vector& vec, size_t size) { readVector(vec, size); } + void getUInts(std::vector& vec, size_t size) { readVector(vec, size); } + void getVector2s(std::vector& vec, size_t size) { readVector(vec, size); } + void getVector3s(std::vector& vec, size_t size) { readVector(vec, size); } + void getVector4s(std::vector& vec, size_t size) { readVector(vec, size); } + void getQuaternions(std::vector& vec, size_t size) { readVector(vec, size); } + void getStrings(std::vector& vec, size_t size) { readVector(vec, size); } }; + template <> + void NIFStream::read(osg::Vec2f& vec); + template <> + void NIFStream::read(osg::Vec3f& vec); + template <> + void NIFStream::read(osg::Vec4f& vec); + template <> + void NIFStream::read(Matrix3& mat); + template <> + void NIFStream::read(osg::Quat& quat); + template <> + void NIFStream::read(Transformation& t); + template <> + void NIFStream::read(bool& data); + template <> + void NIFStream::read(std::string& str); + + template <> + void NIFStream::read(osg::Vec2f* dest, size_t size); + template <> + void NIFStream::read(osg::Vec3f* dest, size_t size); + template <> + void NIFStream::read(osg::Vec4f* dest, size_t size); + template <> + void NIFStream::read(Matrix3* dest, size_t size); + template <> + void NIFStream::read(osg::Quat* dest, size_t size); + template <> + void NIFStream::read(Transformation* dest, size_t size); + template <> + void NIFStream::read(bool* dest, size_t size); + template <> + void NIFStream::read(std::string* dest, size_t size); + } #endif From 6dca3e68a6ecfd4dcabd7d999744ce546c26a12f Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 05:36:03 +0300 Subject: [PATCH 07/10] NIFStream: don't read vectors of zero length --- components/nif/nifstream.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index b9b2f1923d..a9db083406 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -107,6 +107,8 @@ namespace Nif template void readVector(std::vector& vec, size_t size) { + if (size == 0) + return; vec.resize(size); read(vec.data(), size); } From e7bbfed1fd2037b7b1e0cf1a2de4fcc26f649712 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 26 Jul 2023 05:36:52 +0300 Subject: [PATCH 08/10] Read BSTriShape bounds into array --- components/nif/node.cpp | 2 +- components/nif/node.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 118329fa53..3f12f23840 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -342,7 +342,7 @@ namespace Nif if (nif->getBethVersion() == NIFFile::BethVersion::BETHVER_F76) { - nif->readVector(mBoundMinMax, 6); + nif->readArray(mBoundMinMax); } mSkin.read(nif); diff --git a/components/nif/node.hpp b/components/nif/node.hpp index 38ff36049c..dacaa1e823 100644 --- a/components/nif/node.hpp +++ b/components/nif/node.hpp @@ -356,7 +356,7 @@ namespace Nif struct BSTriShape : Node { NiBoundingVolume::NiSphereBV mBoundingSphere; - std::vector mBoundMinMax; + std::array mBoundMinMax; NiSkinInstancePtr mSkin; BSShaderPropertyPtr mShaderProperty; From 57144accf676aedd9ff3c3197345caec49b6843a Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 10 Jul 2023 02:07:51 +0300 Subject: [PATCH 09/10] Phase out NIFStream::get[Types] --- components/nif/data.cpp | 63 +++++++++++++++++------------------- components/nif/extra.cpp | 15 +++------ components/nif/niffile.cpp | 18 +++++------ components/nif/nifstream.hpp | 13 -------- components/nif/node.cpp | 11 ++----- components/nif/physics.cpp | 36 ++++++--------------- 6 files changed, 55 insertions(+), 101 deletions(-) diff --git a/components/nif/data.cpp b/components/nif/data.cpp index 018e9fc603..1574462892 100644 --- a/components/nif/data.cpp +++ b/components/nif/data.cpp @@ -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 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 flags; - nif->getChars(flags, num); + std::vector 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 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(); mMorphs[i].mKeyFrames->read(nif, /*morph*/ true); - nif->getVector3s(mMorphs[i].mVertices, vertCount); + nif->readVector(mMorphs[i].mVertices, vertCount); } } diff --git a/components/nif/extra.cpp b/components/nif/extra.cpp index 0e93f1a0ce..4384289f9e 100644 --- a/components/nif/extra.cpp +++ b/components/nif/extra.cpp @@ -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) diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index 1757488e3d..0edabdd7a7 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -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 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 groups; // Currently unused unsigned int groupNum = nif.getUInt(); - if (groupNum) - nif.getUInts(groups, groupNum); + nif.readVector(groups, groupNum); } } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index a9db083406..93f2613f11 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -155,19 +155,6 @@ namespace Nif Transformation getTrafo() { return get(); } bool getBoolean() { return get(); } std::string getString() { return get(); } - - /// DEPRECATED: Use readVector() - void getChars(std::vector& vec, size_t size) { readVector(vec, size); } - void getUChars(std::vector& vec, size_t size) { readVector(vec, size); } - void getUShorts(std::vector& vec, size_t size) { readVector(vec, size); } - void getFloats(std::vector& vec, size_t size) { readVector(vec, size); } - void getInts(std::vector& vec, size_t size) { readVector(vec, size); } - void getUInts(std::vector& vec, size_t size) { readVector(vec, size); } - void getVector2s(std::vector& vec, size_t size) { readVector(vec, size); } - void getVector3s(std::vector& vec, size_t size) { readVector(vec, size); } - void getVector4s(std::vector& vec, size_t size) { readVector(vec, size); } - void getQuaternions(std::vector& vec, size_t size) { readVector(vec, size); } - void getStrings(std::vector& vec, size_t size) { readVector(vec, size); } }; template <> diff --git a/components/nif/node.cpp b/components/nif/node.cpp index 3f12f23840..a2ae77830a 100644 --- a/components/nif/node.cpp +++ b/components/nif/node.cpp @@ -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) @@ -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) { diff --git a/components/nif/physics.cpp b/components/nif/physics.cpp index 450f5588cb..daa4f90c2a 100644 --- a/components/nif/physics.cpp +++ b/components/nif/physics.cpp @@ -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); From 4e156f2a7d031b78c5752464ab3df7c2fe826f40 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Mon, 7 Aug 2023 14:15:12 +0300 Subject: [PATCH 10/10] NIFStream: improve handling of ranges and aligned types --- components/nif/nifstream.cpp | 71 ++++++++++++++++++++++++------------ components/nif/nifstream.hpp | 6 +++ 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/components/nif/nifstream.cpp b/components/nif/nifstream.cpp index d387ae5259..db7528b2d0 100644 --- a/components/nif/nifstream.cpp +++ b/components/nif/nifstream.cpp @@ -1,7 +1,36 @@ #include "nifstream.hpp" +#include + #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 + 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 { @@ -61,25 +90,25 @@ namespace Nif template <> void NIFStream::read(osg::Vec2f& vec) { - readBufferOfType<2>(mStream, vec._v); + readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(osg::Vec3f& vec) { - readBufferOfType<3>(mStream, vec._v); + readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(osg::Vec4f& vec) { - readBufferOfType<4>(mStream, vec._v); + readBufferOfType(mStream, vec._v); } template <> void NIFStream::read(Matrix3& mat) { - readBufferOfType<9>(mStream, (float*)&mat.mValues); + readBufferOfType<9>(mStream, reinterpret_cast(&mat.mValues)); } template <> @@ -122,43 +151,37 @@ namespace Nif template <> void NIFStream::read(osg::Vec2f* dest, size_t size) { - // The packed storage of each Vec2f is 2 floats exactly - readDynamicBufferOfType(mStream, (float*)dest, size * 2); + readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Vec3f* dest, size_t size) { - // The packed storage of each Vec3f is 3 floats exactly - readDynamicBufferOfType(mStream, (float*)dest, size * 3); + readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Vec4f* dest, size_t size) { - // The packed storage of each Vec4f is 4 floats exactly - readDynamicBufferOfType(mStream, (float*)dest, size * 4); + readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(Matrix3* dest, size_t size) { - // The packed storage of each Matrix3 is 9 floats exactly - readDynamicBufferOfType(mStream, (float*)dest, size * 9); + readAlignedRange(mStream, dest, size); } template <> void NIFStream::read(osg::Quat* dest, size_t size) { - for (size_t i = 0; i < size; i++) - read(dest[i]); + readRange(*this, dest, size); } template <> void NIFStream::read(Transformation* dest, size_t size) { - for (size_t i = 0; i < size; i++) - read(dest[i]); + readRange(*this, dest, size); } template <> @@ -166,13 +189,13 @@ namespace Nif { if (getVersion() < generateVersion(4, 1, 0, 0)) { - for (size_t i = 0; i < size; i++) - dest[i] = get() != 0; + for (bool& value : std::span(dest, size)) + value = get() != 0; } else { - for (size_t i = 0; i < size; i++) - dest[i] = get() != 0; + for (bool& value : std::span(dest, size)) + value = get() != 0; } } @@ -181,13 +204,13 @@ namespace Nif { if (getVersion() < generateVersion(20, 1, 0, 1)) { - for (size_t i = 0; i < size; i++) - dest[i] = getSizedString(); + for (std::string& value : std::span(dest, size)) + value = getSizedString(); } else { - for (size_t i = 0; i < size; i++) - dest[i] = mReader.getString(get()); + for (std::string& value : std::span(dest, size)) + value = mReader.getString(get()); } } diff --git a/components/nif/nifstream.hpp b/components/nif/nifstream.hpp index 93f2613f11..58b160fcca 100644 --- a/components/nif/nifstream.hpp +++ b/components/nif/nifstream.hpp @@ -41,6 +41,12 @@ namespace Nif Misc::swapEndiannessInplace(dest[i]); } + template + inline void readBufferOfType(Files::IStreamPtr& pIStream, T (&dest)[numInstances]) + { + readBufferOfType(pIStream, static_cast(dest)); + } + template inline void readDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) {