Pārlūkot izejas kodu

Serialization stores (and automatically figures out on loading) types

Marko Pintera 13 gadi atpakaļ
vecāks
revīzija
be9a1abb9e

+ 3 - 0
CamelotClient/CamelotClient.cpp

@@ -7,6 +7,7 @@
 #include "CmDynLibManager.h"
 #include "CmDynLibManager.h"
 
 
 #include "TestingGround.h"
 #include "TestingGround.h"
+#include "CmIReflectable.h"
 
 
 using namespace CamelotEngine;
 using namespace CamelotEngine;
 
 
@@ -22,6 +23,8 @@ int _tmain(int argc, _TCHAR* argv[])
 
 
 	test();
 	test();
 
 
+	IReflectable* refl = IReflectable::createInstanceFromTypeId(102);
+
 	gApplication().runMainLoop();
 	gApplication().runMainLoop();
 
 
 	int a = 5;
 	int a = 5;

+ 2 - 4
CamelotClient/TestingGround.cpp

@@ -16,10 +16,8 @@ void test()
 	dbgTexture->setFSAA(0, "test");
 	dbgTexture->setFSAA(0, "test");
 	fs.encode(dbgTexture.get(), "C:\\DbgTexture.tex");
 	fs.encode(dbgTexture.get(), "C:\\DbgTexture.tex");
 
 
-	TexturePtr emptyTexture = TextureManager::instance().create(TEX_TYPE_2D, 512, 512, 1, PF_UNKNOWN);
-	fs.decode(emptyTexture, "C:\\DbgTexture.tex");
-
-
+	TexturePtr emptyTexture = std::static_pointer_cast<Texture>(fs.decode("C:\\DbgTexture.tex"));
+	
 	TextureDataPtr data = emptyTexture->mTextureData[0];
 	TextureDataPtr data = emptyTexture->mTextureData[0];
 	UINT32 size2 = data->getSize();
 	UINT32 size2 = data->getSize();
 
 

+ 9 - 2
CamelotRenderer/Include/CmTextureST.h

@@ -6,6 +6,9 @@
 #include "CmTextureData.h"
 #include "CmTextureData.h"
 #include "CmManagedDataBlock.h"
 #include "CmManagedDataBlock.h"
 
 
+// DEBUG ONLY
+#include "CmTextureManager.h"
+
 namespace CamelotEngine
 namespace CamelotEngine
 {
 {
 	class CM_EXPORT TextureST : public RTTIType<Texture, Resource, TextureST>
 	class CM_EXPORT TextureST : public RTTIType<Texture, Resource, TextureST>
@@ -87,9 +90,13 @@ namespace CamelotEngine
 			return 101;
 			return 101;
 		}
 		}
 
 
-		virtual Texture* newRTTIObject()
+		TexturePtr texPtr; // HACK - this is just for testing before I port all newRTTI methods to use shared_ptr
+		virtual Resource* newRTTIObject()
 		{
 		{
-			CM_EXCEPT(InternalErrorException, "Cannot instantiate abstract class!");
+			// DEBUG ONLY
+			texPtr = TextureManager::instance().create(TEX_TYPE_2D, 128, 128, 1, PF_A8B8G8R8);
+			return texPtr.get();
+			//CM_EXCEPT(InternalErrorException, "Cannot instantiate abstract class!");
 		}
 		}
 	};
 	};
 }
 }

+ 11 - 6
CamelotUtility/Include/CmBinarySerializer.h

@@ -55,13 +55,12 @@ namespace CamelotEngine
 			boost::function<UINT8*(UINT8* buffer, int bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
 			boost::function<UINT8*(UINT8* buffer, int bytesWritten, UINT32& newBufferSize)> flushBufferCallback);
 
 
 		/**
 		/**
-		 * @brief	Decodes all of the fields of the provided object from a binary format.
+		 * @brief	Decodes an object from a binary format.
 		 *
 		 *
-		 * @param [in]	object	Object whos fields to decode.
 		 * @param [in]	data  	Binary data to decode.
 		 * @param [in]	data  	Binary data to decode.
 		 * @param	dataLength	Length of the data.
 		 * @param	dataLength	Length of the data.
 		 */
 		 */
