Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
scriptName DynamicPersistentForms hidden
scriptName DynamicPersistentForms hidden

; Creates a new form that is a copy of given base form, changes to that form will be persisted in the save game.
Form function Create(Form item) global native
; Creates a new empty form of the requested FormType and lets DynamicPersistentForms own its persistent FormID slot.
Form function CreateByType(int formType) global native
; Returns or creates a persistent form using a known localId from Dynamic Persistent Forms.esp.
Form function GetOrCreateByLocalId(int localId, int formType) global native
; Returns or creates a persistent form using a form whose ID belongs to Dynamic Persistent Forms.esp.
Form function GetOrCreateByFormId(Form formIdSource, int formType) global native
; Creates or returns a form owned by owner/key.
Form function GetOrCreateByOwnerKey(String owner, String key, int formType) global native
; Releases one owned slot by owner/key.
bool function ReleaseByOwnerKey(String owner, String key) global native
; Releases one slot by localId if owner matches.
bool function ReleaseByLocalId(int localId, String owner) global native
; Releases all slots owned by owner and returns count.
int function ReleaseOwner(String owner) global native
; Dispose a form that was created using the previous function.
function Dispose(Form item) global native

Expand Down Expand Up @@ -44,4 +58,7 @@ function SetAmmoProjectile(Ammo ammo, Projectile projectile) global native
; Grand = 5
function SetSoulGemCapacity(SoulGem gem, int capacity) global native
function SetSoulGemCurrentSoul(SoulGem gem, int capacity) global native
function LinkSoulGems(SoulGem empty, SoulGem filled) global native
function LinkSoulGems(SoulGem empty, SoulGem filled) global native



21 changes: 19 additions & 2 deletions PapyrusInterface/Source/Scripts/DynamicPersistentForms.psc
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
scriptName DynamicPersistentForms hidden
scriptName DynamicPersistentForms hidden

; Creates a new form that is a copy of given base form, changes to that form will be persisted in the save game.
Form function Create(Form item) global native
; Creates a new empty form of the requested FormType and lets DynamicPersistentForms own its persistent FormID slot.
Form function CreateByType(int formType) global native
; Returns or creates a persistent form using a known localId from Dynamic Persistent Forms.esp.
Form function GetOrCreateByLocalId(int localId, int formType) global native
; Returns or creates a persistent form using a form whose ID belongs to Dynamic Persistent Forms.esp.
Form function GetOrCreateByFormId(Form formIdSource, int formType) global native
; Creates or returns a form owned by owner/key.
Form function GetOrCreateByOwnerKey(String owner, String key, int formType) global native
; Releases one owned slot by owner/key.
bool function ReleaseByOwnerKey(String owner, String key) global native
; Releases one slot by localId if owner matches.
bool function ReleaseByLocalId(int localId, String owner) global native
; Releases all slots owned by owner and returns count.
int function ReleaseOwner(String owner) global native
; Dispose a form that was created using the previous function.
function Dispose(Form item) global native

Expand Down Expand Up @@ -44,4 +58,7 @@ function SetAmmoProjectile(Ammo ammo, Projectile projectile) global native
; Grand = 5
function SetSoulGemCapacity(SoulGem gem, int capacity) global native
function SetSoulGemCurrentSoul(SoulGem gem, int capacity) global native
function LinkSoulGems(SoulGem empty, SoulGem filled) global native
function LinkSoulGems(SoulGem empty, SoulGem filled) global native



35 changes: 16 additions & 19 deletions SKSE_Plugin/include/Services.h
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
#pragma once

#include "../public/DPFAPI.h"
#include <mutex>

