Browse Source

String serialization and some more work on texture serialization

Marko Pintera 13 years ago
parent
commit
e422eb43b3

+ 4 - 2
CamelotClient/CamelotClient.cpp

@@ -11,11 +11,13 @@ using namespace CamelotEngine;
 
 int _tmain(int argc, _TCHAR* argv[])
 {
-	test();
-
 	gApplication().startUp("CamelotGLRenderer.dll");
 	//gApplication().startUp("CamelotD3D9Renderer.dll");
 
+	test();
+
+	gApplication().runMainLoop();
+
 	int a = 5;
 
 	gApplication().shutDown();

+ 8 - 0
CamelotClient/TestingGround.cpp

@@ -2,6 +2,7 @@
 
 #include "CmFileSerializer.h"
 #include "CmResource.h"
+#include "CmTextureManager.h"
 
 using namespace CamelotEngine;
 
@@ -16,4 +17,11 @@ void test()
 
 	Resource* loadedResource = new Resource();
 	fs.decode(loadedResource, "C:\\ResourceTest.res");
+
+	TexturePtr dbgTexture = TextureManager::instance().create(TEX_TYPE_2D, 128, 128, 1, PF_A8B8G8R8);
+	dbgTexture->setFSAA(0, "test");
+	fs.encode(dbgTexture.get(), "C:\\DbgTexture.tex");
+
+	TexturePtr emptyTexture = TextureManager::instance().create(TEX_TYPE_2D, 512, 512, 1, PF_UNKNOWN);
+	fs.decode(emptyTexture.get(), "C:\\DbgTexture.tex");
 }

+ 1 - 0
CamelotRenderer/CamelotRenderer.vcxproj

@@ -127,6 +127,7 @@
     <ClInclude Include="Include\CmSpecificImporter.h" />
     <ClInclude Include="Include\CmTexture.h" />
     <ClInclude Include="Include\CmTextureManager.h" />
+    <ClInclude Include="Include\CmTextureST.h" />
     <ClInclude Include="Include\CmTextureState.h" />
     <ClInclude Include="Include\CmVertexIndexData.h" />
     <ClInclude Include="Include\CmViewport.h" />

+ 3 - 0
CamelotRenderer/CamelotRenderer.vcxproj.filters

@@ -170,6 +170,9 @@
     <ClInclude Include="Include\CmResourceST.h">
       <Filter>Header Files\Serialization</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmTextureST.h">
+      <Filter>Header Files\Serialization</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\CamelotRenderer.cpp">

+ 1 - 0
CamelotRenderer/Include/CmApplication.h

@@ -19,6 +19,7 @@ namespace CamelotEngine
 			Application();
 
 			void startUp(String renderSystemDll);
+			void runMainLoop();
 			void shutDown();
 
 			void DBG_renderSimpleFrame();

+ 0 - 3
CamelotRenderer/Include/CmResourceST.h

@@ -1,14 +1,11 @@
 #pragma once
 
 #include "CmPrerequisites.h"
