diff options
Diffstat (limited to 'Source/Scripting/angelscript/add_on/serializer/serializer.cpp')
-rw-r--r-- | Source/Scripting/angelscript/add_on/serializer/serializer.cpp | 548 |
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 |