/// Functions used to read raw binary data from .nif files #ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "niftypes.hpp" namespace ToUTF8 { class StatelessUtf8Encoder; } namespace Nif { class Reader; template inline void readBufferOfType(Files::IStreamPtr& pIStream, T* dest) { 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 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 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) { 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 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]); } class NIFStream { const Reader& mReader; Files::IStreamPtr mStream; const ToUTF8::StatelessUtf8Encoder* mEncoder; std::string mBuffer; public: explicit NIFStream( const Reader& reader, Files::IStreamPtr&& stream, const ToUTF8::StatelessUtf8Encoder* encoder) : mReader(reader) , mStream(std::move(stream)) , mEncoder(encoder) { } 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) { readBufferOfType<1>(mStream, &data); } /// Read multiple instances of type into an array template void readArray(std::array& arr) { 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 template void readVector(std::vector& vec, size_t size) { if (size == 0) return; vec.resize(size); read(vec.data(), size); } /// Extract an instance of type template 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()); } /// Read a list of strings without using the string table, e.g. the string table itself 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(); }; 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(osg::BoundingSpheref& sphere); template <> void NIFStream::read(NiTransform& transform); template <> void NIFStream::read(NiQuatTransform& transform); 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(osg::BoundingSpheref* dest, size_t size); template <> void NIFStream::read(NiTransform* dest, size_t size); template <> void NIFStream::read(NiQuatTransform* dest, size_t size); template <> void NIFStream::read(bool* dest, size_t size); template <> void NIFStream::read(std::string* dest, size_t size); } #endif