-#include "CmRTTIType.h"
 #include "CmSerializableType.h"
 #include "CmResource.h"
 
 namespace CamelotEngine
 {
-	CM_SERIALIZABLE_SIMPLE_TYPE(UUID, 11);
-	CM_SERIALIZABLE_SIMPLE_TYPE(UINT32&, 12);
 	class CM_EXPORT ResourceST : public SerializableType
 	{
 	private:

+ 8 - 0
CamelotRenderer/Include/CmTexture.h

@@ -382,6 +382,14 @@ namespace CamelotEngine {
 
 		/** Default implementation of unload which calls freeInternalResources */
 		void unloadImpl(void);
+
+		/************************************************************************/
+		/* 								SERIALIZATION                      		*/
+		/************************************************************************/
+	public:
+		friend class TextureST;
+		virtual SerializableType* getSerializable() const;
+		static Texture* newObject();
     };
 
 	/** @} */

+ 41 - 0
CamelotRenderer/Include/CmTextureST.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include "CmSerializableType.h"
+#include "CmTexture.h"
+
+namespace CamelotEngine
+{
+	class CM_EXPORT TextureST : public SerializableType
+	{
+	private:
+		CM_SETGET_MEMBER(mSize, UINT32, Texture)
+		CM_SETGET_MEMBER(mSourceUUID, UUID, Texture);
+		CM_SETGET_MEMBER(mHeight, UINT32, Texture)
+		CM_SETGET_MEMBER(mWidth, UINT32, Texture)
+		CM_SETGET_MEMBER(mDepth, UINT32, Texture)
+		CM_SETGET_MEMBER(mNumRequestedMipmaps, UINT32, Texture)
+		CM_SETGET_MEMBER(mNumMipmaps, UINT32, Texture)
+		CM_SETGET_MEMBER(mMipmapsHardwareGenerated, bool, Texture)
+		CM_SETGET_MEMBER(mGamma, float, Texture)
+		CM_SETGET_MEMBER(mHwGamma, bool, Texture)
+		CM_SETGET_MEMBER(mFSAA, UINT32, Texture)
+		CM_SETGET_MEMBER(mFSAAHint, String, Texture)
+	public:
+		TextureST()
+		{
+			CM_ADD_PLAINFIELD(mSize, 0, TextureST)
+			CM_ADD_PLAINFIELD(mSourceUUID, 1, TextureST)
+			CM_ADD_PLAINFIELD(mHeight, 2, TextureST)
+			CM_ADD_PLAINFIELD(mWidth, 3, TextureST)
+			CM_ADD_PLAINFIELD(mDepth, 4, TextureST)
+			CM_ADD_PLAINFIELD(mNumRequestedMipmaps, 5, TextureST)
+			CM_ADD_PLAINFIELD(mNumMipmaps, 6, TextureST)
+			CM_ADD_PLAINFIELD(mMipmapsHardwareGenerated, 7, TextureST)
+			CM_ADD_PLAINFIELD(mGamma, 8, TextureST)
+			CM_ADD_PLAINFIELD(mHwGamma, 9, TextureST)
+			CM_ADD_PLAINFIELD(mFSAA, 10, TextureST)
+			CM_ADD_PLAINFIELD(mFSAAHint, 11, TextureST)
+		}
+	};
+}

+ 3 - 1
CamelotRenderer/Source/CmApplication.cpp

@@ -82,8 +82,10 @@ namespace CamelotEngine
 
 		mVertProg = HighLevelGpuProgramManager::instance().createProgram(vertShaderCode, "main", "glsl", GPT_VERTEX_PROGRAM, GPP_VS_2_0);
 		mVertProg->load();
+	}
 
-
+	void Application::runMainLoop()
+	{
 		while(true)
 		{
 			WindowEventUtilities::messagePump();

+ 16 - 0
CamelotRenderer/Source/CmTexture.cpp

@@ -27,6 +27,7 @@ THE SOFTWARE.
 */
 #include "CmHardwarePixelBuffer.h"
 #include "CmTexture.h"
+#include "CmTextureST.h"
 #include "CmTextureData.h"
 #include "CmDataStream.h"
 #include "CmException.h"
@@ -252,4 +253,19 @@ namespace CamelotEngine {
             }
         }
     }
+
+	/************************************************************************/
+	/* 								SERIALIZATION                      		*/
+	/************************************************************************/
+
+	SerializableType* Texture::getSerializable() const
+	{
+		static TextureST serializableType;
+		return &serializableType;
+	}
+
+	Texture* Texture::newObject()
+	{
+		CM_EXCEPT(InternalErrorException, "Cannot instantiate abstract class!");
+	}
 }

+ 7 - 4
CamelotRenderer/TODO.txt

@@ -45,12 +45,15 @@ TODO:
  - OpenGL too
 
 TOMORROW:
+ - How do I serialize derived classes, without rewriting all the base class serialization?
+   - Unless something better dawns on me by Monday, just inherit from parents SerializableType and manually make sure no IDs overlap.
+     - We can improve later if needed. I though about it too much for now.
+ - How do I deserialize binary data without knowing what type of class (i.e. Resource) is it?
+  - Each Resource (i.e. Texture, Mesh) needs to have its own manager, which then hooks into the global Resources class
+  - When saving resources, save an unique resource identifier
+  - Then we can call individual resource manager and it can create proper resource based on active render system and whatever else
 TextureData
- - Keeps num mips, width, height, depth, format, flags and raw image data that can be used for initializing textures
- - Will need some methods like getPixelBox, from OgreImage
- - Cube textures require 6 of these
  - When serializing this texture, request data from GPU, populate TextureData array and return
-
 Texture
  - Gets initialized by calling SetData(vector<TextureData). 
  - Returns TextureData by GetData (retrieves data from GPU)

+ 1 - 0
CamelotUtility/CamelotUtility.vcxproj

@@ -98,6 +98,7 @@
     <ClInclude Include="Include\CmRTTIReflectableField.h" />
     <ClInclude Include="Include\CmRTTIReflectablePtrField.h" />
     <ClInclude Include="Include\CmRTTIType.h" />
+    <ClInclude Include="Include\CmRTTITypes.h" />
     <ClInclude Include="Include\CmSerializableType.h" />
     <ClInclude Include="Include\CmString.h" />
     <ClInclude Include="Include\CmThreadDefines.h" />

+ 3 - 0
CamelotUtility/CamelotUtility.vcxproj.filters

@@ -174,6 +174,9 @@
     <ClInclude Include="Include\CmSerializableType.h">
       <Filter>Header Files\Serialization</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmRTTITypes.h">
+      <Filter>Header Files\Prerequisites</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Include\CmAxisAlignedBox.cpp">

+ 2 - 2
CamelotUtility/Include/CmBinarySerializer.h

@@ -123,12 +123,12 @@ namespace CamelotEngine
 		/**
 		* @brief	Encodes data required for representing a serialized field, into 4 bytes.
 		*/
-		UINT32 encodeFieldMetaData(UINT16 id, UINT8 size, bool array, SerializableFieldType type);
+		UINT32 encodeFieldMetaData(UINT16 id, UINT8 size, bool array, SerializableFieldType type, bool hasDynamicSize);
 
 		/**
 		* @brief	Decode meta field that was encoded using encodeFieldMetaData.
 		*/
-		void decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array, SerializableFieldType& type);
+		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.

+ 3 - 0
CamelotUtility/Include/CmPrerequisitesUtil.h

@@ -56,6 +56,9 @@ THE SOFTWARE
 // Short-hand names for various built-in types
 #include "CmTypes.h"
 
+// RTTI supported types and a macro for defining new ones
+#include "CmRTTITypes.h"
+
 // Useful threading defines
 #include "CmThreadDefines.h"
 

+ 39 - 11
CamelotUtility/Include/CmRTTIField.h

@@ -15,15 +15,6 @@ namespace CamelotEngine
 	class RTTITypeBase;
 	struct RTTIField;
 
-	template<class T>
-	struct SerializableSimpleType 
-	{ 
-		BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, // We need this hacky check, otherwise if we only use "false" compiler will evaluate the template and trigger this message 
-			"Provided serializable data type doesn't define have a type ID. Use CM_SERIALIZABLE_SIMPLE_TYPE(type, id) macro to define a unique ID for the type.");
-
-		enum { id = T::TYPE_ID }; enum { isNativeType = 0 }; 
-	};
-
 	CM_SERIALIZABLE_SIMPLE_TYPE(UINT8, 0);
 	CM_SERIALIZABLE_SIMPLE_TYPE(UINT16, 1);
 	CM_SERIALIZABLE_SIMPLE_TYPE(UINT32, 2);
@@ -35,12 +26,48 @@ namespace CamelotEngine
 	CM_SERIALIZABLE_SIMPLE_TYPE(float, 8);
 	CM_SERIALIZABLE_SIMPLE_TYPE(double, 9);
 	CM_SERIALIZABLE_SIMPLE_TYPE(bool, 10);
