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