-		void decode(std::shared_ptr<IReflectable> object, UINT8* data, UINT32 dataLength);
+		std::shared_ptr<IReflectable> decode(UINT8* data, UINT32 dataLength);
 
 
 	private:
 	private:
 		struct ObjectToEncode
 		struct ObjectToEncode
@@ -98,6 +97,12 @@ namespace CamelotEngine
 			UINT32 id;
 			UINT32 id;
 		};
 		};
 
 
+		struct ObjectMetaData
+		{
+			UINT32 objectMeta;
+			UINT32 typeId;
+		};
+
 		std::unordered_map<UINT32, UINT32> mObjectAddrToId;
 		std::unordered_map<UINT32, UINT32> mObjectAddrToId;
 		UINT32 mLastUsedObjectId;
 		UINT32 mLastUsedObjectId;
 		std::vector<ObjectToEncode> mObjectsToEncode;
 		std::vector<ObjectToEncode> mObjectsToEncode;
@@ -131,18 +136,18 @@ namespace CamelotEngine
 		void decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array, SerializableFieldType& type, bool& hasDynamicSize);
 		void decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array, SerializableFieldType& type, bool& hasDynamicSize);
 
 
 		/**
 		/**
-		* @brief	Encodes data required for representing an object identifier, into 4 bytes.
+		* @brief	Encodes data required for representing an object identifier, into 8 bytes.
 		*
 		*
 		* @note		Id can be a maximum of 31 bits, as one bit is reserved.
 		* @note		Id can be a maximum of 31 bits, as one bit is reserved.
 		*/
 		*/
-		UINT32 encodeObjectMetaData(UINT32 objId);
+		ObjectMetaData encodeObjectMetaData(UINT32 objId, UINT32 objTypeId);
 
 
 		/**
 		/**
 		* @brief	Decode meta field that was encoded using encodeObjectMetaData.
 		* @brief	Decode meta field that was encoded using encodeObjectMetaData.
 		* 			
 		* 			
 		* @note		Id can be a maximum of 31 bits, as one bit is reserved.
 		* @note		Id can be a maximum of 31 bits, as one bit is reserved.
 		*/
 		*/
-		void decodeObjectMetaData(UINT32 encodedData, UINT32& objId);
+		void decodeObjectMetaData(ObjectMetaData encodedData, UINT32& objId, UINT32& objTypeId);
 
 
 		/**
 		/**
 		 * @brief	Returns true if the provided encoded meta data represents object meta data.
 		 * @brief	Returns true if the provided encoded meta data represents object meta data.

+ 1 - 1
CamelotUtility/Include/CmFileSerializer.h

@@ -13,7 +13,7 @@ namespace CamelotEngine
 		~FileSerializer();
 		~FileSerializer();
 
 
 		void encode(IReflectable* object, std::string fileLocation);
 		void encode(IReflectable* object, std::string fileLocation);
-		void decode(std::shared_ptr<IReflectable> object, std::string fileLocation);
+		std::shared_ptr<IReflectable> decode(std::string fileLocation);
 
 
 	private:
 	private:
 		std::ofstream mOutputStream;
 		std::ofstream mOutputStream;

+ 2 - 0
CamelotUtility/Include/CmIReflectable.h

@@ -37,5 +37,7 @@ namespace CamelotEngine
 		{
 		{
 			getDerivedClasses().push_back(derivedClass);
 			getDerivedClasses().push_back(derivedClass);
 		}
 		}
+
+		static IReflectable* createInstanceFromTypeId(UINT32 rttiTypeId);
 	};
 	};
 }
 }

+ 7 - 6
CamelotUtility/Include/CmRTTIType.h

@@ -44,6 +44,7 @@ namespace CamelotEngine
 		RTTITypeBase();
 		RTTITypeBase();
 		virtual ~RTTITypeBase();
 		virtual ~RTTITypeBase();
 
 
+		virtual vector<RTTITypeBase*>::type& getDerivedClasses() = 0;
 		virtual void registerDerivedClass(RTTITypeBase* derivedClass) = 0;
 		virtual void registerDerivedClass(RTTITypeBase* derivedClass) = 0;
 		virtual IReflectable* newRTTIObject() = 0;
 		virtual IReflectable* newRTTIObject() = 0;
 		virtual const String& getRTTIName() = 0;
 		virtual const String& getRTTIName() = 0;
@@ -290,12 +291,6 @@ namespace CamelotEngine
 		/* 						RTTI CLASS META DATA							*/
 		/* 						RTTI CLASS META DATA							*/
 		/************************************************************************/
 		/************************************************************************/
 
 
