1 ///Functions used to read raw binary data from .nif files
2 
3 #ifndef OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
4 #define OPENMW_COMPONENTS_NIF_NIFSTREAM_HPP
5 
6 #include <cassert>
7 #include <stdint.h>
8 #include <stdexcept>
9 #include <vector>
10 #include <typeinfo>
11 #include <type_traits>
12 
13 #include <components/files/constrainedfilestream.hpp>
14 #include <components/misc/endianness.hpp>
15 
16 #include <osg/Vec3f>
17 #include <osg/Vec4f>
18 #include <osg/Quat>
19 
20 #include "niftypes.hpp"
21 
22 namespace Nif
23 {
24 
25 class NIFFile;
26 
readLittleEndianBufferOfType(Files::IStreamPtr & pIStream,T * dest)27 template <std::size_t numInstances, typename T> inline void readLittleEndianBufferOfType(Files::IStreamPtr &pIStream, T* dest)
28 {
29     static_assert(std::is_arithmetic_v<T>, "Buffer element type is not arithmetic");
30     pIStream->read((char*)dest, numInstances * sizeof(T));
31     if (pIStream->bad())
32         throw std::runtime_error("Failed to read little endian typed (" + std::string(typeid(T).name()) + ") buffer of "
33                                  + std::to_string(numInstances) + " instances");
34     if constexpr (Misc::IS_BIG_ENDIAN)
35         for (std::size_t i = 0; i < numInstances; i++)
36             Misc::swapEndiannessInplace(dest[i]);
37 }
38 
readLittleEndianDynamicBufferOfType(Files::IStreamPtr & pIStream,T * dest,std::size_t numInstances)39 template <typename T> inline void readLittleEndianDynamicBufferOfType(Files::IStreamPtr &pIStream, T* dest, std::size_t numInstances)
40 {
41     static_assert(std::is_arithmetic_v<T>, "Buffer element type is not arithmetic");
42     pIStream->read((char*)dest, numInstances * sizeof(T));
43     if (pIStream->bad())
44         throw std::runtime_error("Failed to read little endian dynamic buffer of " + std::to_string(numInstances) + " instances");
45     if constexpr (Misc::IS_BIG_ENDIAN)
46         for (std::size_t i = 0; i < numInstances; i++)
47             Misc::swapEndiannessInplace(dest[i]);
48 }
readLittleEndianType(Files::IStreamPtr & pIStream)49 template<typename type> type inline readLittleEndianType(Files::IStreamPtr &pIStream)
50 {
51     type val;
52     readLittleEndianBufferOfType<1, type>(pIStream, &val);
53     return val;
54 }
55 
56 class NIFStream
57 {
58     /// Input stream
59     Files::IStreamPtr inp;
60 
61 public:
62 
63     NIFFile * const file;
64 
NIFStream(NIFFile * file,Files::IStreamPtr inp)65     NIFStream (NIFFile * file, Files::IStreamPtr inp): inp (inp), file (file) {}
66 
skip(size_t size)67     void skip(size_t size) { inp->ignore(size); }
68 
getChar()69     char getChar()
70     {
71         return readLittleEndianType<char>(inp);
72     }
73 
getShort()74     short getShort()
75     {
76         return readLittleEndianType<short>(inp);
77     }
78 
getUShort()79     unsigned short getUShort()
80     {
81         return readLittleEndianType<unsigned short>(inp);
82     }
83 
getInt()84     int getInt()
85     {
86         return readLittleEndianType<int>(inp);
87     }
88 
getUInt()89     unsigned int getUInt()
90     {
91         return readLittleEndianType<unsigned int>(inp);
92     }
93 
getFloat()94     float getFloat()
95     {
96         return readLittleEndianType<float>(inp);
97     }
98 
getVector2()99     osg::Vec2f getVector2()
100     {
101         osg::Vec2f vec;
102         readLittleEndianBufferOfType<2,float>(inp, vec._v);
103         return vec;
104     }
105 
getVector3()106     osg::Vec3f getVector3()
107     {
108         osg::Vec3f vec;
109         readLittleEndianBufferOfType<3, float>(inp, vec._v);
110         return vec;
111     }
112 
getVector4()113     osg::Vec4f getVector4()
114     {
115         osg::Vec4f vec;
116         readLittleEndianBufferOfType<4, float>(inp, vec._v);
117         return vec;
118     }
119 
getMatrix3()120     Matrix3 getMatrix3()
121     {
122         Matrix3 mat;
123         readLittleEndianBufferOfType<9, float>(inp, (float*)&mat.mValues);
124         return mat;
125     }
126 
127     osg::Quat getQuaternion();
128 
129     Transformation getTrafo();
130 
131     bool getBoolean();
132 
133     std::string getString();
134 
135     unsigned int getVersion() const;
136     unsigned int getUserVersion() const;
137     unsigned int getBethVersion() const;
138 
139     // Convert human-readable version numbers into a number that can be compared.
generateVersion(uint8_t major,uint8_t minor,uint8_t patch,uint8_t rev)140     static constexpr uint32_t generateVersion(uint8_t major, uint8_t minor, uint8_t patch, uint8_t rev)
141     {
142         return (major << 24) + (minor << 16) + (patch << 8) + rev;
143     }
144 
145     ///Read in a string of the given length
getSizedString(size_t length)146     std::string getSizedString(size_t length)
147     {
148         std::string str(length, '\0');
149         inp->read(str.data(), length);
150         if (inp->bad())
151             throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars");
152         return str;
153     }
154     ///Read in a string of the length specified in the file
getSizedString()155     std::string getSizedString()
156     {
157         size_t size = readLittleEndianType<uint32_t>(inp);
158         return getSizedString(size);
159     }
160 
161     ///Specific to Bethesda headers, uses a byte for length
getExportString()162     std::string getExportString()
163     {
164         size_t size = static_cast<size_t>(readLittleEndianType<uint8_t>(inp));
165         return getSizedString(size);
166     }
167 
168     ///This is special since the version string doesn't start with a number, and ends with "\n"
getVersionString()169     std::string getVersionString()
170     {
171         std::string result;
172         std::getline(*inp, result);
173         if (inp->bad())
174             throw std::runtime_error("Failed to read version string");
175         return result;
176     }
177 
getChars(std::vector<char> & vec,size_t size)178     void getChars(std::vector<char> &vec, size_t size)
179     {
180         vec.resize(size);
181         readLittleEndianDynamicBufferOfType<char>(inp, vec.data(), size);
182     }
183 
getUChars(std::vector<unsigned char> & vec,size_t size)184     void getUChars(std::vector<unsigned char> &vec, size_t size)
185     {
186         vec.resize(size);
187         readLittleEndianDynamicBufferOfType<unsigned char>(inp, vec.data(), size);
188     }
189 
getUShorts(std::vector<unsigned short> & vec,size_t size)190     void getUShorts(std::vector<unsigned short> &vec, size_t size)
191     {
192         vec.resize(size);
193         readLittleEndianDynamicBufferOfType<unsigned short>(inp, vec.data(), size);
194     }
195 
getFloats(std::vector<float> & vec,size_t size)196     void getFloats(std::vector<float> &vec, size_t size)
197     {
198         vec.resize(size);
199         readLittleEndianDynamicBufferOfType<float>(inp, vec.data(), size);
200     }
201 
getInts(std::vector<int> & vec,size_t size)202     void getInts(std::vector<int> &vec, size_t size)
203     {
204         vec.resize(size);
205         readLittleEndianDynamicBufferOfType<int>(inp, vec.data(), size);
206     }
207 
getUInts(std::vector<unsigned int> & vec,size_t size)208     void getUInts(std::vector<unsigned int> &vec, size_t size)
209     {
210         vec.resize(size);
211         readLittleEndianDynamicBufferOfType<unsigned int>(inp, vec.data(), size);
212     }
213 
getVector2s(std::vector<osg::Vec2f> & vec,size_t size)214     void getVector2s(std::vector<osg::Vec2f> &vec, size_t size)
215     {
216         vec.resize(size);
217         /* The packed storage of each Vec2f is 2 floats exactly */
218         readLittleEndianDynamicBufferOfType<float>(inp,(float*)vec.data(), size*2);
219     }
220 
getVector3s(std::vector<osg::Vec3f> & vec,size_t size)221     void getVector3s(std::vector<osg::Vec3f> &vec, size_t size)
222     {
223         vec.resize(size);
224         /* The packed storage of each Vec3f is 3 floats exactly */
225         readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size*3);
226     }
227 
getVector4s(std::vector<osg::Vec4f> & vec,size_t size)228     void getVector4s(std::vector<osg::Vec4f> &vec, size_t size)
229     {
230         vec.resize(size);
231         /* The packed storage of each Vec4f is 4 floats exactly */
232         readLittleEndianDynamicBufferOfType<float>(inp, (float*)vec.data(), size*4);
233     }
234 
getQuaternions(std::vector<osg::Quat> & quat,size_t size)235     void getQuaternions(std::vector<osg::Quat> &quat, size_t size)
236     {
237         quat.resize(size);
238         for (size_t i = 0;i < quat.size();i++)
239             quat[i] = getQuaternion();
240     }
241 
getStrings(std::vector<std::string> & vec,size_t size)242     void getStrings(std::vector<std::string> &vec, size_t size)
243     {
244         vec.resize(size);
245         for (size_t i = 0; i < vec.size(); i++)
246             vec[i] = getString();
247     }
248     /// We need to use this when the string table isn't actually initialized.
getSizedStrings(std::vector<std::string> & vec,size_t size)249     void getSizedStrings(std::vector<std::string> &vec, size_t size)
250     {
251         vec.resize(size);
252         for (size_t i = 0; i < vec.size(); i++)
253             vec[i] = getSizedString();
254     }
255 };
256 
257 }
258 
259 #endif
260