1. manski
  2. myOgre

Commits

Steve Streeting  committed 9286ff7

Add new StreamSerialiser class.
- Large improvements over old Serializer class
- Read / write binary chunk-based files easily, via DataStream so it can be from / to any source that supports that interface
- Chunks are now easier to read, validate (checksums) and skip without manual code to do so (you can quit reading at any point and it will automatically skip to the next chunk, including nested chunks)
- Chunk size calculations are now completely automated, meaning they're 100% accurate no matter what the client code. Nested chunks are fully supported with this.
- Wraps a stream, so that other classes can perform actual serialisation, sharing the same stream if needed (e.g. multiple objects in one file, handled by different classes). No need to subclass, much easier to integrate into different usage patterns

  • Participants
  • Parent commits aa3c986
  • Branches default

Comments (0)

Files changed (8)

File OgreMain/CMakeLists.txt

View file
   include/OgreStaticFaceGroup.h
   include/OgreStaticGeometry.h
   include/OgreStdHeaders.h
+  include/OgreStreamSerialiser.h
   include/OgreString.h
   include/OgreStringConverter.h
   include/OgreStringInterface.h
   src/OgreSkeletonManager.cpp
   src/OgreSkeletonSerializer.cpp
   src/OgreStaticGeometry.cpp
+  src/OgreStreamSerialiser.cpp
   src/OgreString.cpp
   src/OgreStringConverter.cpp
   src/OgreStringInterface.cpp

File OgreMain/include/OgreDataStream.h

View file
 		@param count Number of bytes to write
 		@returns The number of bytes written
 		*/