-		static vector<RTTITypeBase*>::type& getDerivedClasses()
-		{
-			static vector<RTTITypeBase*>::type mRTTIDerivedClasses;
-			return mRTTIDerivedClasses;
-		}
-
 		static InitRTTIOnStart<Type, BaseType> initOnStart;
 		static InitRTTIOnStart<Type, BaseType> initOnStart;
 
 
 	public:
 	public:
@@ -308,6 +303,12 @@ namespace CamelotEngine
 			return &inst;
 			return &inst;
 		}
 		}
 
 
+		virtual vector<RTTITypeBase*>::type& getDerivedClasses()
+		{
+			static vector<RTTITypeBase*>::type mRTTIDerivedClasses;
+			return mRTTIDerivedClasses;
+		}
+
 		virtual void registerDerivedClass(RTTITypeBase* derivedClass)
 		virtual void registerDerivedClass(RTTITypeBase* derivedClass)
 		{
 		{
 			getDerivedClasses().push_back(derivedClass);
 			getDerivedClasses().push_back(derivedClass);

+ 7 - 0
CamelotUtility/Include/CmStdHeaders.h

@@ -30,6 +30,7 @@
 
 
 // STL containers
 // STL containers
 #include <vector>
 #include <vector>
+#include <stack>
 #include <map>
 #include <map>
 #include <string>
 #include <string>
 #include <set>
 #include <set>
@@ -169,6 +170,12 @@ namespace CamelotEngine
 		typedef typename std::list<T> type;    
 		typedef typename std::list<T> type;    
 	}; 
 	}; 
 
 
+	template <typename T, typename A = char > 
+	struct stack 
+	{ 
+		typedef typename std::stack<T> type;    
+	}; 
+
 	template <typename T, typename P = std::less<T>, typename A = char > 
 	template <typename T, typename P = std::less<T>, typename A = char > 
 	struct set 
 	struct set 
 	{ 
 	{ 

+ 73 - 29
CamelotUtility/Source/CmBinarySerializer.cpp

@@ -101,11 +101,30 @@ namespace CamelotEngine
 		*bytesWritten = mTotalBytesWritten;
 		*bytesWritten = mTotalBytesWritten;
 	}
 	}
 
 
