From 05d9979ab34f0e621df661218a4e7efbcc77e7a3 Mon Sep 17 00:00:00 2001 From: ozooma10 <98544147+ozooma10@users.noreply.github.com> Date: Thu, 11 Jun 2026 04:12:26 -0400 Subject: [PATCH] Add SFSE Serialization Interface --- include/RE/IDs.h | 4 +- include/SFSE/API.h | 9 ++-- include/SFSE/Interfaces.h | 104 ++++++++++++++++++++++++++++++++++++++ src/SFSE/API.cpp | 15 ++++-- src/SFSE/Interfaces.cpp | 50 ++++++++++++++++++ 5 files changed, 172 insertions(+), 10 deletions(-) diff --git a/include/RE/IDs.h b/include/RE/IDs.h index 0c42e487..2b3365c7 100644 --- a/include/RE/IDs.h +++ b/include/RE/IDs.h @@ -2259,12 +2259,12 @@ namespace RE::ID namespace TESFormDeleteEvent { - inline constexpr REL::ID GetEventSource{ 0 }; // 107166 + inline constexpr REL::ID GetEventSource{ 64137 }; // 107166 } namespace TESFormIDRemapEvent { - inline constexpr REL::ID GetEventSource{ 0 }; // 107167 + inline constexpr REL::ID GetEventSource{ 64138 }; // 107167 } namespace TESFurnitureEvent diff --git a/include/SFSE/API.h b/include/SFSE/API.h index dfd10f64..9156d26c 100644 --- a/include/SFSE/API.h +++ b/include/SFSE/API.h @@ -41,10 +41,11 @@ namespace SFSE [[nodiscard]] const PluginInfo* GetPluginInfo(std::string_view a_plugin) noexcept; [[nodiscard]] std::uint32_t GetSFSEVersion() noexcept; - [[nodiscard]] const TrampolineInterface* GetTrampolineInterface() noexcept; - [[nodiscard]] const MessagingInterface* GetMessagingInterface() noexcept; - [[nodiscard]] const MenuInterface* GetMenuInterface() noexcept; - [[nodiscard]] const TaskInterface* GetTaskInterface() noexcept; + [[nodiscard]] const TrampolineInterface* GetTrampolineInterface() noexcept; + [[nodiscard]] const MessagingInterface* GetMessagingInterface() noexcept; + [[nodiscard]] const MenuInterface* GetMenuInterface() noexcept; + [[nodiscard]] const TaskInterface* GetTaskInterface() noexcept; + [[nodiscard]] const SerializationInterface* GetSerializationInterface() noexcept; } namespace SFSE diff --git a/include/SFSE/Interfaces.h b/include/SFSE/Interfaces.h index aa2ed8b1..9ab8228e 100644 --- a/include/SFSE/Interfaces.h +++ b/include/SFSE/Interfaces.h @@ -53,6 +53,23 @@ namespace SFSE void (*Register)(void*); }; + struct SFSESerializationInterface + { + std::uint32_t version; + void (*SetUniqueID)(PluginHandle, std::uint32_t); + void (*SetRevertCallback)(PluginHandle, void*); + void (*SetSaveCallback)(PluginHandle, void*); + void (*SetLoadCallback)(PluginHandle, void*); + void (*SetFormDeleteCallback)(PluginHandle, void*); + bool (*WriteRecord)(std::uint32_t, std::uint32_t, const void*, std::uint32_t); + bool (*OpenRecord)(std::uint32_t, std::uint32_t); + bool (*WriteRecordData)(const void*, std::uint32_t); + bool (*GetNextRecordInfo)(std::uint32_t*, std::uint32_t*, std::uint32_t*); + std::uint32_t (*ReadRecordData)(void*, std::uint32_t); + bool (*ResolveHandle)(std::uint64_t, std::uint64_t*); + bool (*ResolveFormID)(std::uint32_t, std::uint32_t*); + }; + struct SFSETaskInterface { std::uint32_t interfaceVersion; @@ -121,6 +138,7 @@ namespace SFSE kTrampoline, kMenu, kTask, + kSerialization, kTotal }; @@ -215,6 +233,92 @@ namespace SFSE void Register(RegCallback* a_callback) const; }; + class SerializationInterface + { + private: + [[nodiscard]] decltype(auto) GetProxy() const noexcept + { + return reinterpret_cast(*this); + } + + public: + enum Version : std::uint32_t + { + kVersion = 1 + }; + + using EventCallback = std::add_pointer_t; + using FormDeleteCallback = std::add_pointer_t; + + [[nodiscard]] std::uint32_t Version() const noexcept { return GetProxy().version; } + + void SetUniqueID(std::uint32_t a_uid) const; + void SetRevertCallback(EventCallback a_callback) const; + void SetSaveCallback(EventCallback a_callback) const; + void SetLoadCallback(EventCallback a_callback) const; + void SetFormDeleteCallback(FormDeleteCallback a_callback) const; + + bool WriteRecord(std::uint32_t a_type, std::uint32_t a_version, const void* a_buf, std::uint32_t a_length) const; + bool OpenRecord(std::uint32_t a_type, std::uint32_t a_version) const; + bool WriteRecordData(const void* a_buf, std::uint32_t a_length) const; + + template + requires(std::negation_v>) + bool WriteRecordData(const T& a_buf) const + { + return WriteRecordData(std::addressof(a_buf), sizeof(T)); + } + + template + bool WriteRecordData(const T (&a_buf)[N]) const + { + return WriteRecordData(std::addressof(a_buf), sizeof(T) * N); + } + + bool GetNextRecordInfo(std::uint32_t& a_type, std::uint32_t& a_version, std::uint32_t& a_length) const; + std::uint32_t ReadRecordData(void* a_buf, std::uint32_t a_length) const; + + template + requires(std::negation_v>) + std::uint32_t ReadRecordData(T& a_buf) const + { + return ReadRecordData(std::addressof(a_buf), sizeof(T)); + } + + template + requires(std::negation_v>) + std::uint32_t ReadRecordDataEx(std::uint32_t& a_length, T& a_buf) const + { + a_length -= sizeof(T); + return ReadRecordData(std::addressof(a_buf), sizeof(T)); + } + + template + std::uint32_t ReadRecordData(T (&a_buf)[N]) const + { + return ReadRecordData(std::addressof(a_buf), sizeof(T) * N); + } + + template + std::uint32_t ReadRecordDataEx(std::uint32_t& a_length, T (&a_buf)[N]) const + { + a_length -= sizeof(T) * N; + return ReadRecordData(std::addressof(a_buf), sizeof(T) * N); + } + + [[nodiscard]] std::optional ResolveHandle(std::uint64_t a_handle) const + { + std::uint64_t result{ 0 }; + return GetProxy().ResolveHandle(a_handle, std::addressof(result)) ? std::optional{ result } : std::nullopt; + } + + [[nodiscard]] std::optional ResolveFormID(std::uint32_t a_formID) const + { + std::uint32_t result{ 0 }; + return GetProxy().ResolveFormID(a_formID, std::addressof(result)) ? std::optional{ result } : std::nullopt; + } + }; + class ITaskDelegate { public: diff --git a/src/SFSE/API.cpp b/src/SFSE/API.cpp index 57da83e9..6ff8ce84 100644 --- a/src/SFSE/API.cpp +++ b/src/SFSE/API.cpp @@ -31,10 +31,11 @@ namespace SFSE PluginHandle pluginHandle{ static_cast(-1) }; std::function pluginInfoAccessor; - TrampolineInterface* trampolineInterface{ nullptr }; - MessagingInterface* messagingInterface{ nullptr }; - MenuInterface* menuInterface{ nullptr }; - TaskInterface* taskInterface{ nullptr }; + TrampolineInterface* trampolineInterface{ nullptr }; + MessagingInterface* messagingInterface{ nullptr }; + MenuInterface* menuInterface{ nullptr }; + TaskInterface* taskInterface{ nullptr }; + SerializationInterface* serializationInterface{ nullptr }; std::mutex apiLock; std::vector> apiInitRegs; @@ -158,6 +159,7 @@ namespace SFSE api->trampolineInterface = a_intfc->QueryInterface(LoadInterface::kTrampoline); api->menuInterface = a_intfc->QueryInterface(LoadInterface::kMenu); api->taskInterface = a_intfc->QueryInterface(LoadInterface::kTask); + api->serializationInterface = a_intfc->QueryInterface(LoadInterface::kSerialization); const std::scoped_lock lock{ api->apiLock }; if (!api->apiInit) { @@ -243,6 +245,11 @@ namespace SFSE { return Impl::API::GetSingleton()->taskInterface; } + + const SerializationInterface* GetSerializationInterface() noexcept + { + return Impl::API::GetSingleton()->serializationInterface; + } } namespace SFSE diff --git a/src/SFSE/Interfaces.cpp b/src/SFSE/Interfaces.cpp index 0c642e8a..0ca1d65e 100644 --- a/src/SFSE/Interfaces.cpp +++ b/src/SFSE/Interfaces.cpp @@ -39,6 +39,56 @@ namespace SFSE return GetProxy().Register(reinterpret_cast(a_callback)); } + void SerializationInterface::SetUniqueID(const std::uint32_t a_uid) const + { + GetProxy().SetUniqueID(GetPluginHandle(), a_uid); + } + + void SerializationInterface::SetRevertCallback(const EventCallback a_callback) const + { + GetProxy().SetRevertCallback(GetPluginHandle(), std::bit_cast(a_callback)); + } + + void SerializationInterface::SetSaveCallback(const EventCallback a_callback) const + { + GetProxy().SetSaveCallback(GetPluginHandle(), std::bit_cast(a_callback)); + } + + void SerializationInterface::SetLoadCallback(const EventCallback a_callback) const + { + GetProxy().SetLoadCallback(GetPluginHandle(), std::bit_cast(a_callback)); + } + + void SerializationInterface::SetFormDeleteCallback(const FormDeleteCallback a_callback) const + { + GetProxy().SetFormDeleteCallback(GetPluginHandle(), std::bit_cast(a_callback)); + } + + bool SerializationInterface::WriteRecord(const std::uint32_t a_type, const std::uint32_t a_version, const void* a_buf, const std::uint32_t a_length) const + { + return GetProxy().WriteRecord(a_type, a_version, a_buf, a_length); + } + + bool SerializationInterface::OpenRecord(const std::uint32_t a_type, const std::uint32_t a_version) const + { + return GetProxy().OpenRecord(a_type, a_version); + } + + bool SerializationInterface::WriteRecordData(const void* a_buf, const std::uint32_t a_length) const + { + return GetProxy().WriteRecordData(a_buf, a_length); + } + + bool SerializationInterface::GetNextRecordInfo(std::uint32_t& a_type, std::uint32_t& a_version, std::uint32_t& a_length) const + { + return GetProxy().GetNextRecordInfo(std::addressof(a_type), std::addressof(a_version), std::addressof(a_length)); + } + + std::uint32_t SerializationInterface::ReadRecordData(void* a_buf, const std::uint32_t a_length) const + { + return GetProxy().ReadRecordData(a_buf, a_length); + } + const PluginVersionData* PluginVersionData::GetSingleton() noexcept { return reinterpret_cast(REX::W32::GetProcAddress(REX::W32::GetCurrentModule(), "SFSEPlugin_Version"));