#pragma once namespace RE { // 18 template class tArray { public: T* entries; // 00 uint32_t capacity; // 08 uint32_t pad0C; // 0C uint32_t count; // 10 uint32_t pad14; // 14 tArray() : entries(NULL), capacity(0), count(0), pad0C(0), pad14(0) {} T& operator[](uint64_t index) { return entries[index]; } RE::MemoryManager* Heap() { return RE::MemoryManager::GetSingleton(); } void Clear() { Heap()->Deallocate(entries, false); entries = NULL; capacity = 0; count = 0; } bool Allocate(uint32_t numEntries) { entries = (T*)Heap()->Allocate(sizeof(T) * numEntries, 0, false); if (!entries) return false; for (uint32_t i = 0; i < numEntries; i++) new (&entries[i]) T; capacity = numEntries; count = numEntries; return true; } bool CopyFrom(const tArray* rhs) { if (rhs->count == 0) return false; if (!rhs->entries) return false; if (entries) Clear(); if (!Allocate(rhs->count)) return false; memcpy(entries, rhs->entries, sizeof(T) * count); return true; } bool Resize(uint32_t numEntries) { if (numEntries == capacity) return false; if (!entries) { Allocate(numEntries); return true; } if (numEntries < capacity) { // Delete the truncated entries for (uint32_t i = numEntries; i < capacity; i++) delete &entries[i]; } T* newBlock = (T*)Heap()->Allocate(sizeof(T) * numEntries, 0, false); // Create a new block memmove_s(newBlock, sizeof(T) * numEntries, entries, sizeof(T) * numEntries); // Move the old memory to the new block if (numEntries > capacity) { // Fill in new remaining entries for (uint32_t i = capacity; i < numEntries; i++) new (&entries[i]) T; } Heap()->Deallocate(entries, false); // Free the old block entries = newBlock; // Assign the new block capacity = numEntries; // Capacity is now the number of total entries in the block count = std::min(capacity, count); // Count stays the same, or is truncated to capacity return true; } bool Push(const T& entry) { if (!entries || count + 1 > capacity) { if (!Grow(nGrow)) return false; } new (&entries[count]) T(entry); count++; return true; }; bool Insert(uint32_t index, const T& entry) { if (!entries || index < count) return false; entries[index] = entry; return true; }; bool Remove(uint32_t index) { if (!entries || index >= count) return false; // This might not be right for pointer types... (&entries[index])->~T(); if (index + 1 < count) { uint32_t remaining = count - index; memmove_s(&entries[index + 1], sizeof(T) * remaining, &entries[index], sizeof(T) * remaining); // Move the rest up } count--; if (capacity > count + nShrink) Shrink(); return true; } bool Shrink() { if (!entries || count == capacity) return false; try { uint32_t newSize = count; T* oldArray = entries; T* newArray = (T*)Heap()->Allocate(sizeof(T) * newSize, 0, false); // Allocate new block memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * newSize); // Move the old block entries = newArray; capacity = count; Heap()->Deallocate(oldArray, false); // Free the old block return true; } catch (...) { return false; } return false; } bool Grow(uint32_t numEntries) { if (!entries) { entries = (T*)Heap()->Allocate(sizeof(T) * numEntries, 0, false); count = 0; capacity = numEntries; return true; } try { uint32_t oldSize = capacity; uint32_t newSize = oldSize + numEntries; T* oldArray = entries; T* newArray = (T*)Heap()->Allocate(sizeof(T) * newSize, 0, false); // Allocate new block if (oldArray) memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * capacity); // Move the old block entries = newArray; capacity = newSize; if (oldArray) Heap()->Deallocate(oldArray, false); // Free the old block for (uint32_t i = oldSize; i < newSize; i++) // Allocate the rest of the free blocks new (&entries[i]) T; return true; } catch (...) { return false; } return false; } bool GetNthItem(uint64_t index, T& pT) const { if (index < count) { pT = entries[index]; return true; } return false; } int64_t GetItemIndex(T& pFind) const { for (uint64_t n = 0; n < count; n++) { T& pT = entries[n]; if (pT == pFind) return n; } return -1; } }; typedef tArray UnkArray; typedef tArray UnkFormArray; // D0 class PersistentFormManager { public: // 10 struct EnchantData { EnchantmentItem* enchantment; // 00 volatile long refCount; // 08 uint32_t pad; // 0C }; uint64_t unk00; // 00 tArray weaponEnchants; // 08 tArray armorEnchants; // 20 uint64_t unk38; // 38 uint32_t unk40; // 40 uint32_t unk44; // 44 uint32_t unk48; // 48 uint32_t unk4C; // 4C // 30 struct Data { void* unk00; // 00 uint64_t unk08; // 08 void* unk10; // 10 uint64_t unk18; // 18 uint32_t unk20; // 20 uint32_t unk24; // 24 uint32_t unk28; // 28 uint32_t unk2C; // 2C }; Data unk50; // 50 Data unk80; // 80 void* unkB0; // B0 uint64_t unkB8; // B8 uint64_t unkC0; // C0 uint64_t unkC8; // C8 //RelocPtr g_persistentFormManager(0x01EBEAE8); // SE //RelocPtr g_persistentFormManager(0x01F5A468); // AE [[nodiscard]] static PersistentFormManager* GetSingleton() { REL::Relocation singleton{ RELOCATION_ID(514172, 400320) }; return *singleton; } void IncRefEnchantment(EnchantmentItem* enchantment) { if (enchantment && enchantment->formID >= 0xFF000000) { for (uint32_t i = 0; i < weaponEnchants.count; i++) { EnchantData foundData; weaponEnchants.GetNthItem(i, foundData); if (foundData.enchantment == enchantment) { InterlockedIncrement(&weaponEnchants[i].refCount); break; } } for (uint32_t i = 0; i < armorEnchants.count; i++) { EnchantData foundData; armorEnchants.GetNthItem(i, foundData); if (foundData.enchantment == enchantment) { InterlockedIncrement(&armorEnchants[i].refCount); break; } } } } // The game doesn't bother to dec ref or even delete custom enchants // when they are no longer used, maybe we can fix this? void DecRefEnchantment(EnchantmentItem* enchantment) { if (enchantment && enchantment->formID >= 0xFF000000) { for (uint32_t i = 0; i < weaponEnchants.count; i++) { EnchantData foundData; weaponEnchants.GetNthItem(i, foundData); if (foundData.enchantment == enchantment) { if (!InterlockedDecrement(&weaponEnchants[i].refCount)) ScheduleForDeletion(enchantment); break; } } for (uint32_t i = 0; i < armorEnchants.count; i++) { EnchantData foundData; armorEnchants.GetNthItem(i, foundData); if (foundData.enchantment == enchantment) { if (!InterlockedDecrement(&armorEnchants[i].refCount)) ScheduleForDeletion(enchantment); break; } } } } //MEMBER_FN_PREFIX(PersistentFormManager); // SE /* DEFINE_MEMBER_FN(CreateOffensiveEnchantment, EnchantmentItem*, 0x0059F0F0, tArray* effectArray); DEFINE_MEMBER_FN(CreateDefensiveEnchantment, EnchantmentItem*, 0x0059F190, tArray* effectArray); DEFINE_MEMBER_FN(CreatePoison, void, 0x0059F2E0, tArray* effectArray, AlchemyItem** poison); DEFINE_MEMBER_FN(CreatePotion, void, 0x0059F230, AlchemyItem** potion, tArray* effectArray); DEFINE_MEMBER_FN(ScheduleForDeletion, void, 0x0059F6E0, TESForm*); */ // AE /* DEFINE_MEMBER_FN(CreateOffensiveEnchantment, EnchantmentItem*, 0x005C1350, tArray* effectArray); DEFINE_MEMBER_FN(CreateDefensiveEnchantment, EnchantmentItem*, 0x005C13F0, tArray* effectArray); DEFINE_MEMBER_FN(CreatePoison, void, 0x005C1540, tArray* effectArray, AlchemyItem** poison); DEFINE_MEMBER_FN(CreatePotion, void, 0x005C1490, AlchemyItem** potion, tArray* effectArray); DEFINE_MEMBER_FN(ScheduleForDeletion, void, 0x005C1870, TESForm*); */ void CreatePotion(AlchemyItem** potion, tArray* effectArray) { using func_t = decltype(&PersistentFormManager::CreatePotion); REL::Relocation func{ RELOCATION_ID(35265, 36167) }; return func(this, potion, effectArray); } void ScheduleForDeletion(TESForm* form) { using func_t = decltype(&PersistentFormManager::ScheduleForDeletion); REL::Relocation func{ RELOCATION_ID(35269, 36171) }; return func(this, form); } }; //STATIC_ASSERT(sizeof(PersistentFormManager) == 0xD0); }