-	void BinarySerializer::decode(std::shared_ptr<IReflectable> object, UINT8* data, UINT32 dataLength)
+	std::shared_ptr<IReflectable> BinarySerializer::decode(UINT8* data, UINT32 dataLength)
 	{
 	{
 		mPtrsToResolve.clear();
 		mPtrsToResolve.clear();
 		mDecodedObjects.clear();
 		mDecodedObjects.clear();
 
 
+		// Find and instantiate root object
+		if(sizeof(UINT32) > dataLength)
+		{
+			CM_EXCEPT(InternalErrorException, 
+				"Error decoding data.");
+		}
+
+		ObjectMetaData objectMetaData;
+		objectMetaData.objectMeta = 0;
+		objectMetaData.typeId = 0;
+		memcpy(&objectMetaData, data, sizeof(ObjectMetaData));
+
+		UINT32 objectId = 0;
+		UINT32 objectTypeId = 0;
+		decodeObjectMetaData(objectMetaData, objectId, objectTypeId);
+
+		std::shared_ptr<IReflectable> object = std::shared_ptr<IReflectable>(IReflectable::createInstanceFromTypeId(objectTypeId));
+		std::shared_ptr<IReflectable> rootObject = object;
+
 		// Create initial object + all other objects that are being referenced.
 		// Create initial object + all other objects that are being referenced.
 		// Use fields to find the type of referenced object.
 		// Use fields to find the type of referenced object.
 		UINT32 bytesRead = 0;
 		UINT32 bytesRead = 0;
@@ -113,22 +132,19 @@ namespace CamelotEngine
 		{
 		{
 			data += bytesRead;
 			data += bytesRead;
 
 
-			UINT8* localDataPtr = data;
-			UINT32 localBytesRead = bytesRead;
-
 			if((bytesRead + sizeof(UINT32)) > dataLength)
 			if((bytesRead + sizeof(UINT32)) > dataLength)
 			{
 			{
 				CM_EXCEPT(InternalErrorException, 
 				CM_EXCEPT(InternalErrorException, 
 					"Error decoding data.");
 					"Error decoding data.");
 			}
 			}
 
 
-			UINT32 objectMetaData = 0;
-			memcpy(&objectMetaData, localDataPtr, sizeof(UINT32));
-			localDataPtr += sizeof(UINT32);
-			localBytesRead += sizeof(UINT32);
+			objectMetaData.objectMeta = 0;
+			objectMetaData.typeId = 0;
+			memcpy(&objectMetaData, data, sizeof(ObjectMetaData));
 
 
-			UINT32 objectId = 0;
-			decodeObjectMetaData(objectMetaData, objectId);
+			objectId = 0;
+			objectTypeId = 0;
+			decodeObjectMetaData(objectMetaData, objectId, objectTypeId);
 
 
 			object = nullptr;
 			object = nullptr;
 			if(objectId != 0)
 			if(objectId != 0)
@@ -136,7 +152,9 @@ namespace CamelotEngine
 				auto iterFind = std::find_if(mPtrsToResolve.begin(), mPtrsToResolve.end(), [objectId](PtrToResolve x) { return x.id == objectId; });
 				auto iterFind = std::find_if(mPtrsToResolve.begin(), mPtrsToResolve.end(), [objectId](PtrToResolve x) { return x.id == objectId; });
 
 
 				if(iterFind != mPtrsToResolve.end())
 				if(iterFind != mPtrsToResolve.end())
-					object = iterFind->field->newObject();
+				{
+					object = std::shared_ptr<IReflectable>(IReflectable::createInstanceFromTypeId(objectTypeId));
+				}
 			}
 			}
 		}
 		}
 
 
@@ -157,9 +175,12 @@ namespace CamelotEngine
 			else
 			else
 				curPtr.field->setValue(curPtr.object.get(), resolvedObject);
 				curPtr.field->setValue(curPtr.object.get(), resolvedObject);
 		}
 		}
+
+		return rootObject;
 	}
 	}
 
 