-		virtual size_t write(void* buf, size_t count)
+		virtual size_t write(const void* buf, size_t count)
 		{
 			// default to not supported
 			return 0;
 
 		/** @copydoc DataStream::write
 		*/
-		size_t write(void* buf, size_t count);
+		size_t write(const void* buf, size_t count);
 
 		/** @copydoc DataStream::readLine
 		*/
 
 		/** @copydoc DataStream::write
 		*/
-		size_t write(void* buf, size_t count);
+		size_t write(const void* buf, size_t count);
 
 		/** @copydoc DataStream::readLine
 		*/
 
 		/** @copydoc DataStream::write
 		*/
-		size_t write(void* buf, size_t count);
+		size_t write(const void* buf, size_t count);
 
 		/** @copydoc DataStream::skip
 		*/

File OgreMain/include/OgreStreamSerialiser.h

View file
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#ifndef __StreamSerialiser_H__
+#define __StreamSerialiser_H__
+
+#include "OgrePrerequisites.h"
+#include "OgreDataStream.h"
+
+namespace Ogre 
+{
+	/** Utility class providing helper methods for reading / writing 
+		structured data held in a DataStream.
+	@remarks
+		The structure of a file read / written by this class is a series of 
+		'chunks'. A chunk-based format has the advantage of being extensible later, 
+		and it's robust, in that a reader can skip chunks that they are not 
+		able (or willing) to process.
+	@par
+		Chunks are contained serially in the file, but they can also be 
+		nested in order both to provide context, and to group chunks together for 
+		potential skipping. 
+	@par
+		The data format of a chunk is as follows:
+		-# 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
+		-# Chunk version (16-bit uint). Chunks can change over time so this version number reflects that
+		-# Length (32-bit uint). The length of the chunk data section, including nested chunks. Note that
+			this length excludes this header, but includes the header of any nested chunks. 
+		-# Checksum (32-bit uint). Checksum value generated from the above - basically lets us check this is a valid chunk.
+		-# Chunk data
+		The 'Chunk data' section will contain chunk-specific data, which may include
+		other nested chunks.
+	*/
+	class _OgreExport StreamSerialiser : public StreamAlloc
+	{
+	public:
+		/// The endianness of files
+		enum Endian
+		{
+			/// Automatically determine endianness
+			ENDIAN_AUTO,
+			/// Use big endian (0x1000 is serialised as 0x10 0x00)
+			ENDIAN_BIG,
+			/// Use little endian (0x1000 is serialised as 0x00 0x10)
+			ENDIAN_LITTLE
+		};
+
+		/// Definition of a chunk of data in a file
+		struct Chunk : public StreamAlloc
+		{
+			/// Identifier of the chunk (for example from makeIdentifier)  (stored)
+			uint32 id;
+			/// Version of the chunk (stored)
+			uint16 version;
+			/// Length of the chunk in bytes (stored)
+			uint32 length;
+			/// Location of the chunk in bytes from the start of a stream (derived)
+			uint32 offset;
+
+			Chunk() : id(0), version(1), length(0), offset(0) {}
+		};
+
+		/** Constructor.
+		@param stream The stream on which you will read / write data.
+		@param endianMode The endian mode in which to read / writedata. If left at
+			the default, when writing the endian mode will be the native platform mode, 
+			and when reading it's expected that the first chunk encountered will be 
+			the header chunk, which will determine the endian mode.
+		@param autoHeader If true, the first write or read to this stream will 
+			automatically read / write the header too. This is required if you
+			set endianMode to ENDIAN_AUTO, but if you manually set the endian mode, 
+			then you can skip writing / reading the header if you wish, if for example
+			this stream is midway through a file which has already included header
+			information.
+		*/
+		StreamSerialiser(const DataStreamPtr& stream, Endian endianMode = ENDIAN_AUTO, 
+			bool autoHeader = true);
+		virtual ~StreamSerialiser();
+
+		/** Get the endian mode.
+		@remarks
+			If the result is ENDIAN_AUTO, this mode will change when the first piece of
+			data is read / written. 
+		*/
+		virtual Endian getEndian() const { return mEndian; }
+
+		/** Pack a 4-character code into a 32-bit identifier.
+		@remarks
+			You can use this to generate id's for your chunks based on friendlier
+			4-character codes rather than assigning numerical IDs, if you like.
+		@param code String to pack - must be 4 characters.
+		*/
+		static uint32 makeIdentifier(const String& code);
+
+		/** Report the current depth of the chunk nesting, whether reading or writing. 
+		@remarks
+			Returns how many levels of nested chunks are currently being processed, 
+			either writing or reading. In order to tidily finish, you must call
+			read/writeChunkEnd this many times.
+		*/
+		size_t getCurrentChunkDepth() const { return mChunkStack.size(); }
+
+		/** Get the ID of the chunk that's currently being read/written, if any.
+		@returns The id of the current chunk being read / written (at the tightest
+			level of nesting), or zero if no chunk is being processed.
+		*/
+		uint32 getCurrentChunkID() const;
+
+
+
+		/** Reads the start of the next chunk in the file.
+		@remarks
+			Files are serialised in a chunk-based manner, meaning that each section
+			of data is prepended by a chunk header. After reading this chunk header, 
+			the next set of data is available directly afterwards. 
+		@note
+			When you have finished with this chunk, you should call readChunkEnd. 
+			This will perform a bit of validation and clear the chunk from 
+			the stack. 
+		@returns The Chunk that comes next
+		*/
+		virtual const Chunk* readChunkBegin();
+
+		/** Finish the reading of a chunk.
+		@remarks
+			You can call this method at any point after calling readChunkBegin, even
+			if you didn't read all the rest of the data in the chunk. If you did 
+			not read to the end of a chunk, this method will automatically skip 
+			over the remainder of the chunk and position the stream just after it. 
+		@param id The id of the chunk that you were reading (for validation purposes)
+		*/
+		virtual void readChunkEnd(uint32 id);
+
+		/// Reports whether the stream is at the end of file
+		virtual bool eof() const;
+
+		/** Get the definition of the current chunk being read (if any). */
+		virtual const Chunk* getCurrentChunk() const;
+
+		/** Begin writing a new chunk.
+		@remarks
+			This starts the process of writing a new chunk to the stream. This will 
+			write the chunk header for you, and store a pointer so that the
+			class can automatically go back and fill in the size for you later
+			should you need it to. If you have already begun a chunk without ending
+			it, then this method will start a nested chunk within it. Once written, 
+			you can then start writing chunk-specific data into your stream.
+		@note If this is the first chunk in the file
+		@param id The identifier of the new chunk. Any value that's unique in the
+			file context is valid, except for the numbers 0x0001 and 0x1000 which are reserved
+			for internal header identification use. 
+		@param version The version of the chunk you're writing
+		*/
+		virtual void writeChunkBegin(uint32 id, uint16 version = 1);
+		/** End writing a chunk. 
+		@param id The identifier of the chunk - this is really just a safety check, 
+			since you can only end the chunk you most recently started.
+		*/
+		virtual void writeChunkEnd(uint32 id);
+
+		/** Write arbitrary data to a stream. 
+		@param buf Pointer to bytes
+		@param size The size of each element to write; each will be endian-flipped if
+			necessary
+		@param count The number of elements to write
+		*/
+		virtual void writeData(const void* buf, size_t size, size_t count);
+
+		/** Catch-all method to write primitive types. */
+		template <typename T>
+		void write(const T* pT, size_t count = 1)
+		{
+			writeData(pT, sizeof(T), count);
+		}
+
+		virtual void write(const Vector2* vec);
+		virtual void write(const Vector3* vec);
+		virtual void write(const Vector4* vec);
+		virtual void write(const Quaternion* q);
+		virtual void write(const Matrix3* m);
+		virtual void write(const Matrix4* m);
+		virtual void write(const String* string);
+
+		/** Read arbitrary data from a stream. 
+		@param buf Pointer to bytes
+		@param size The size of each element to read; each will be endian-flipped if
+		necessary
+		@param count The number of elements to read
+		*/
+		virtual void readData(void* buf, size_t size, size_t count);
+
+		/** Catch-all method to read primitive types. */
+		template <typename T>
+		void read(T* pT, size_t count = 1)
+		{
+			readData(pT, sizeof(T), count);
+		}
+
+		/// read a Vector3
+		virtual void read(Vector2* vec);
+		virtual void read(Vector3* vec);
+		virtual void read(Vector4* vec);
+		virtual void read(Quaternion* q);
+		virtual void read(Matrix3* m);
+		virtual void read(Matrix4* m);
+		virtual void read(String* string);
+
+	protected:
+		DataStreamPtr mStream;
+		Endian mEndian;
+		bool mFlipEndian;
+		bool mReadWriteHeader;
+		typedef deque<Chunk*>::type ChunkStack;
+		/// Current list of open chunks
+		ChunkStack mChunkStack;
+
+		static uint32 HEADER_ID;
+		static uint32 REVERSE_HEADER_ID;
+		static uint32 CHUNK_HEADER_SIZE;
+
+		virtual Chunk* readChunkImpl();
+		virtual void writeChunkImpl(uint32 id, uint16 version);
+		virtual void readHeader();
+		virtual void writeHeader();
+		virtual uint32 calculateChecksum(Chunk* c);
+		virtual void checkStream(bool failOnEof = false, 
+			bool validateReadable = false, bool validateWriteable = false) const;
+
+		virtual void flipEndian(void * pData, size_t size, size_t count);
+		virtual void flipEndian(void * pData, size_t size);
+		virtual void determineEndianness();
+		virtual Chunk* popChunk(uint id);
+
+	};
+}
+
+#endif
+

File OgreMain/src/OgreDataStream.cpp

View file
         return cnt;
     }
 	//---------------------------------------------------------------------