namespace DPF {
// Defina um ID único para sua interface
constexpr auto InterfaceName = "DynamicPersistentForms";
constexpr uint32_t InterfaceVersion = 1;
namespace Services {
inline std::mutex serviceMutex;

class IDynamicPersistentForms {
public:
virtual ~IDynamicPersistentForms() = default;
virtual uint32_t GetVersion() const = 0;
RE::TESForm* Create(RE::TESForm* baseItem);

RE::TESForm* CreateByType(uint32_t formType);

virtual RE::TESForm* Create(RE::TESForm* baseItem) = 0;
virtual void Dispose(RE::TESForm* form) = 0;
virtual void Track(RE::TESForm* item) = 0;
virtual void UnTrack(RE::TESForm* item) = 0;
};
}
RE::TESForm* GetOrCreateByLocalId(uint32_t localId, uint32_t formType);

namespace Services {
// Mutex global para thread safety
inline std::mutex serviceMutex;
RE::TESForm* GetOrCreateByFormId(RE::FormID formId, uint32_t formType);

RE::TESForm* Create(RE::TESForm* baseItem);
RE::TESForm* GetOrCreateByOwnerKey(const char* owner, const char* key, uint32_t formType, uint32_t* localId, bool* existed);

bool ReleaseByOwnerKey(const char* owner, const char* key);

bool ReleaseByLocalId(uint32_t localId, const char* owner);

uint32_t ReleaseOwner(const char* owner);

void Track(RE::TESForm* baseItem);

void UnTrack(RE::TESForm* form);

void Dispose(RE::TESForm* form);
}
}
15 changes: 14 additions & 1 deletion SKSE_Plugin/include/form.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,18 @@ void copyComponent(RE::TESForm* from, RE::TESForm* to) {

void copyAppearence(RE::TESForm* source, RE::TESForm* target);

RE::TESForm* AddForm(RE::TESForm* baseItem);

RE::TESForm* AddForm(RE::TESForm* baseItem);
RE::TESForm* AddFormByType(RE::FormType formType);

RE::TESForm* GetOrCreateFormByLocalId(uint32_t localId, RE::FormType formType);

RE::TESForm* GetOrCreateFormByFormId(RE::FormID formId, RE::FormType formType);

RE::TESForm* GetOrCreateFormByOwnerKey(const char* owner, const char* key, RE::FormType formType, uint32_t* localId, bool* existed);

bool ReleaseFormByOwnerKey(const char* owner, const char* key);

bool ReleaseFormByLocalId(uint32_t localId, const char* owner);

uint32_t ReleaseFormsByOwner(const char* owner);
3 changes: 2 additions & 1 deletion SKSE_Plugin/include/form_record.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ class FormRecord {
bool deleted = false;
RE::FormType formType{};
RE::FormID formId{};
};
uint32_t order = 0;
};
63 changes: 53 additions & 10 deletions SKSE_Plugin/include/model.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
#pragma once
#include <vector>
#include <functional>
#pragma once
#include <cstdint>
#include <functional>
#include <optional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "form_record.h"

struct DynamicSlot {
uint32_t localId = 0;
RE::FormType formType = RE::FormType::None;
std::string owner;
std::string key;
};

inline std::vector<FormRecord*> formData;
inline std::vector<FormRecord*> formRef;

inline bool espFound = false;
inline uint32_t lastFormId = 0;
inline uint32_t firstFormId = 0;
inline uint32_t firstDynamicLocalId = 0x801;
inline uint32_t nextDynamicLocalId = 0x801;
inline uint32_t nextRecordOrder = 1;
inline uint32_t dynamicModId = 0;

inline std::string dynamicPluginName = "Dynamic Persistent Forms.esp";
inline std::unordered_set<uint32_t> reservedDynamicLocalIds;
inline std::unordered_map<uint32_t, DynamicSlot> dynamicSlots;
inline std::unordered_map<std::string, uint32_t> dynamicOwnerKeyIndex;

void AddFormData(FormRecord* item);

Expand All @@ -21,12 +36,40 @@ void EachFormData(const std::function<bool(FormRecord*)>& iteration);

void EachFormRef(const std::function<bool(FormRecord*)>& iteration);

bool IsDynamicFormID(RE::FormID formId);

uint32_t ToDynamicLocalID(RE::FormID formId);

RE::FormID MakeDynamicFormID(uint32_t localId);

void ReserveDynamicLocalID(uint32_t localId);

void ReserveDynamicFormID(RE::FormID formId);

std::string NormalizeOwnerKeyPart(const char* value);

std::string MakeOwnerKey(std::string_view owner, std::string_view key);

bool RegisterDynamicSlot(uint32_t localId, RE::FormType formType, std::string owner = {}, std::string key = {});

std::optional<DynamicSlot> GetRegisteredDynamicSlot(uint32_t localId);

std::optional<RE::FormType> GetRegisteredDynamicSlotType(uint32_t localId);

std::optional<uint32_t> FindDynamicSlotByOwnerKey(std::string_view owner, std::string_view key);

bool IsDynamicLocalIDRegistered(uint32_t localId);

bool ReleaseDynamicSlot(uint32_t localId, std::string_view owner);

bool ReleaseDynamicSlotByOwnerKey(std::string_view owner, std::string_view key);

void incrementLastFormID();
uint32_t ReleaseDynamicSlotsByOwner(std::string_view owner);

RE::FormID AllocateDynamicFormID();

void UpdateId();
void ReadFirstFormIdFromESP();

void ResetId();
void ResetDynamicState();

