#include "PapyrusObjects.h" #include "Serialization.h" #include "PapyrusVM.h" #include namespace { static const size_t kMaxNameLen = 1024; } /// /// SKSEObjectRegistry /// void SKSEObjectRegistry::RegisterFactory(ISKSEObjectFactory * factory) { uintptr_t vtbl = *reinterpret_cast(factory); std::string className( factory->ClassName() ); factoryMap_[className] = vtbl; } const ISKSEObjectFactory* SKSEObjectRegistry::GetFactoryByName(const char* name) const { std::string t( name ); const ISKSEObjectFactory* result = NULL; FactoryMapT::const_iterator it = factoryMap_.find(t); if (it != factoryMap_.end()) { result = reinterpret_cast(&it->second); } return result; } /// /// SKSEPersistentObjectStorage /// void SKSEPersistentObjectStorage::CleanDroppedStacks() { VMClassRegistry* registry = (*g_skyrimVM)->GetClassRegistry(); for (UInt32 i=0; iGetStackInfo(e.owningStackId) != NULL) continue; // Stack no longer active, drop this entry delete e.obj; e.obj = NULL; freeIndices_.push_back(i); _MESSAGE("SKSEPersistentObjectStorage::CleanDroppedStacks: Freed object at index %d.", i); } } void SKSEPersistentObjectStorage::ClearAndRelease() { freeIndices_.clear(); for (DataT::iterator it = data_.begin(); it != data_.end(); ++it) { Entry& e = *it; if (e.obj != NULL) delete e.obj; } data_.clear(); } bool SKSEPersistentObjectStorage::Save(SKSESerializationInterface* intfc) { using namespace Serialization; // Before saving, purge entries whose owning stack is no longer running. // This can happen if someone forgot to release an object. // We don't want these resource leaks to pile up in the co-save. CleanDroppedStacks(); // Save data UInt32 dataSize = data_.size(); if (! WriteData(intfc, &dataSize)) return false; UInt32 filledSize = data_.size() - freeIndices_.size(); if (! WriteData(intfc, &filledSize)) return false; for (UInt32 i=0; i= data_.size()) { _MESSAGE("SKSEPersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); return NULL; } Entry& e = data_[index]; if (e.obj == NULL) { _MESSAGE("SKSEPersistentObjectStorage::AccessObject(%d): Object was NULL.", handle); return NULL; } ISKSEObject* result = e.obj; if (result == NULL) { _MESSAGE("SKSEPersistentObjectStorage::AccessObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); return NULL; } return result; } ISKSEObject* SKSEPersistentObjectStorage::Take(SInt32 handle) { IScopedCriticalSection scopedLock( &lock_ ); SInt32 index = handle - 1; if (index < 0 || index >= data_.size()) { _MESSAGE("SKSEPersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); return NULL; } Entry& e = data_[index]; if (e.obj == NULL) { _MESSAGE("SKSEPersistentObjectStorage::TakeObject(%d): Object was NULL.", handle); return NULL; } ISKSEObject* result = e.obj; if (result != NULL) { e.obj = NULL; freeIndices_.push_back(index); } else { _MESSAGE("SKSEPersistentObjectStorage::TakeObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); return NULL; } return result; } /// /// Serialization helpers /// bool WriteSKSEObject(SKSESerializationInterface* intfc, ISKSEObject* obj) { using namespace Serialization; const char* name = obj->ClassName(); const UInt32 version = obj->ClassVersion(); intfc->OpenRecord('OBJE', version); size_t rawLen = strlen(name); UInt32 len = (std::min)(rawLen, kMaxNameLen); if (! WriteData(intfc, &len)) return false; if (! intfc->WriteRecordData(name, len)) return false; return obj->Save(intfc); } bool ReadSKSEObject(SKSESerializationInterface* intfc, ISKSEObject*& objOut) { UInt32 type, length, objVersion; if (! intfc->GetNextRecordInfo(&type, &objVersion, &length)) return false; if (type != 'OBJE') { _MESSAGE("ReadSKSEObject: Error loading unexpected chunk type %08X (%.4s)", type, &type); return false; } // Read the name of the serialized class UInt32 len; if (! intfc->ReadRecordData(&len, sizeof(len))) return false; if (len > kMaxNameLen) { _MESSAGE("ReadSKSEObject: Serialization error. Class name len extended kMaxNameLen."); return false; } char buf[kMaxNameLen+1] = { 0 }; if (! intfc->ReadRecordData(&buf, len)) return false; // Get the factory const ISKSEObjectFactory* factory = SKSEObjectRegistryInstance().GetFactoryByName(buf); if (factory == NULL) { _MESSAGE("ReadSKSEObject: Serialization error. Factory missing for %s.", &buf); return false; } // Intantiate and load the actual data ISKSEObject* obj = factory->Create(); if (! obj->Load(intfc, objVersion)) { // Load failed. clean up. objOut = NULL; delete obj; return false; } objOut = obj; return true; } /// /// Global instances /// SKSEObjectRegistry& SKSEObjectRegistryInstance() { static SKSEObjectRegistry instance; return instance; } SKSEPersistentObjectStorage& SKSEObjectStorageInstance() { static SKSEPersistentObjectStorage instance; return instance; }