-	size_t MemoryDataStream::write(void* buf, size_t count)
+	size_t MemoryDataStream::write(const void* buf, size_t count)
 	{
 		size_t written = 0;
 		if (isWriteable())
         return mpInStream->gcount();
     }
 	//-----------------------------------------------------------------------
-	size_t FileStreamDataStream::write(void* buf, size_t count)
+	size_t FileStreamDataStream::write(const void* buf, size_t count)
 	{
 		size_t written = 0;
 		if (isWriteable() && mpFStream)
 		{
-			mpFStream->write(static_cast<char*>(buf), static_cast<std::streamsize>(count));
+			mpFStream->write(static_cast<const char*>(buf), static_cast<std::streamsize>(count));
 			written = count;
 		}
 		return written;
         return fread(buf, 1, count, mFileHandle);
     }
 	//-----------------------------------------------------------------------
-	size_t FileHandleDataStream::write(void* buf, size_t count)
+	size_t FileHandleDataStream::write(const void* buf, size_t count)
 	{
 		if (!isWriteable())
 			return 0;

File OgreMain/src/OgreStreamSerialiser.cpp

View file
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include "OgreStableHeaders.h"
+
+#include "OgreStreamSerialiser.h"
+#include "OgreException.h"
+#include "OgreStringConverter.h"
+#include "OgreLogManager.h"
+#include "OgreVector2.h"
+#include "OgreVector3.h"
+#include "OgreVector4.h"
+#include "OgreMatrix3.h"
+#include "OgreMatrix4.h"
+#include "OgreQuaternion.h"
+
+namespace Ogre
+{
+	//---------------------------------------------------------------------
+	uint32 StreamSerialiser::HEADER_ID = 0x0001;
+	uint32 StreamSerialiser::REVERSE_HEADER_ID = 0x1000;
+	uint32 StreamSerialiser::CHUNK_HEADER_SIZE = 
+		sizeof(uint32) + // id
+		sizeof(uint16) + // version
+		sizeof(uint32) + // length
+		sizeof(uint32); // checksum
+	//---------------------------------------------------------------------
+	StreamSerialiser::StreamSerialiser(const DataStreamPtr& stream, Endian endianMode, 
+		bool autoHeader)
+		: mStream(stream)
+		, mEndian(endianMode)
+		, mFlipEndian(false)
+		, mReadWriteHeader(autoHeader)
+	{
+		if (mEndian != ENDIAN_AUTO)
+		{
+#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
+			if (mEndian == ENDIAN_LITTLE)
+				mFlipEndian = true;
+#else
+			if (mEndian == ENDIAN_BIG)
+				mFlipEndian = true;
+#endif
+
+		}
+
+		checkStream();
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser::~StreamSerialiser()
+	{
+		// really this should be empty if read/write was complete, but be tidy
+		if (!mChunkStack.empty())
+		{
+			LogManager::getSingleton().stream() <<
+				"Warning: stream " << mStream->getName() << " was not fully read / written; " <<
+				mChunkStack.size() << " chunks remain unterminated.";
+		}
+		for (ChunkStack::iterator i = mChunkStack.begin(); i != mChunkStack.end(); ++i)
+			delete *i;
+		mChunkStack.clear();
+
+	}
+	//---------------------------------------------------------------------
+	uint32 StreamSerialiser::makeIdentifier(const String& code)
+	{
+		assert(code.length() <= 4 && "Characters after the 4th are being ignored");
+		uint32 ret = 0;
+		size_t c = std::min((size_t)4, code.length());
+		for (size_t i = 0; i < c; ++i)
+		{
+			ret += (code.at(i) << (i * 8));
+		}
+		return ret;
+
+	}
+	//---------------------------------------------------------------------
+	uint32 StreamSerialiser::getCurrentChunkID() const
+	{
+		if (mChunkStack.empty())
+			return 0;
+		else
+			return mChunkStack.back()->id;
+	}
+	//---------------------------------------------------------------------
+	const StreamSerialiser::Chunk* StreamSerialiser::readChunkBegin()
+	{
+		// Have we figured out the endian mode yet?
+		if (mReadWriteHeader)
+			readHeader();
+
+		if (mEndian == ENDIAN_AUTO)
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Endian mode has not been determined, did you disable header without setting?", 
+			"StreamSerialiser::readChunkBegin");
+		
+		Chunk* chunk = readChunkImpl();
+		mChunkStack.push_back(chunk);
+
+		return chunk;
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::readChunkEnd(uint32 id)
+	{
+		Chunk* c = popChunk(id);
+
+		checkStream();
+
+		// skip to the end of the chunk if we were not there already
+		// this lets us quite reading a chunk anywhere and have the read marker
+		// automatically skip to the next one
+		if (mStream->tell() < (c->offset + c->length))
+			mStream->seek(c->offset + c->length);
+
+		OGRE_DELETE c;
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::readHeader()
+	{
+		uint32 headerid;
+		size_t actually_read = mStream->read(&headerid, sizeof(uint32));
+		// skip back
+		mStream->skip(0 - (long)actually_read);
+		// validate that this is a header chunk
+		if (headerid == REVERSE_HEADER_ID)
+		{
+			mFlipEndian = true;
+		}
+		else if (headerid == HEADER_ID)
+		{
+			mFlipEndian = false;
+		}
+		else
+		{
+			// no good
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+				"Cannot determine endian mode because header is missing", 
+				"StreamSerialiser::readHeader");
+		}
+		determineEndianness();
+
+		mReadWriteHeader = false;
+
+		const Chunk* c = readChunkBegin();
+		// endian should be flipped now
+		assert(c->id == HEADER_ID);
+		readChunkEnd(HEADER_ID);
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::determineEndianness()
+	{
+#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
+		if (mFlipEndian)
+			mEndian = ENDIAN_LITTLE;
+		else
+			mEndian = ENDIAN_BIG;
+#else
+		if (mFlipEndian)
+			mEndian = ENDIAN_BIG;
+		else
+			mEndian = ENDIAN_LITTLE;
+#endif
+	}
+	//---------------------------------------------------------------------
+	const StreamSerialiser::Chunk* StreamSerialiser::getCurrentChunk() const
+	{
+		if (mChunkStack.empty())
+			return 0;
+		else
+			return mChunkStack.back();
+	}
+	//---------------------------------------------------------------------
+	bool StreamSerialiser::eof() const
+	{ 
+		checkStream();
+		return mStream->eof(); 
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::checkStream(bool failOnEof, bool validateReadable, bool validateWriteable) const
+	{
+		if (mStream.isNull())
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Invalid operation, stream is null", "StreamSerialiser::checkStream");
+
+		if (failOnEof && mStream->eof())
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Invalid operation, end of file on stream", "StreamSerialiser::checkStream");
+
+		if (validateReadable && !mStream->isReadable())
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Invalid operation, file is not readable", "StreamSerialiser::checkStream");
+
+		if (validateWriteable && !mStream->isWriteable())
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Invalid operation, file is not writeable", "StreamSerialiser::checkStream");
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::writeHeader()
+	{
+		if (mEndian == ENDIAN_AUTO)
+			determineEndianness();
+
+		// Header chunk has zero data size
+		writeChunkImpl(HEADER_ID, 1);
+		writeChunkEnd(HEADER_ID);
+
+		mReadWriteHeader = false;
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::writeChunkBegin(uint32 id, uint16 version /* = 1 */)
+	{
+		checkStream(false, false, true);
+
+		if (mReadWriteHeader)
+			writeHeader();
+
+		if (mEndian == ENDIAN_AUTO)
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+				"Endian mode has not been determined, did you disable header without setting?", 
+				"StreamSerialiser::writeChunkBegin");
+
+		writeChunkImpl(id, version);
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::writeChunkEnd(uint32 id)
+	{
+		checkStream(false, false, true);
+
+		Chunk* c = popChunk(id);
+
+		// update the sizes
+		size_t currPos = mStream->tell();
+		c->length = static_cast<uint32>(currPos - c->offset - CHUNK_HEADER_SIZE);
+
+		// seek to 'length' position in stream for this chunk
+		// skip id (32) and version (16)
+		mStream->seek(c->offset + sizeof(uint32) + sizeof(uint16));
+		write(&c->length);
+		// write updated checksum
+		uint32 checksum = calculateChecksum(c);
+		write(&checksum);
+
+		// seek back to previous position
+		mStream->seek(currPos);
+
+		OGRE_DELETE c;
+
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser::Chunk* StreamSerialiser::readChunkImpl()
+	{
+		Chunk *chunk = OGRE_NEW Chunk();
+		chunk->offset = static_cast<uint32>(mStream->tell());
+		read(&chunk->id);
+		read(&chunk->version);
+		read(&chunk->length);
+		
+		uint32 checksum;
+		read(&checksum);
+		
+		if (checksum != calculateChecksum(chunk))
+		{
+			// no good, this is an invalid chunk
+			uint32 off = chunk->offset;
+			OGRE_DELETE chunk;
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+				"Corrupt chunk detected in stream " + mStream->getName() + " at byte "
+				+ StringConverter::toString(off), 
+				"StreamSerialiser::readChunkImpl");
+		}
+		else
+		{
+			return chunk;
+		}
+
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::writeChunkImpl(uint32 id, uint16 version)
+	{
+		Chunk* c = OGRE_NEW Chunk();
+		c->id = id;
+		c->version = version;
+		c->offset = mStream->tell();
+		c->length = 0;
+
+		mChunkStack.push_back(c);
+
+		write(&c->id);
+		write(&c->version);
+		write(&c->length);
+		// write length again, this is just a placeholder for the checksum (to come later)
+		write(&c->length);
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::writeData(const void* buf, size_t size, size_t count)
+	{
+		checkStream(false, false, true);
+
+		size_t totSize = size * count;
+		if (mFlipEndian)
+		{
+			void* pToWrite = OGRE_MALLOC(totSize, MEMCATEGORY_GENERAL);
+			memcpy(pToWrite, buf, totSize);
+
+			flipEndian(pToWrite, size, count);
+			mStream->write(pToWrite, totSize);
+
+			OGRE_FREE(pToWrite, MEMCATEGORY_GENERAL);
+		}
+		else
+		{
+			mStream->write(buf, totSize);
+		}
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Vector2* vec)
+	{
+		write(vec->ptr(), 2);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Vector3* vec)
+	{
+		write(vec->ptr(), 3);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Vector4* vec)
+	{
+		write(vec->ptr(), 4);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Quaternion* q)
+	{
+		write(q->ptr(), 4);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const String* string)
+	{
+		mStream->write(string->c_str(), string->size());
+		// write terminator (newline)
+		char eol = '\n';
+		mStream->write(&eol, 1);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Matrix3* m)
+	{
+		write((*m)[0], 9);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::write(const Matrix4* m)
+	{
+		write((*m)[0], 12);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::readData(void* buf, size_t size, size_t count)
+	{
+		checkStream(true, true, false);
+
+		size_t totSize = size * count;
+		mStream->read(buf, totSize);
+
+		if (mFlipEndian)
+			flipEndian(buf, size, count);
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Vector2* vec)
+	{
+		read(vec->ptr(), 2);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Vector3* vec)
+	{
+		read(vec->ptr(), 3);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Vector4* vec)
+	{
+		read(vec->ptr(), 4);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Quaternion* q)
+	{
+		read(q->ptr(), 4);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Matrix3* m)
+	{
+		read((*m)[0], 9);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(Matrix4* m)
+	{
+		read((*m)[0], 12);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::read(String* string)
+	{
+		String readStr = mStream->getLine(false);
+		string->swap(readStr);
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::flipEndian(void * pBase, size_t size, size_t count)
+	{
+		for (size_t c = 0; c < count; ++c)
+		{
+			void *pData = (void *)((long)pBase + (c * size));
+			char swapByte;
+			for(size_t byteIndex = 0; byteIndex < size/2; byteIndex++)
+			{
+				swapByte = *(char *)((long)pData + byteIndex);
+				*(char *)((long)pData + byteIndex) = *(char *)((long)pData + size - byteIndex - 1);
+				*(char *)((long)pData + size - byteIndex - 1) = swapByte;
+			}
+		}
+
+	}
+	//---------------------------------------------------------------------
+	void StreamSerialiser::flipEndian(void * pData, size_t size)
+	{
+		flipEndian(pData, size, 1);
+	}
+	//---------------------------------------------------------------------
+	uint32 StreamSerialiser::calculateChecksum(Chunk* c)
+	{
+		return FastHash((const char*)c, sizeof(uint32) * 2 + sizeof(uint16));
+	}
+	//---------------------------------------------------------------------
+	StreamSerialiser::Chunk* StreamSerialiser::popChunk(uint id)
+	{
+		if (mChunkStack.empty())
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"No active chunk!", "StreamSerialiser::popChunk");
+
+		const Chunk* chunk = mChunkStack.back();
+		if (chunk->id != id)
+			OGRE_EXCEPT(Exception::ERR_INVALID_STATE, 
+			"Incorrect chunk id!", "StreamSerialiser::popChunk");
+
+		Chunk* c = mChunkStack.back();
+		mChunkStack.pop_back();
+		return c;
+
+	}
+
+
+
+}
+
+

File Tests/CMakeLists.txt

View file
 		OgreMain/include/PixelFormatTests.h
 		OgreMain/include/RadixSortTests.h
 		OgreMain/include/RenderSystemCapabilitiesTests.h
+		OgreMain/include/StreamSerialiserTests.h
 		OgreMain/include/StringTests.h
 		OgreMain/include/Suite.h
 		OgreMain/include/UseCustomCapabilitiesTests.h
 		OgreMain/src/PixelFormatTests.cpp
 		OgreMain/src/RadixSort.cpp
 		OgreMain/src/RenderSystemCapabilitiesTests.cpp
+		OgreMain/src/StreamSerialiserTests.cpp
 		OgreMain/src/StringTests.cpp
 		OgreMain/src/Suite.cpp
 		OgreMain/src/UseCustomCapabilitiesTests.cpp

File Tests/OgreMain/include/StreamSerialiserTests.h

View file
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+
+class StreamSerialiserTests : public CppUnit::TestFixture
+{
+	// CppUnit macros for setting up the test suite
+	CPPUNIT_TEST_SUITE( StreamSerialiserTests );
+	CPPUNIT_TEST(testWriteBasic);
+
+	CPPUNIT_TEST_SUITE_END();
+protected:
+public:
+	void setUp();
+	void tearDown();
+
+	void testWriteBasic();
+
+
+};

File Tests/OgreMain/src/StreamSerialiserTests.cpp

View file
+/*
+-----------------------------------------------------------------------------
+This source file is part of OGRE
+(Object-oriented Graphics Rendering Engine)
+For the latest info, see http://www.ogre3d.org/
+
+Copyright (c) 2000-2009 Torus Knot Software Ltd
+Also see acknowledgements in Readme.html
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU Lesser General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA 02111-1307, USA, or go to
+http://www.gnu.org/copyleft/lesser.txt.
+
+You may alternatively use this source under the terms of a specific version of
+the OGRE Unrestricted License provided you have obtained such a license from
+Torus Knot Software Ltd.
+-----------------------------------------------------------------------------
+*/
+#include "StreamSerialiserTests.h"
+#include "OgreStreamSerialiser.h"
+#include "OgreFileSystem.h"
+#include "OgreException.h"
+#include "OgreVector3.h"
+
+using namespace Ogre;
+
+// Regsiter the suite
+CPPUNIT_TEST_SUITE_REGISTRATION( StreamSerialiserTests );
+
+void StreamSerialiserTests::setUp()
+{
+}
+void StreamSerialiserTests::tearDown()
+{
+}
+
+void StreamSerialiserTests::testWriteBasic()
+{
+	FileSystemArchive arch("./", "FileSystem");
+	arch.load();
+
+	String fileName = "testSerialiser.dat";
+	Vector3 aTestVector(0.3, 15.2, -12.0);
+	String aTestString = "Some text here";
+	int aTestValue = 99;
+	uint32 chunkID = StreamSerialiser::makeIdentifier("TEST");
+	// write the data
+	{
+
+		DataStreamPtr stream = arch.create(fileName);
+
+		StreamSerialiser serialiser(stream);
+
+		serialiser.writeChunkBegin(chunkID);
+
+
+		serialiser.write(&aTestVector);
+		serialiser.write(&aTestString);
+		serialiser.write(&aTestValue);
+		serialiser.writeChunkEnd(chunkID);
+	}
+
+	// read it back
+	{
+
+		DataStreamPtr stream = arch.open(fileName);
+
+		StreamSerialiser serialiser(stream);
+
+		const StreamSerialiser::Chunk* c = serialiser.readChunkBegin();
+
+		CPPUNIT_ASSERT_EQUAL(chunkID, c->id);
+		CPPUNIT_ASSERT_EQUAL(sizeof(Vector3) + sizeof(int) + aTestString.size() + 1, c->length);
+
+		Vector3 inVector;
+		String inString;
+		int inValue;
+
+		serialiser.read(&inVector);
+		serialiser.read(&inString);
+		serialiser.read(&inValue);
+		serialiser.readChunkEnd(chunkID);
+
+		CPPUNIT_ASSERT_EQUAL(aTestVector, inVector);
+		CPPUNIT_ASSERT_EQUAL(aTestString, inString);
+		CPPUNIT_ASSERT_EQUAL(aTestValue, inValue);
+
+	}
+
+
+
+	arch.remove(fileName);
+
+	CPPUNIT_ASSERT(!arch.exists(fileName));
+}