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