/src/ogre/OgreMain/include/OgreStreamSerialiser.h
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #ifndef __StreamSerialiser_H__ |
29 | | #define __StreamSerialiser_H__ |
30 | | |
31 | | #include "OgrePrerequisites.h" |
32 | | #include "OgreCommon.h" |
33 | | #include "OgreDataStream.h" |
34 | | #include "OgreHeaderPrefix.h" |
35 | | |
36 | | #include <deque> |
37 | | |
38 | | namespace Ogre |
39 | | { |
40 | | /** \addtogroup Core |
41 | | * @{ |
42 | | */ |
43 | | /** \addtogroup Resources |
44 | | * @{ |
45 | | */ |
46 | | |
47 | | /** Utility class providing helper methods for reading / writing |
48 | | structured data held in a DataStream. |
49 | | |
50 | | The structure of a file read / written by this class is a series of |
51 | | 'chunks'. A chunk-based format has the advantage of being extensible later, |
52 | | and it's robust, in that a reader can skip chunks that they are not |
53 | | able (or willing) to process. |
54 | | @par |
55 | | Chunks are contained serially in the file, but they can also be |
56 | | nested in order both to provide context, and to group chunks together for |
57 | | potential skipping. |
58 | | @par |
59 | | The data format of a chunk is as follows: |
60 | | -# Chunk ID (32-bit uint). This can be any number unique in a context, except the numbers 0x0000, 0x0001 and 0x1000, which are reserved for Ogre's use |
61 | | -# Chunk version (16-bit uint). Chunks can change over time so this version number reflects that |
62 | | -# Length (32-bit uint). The length of the chunk data section, including nested chunks. Note that |
63 | | this length excludes this header, but includes the header of any nested chunks. |
64 | | -# Checksum (32-bit uint). Checksum value generated from the above - basically lets us check this is a valid chunk. |
65 | | -# Chunk data |
66 | | The 'Chunk data' section will contain chunk-specific data, which may include |
67 | | other nested chunks. |
68 | | */ |
69 | | class _OgreExport StreamSerialiser : public StreamAlloc |
70 | | { |
71 | | public: |
72 | | /// The endianness of files |
73 | | enum Endian |
74 | | { |
75 | | /// Automatically determine endianness |
76 | | ENDIAN_AUTO, |
77 | | /// Use big endian (0x1000 is serialised as 0x10 0x00) |
78 | | ENDIAN_BIG, |
79 | | /// Use little endian (0x1000 is serialised as 0x00 0x10) |
80 | | ENDIAN_LITTLE |
81 | | }; |
82 | | |
83 | | /// The storage format of Real values |
84 | | enum RealStorageFormat |
85 | | { |
86 | | /// Real is stored as float, reducing precision if you're using OGRE_DOUBLE_PRECISION |
87 | | REAL_FLOAT, |
88 | | /// Real as stored as double, not useful unless you're using OGRE_DOUBLE_PRECISION |
89 | | REAL_DOUBLE |
90 | | }; |
91 | | |
92 | | |
93 | | /// Definition of a chunk of data in a file |
94 | | struct Chunk : public StreamAlloc |
95 | | { |
96 | | /// Identifier of the chunk (for example from makeIdentifier) (stored) |
97 | | uint32 id; |
98 | | /// Version of the chunk (stored) |
99 | | uint16 version; |
100 | | /// Length of the chunk data in bytes, excluding the header of this chunk (stored) |
101 | | uint32 length; |
102 | | /// Location of the chunk (header) in bytes from the start of a stream (derived) |
103 | | uint32 offset; |
104 | | |
105 | 261 | Chunk() : id(0), version(1), length(0), offset(0) {} |
106 | | }; |
107 | | |
108 | | /** Constructor. |
109 | | @param stream The stream on which you will read / write data. |
110 | | @param endianMode The endian mode in which to read / writedata. If left at |
111 | | the default, when writing the endian mode will be the native platform mode, |
112 | | and when reading it's expected that the first chunk encountered will be |
113 | | the header chunk, which will determine the endian mode. |
114 | | @param autoHeader If true, the first write or read to this stream will |
115 | | automatically read / write the header too. This is required if you |
116 | | set endianMode to ENDIAN_AUTO, but if you manually set the endian mode, |
117 | | then you can skip writing / reading the header if you wish, if for example |
118 | | this stream is midway through a file which has already included header |
119 | | information. |
120 | | @param realFormat Set the format you want to write reals in. Only useful for files that |
121 | | you're writing (since when reading this is picked up from the file), |
122 | | and can only be changed if autoHeader is true, since real format is stored in the header. |
123 | | Defaults to float unless you're using OGRE_DOUBLE_PRECISION. |
124 | | */ |
125 | | StreamSerialiser(const DataStreamPtr& stream, Endian endianMode = ENDIAN_AUTO, |
126 | | bool autoHeader = true, |
127 | | #if OGRE_DOUBLE_PRECISION |
128 | | RealStorageFormat realFormat = REAL_DOUBLE |
129 | | #else |
130 | | RealStorageFormat realFormat = REAL_FLOAT |
131 | | #endif |
132 | | ); |
133 | | virtual ~StreamSerialiser(); |
134 | | |
135 | | /** Get the endian mode. |
136 | | |
137 | | If the result is ENDIAN_AUTO, this mode will change when the first piece of |
138 | | data is read / written. |
139 | | */ |
140 | 0 | virtual Endian getEndian() const { return mEndian; } |
141 | | |
142 | | /** Pack a 4-character code into a 32-bit identifier. |
143 | | |
144 | | You can use this to generate id's for your chunks based on friendlier |
145 | | 4-character codes rather than assigning numerical IDs, if you like. |
146 | | @param code String to pack - must be 4 characters and '\0' |
147 | | */ |
148 | | static uint32 makeIdentifier(const char (&code)[5]); |
149 | | |
150 | | /** Report the current depth of the chunk nesting, whether reading or writing. |
151 | | |
152 | | Returns how many levels of nested chunks are currently being processed, |
153 | | either writing or reading. In order to tidily finish, you must call |
154 | | read/writeChunkEnd this many times. |
155 | | */ |
156 | 0 | size_t getCurrentChunkDepth() const { return mChunkStack.size(); } |
157 | | |
158 | | /** Get the ID of the chunk that's currently being read/written, if any. |
159 | | @return The id of the current chunk being read / written (at the tightest |
160 | | level of nesting), or zero if no chunk is being processed. |
161 | | */ |
162 | | uint32 getCurrentChunkID() const; |
163 | | |
164 | | /** Get the current byte position relative to the start of the data section |
165 | | of the last chunk that was read or written. |
166 | | @return the offset. Note that a return value of 0 means that either the |
167 | | position is at the start of the chunk data section (ie right after the |
168 | | header), or that no chunk is currently active. Use getCurrentChunkID |
169 | | or getCurrentChunkDepth to determine if a chunk is active. |
170 | | */ |
171 | | size_t getOffsetFromChunkStart() const; |
172 | | |
173 | | /** Reads the start of the next chunk in the file. |
174 | | |
175 | | Files are serialised in a chunk-based manner, meaning that each section |
176 | | of data is prepended by a chunk header. After reading this chunk header, |
177 | | the next set of data is available directly afterwards. |
178 | | @note |
179 | | When you have finished with this chunk, you should call readChunkEnd. |
180 | | This will perform a bit of validation and clear the chunk from |
181 | | the stack. |
182 | | @return The Chunk that comes next |
183 | | */ |
184 | | virtual const Chunk* readChunkBegin(); |
185 | | |
186 | | /** Reads the start of the next chunk so long as it's of a given ID and version. |
187 | | |
188 | | This method operates like readChunkBegin, except it checks the ID and |
189 | | version. |
190 | | @param id The ID you're expecting. If the next chunk isn't of this ID, then |
191 | | the chunk read is undone and the method returns null. |
192 | | @param maxVersion The maximum version you're able to process. If the ID is correct |
193 | | but the version exceeds what is passed in here, the chunk is skipped over, |
194 | | the problem logged and null is returned. |
195 | | @param msg Descriptive text added to the log if versions are not compatible |
196 | | @return The chunk if it passes the validation. |
197 | | */ |
198 | | virtual const Chunk* readChunkBegin(uint32 id, uint16 maxVersion, const String& msg = BLANKSTRING); |
199 | | |
200 | | /** Call this to 'rewind' the stream to just before the start of the current |
201 | | chunk. |
202 | | |
203 | | The most common case of wanting to use this is if you'd calledReadChunkBegin(), |
204 | | but the chunk you read wasn't one you wanted to process, and rather than |
205 | | skipping over it (which readChunkEnd() would do), you want to backtrack |
206 | | and give something else an opportunity to read it. |
207 | | @param id The id of the chunk that you were reading (for validation purposes) |
208 | | */ |
209 | | virtual void undoReadChunk(uint32 id); |
210 | | |
211 | | /** Call this to 'peek' at the next chunk ID without permanently moving the stream pointer. */ |
212 | | virtual uint32 peekNextChunkID(); |
213 | | |
214 | | /** Finish the reading of a chunk. |
215 | | |
216 | | You can call this method at any point after calling readChunkBegin, even |
217 | | if you didn't read all the rest of the data in the chunk. If you did |
218 | | not read to the end of a chunk, this method will automatically skip |
219 | | over the remainder of the chunk and position the stream just after it. |
220 | | @param id The id of the chunk that you were reading (for validation purposes) |
221 | | */ |
222 | | virtual void readChunkEnd(uint32 id); |
223 | | |
224 | | /** Return whether the current data pointer is at the end of the current chunk. |
225 | | @param id The id of the chunk that you were reading (for validation purposes) |
226 | | */ |
227 | | virtual bool isEndOfChunk(uint32 id); |
228 | | |
229 | | /// Reports whether the stream is at the end of file |
230 | | virtual bool eof() const; |
231 | | |
232 | | /** Get the definition of the current chunk being read (if any). */ |
233 | | virtual const Chunk* getCurrentChunk() const; |
234 | | |
235 | | /** Begin writing a new chunk. |
236 | | |
237 | | This starts the process of writing a new chunk to the stream. This will |
238 | | write the chunk header for you, and store a pointer so that the |
239 | | class can automatically go back and fill in the size for you later |
240 | | should you need it to. If you have already begun a chunk without ending |
241 | | it, then this method will start a nested chunk within it. Once written, |
242 | | you can then start writing chunk-specific data into your stream. |
243 | | @note If this is the first chunk in the file |
244 | | @param id The identifier of the new chunk. Any value that's unique in the |
245 | | file context is valid, except for the numbers 0x0001 and 0x1000 which are reserved |
246 | | for internal header identification use. |
247 | | @param version The version of the chunk you're writing |
248 | | */ |
249 | | virtual void writeChunkBegin(uint32 id, uint16 version = 1); |
250 | | /** End writing a chunk. |
251 | | @param id The identifier of the chunk - this is really just a safety check, |
252 | | since you can only end the chunk you most recently started. |
253 | | */ |
254 | | virtual void writeChunkEnd(uint32 id); |
255 | | |
256 | | /** Write arbitrary data to a stream. |
257 | | @param buf Pointer to bytes |
258 | | @param size The size of each element to write; each will be endian-flipped if |
259 | | necessary |
260 | | @param count The number of elements to write |
261 | | */ |
262 | | virtual void writeData(const void* buf, size_t size, size_t count); |
263 | | |
264 | | /** Catch-all method to write primitive types. */ |
265 | | template <typename T> |
266 | | void write(const T* pT, size_t count = 1) |
267 | 0 | { |
268 | 0 | writeData(pT, sizeof(T), count); |
269 | 0 | } Unexecuted instantiation: void Ogre::StreamSerialiser::write<unsigned int>(unsigned int const*, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::write<unsigned short>(unsigned short const*, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::write<char>(char const*, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::write<unsigned char>(unsigned char const*, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::write<Ogre::Terrain::Alignment>(Ogre::Terrain::Alignment const*, unsigned long) |
270 | | |
271 | | // Special-case Real since we need to deal with single/double precision |
272 | | virtual void write(const Real* val, size_t count = 1); |
273 | | |
274 | | virtual void write(const Vector2* vec, size_t count = 1); |
275 | | virtual void write(const Vector3* vec, size_t count = 1); |
276 | | virtual void write(const Vector4* vec, size_t count = 1); |
277 | | virtual void write(const Quaternion* q, size_t count = 1); |
278 | | virtual void write(const Matrix3* m, size_t count = 1); |
279 | | virtual void write(const Matrix4* m, size_t count = 1); |
280 | | virtual void write(const String* string); |
281 | | virtual void write(const AxisAlignedBox* aabb, size_t count = 1); |
282 | | virtual void write(const Sphere* sphere, size_t count = 1); |
283 | | virtual void write(const Plane* plane, size_t count = 1); |
284 | | virtual void write(const Ray* ray, size_t count = 1); |
285 | | virtual void write(const Radian* angle, size_t count = 1); |
286 | | virtual void write(const Node* node, size_t count = 1); |
287 | | virtual void write(const bool* boolean, size_t count = 1); |
288 | | |
289 | | |
290 | | /** Read arbitrary data from a stream. |
291 | | @param buf Pointer to bytes |
292 | | @param size The size of each element to read; each will be endian-flipped if |
293 | | necessary |
294 | | @param count The number of elements to read |
295 | | */ |
296 | | virtual void readData(void* buf, size_t size, size_t count); |
297 | | |
298 | | /** Catch-all method to read primitive types. */ |
299 | | template <typename T> |
300 | | void read(T* pT, size_t count = 1) |
301 | 1.04k | { |
302 | 1.04k | readData(pT, sizeof(T), count); |
303 | 1.04k | } void Ogre::StreamSerialiser::read<unsigned int>(unsigned int*, unsigned long) Line | Count | Source | 301 | 694 | { | 302 | 694 | readData(pT, sizeof(T), count); | 303 | 694 | } |
void Ogre::StreamSerialiser::read<unsigned short>(unsigned short*, unsigned long) Line | Count | Source | 301 | 261 | { | 302 | 261 | readData(pT, sizeof(T), count); | 303 | 261 | } |
void Ogre::StreamSerialiser::read<char>(char*, unsigned long) Line | Count | Source | 301 | 91 | { | 302 | 91 | readData(pT, sizeof(T), count); | 303 | 91 | } |
Unexecuted instantiation: void Ogre::StreamSerialiser::read<unsigned char>(unsigned char*, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::read<Ogre::Terrain::Alignment>(Ogre::Terrain::Alignment*, unsigned long) |
304 | | |
305 | | // Special case Real, single/double-precision issues |
306 | | virtual void read(Real* val, size_t count = 1); |
307 | | |
308 | | /// read a Vector3 |
309 | | virtual void read(Vector2* vec, size_t count = 1); |
310 | | virtual void read(Vector3* vec, size_t count = 1); |
311 | | virtual void read(Vector4* vec, size_t count = 1); |
312 | | virtual void read(Quaternion* q, size_t count = 1); |
313 | | virtual void read(Matrix3* m, size_t count = 1); |
314 | | virtual void read(Matrix4* m, size_t count = 1); |
315 | | virtual void read(String* string); |
316 | | virtual void read(AxisAlignedBox* aabb, size_t count = 1); |
317 | | virtual void read(Sphere* sphere, size_t count = 1); |
318 | | virtual void read(Plane* plane, size_t count = 1); |
319 | | virtual void read(Ray* ray, size_t count = 1); |
320 | | virtual void read(Radian* angle, size_t count = 1); |
321 | | virtual void read(Node* node, size_t count = 1); |
322 | | virtual void read(bool* val, size_t count = 1); |
323 | | |
324 | | /** Start (un)compressing data |
325 | | @param avail_in Available bytes for uncompressing |
326 | | */ |
327 | | virtual void startDeflate(size_t avail_in = 0); |
328 | | /** Stop (un)compressing data |
329 | | */ |
330 | | virtual void stopDeflate(); |
331 | | protected: |
332 | | DataStreamPtr mStream; |
333 | | DataStreamPtr mOriginalStream; |
334 | | Endian mEndian; |
335 | | bool mFlipEndian; |
336 | | bool mReadWriteHeader; |
337 | | RealStorageFormat mRealFormat; |
338 | | typedef std::deque<Chunk*> ChunkStack; |
339 | | /// Current list of open chunks |
340 | | ChunkStack mChunkStack; |
341 | | |
342 | | static uint32 HEADER_ID; |
343 | | static uint32 REVERSE_HEADER_ID; |
344 | | static uint32 CHUNK_HEADER_SIZE; |
345 | | |
346 | | virtual Chunk* readChunkImpl(); |
347 | | virtual void writeChunkImpl(uint32 id, uint16 version); |
348 | | virtual void readHeader(); |
349 | | virtual void writeHeader(); |
350 | | virtual uint32 calculateChecksum(Chunk* c); |
351 | | virtual void checkStream(bool failOnEof = false, |
352 | | bool validateReadable = false, bool validateWriteable = false) const; |
353 | | |
354 | | |
355 | | virtual void determineEndianness(); |
356 | | virtual Chunk* popChunk(uint id); |
357 | | |
358 | | virtual void writeFloatsAsDoubles(const float* val, size_t count); |
359 | | virtual void writeDoublesAsFloats(const double* val, size_t count); |
360 | | virtual void readFloatsAsDoubles(double* val, size_t count); |
361 | | virtual void readDoublesAsFloats(float* val, size_t count); |
362 | | template <typename T, typename U> |
363 | | void writeConverted(const T* src, U typeToWrite, size_t count) |
364 | 0 | { |
365 | 0 | U* tmp = OGRE_ALLOC_T(U, count, MEMCATEGORY_GENERAL); |
366 | 0 | U* pDst = tmp; |
367 | 0 | const T* pSrc = src; |
368 | 0 | for (size_t i = 0; i < count; ++i) |
369 | 0 | *pDst++ = static_cast<U>(*pSrc++); |
370 | | |
371 | 0 | writeData(tmp, sizeof(U), count); |
372 | |
|
373 | 0 | OGRE_FREE(tmp, MEMCATEGORY_GENERAL); |
374 | 0 | } Unexecuted instantiation: void Ogre::StreamSerialiser::writeConverted<float, double>(float const*, double, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::writeConverted<double, float>(double const*, float, unsigned long) |
375 | | template <typename T, typename U> |
376 | | void readConverted(T* dst, U typeToRead, size_t count) |
377 | 0 | { |
378 | 0 | U* tmp = OGRE_ALLOC_T(U, count, MEMCATEGORY_GENERAL); |
379 | 0 | readData(tmp, sizeof(U), count); |
380 | |
|
381 | 0 | T* pDst = dst; |
382 | 0 | const U* pSrc = tmp; |
383 | 0 | for (size_t i = 0; i < count; ++i) |
384 | 0 | *pDst++ = static_cast<T>(*pSrc++); |
385 | | |
386 | |
|
387 | | OGRE_FREE(tmp, MEMCATEGORY_GENERAL); |
388 | 0 | } Unexecuted instantiation: void Ogre::StreamSerialiser::readConverted<double, float>(double*, float, unsigned long) Unexecuted instantiation: void Ogre::StreamSerialiser::readConverted<float, double>(float*, double, unsigned long) |
389 | | |
390 | | }; |
391 | | /** @} */ |
392 | | /** @} */ |
393 | | } |
394 | | |
395 | | #include "OgreHeaderSuffix.h" |
396 | | |
397 | | #endif |
398 | | |