void ReadFirstFormIdFromESP();
void ClearRecords(bool deleteActualForms);
20 changes: 18 additions & 2 deletions SKSE_Plugin/include/papyrus.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
#pragma once
#pragma once


RE::TESForm* Create(RE::StaticFunctionTag*, RE::TESForm* baseItem);

RE::TESForm* CreateByType(RE::StaticFunctionTag*, uint32_t formType);

RE::TESForm* GetOrCreateByLocalId(RE::StaticFunctionTag*, uint32_t localId, uint32_t formType);

RE::TESForm* GetOrCreateByFormId(RE::StaticFunctionTag*, RE::TESForm* formIdSource, uint32_t formType);

RE::TESForm* GetOrCreateByOwnerKey(RE::StaticFunctionTag*, RE::BSFixedString owner, RE::BSFixedString key, uint32_t formType);

bool ReleaseByOwnerKey(RE::StaticFunctionTag*, RE::BSFixedString owner, RE::BSFixedString key);

bool ReleaseByLocalId(RE::StaticFunctionTag*, uint32_t localId, RE::BSFixedString owner);

uint32_t ReleaseOwner(RE::StaticFunctionTag*, RE::BSFixedString owner);

void Track(RE::StaticFunctionTag*, RE::TESForm* baseItem);

void UnTrack(RE::StaticFunctionTag*, RE::TESForm* form);
Expand Down Expand Up @@ -57,4 +71,6 @@ void SetSoulGemCurrentSoul(RE::StaticFunctionTag*, RE::TESSoulGem* soulGem, uint

void LinkSoulGems(RE::StaticFunctionTag*, RE::TESSoulGem* empty, RE::TESSoulGem* filled);

bool PapyrusFunctions(RE::BSScript::IVirtualMachine* vm);
bool PapyrusFunctions(RE::BSScript::IVirtualMachine* vm);


12 changes: 5 additions & 7 deletions SKSE_Plugin/include/persistence.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
#pragma once
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h"
#pragma once
#include <filesystem>
#include <fstream>
#include <string>

namespace fs = std::filesystem;

std::string GetCacheFilePath();
void LoadCache();
void SaveCache();
bool LoadGlobalRegistry();
bool SaveGlobalRegistry();
std::string GetGlobalRegistryPath();

void SaveCallback(SKSE::SerializationInterface* a_intfc);

Expand Down
50 changes: 47 additions & 3 deletions SKSE_Plugin/include/serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include <functional>
#include <string>
#include <cstring>
#include <utility>
#include <vector>

class StreamWrapper {
std::stringstream stream;
Expand Down Expand Up @@ -194,7 +196,7 @@ class Serializer {
Write<uint32_t>(localId);
} else if (modId == 0xfe) {
logger::trace("light");
const auto lightId = (formId >> 12) & 0xFFF;
const auto lightId = static_cast<uint16_t>((formId >> 12) & 0xFFF);
const auto file = dataHandler->LookupLoadedLightModByIndex(lightId);
if (file) {
const auto localId = formId & 0xFFF;
Expand All @@ -208,7 +210,7 @@ class Serializer {
}
} else {
logger::trace("regular");
const auto file = dataHandler->LookupLoadedModByIndex(modId);
const auto file = dataHandler->LookupLoadedModByIndex(static_cast<uint8_t>(modId));
if (file) {
const auto localId = formId & 0xFFFFFF;
const std::string fileName = file->fileName;
Expand Down Expand Up @@ -322,4 +324,46 @@ class FileReader : public Serializer<FileReader> {
logger::error("Error: File not open for reading.");
return T();
}
};
};

class MemoryWriter : public Serializer<MemoryWriter> {
std::vector<uint8_t> bytes;

public:
const std::vector<uint8_t>& Data() const { return bytes; }

template <class T>
T ReadImplementation() {
return T();
}

template <class T>
void WriteImplementation(const T value) {
const auto* raw = reinterpret_cast<const uint8_t*>(&value);
bytes.insert(bytes.end(), raw, raw + sizeof(T));
}
};

class MemoryReader : public Serializer<MemoryReader> {
std::vector<uint8_t> bytes;
size_t offset = 0;

public:
explicit MemoryReader(std::vector<uint8_t> input) : bytes(std::move(input)) {}

template <class T>
void WriteImplementation(T) {
}

template <class T>
T ReadImplementation() {
T value{};
if (offset + sizeof(T) > bytes.size()) {
logger::error("MemoryReader reached end of buffer");
return value;
}
std::memcpy(&value, bytes.data() + offset, sizeof(T));
offset += sizeof(T);
return value;
}
};
Loading