#pragma once #include #include #include #include "common/ICriticalSection.h" struct SKSESerializationInterface; class TESForm; class VMClassRegistry; // Serializable classes deriving from IStorableObject must define a // serialization constructor that takes SerializationTag as the single argument. // After it has been called, either Load() is used to fill in the data, // or in case the load failed, the instance is destroyed again. struct StorableSerializationTag {}; // IStorageObject: Interface for a heap-allocated, co-save-serializable object. // class IStorableObject { // Copy of IStorableObject public: virtual ~IStorableObject() {} virtual const char* ClassName() const = 0; virtual UInt32 ClassVersion() const = 0; // virtual bool Save(SKSESerializationInterface* intfc) = 0; virtual bool Load(SKSESerializationInterface* intfc, UInt32 version) = 0; }; class IStorableObjectFactory { // Copy of IStorableObjectFactory public: virtual ~IStorableObjectFactory() {} // virtual IStorableObject* Create() const = 0; }; template class ConcreteStorableObjectFactory : public IStorableObjectFactory { // Copy of ConcreteSKSEObjectFactory public: virtual IStorableObject* Create() const { StorableSerializationTag tag; return new T(tag); } }; class StorableObjectRegistry { // Copy of SKSEObjectRegistry private: typedef std::map FactoryMapT; public: template bool RegisterClass() { ConcreteStorableObjectFactory factory; UInt32 vtbl = *reinterpret_cast(&factory); // StorableSerializationTag tag; T tempInstance(tag); // std::string className(tempInstance.ClassName()); // factoryMap_[className] = vtbl; // return true; } const IStorableObjectFactory* GetFactoryByName(const char* name) const; private: FactoryMapT factoryMap_; // Stores the vtables directly }; class StorablePersistentObjectStorage { // Copy of SKSEPersistentObjectStorage // Note: handle = index + 1 // +1, because in previous versions the invalid handle was 0 so people // might test for != 0 private: struct Entry { IStorableObject* obj; UInt32 owningStackId; }; typedef std::vector DataT; typedef std::vector IndexCacheT; // ICriticalSection lock_; DataT data_; IndexCacheT freeIndices_; public: template SInt32 StoreObject(T* obj, UInt32 owningStackId) { // Transfer ownership of object to the registry IScopedCriticalSection scopedLock( &lock_ ); Entry e = { obj, owningStackId }; SInt32 index; if (freeIndices_.empty()) { index = data_.size(); data_.push_back(e); } else { index = freeIndices_.back(); freeIndices_.pop_back(); data_[index] = e; } return index + 1; }; // template T* AccessObject(SInt32 handle) { // Access object while keeping it in storage. IScopedCriticalSection scopedLock(&lock_); // SInt32 index = handle - 1; if (index < 0 || index >= data_.size()) { _MESSAGE("StorablePersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); return nullptr; } Entry& e = data_[index]; if (e.obj == nullptr) { _MESSAGE("StorablePersistentObjectStorage::AccessObject(%d): Object was NULL.", handle); return nullptr; } T* result = dynamic_cast(e.obj); if (result == nullptr) { _MESSAGE("StorablePersistentObjectStorage::AccessObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); return nullptr; } return result; }; // template T* TakeObject(SInt32 handle) { // Remove object from storage and take ownership of the pointer. IScopedCriticalSection scopedLock( &lock_ ); // SInt32 index = handle - 1; if (index < 0 || index >= data_.size()) { _MESSAGE("StorablePersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); return nullptr; } Entry& e = data_[index]; if (e.obj == nullptr) { _MESSAGE("StorablePersistentObjectStorage::TakeObject(%d): Object was NULL.", handle); return nullptr; } T* result = dynamic_cast(e.obj); if (result != nullptr) { e.obj = nullptr; freeIndices_.push_back(index); } else { _MESSAGE("StorablePersistentObjectStorage::TakeObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); return nullptr; } return result; }; // void CleanDroppedStacks(); void ClearAndRelease(); // enum { kSaveVersion = 2 }; bool Save(SKSESerializationInterface* intfc); bool Load(SKSESerializationInterface* intfc, UInt32 version); }; // Serialization helpers: // bool WriteStorableObject(SKSESerializationInterface* intfc, IStorableObject* obj); bool ReadStorableObject(SKSESerializationInterface* intfc, IStorableObject*& objOut); // Global instances: // StorableObjectRegistry& StorableObjectRegistryInstance(); StorablePersistentObjectStorage& StorableObjectStorageInstance();