-	UINT8* BinarySerializer::encodeInternal(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, int* bytesWritten, boost::function<UINT8*(UINT8*, int, UINT32&)> flushBufferCallback)
+	UINT8* BinarySerializer::encodeInternal(IReflectable* object, UINT32 objectId, UINT8* buffer, UINT32& bufferLength, 
+		int* bytesWritten, boost::function<UINT8*(UINT8*, int, UINT32&)> flushBufferCallback)
 	{
 	{
 		static const UINT32 META_SIZE = 4; // Meta field size
 		static const UINT32 META_SIZE = 4; // Meta field size
 		static const UINT32 NUM_ELEM_FIELD_SIZE = 4; // Size of the field storing number of array elements
 		static const UINT32 NUM_ELEM_FIELD_SIZE = 4; // Size of the field storing number of array elements
@@ -167,9 +188,9 @@ namespace CamelotEngine
 
 
 		RTTITypeBase* si = object->getRTTI();
 		RTTITypeBase* si = object->getRTTI();
 
 
-		// Encode object ID if it has one
-		UINT32 objectMetaData = encodeObjectMetaData(objectId);
-		COPY_TO_BUFFER(&objectMetaData, sizeof(UINT32))
+		// Encode object ID & type
+		ObjectMetaData objectMetaData = encodeObjectMetaData(objectId, si->getRTTIId());
+		COPY_TO_BUFFER(&objectMetaData, sizeof(ObjectMetaData))
 
 
 		int numFields = si->getNumFields();
 		int numFields = si->getNumFields();
 		for(int i = 0; i < numFields; i++)
 		for(int i = 0; i < numFields; i++)
@@ -362,19 +383,22 @@ namespace CamelotEngine
 		if(object != nullptr)
 		if(object != nullptr)
 			si = object->getRTTI();
 			si = object->getRTTI();
 
 
-		if((bytesRead + sizeof(UINT32)) > dataLength)
+		if((bytesRead + sizeof(ObjectMetaData)) > dataLength)
 		{
 		{
 			CM_EXCEPT(InternalErrorException, 
 			CM_EXCEPT(InternalErrorException, 
 				"Error decoding data.");
 				"Error decoding data.");
 		}
 		}
 
 
-		UINT32 objectMetaData = 0;
-		memcpy(&objectMetaData, data, sizeof(UINT32));
-		data += sizeof(UINT32);
-		bytesRead += sizeof(UINT32);
+		ObjectMetaData objectMetaData;
+		objectMetaData.objectMeta = 0;
+		objectMetaData.typeId = 0;
+		memcpy(&objectMetaData, data, sizeof(ObjectMetaData));
+		data += sizeof(ObjectMetaData);
+		bytesRead += sizeof(ObjectMetaData);
 
 
 		UINT32 objectId = 0;
 		UINT32 objectId = 0;
-		decodeObjectMetaData(objectMetaData, objectId);
+		UINT32 objectTypeId = 0;
+		decodeObjectMetaData(objectMetaData, objectId, objectTypeId);
 
 
 		if(object != nullptr && objectId != 0)
 		if(object != nullptr && objectId != 0)
 			mDecodedObjects.insert(std::make_pair(objectId, object));
 			mDecodedObjects.insert(std::make_pair(objectId, object));
@@ -393,11 +417,27 @@ namespace CamelotEngine
 
 
 			if(isObjectMetaData(metaData)) // We've reached a new object
 			if(isObjectMetaData(metaData)) // We've reached a new object
 			{
 			{
+				if((bytesRead + sizeof(ObjectMetaData)) > dataLength)
+				{
+					CM_EXCEPT(InternalErrorException, 
+						"Error decoding data.");
+				}
+
+				ObjectMetaData objMetaData;
+				objMetaData.objectMeta = 0;
+				objMetaData.typeId = 0;
+				memcpy(&objMetaData, data, sizeof(ObjectMetaData));
+
 				UINT32 objId = 0;
 				UINT32 objId = 0;
-				decodeObjectMetaData(metaData, objId);
+				UINT32 objTypeId = 0;
+				decodeObjectMetaData(objMetaData, objId, objTypeId);
+
+				if(objId != 0) 
+					return true; // Pointers we decode later, only if they're being referenced by something
 
 
-				if(objId != 0) // Objects with 0 ID represent complex types serialized by value, in which case we just decode them on the spot
-					return true;
+				// Objects with ID == 0 represent complex types serialized by value, but they should only get serialized
+				// if we encounter a field with one, not by just iterating through the file.
+				CM_EXCEPT(InternalErrorException, "Object with ID 0 encountered. Cannot proceed with serialization.");
 			}
 			}
 
 
 			data += META_SIZE;
 			data += META_SIZE;
@@ -784,25 +824,29 @@ namespace CamelotEngine
 		id = (UINT16)((encodedData >> 16) & 0xFFFF);
 		id = (UINT16)((encodedData >> 16) & 0xFFFF);
 	}
 	}
 
 
-	UINT32 BinarySerializer::encodeObjectMetaData(UINT32 objId)
+	BinarySerializer::ObjectMetaData BinarySerializer::encodeObjectMetaData(UINT32 objId, UINT32 objTypeId)
 	{
 	{
 		// If O == 1 - Meta contains object instance information (Encoded using encodeObjectMetaData)
 		// If O == 1 - Meta contains object instance information (Encoded using encodeObjectMetaData)
 		//// Encoding: SSSS SSSS SSSS SSSS xxxx xxxx xxxx xxxO
 		//// Encoding: SSSS SSSS SSSS SSSS xxxx xxxx xxxx xxxO
 		//// S - Size of the object identifier
 		//// S - Size of the object identifier
 		//// O - Object descriptor
 		//// O - Object descriptor
 
 
-		return (objId << 1) | 0x01;
+		ObjectMetaData metaData;
+		metaData.objectMeta = (objId << 1) | 0x01;
+		metaData.typeId = objTypeId;
+		return metaData;
 	}
 	}
 
 
