Coverage Report

Created: 2025-10-10 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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