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