Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/WolfireGames/overgrowth.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Scripting/angelscript/add_on/serializer/serializer.cpp')
-rw-r--r--Source/Scripting/angelscript/add_on/serializer/serializer.cpp548
1 files changed, 548 insertions, 0 deletions
diff --git a/Source/Scripting/angelscript/add_on/serializer/serializer.cpp b/Source/Scripting/angelscript/add_on/serializer/serializer.cpp
new file mode 100644
index 00000000..a359fcdd
--- /dev/null
+++ b/Source/Scripting/angelscript/add_on/serializer/serializer.cpp
@@ -0,0 +1,548 @@
+//
+// CSerializer
+//
+// This code was based on the CScriptReloader written by FDsagizi
+// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
+//
+
+#include <assert.h>
+#include <string.h> // strstr
+#include <stdio.h> // sprintf
+#include "serializer.h"
+
+using namespace std;
+
+BEGIN_AS_NAMESPACE
+
+///////////////////////////////////////////////////////////////////////////////////
+
+CSerializer::CSerializer()
+{
+ m_engine = 0;
+}
+
+CSerializer::~CSerializer()
+{
+ // Extra objects need to be released, since they are not stored in
+ // the module and we cannot rely on the application releasing them
+ for( size_t i = 0; i < m_extraObjects.size(); i++ )
+ {
+ SExtraObject &o = m_extraObjects[i];
+ for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
+ {
+ if( m_root.m_children[i2]->m_originalPtr == o.originalObject && m_root.m_children[i2]->m_restorePtr )
+ reinterpret_cast<asIScriptObject*>(m_root.m_children[i2]->m_restorePtr)->Release();
+ }
+ }
+
+ // Clean the serialized values before we remove the user types
+ m_root.Uninit();
+
+ // Delete the user types
+ std::map<std::string, CUserType*>::iterator it;
+ for( it = m_userTypes.begin(); it != m_userTypes.end(); it++ )
+ delete it->second;
+
+ if( m_engine )
+ m_engine->Release();
+}
+
+void CSerializer::AddUserType(CUserType *ref, const std::string &name)
+{
+ m_userTypes[name] = ref;
+}
+
+int CSerializer::Store(asIScriptModule *mod)
+{
+ m_mod = mod;
+
+ // The engine must not be destroyed before we're completed, so we'll hold on to a reference
+ mod->GetEngine()->AddRef();
+ if( m_engine ) m_engine->Release();
+ m_engine = mod->GetEngine();
+
+ m_root.m_serializer = this;
+
+ // First store global variables
+ asUINT i;
+ for( i = 0; i < mod->GetGlobalVarCount(); i++ )
+ {
+ const char *name, *nameSpace;
+ int typeId;
+ mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
+ m_root.m_children.push_back(new CSerializedValue(&m_root, name, nameSpace, mod->GetAddressOfGlobalVar(i), typeId));
+ }
+
+ // Second store extra objects
+ for( i = 0; i < m_extraObjects.size(); i++ )
+ m_root.m_children.push_back(new CSerializedValue(&m_root, "", "", m_extraObjects[i].originalObject, m_extraObjects[i].originalTypeId));
+
+ // For the handles that were stored, we need to substitute the stored pointer
+ // that is still pointing to the original object to an internal reference so
+ // it can be restored later on.
+ m_root.ReplaceHandles();
+
+ return 0;
+}
+
+// Retrieve all global variables after reload script.
+int CSerializer::Restore(asIScriptModule *mod)
+{
+ m_mod = mod;
+
+ // The engine must not be destroyed before we're completed, so we'll hold on to a reference
+ mod->GetEngine()->AddRef();
+ if( m_engine ) m_engine->Release();
+ m_engine = mod->GetEngine();
+
+ // First restore extra objects, i.e. the ones that are not directly seen from the module's global variables
+ asUINT i;
+ for( i = 0; i < m_extraObjects.size(); i++ )
+ {
+ SExtraObject &o = m_extraObjects[i];
+ asITypeInfo *type = m_mod->GetTypeInfoByName( o.originalClassName.c_str() );
+ if( type )
+ {
+ for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
+ {
+ if( m_root.m_children[i2]->m_originalPtr == o.originalObject )
+ {
+ // Create a new script object, but don't call its constructor as we will initialize the members.
+ // Calling the constructor may have unwanted side effects if for example the constructor changes
+ // any outside entities, such as setting global variables to point to new objects, etc.
+ void *newPtr = m_engine->CreateUninitializedScriptObject( type );
+ m_root.m_children[i2]->Restore( newPtr, type->GetTypeId() );
+ }
+ }
+ }
+ }
+
+ // Second restore the global variables
+ asUINT varCount = mod->GetGlobalVarCount();
+ for( i = 0; i < varCount; i++ )
+ {
+ const char *name, *nameSpace;
+ int typeId;
+ mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
+
+ CSerializedValue *v = m_root.FindByName(name, nameSpace);
+ if( v )
+ v->Restore(mod->GetAddressOfGlobalVar(i), typeId);
+ }
+
+ // The handles that were restored needs to be
+ // updated to point to their final objects.
+ m_root.RestoreHandles();
+
+ return 0;
+}
+
+void *CSerializer::GetPointerToRestoredObject(void *ptr)
+{
+ return m_root.GetPointerToRestoredObject( ptr );
+}
+
+void CSerializer::AddExtraObjectToStore( asIScriptObject *object )
+{
+ if( !object )
+ return;
+
+ // Check if the object hasn't been included already
+ for( size_t i=0; i < m_extraObjects.size(); i++ )
+ if( m_extraObjects[i].originalObject == object )
+ return;
+
+ SExtraObject o;
+ o.originalObject = object;
+ o.originalClassName = object->GetObjectType()->GetName();
+ o.originalTypeId = object->GetTypeId();
+
+ m_extraObjects.push_back( o );
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+CSerializedValue::CSerializedValue()
+{
+ Init();
+}
+
+CSerializedValue::CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId)
+{
+ Init();
+
+ m_name = name;
+ m_nameSpace = nameSpace;
+ m_serializer = parent->m_serializer;
+ Store(ref, typeId);
+}
+
+void CSerializedValue::Init()
+{
+ m_handlePtr = 0;
+ m_restorePtr = 0;
+ m_typeId = 0;
+ m_isInit = false;
+ m_serializer = 0;
+ m_userData = 0;
+ m_originalPtr = 0;
+}
+
+void CSerializedValue::Uninit()
+{
+ m_isInit = false;
+
+ ClearChildren();
+
+ if( m_userData )
+ {
+ CUserType *type = m_serializer->m_userTypes[m_typeName];
+ if( type )
+ type->CleanupUserData(this);
+ m_userData = 0;
+ }
+}
+
+void CSerializedValue::ClearChildren()
+{
+ // If this value is for an object handle that created an object during the restore
+ // then it is necessary to release the handle here, so we won't get a memory leak
+ if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 && m_children[0]->m_restorePtr )
+ {
+ m_serializer->m_engine->ReleaseScriptObject(m_children[0]->m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_children[0]->m_typeId));
+ }
+
+ for( size_t n = 0; n < m_children.size(); n++ )
+ delete m_children[n];
+ m_children.clear();
+}
+
+CSerializedValue::~CSerializedValue()
+{
+ Uninit();
+}
+
+CSerializedValue *CSerializedValue::FindByName(const std::string &name, const std::string &nameSpace)
+{
+ for( size_t i = 0; i < m_children.size(); i++ )
+ if( m_children[i]->m_name == name &&
+ m_children[i]->m_nameSpace == nameSpace )
+ return m_children[i];
+
+ return 0;
+}
+
+void CSerializedValue::GetAllPointersOfChildren(std::vector<void*> *ptrs)
+{
+ ptrs->push_back(m_originalPtr);
+
+ for( size_t i = 0; i < m_children.size(); ++i )
+ m_children[i]->GetAllPointersOfChildren(ptrs);
+}
+
+CSerializedValue *CSerializedValue::FindByPtr(void *ptr)
+{
+ if( m_originalPtr == ptr )
+ return this;
+
+ for( size_t i = 0; i < m_children.size(); i++ )
+ {
+ CSerializedValue *find = m_children[i]->FindByPtr(ptr);
+ if( find )
+ return find;
+ }
+
+ return 0;
+}
+
+void *CSerializedValue::GetPointerToRestoredObject(void *ptr)
+{
+ if( m_originalPtr == ptr )
+ return m_restorePtr;
+
+ for( size_t i = 0; i < m_children.size(); ++i )
+ {
+ void *ret = m_children[i]->GetPointerToRestoredObject(ptr);
+ if( ret )
+ return ret;
+ }
+
+ return 0;
+}
+
+// find variable by ptr but looking only at those in the references, which will create a new object
+CSerializedValue *CSerializedValue::FindByPtrInHandles(void *ptr)
+{
+ // if this handle created object
+ if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 )
+ {
+ if( m_children[0]->m_originalPtr == ptr )
+ return this;
+ }
+
+ if( !(m_typeId & asTYPEID_OBJHANDLE) )
+ {
+ for( size_t i = 0; i < m_children.size(); i++ )
+ {
+ CSerializedValue *find = m_children[i]->FindByPtrInHandles(ptr);
+ if( find )
+ return find;
+ }
+ }
+
+ return 0;
+}
+
+void CSerializedValue::Store(void *ref, int typeId)
+{
+ m_isInit = true;
+ SetType(typeId);
+ m_originalPtr = ref;
+
+ if( m_typeId & asTYPEID_OBJHANDLE )
+ {
+ m_handlePtr = *(void**)ref;
+ }
+ else if( m_typeId & asTYPEID_SCRIPTOBJECT )
+ {
+ asIScriptObject *obj = (asIScriptObject *)ref;
+ asITypeInfo *type = obj->GetObjectType();
+ SetType(type->GetTypeId());
+
+ // Store children
+ for( asUINT i = 0; i < type->GetPropertyCount(); i++ )
+ {
+ int childId;
+ const char *childName;
+ type->GetProperty(i, &childName, &childId);
+
+ m_children.push_back(new CSerializedValue(this, childName, "", obj->GetAddressOfProperty(i), childId));
+ }
+ }
+ else
+ {
+ int size = m_serializer->m_engine->GetSizeOfPrimitiveType(m_typeId);
+
+ if( size == 0 )
+ {
+ // if it is user type( string, array, etc ... )
+ if( m_serializer->m_userTypes[m_typeName] )
+ m_serializer->m_userTypes[m_typeName]->Store(this, m_originalPtr);
+ else
+ {
+ // POD-types can be stored without need for user type
+ asITypeInfo *type = GetType();
+ if( type && (type->GetFlags() & asOBJ_POD) )
+ size = GetType()->GetSize();
+
+ // It is not necessary to report an error here if it is not a POD-type as that will be done when restoring
+ }
+ }
+
+ if( size )
+ {
+ m_mem.resize(size);
+ memcpy(&m_mem[0], ref, size);
+ }
+ }
+}
+
+void CSerializedValue::Restore(void *ref, int typeId)
+{
+ if( !this || !m_isInit || !ref )
+ return;
+
+ // Verify that the stored type matched the new type of the value being restored
+ if( typeId <= asTYPEID_DOUBLE && typeId != m_typeId ) return; // TODO: We may try to do a type conversion for primitives
+ if( (typeId & ~asTYPEID_MASK_SEQNBR) ^ (m_typeId & ~asTYPEID_MASK_SEQNBR) ) return;
+ asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
+ if( type && m_typeName != type->GetName() ) return;
+
+ // Set the new pointer and type
+ m_restorePtr = ref;
+ SetType(typeId);
+
+ // Restore the value
+ if( m_typeId & asTYPEID_OBJHANDLE )
+ {
+ // if need create objects
+ if( m_children.size() == 1 )
+ {
+ asITypeInfo *ctype = m_children[0]->GetType();
+
+ if( ctype->GetFactoryCount() == 0 )
+ {
+ // There are no factories, so assume the same pointer is going to be used
+ m_children[0]->m_restorePtr = m_handlePtr;
+
+ // Increase the refCount for the object as it will be released upon clean-up
+ m_serializer->m_engine->AddRefScriptObject(m_handlePtr, ctype);
+ }
+ else
+ {
+ // Create a new script object, but don't call its constructor as we will initialize the members.
+ // Calling the constructor may have unwanted side effects if for example the constructor changes
+ // any outside entities, such as setting global variables to point to new objects, etc.
+ void *newObject = m_serializer->m_engine->CreateUninitializedScriptObject(ctype);
+ m_children[0]->Restore(newObject, ctype->GetTypeId());
+ }
+ }
+ }
+ else if( m_typeId & asTYPEID_SCRIPTOBJECT )
+ {
+ asIScriptObject *obj = (asIScriptObject *)ref;
+
+ // Retrieve children
+ for( asUINT i = 0; i < type->GetPropertyCount() ; i++ )
+ {
+ const char *nameProperty;
+ int ptypeId;
+ type->GetProperty(i, &nameProperty, &ptypeId);
+
+ CSerializedValue *var = FindByName(nameProperty, "");
+ if( var )
+ var->Restore(obj->GetAddressOfProperty(i), ptypeId);
+ }
+ }
+ else
+ {
+ if( m_mem.size() )
+ {
+ // POD values can be restored with direct copy
+ memcpy(ref, &m_mem[0], m_mem.size());
+ }
+ else if( m_serializer->m_userTypes[m_typeName] )
+ {
+ // user type restore
+ m_serializer->m_userTypes[m_typeName]->Restore(this, m_restorePtr);
+ }
+ else
+ {
+ std::string str = "Cannot restore type '";
+ str += type->GetName();
+ str += "'";
+ m_serializer->m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.c_str());
+ }
+ }
+}
+
+void CSerializedValue::CancelDuplicates(CSerializedValue *from)
+{
+ std::vector<void*> ptrs;
+ from->GetAllPointersOfChildren(&ptrs);
+
+ for( size_t i = 0; i < ptrs.size(); ++i )
+ {
+ CSerializedValue *find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
+
+ while( find )
+ {
+ // cancel create object
+ find->ClearChildren();
+
+ // Find next link to this ptr
+ find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
+ }
+ }
+}
+
+void CSerializedValue::ReplaceHandles()
+{
+ if( m_handlePtr )
+ {
+ // Find the object that the handle is referring to
+ CSerializedValue *handle_to = m_serializer->m_root.FindByPtr(m_handlePtr);
+
+ // If the object hasn't been stored yet...
+ if( handle_to == 0 )
+ {
+ // Store the object now
+ asITypeInfo *type = GetType();
+ CSerializedValue *need_create = new CSerializedValue(this, m_name, m_nameSpace, m_handlePtr, type->GetTypeId());
+
+ // Make sure all other handles that point to the same object
+ // are updated, so we don't end up creating duplicates
+ CancelDuplicates(need_create);
+
+ m_children.push_back(need_create);
+ }
+ }
+
+ // Replace the handles in the children too
+ for( size_t i = 0; i < m_children.size(); ++i )
+ m_children[i]->ReplaceHandles();
+}
+
+void CSerializedValue::RestoreHandles()
+{
+ if( m_typeId & asTYPEID_OBJHANDLE )
+ {
+ if( m_handlePtr )
+ {
+ // Find the object the handle is supposed to point to
+ CSerializedValue *handleTo = m_serializer->m_root.FindByPtr(m_handlePtr);
+
+ if( m_restorePtr && handleTo && handleTo->m_restorePtr )
+ {
+ asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(m_typeId);
+
+ // If the handle is already pointing to something it must be released first
+ if( *(void**)m_restorePtr )
+ m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, type);
+
+ // Update the internal pointer
+ *(void**)m_restorePtr = handleTo->m_restorePtr;
+
+ // Increase the reference
+ m_serializer->m_engine->AddRefScriptObject(handleTo->m_restorePtr, type);
+ }
+ }
+ else
+ {
+ // If the handle is pointing to something, we must release it to restore the null pointer
+ if( m_restorePtr && *(void**)m_restorePtr )
+ {
+ m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_typeId));
+ *(void**)m_restorePtr = 0;
+ }
+ }
+ }
+
+ // Do the same for the children
+ for( size_t i = 0; i < m_children.size(); ++i )
+ m_children[i]->RestoreHandles();
+}
+
+void CSerializedValue::SetType(int typeId)
+{
+ m_typeId = typeId;
+
+ asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
+
+ if( type )
+ m_typeName = type->GetName();
+}
+
+asITypeInfo *CSerializedValue::GetType()
+{
+ if( !m_typeName.empty() )
+ {
+ int newTypeId = m_serializer->m_mod->GetTypeIdByDecl(m_typeName.c_str());
+ return m_serializer->m_engine->GetTypeInfoById(newTypeId);
+ }
+
+ return 0;
+}
+
+void CSerializedValue::SetUserData(void *data)
+{
+ m_userData = data;
+}
+
+void *CSerializedValue::GetUserData()
+{
+ return m_userData;
+}
+
+END_AS_NAMESPACE