-	void BinarySerializer::decodeObjectMetaData(UINT32 encodedData, UINT32& objId)
+	void BinarySerializer::decodeObjectMetaData(BinarySerializer::ObjectMetaData encodedData, UINT32& objId, UINT32& objTypeId)
 	{
 	{
-		if(!isObjectMetaData(encodedData))
+		if(!isObjectMetaData(encodedData.objectMeta))
 		{
 		{
 			CM_EXCEPT(InternalErrorException, 
 			CM_EXCEPT(InternalErrorException, 
 				"Meta data represents a field description but is trying to be decoded as an object descriptor.");
 				"Meta data represents a field description but is trying to be decoded as an object descriptor.");
 		}
 		}
 
 
-		objId = (encodedData >> 1) & 0x7FFFFFFF;
+		objId = (encodedData.objectMeta >> 1) & 0x7FFFFFFF;
+		objTypeId = encodedData.typeId;
 	}
 	}
 
 
 	bool BinarySerializer::isObjectMetaData(UINT32 encodedData)
 	bool BinarySerializer::isObjectMetaData(UINT32 encodedData)

+ 4 - 2
CamelotUtility/Source/CmFileSerializer.cpp

@@ -31,7 +31,7 @@ namespace CamelotEngine
 		mOutputStream.clear();
 		mOutputStream.clear();
 	}
 	}
 
 
-	void FileSerializer::decode(std::shared_ptr<IReflectable> object, std::string fileLocation)
+	std::shared_ptr<IReflectable> FileSerializer::decode(std::string fileLocation)
 	{
 	{
 		mInputStream.open(fileLocation.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
 		mInputStream.open(fileLocation.c_str(), std::ios::in | std::ios::ate | std::ios::binary);
 		
 		
@@ -48,12 +48,14 @@ namespace CamelotEngine
 		mInputStream.read((char*)readBuffer, fileSize);
 		mInputStream.read((char*)readBuffer, fileSize);
 
 
 		BinarySerializer bs;
 		BinarySerializer bs;
-		bs.decode(object, readBuffer, (UINT32)fileSize);
+		std::shared_ptr<IReflectable> object = bs.decode(readBuffer, (UINT32)fileSize);
 
 
 		mInputStream.close();
 		mInputStream.close();
 		mInputStream.clear();
 		mInputStream.clear();
 
 
 		delete[] readBuffer;
 		delete[] readBuffer;
+
+		return object;
 	}
 	}
 
 
 	UINT8* FileSerializer::flushBuffer(UINT8* bufferStart, int bytesWritten, UINT32& newBufferSize)
 	UINT8* FileSerializer::flushBuffer(UINT8* bufferStart, int bytesWritten, UINT32& newBufferSize)

+ 25 - 0
CamelotUtility/Source/CmIReflectable.cpp

@@ -1,5 +1,30 @@
 #include "CmIReflectable.h"
 #include "CmIReflectable.h"
+#include "CmRTTIType.h"
+#include "CmException.h"
 
 
 namespace CamelotEngine
 namespace CamelotEngine
 {
 {
+	IReflectable* IReflectable::createInstanceFromTypeId(UINT32 rttiTypeId)
+	{
+		stack<RTTITypeBase*>::type todo;
+		vector<RTTITypeBase*>::type& rootClasses = getDerivedClasses();
+
+		for(auto iter = rootClasses.begin(); iter != rootClasses.end(); ++iter)
+			todo.push(*iter);
+
+		while(!todo.empty())
+		{
+			RTTITypeBase* curType = todo.top();
+			todo.pop();
+
+			if(curType->getRTTIId() == rttiTypeId)
+				return curType->newRTTIObject();
+
+			vector<RTTITypeBase*>::type& derivedClasses = curType->getDerivedClasses();
+			for(auto iter = derivedClasses.begin(); iter != derivedClasses.end(); ++iter)
+				todo.push(*iter);
+		}
+
+		return nullptr;
+	}
 }
 }