/// 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 "niftypes.hpp" 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"); 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"); if constexpr (Misc::IS_BIG_ENDIAN) for (std::size_t i = 0; i < numInstances; i++) Misc::swapEndiannessInplace(dest[i]); } template inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) { static_assert(is_arithmetic(), "Buffer element type is not arithmetic"); 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"); if constexpr (Misc::IS_BIG_ENDIAN) for (std::size_t i = 0; i < numInstances; i++) Misc::swapEndiannessInplace(dest[i]); } template 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; public: explicit NIFStream(const Reader& file, Files::IStreamPtr&& inp) : file(file) , inp(std::move(inp)) { } const Reader& getFile() const { return file; } void skip(size_t size) { inp->ignore(size); } template void read(T& data) { data = readLittleEndianType(inp); } void read(osg::Vec2f& data) { readLittleEndianBufferOfType<2, float>(inp, data._v); } void read(osg::Vec3f& data) { readLittleEndianBufferOfType<3, float>(inp, data._v); } void read(osg::Vec4f& data) { readLittleEndianBufferOfType<4, float>(inp, data._v); } template T get() { return readLittleEndianType(inp); } template void readVector(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } template void readArray(std::array& arr) { readLittleEndianDynamicBufferOfType(inp, arr.data(), size); } // DEPRECATED: Use read() or get() whenever relevant char getChar() { return readLittleEndianType(inp); } // DEPRECATED: Use read() or get() whenever relevant short getShort() { return readLittleEndianType(inp); } // DEPRECATED: Use read() or get() whenever relevant unsigned short getUShort() { return readLittleEndianType(inp); } // DEPRECATED: Use read() or get() whenever relevant int getInt() { return readLittleEndianType(inp); } // DEPRECATED: Use read() or get() whenever relevant unsigned int getUInt() { return readLittleEndianType(inp); } // DEPRECATED: Use read() or get() whenever relevant float getFloat() { return readLittleEndianType(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(); 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; } /// Read in a string of the given length std::string getSizedString(size_t length) { 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(inp); return getSizedString(size); } /// Specific to Bethesda headers, uses a byte for length std::string getExportString() { size_t size = static_cast(readLittleEndianType(inp)); return getSizedString(size); } /// This is special since the version string doesn't start with a number, and ends with "\n" std::string getVersionString() { std::string result; std::getline(*inp, result); if (inp->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 = readLittleEndianType(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; } // DEPRECATED: Use readVector() void getChars(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUChars(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUShorts(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getFloats(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getInts(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(inp, vec.data(), size); } // DEPRECATED: Use readVector() void getUInts(std::vector& vec, size_t size) { vec.resize(size); readLittleEndianDynamicBufferOfType(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); } 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); } 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); } 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(); } /// We need to use this when the string table isn't actually initialized. void getSizedStrings(std::vector& vec, size_t size) { vec.resize(size); for (size_t i = 0; i < vec.size(); i++) vec[i] = getSizedString(); } }; } #endif