+		
+	/**
+	 * @brief	Strings need to copy their data in a slightly more intricate way than just memcpy.
+	 */
+	template<> struct SerializableSimpleType<String>
+	{	
+		enum { id = 20 }; enum { hasDynamicSize = 1 };
+
+		static void toMemory(String& data, char* memory)
+		{ 
+			UINT32 size = getDynamicSize(data);
+
+			memcpy(memory, &size, sizeof(UINT32));
+			memory += sizeof(UINT32);
+			memcpy(memory, data.data(), size); 
+		}
+
+		static void fromMemory(String& data, char* memory)
+		{ 
+			UINT32 size;
+			memcpy(&size, memory, sizeof(UINT32)); 
+			memory += sizeof(UINT32);
+
+			size -= sizeof(UINT32);
+			char* buffer = new char[size + 1]; // TODO - Use a better allocator
+			memcpy(buffer, memory, size); 
+			buffer[size] = '\0';
+			data = String(buffer);
+
+			delete[] buffer; 
+		}
+
+		static UINT32 getDynamicSize(String& data)	
+		{ 
+			return data.size() + sizeof(UINT32);
+		}	
+	}; 
 
 	/**
 	 * @brief	Types of fields we can serialize:
 	 * 			
-	 * - Simple - Native data types and POD (Plain old data) structures. POD structures  
-	 *			  need to define a static TYPE_ID field with a unique ID. (IDs 0-31 are reserved for native types)
+	 * - Simple - Native data types, POD (Plain old data) structures, or in general types we don't want to (or can't) inherit from IReflectable. 
 	 *			  
 	 * - DataBlock - Array of bytes of a certain size. When returning a data block you may specify if its managed or unmanaged.  
 	 *				 Managed data blocks have their buffers deleted after they go out of scope. This is useful if you need to return some
@@ -83,6 +110,7 @@ namespace CamelotEngine
 		virtual void setArraySize(void* object, UINT32 size) = 0;
 
 		virtual UINT32 getTypeSize() = 0;
+		virtual bool hasDynamicSize() = 0;
 
 		/**
 		 * @brief	Throws an exception if this field doesn't contain a simple value.

+ 5 - 0
CamelotUtility/Include/CmRTTIManagedDataBlockField.h

@@ -36,6 +36,11 @@ namespace CamelotEngine
 			return 0; // Data block types don't store size the conventional way
 		}
 
+		virtual bool hasDynamicSize()
+		{
+			return true;
+		}
+
 		virtual UINT32 getArraySize(void* object)
 		{
 			CM_EXCEPT(InternalErrorException, 

+ 48 - 4
CamelotUtility/Include/CmRTTIPlainField.h

@@ -30,6 +30,21 @@ namespace CamelotEngine
 			return 0;
 		}
 
+		virtual bool hasDynamicSize()
+		{
+			return false;
+		}
+
+		virtual UINT32 getDynamicSize(void* object)
+		{
+			return 0;
+		}
+
+		virtual UINT32 getArrayElemDynamicSize(void* object, int index)
+		{
+			return 0;
+		}
+
 		template<class ObjectType, class DataType>
 		void getValue(ObjectType* object, DataType& value)
 		{
@@ -181,6 +196,35 @@ namespace CamelotEngine
 			return SerializableSimpleType<DataType>::id;
 		}
 
+		virtual bool hasDynamicSize()
+		{
+			return SerializableSimpleType<DataType>::hasDynamicSize != 0;
+		}
+
+		virtual UINT32 getDynamicSize(void* object)
+		{
+			checkIsArray(false);
+			checkType<DataType>();
+
+			ObjectType* castObject = static_cast<ObjectType*>(object);
+
+			DataType value;
+			getValue(castObject, value);
+			return SerializableSimpleType<DataType>::getDynamicSize(value);
+		}
+
+		virtual UINT32 getArrayElemDynamicSize(void* object, int index)
+		{
+			checkIsArray(true);
+			checkType<DataType>();
+
+			ObjectType* castObject = static_cast<ObjectType*>(object);
+
+			DataType value;
+			getArrayValue(castObject, index, value);
+			return SerializableSimpleType<DataType>::getDynamicSize(value);
+		}
+
 		virtual UINT32 getArraySize(void* object)
 		{
 			checkIsArray(true);
@@ -214,7 +258,7 @@ namespace CamelotEngine
 
 			DataType value;
 			getValue(castObject, value);
-			memcpy(buffer, &value, getTypeSize());
+			SerializableSimpleType<DataType>::toMemory(value, (char*)buffer);
 		}
 
 		virtual void arrayElemToBuffer(void* object, int index, void* buffer)
@@ -226,7 +270,7 @@ namespace CamelotEngine
 
 			DataType value;
 			getArrayValue(castObject, index, value);
-			memcpy(buffer, &value, getTypeSize());
+			SerializableSimpleType<DataType>::toMemory(value, (char*)buffer);
 		}
 
 		virtual void fromBuffer(void* object, void* buffer)
@@ -237,7 +281,7 @@ namespace CamelotEngine
 			ObjectType* castObject = static_cast<ObjectType*>(object);
 
 			DataType value;
-			memcpy(&value, buffer, getTypeSize());
+			SerializableSimpleType<DataType>::fromMemory(value, (char*)buffer);
 			setValue(castObject, value);
 		}
 
@@ -249,7 +293,7 @@ namespace CamelotEngine
 			ObjectType* castObject = static_cast<ObjectType*>(object);
 
 			DataType value;
-			memcpy(&value, buffer, getTypeSize());
+			SerializableSimpleType<DataType>::fromMemory(value, (char*)buffer);
 			setArrayValue(castObject, index, value);
 		}
 	};

+ 2 - 0
CamelotUtility/Include/CmRTTIReflectableField.h

@@ -15,6 +15,8 @@ namespace CamelotEngine
 		virtual void setArrayValue(void* object, UINT32 index, IReflectable& value) = 0;
 
 		virtual IReflectable* newObject() = 0;
+
+		virtual bool hasDynamicSize() { return true; }
 	};
 
 	template <class DataType, class ObjectType>

+ 2 - 0
CamelotUtility/Include/CmRTTIReflectablePtrField.h

@@ -15,6 +15,8 @@ namespace CamelotEngine
 		virtual void setArrayValue(void* object, UINT32 index, IReflectable* value) = 0;
 
 		virtual IReflectable* newObject() = 0;
+
+		virtual bool hasDynamicSize() { return true; }
 	};
 
 	template <class DataType, class ObjectType>

+ 7 - 0
CamelotUtility/Include/CmRTTIType.h

@@ -20,6 +20,13 @@
 
 namespace CamelotEngine
 {
+#define CM_SETGET_MEMBER(name, type, parentType)								\
+	type##& get##name(parentType##* obj) { return obj->##name; }				\
+	void set##name(parentType##* obj, type##& val) { obj->##name = val; } 
+
+#define CM_ADD_PLAINFIELD(name, id, parentType) \
+	addPlainField(#name, id##, &##parentType##::get##name, &##parentType##::set##name);
+
 	/**
 	 * @brief	Provides an interface for accessing fields of a certain class.
 	 * 			Data can be easily accessed by getter and setter methods.

+ 31 - 0
CamelotUtility/Include/CmRTTITypes.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include "boost/static_assert.hpp"
+
+namespace CamelotEngine
+{
+	template<class T>
+	struct SerializableSimpleType 
+	{ 
+		BOOST_STATIC_ASSERT_MSG(sizeof(T) == 0, // We need this hacky check, otherwise if we only use "false" compiler will evaluate the template and trigger this message 
+			"Provided data type doesn't isn't marked as serializable. Declare SerializableSimpleType template specialization to define a unique ID and needed methods for the type.");
+
+		enum { id = T::TYPE_ID }; 
+		enum { hasDynamicSize = 0 };
+
+		void toMemory(T& data, char* memory) {}
+		void fromMemory(T& data, char* memory) {}
+		UINT32 getDynamicSize(T& data) {}
+	};
+
+#define CM_SERIALIZABLE_SIMPLE_TYPE(type, type_id)				\
+	template<> struct SerializableSimpleType<##type##>			\
+	{	enum { id=##type_id }; enum { hasDynamicSize = 0 };		\
+		static void toMemory(##type##& data, char* memory)		\
+		 { memcpy(memory, &data, sizeof(##type##)); }			\
+		static void fromMemory(##type##& data, char* memory)	\
+		 { memcpy(&data, memory, sizeof(##type##)); }			\
+		static UINT32 getDynamicSize(##type##& data)			\
+		{ assert(false); return sizeof(##type##); }				\
+	}; 
+}

+ 0 - 3
CamelotUtility/Include/CmTypes.h

@@ -19,7 +19,4 @@ namespace CamelotEngine
 	typedef unsigned long long UINT64;
 	typedef long long INT64;
 #endif
-
-#define CM_SERIALIZABLE_SIMPLE_TYPE(type, type_id) \
-	template<> struct SerializableSimpleType<##type##> { enum { id=##type_id }; enum { isNativeType = 1 }; }; 
 }

+ 3 - 0
CamelotUtility/Include/CmUUID.h

@@ -1,8 +1,11 @@
 #pragma once
 
+#include "CmPrerequisitesUtil.h"
 #include <boost/uuid/uuid.hpp>
 
 namespace CamelotEngine
 {
 	typedef boost::uuids::uuid UUID;
+
+	CM_SERIALIZABLE_SIMPLE_TYPE(UUID, 51);
 }

+ 37 - 13
CamelotUtility/Source/CmBinarySerializer.cpp

@@ -177,7 +177,8 @@ namespace CamelotEngine
 			RTTIField* curGenericField = si->getField(i);
 
 			// Copy field ID & other meta-data like field size and type
-			int metaData = encodeFieldMetaData(curGenericField->mUniqueId, curGenericField->getTypeSize(), curGenericField->mIsVectorType, curGenericField->mType);
+			int metaData = encodeFieldMetaData(curGenericField->mUniqueId, curGenericField->getTypeSize(), 
+				curGenericField->mIsVectorType, curGenericField->mType, curGenericField->hasDynamicSize());
 			COPY_TO_BUFFER(&metaData, META_SIZE)
 
 			if(curGenericField->mIsVectorType)
@@ -222,9 +223,14 @@ namespace CamelotEngine
 					{
 						RTTIPlainFieldBase* curField = static_cast<RTTIPlainFieldBase*>(curGenericField);
 
-						UINT32 typeSize = curField->getTypeSize();
 						for(UINT32 arrIdx = 0; arrIdx < arrayNumElems; arrIdx++)
 						{
+							UINT32 typeSize = 0;
+							if(curField->hasDynamicSize())
+								typeSize = curField->getArrayElemDynamicSize(object, arrIdx);
+							else
+								typeSize = curField->getTypeSize();
+
 							if((*bytesWritten + typeSize) > bufferLength)
 							{
 								mTotalBytesWritten += *bytesWritten;
@@ -275,7 +281,11 @@ namespace CamelotEngine
 					{
 						RTTIPlainFieldBase* curField = static_cast<RTTIPlainFieldBase*>(curGenericField);
 
-						UINT32 typeSize = curField->getTypeSize();
+						UINT32 typeSize = 0;
+						if(curField->hasDynamicSize())
+							typeSize = curField->getDynamicSize(object);
+						else
+							typeSize = curField->getTypeSize();
 
 						if((*bytesWritten + typeSize) > bufferLength)
 						{
@@ -285,6 +295,7 @@ namespace CamelotEngine
 							*bytesWritten = 0;
 						}
 
+
 						curField->toBuffer(object, buffer);
 						buffer += typeSize;
 						*bytesWritten += typeSize;
@@ -396,7 +407,8 @@ namespace CamelotEngine
 			SerializableFieldType fieldType;
 			UINT16 fieldId;
 			UINT8 fieldSize;
-			decodeFieldMetaData(metaData, fieldId, fieldSize, isArray, fieldType);
+			bool hasDynamicSize;
+			decodeFieldMetaData(metaData, fieldId, fieldSize, isArray, fieldType, hasDynamicSize);
 			
 			RTTIField* curGenericField = nullptr;
 			
@@ -497,11 +509,15 @@ namespace CamelotEngine
 
 						for(int i = 0; i < arrayNumElems; i++)
 						{
+							UINT32 typeSize = fieldSize;
+							if(hasDynamicSize)
+								memcpy(&typeSize, data, sizeof(UINT32));
+
 							if(curField != nullptr)
 								curField->arrayElemFromBuffer(object, i, data);
 
-							data += fieldSize;
-							bytesRead += fieldSize;
+							data += typeSize;
+							bytesRead += typeSize;
 						}
 						break;
 					}
@@ -562,11 +578,15 @@ namespace CamelotEngine
 					{
 						RTTIPlainFieldBase* curField = static_cast<RTTIPlainFieldBase*>(curGenericField);
 
+						UINT32 typeSize = fieldSize;
+						if(hasDynamicSize)
+							memcpy(&typeSize, data, sizeof(UINT32));
+
 						if(curField != nullptr)
 							curField->fromBuffer(object, data);
 
-						data += fieldSize;
-						bytesRead += fieldSize;
+						data += typeSize;
+						bytesRead += typeSize;
 						break;
 					}
 				case SerializableFT_DataBlock:
@@ -721,10 +741,10 @@ namespace CamelotEngine
 		return objectSize;
 	}
 
-	UINT32 BinarySerializer::encodeFieldMetaData(UINT16 id, UINT8 size, bool array, SerializableFieldType type)
+	UINT32 BinarySerializer::encodeFieldMetaData(UINT16 id, UINT8 size, bool array, SerializableFieldType type, bool hasDynamicSize)
 	{
 		// If O == 0 - Meta contains field information (Encoded using this method)
-		//// Encoding: IIII IIII IIII IIII SSSS SSSS xxxP DCAO
+		//// Encoding: IIII IIII IIII IIII SSSS SSSS xxYP DCAO
 		//// I - Id
 		//// S - Size
 		//// C - Complex
@@ -732,15 +752,17 @@ namespace CamelotEngine
 		//// D - Data block
 		//// P - Complex ptr
 		//// O - Object descriptor
- 
+		//// Y - Simple field has dynamic size
+
 		return (id << 16 | size << 8 | 
 			(array ? 0x02 : 0) | 
 			((type == SerializableFT_DataBlock) ? 0x04 : 0) | 
 			((type == SerializableFT_Reflectable) ? 0x08 : 0) | 
-			((type == SerializableFT_ReflectablePtr) ? 0x10 : 0)); // TODO - Low priority. Technically I could encode this much more tightly, and use var-ints for ID
+			((type == SerializableFT_ReflectablePtr) ? 0x10 : 0) | 
+			(hasDynamicSize ? 0x20 : 0)); // TODO - Low priority. Technically I could encode this much more tightly, and use var-ints for ID
 	}
 
-	void BinarySerializer::decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array, SerializableFieldType& type)
+	void BinarySerializer::decodeFieldMetaData(UINT32 encodedData, UINT16& id, UINT8& size, bool& array, SerializableFieldType& type, bool& hasDynamicSize)
 	{
 		if(isObjectMetaData(encodedData))
 		{
@@ -748,6 +770,8 @@ namespace CamelotEngine
 				"Meta data represents an object description but is trying to be decoded as a field descriptor.");
 		}
 
+		hasDynamicSize = (encodedData & 0x20) != 0;
+
 		if((encodedData & 0x10) != 0)
 			type = SerializableFT_ReflectablePtr;
 		else if((encodedData & 0x08) != 0)