346 lines
9.0 KiB
C
346 lines
9.0 KiB
C
|
#pragma once
|
||
|
|
||
|
namespace RE
|
||
|
{
|
||
|
// 18
|
||
|
template <class T, int nGrow = 10, int nShrink = 10>
|
||
|
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<T>* 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<uint64_t> UnkArray;
|
||
|
typedef tArray<TESForm*> UnkFormArray;
|
||
|
|
||
|
// D0
|
||
|
class PersistentFormManager
|
||
|
{
|
||
|
public:
|
||
|
// 10
|
||
|
struct EnchantData
|
||
|
{
|
||
|
EnchantmentItem* enchantment; // 00
|
||
|
volatile long refCount; // 08
|
||
|
uint32_t pad; // 0C
|
||
|
};
|
||
|
|
||
|
uint64_t unk00; // 00
|
||
|
tArray<EnchantData> weaponEnchants; // 08
|
||
|
tArray<EnchantData> 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<RE::PersistentFormManager*> g_persistentFormManager(0x01EBEAE8); // SE
|
||
|
//RelocPtr<RE::PersistentFormManager*> g_persistentFormManager(0x01F5A468); // AE
|
||
|
[[nodiscard]] static PersistentFormManager* GetSingleton()
|
||
|
{
|
||
|
REL::Relocation<PersistentFormManager**> 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<Effect::EffectItem>* effectArray);
|
||
|
DEFINE_MEMBER_FN(CreateDefensiveEnchantment, EnchantmentItem*, 0x0059F190, tArray<Effect::EffectItem>* effectArray);
|
||
|
DEFINE_MEMBER_FN(CreatePoison, void, 0x0059F2E0, tArray<Effect::EffectItem>* effectArray, AlchemyItem** poison);
|
||
|
DEFINE_MEMBER_FN(CreatePotion, void, 0x0059F230, AlchemyItem** potion, tArray<Effect::EffectItem>* effectArray);
|
||
|
DEFINE_MEMBER_FN(ScheduleForDeletion, void, 0x0059F6E0, TESForm*);
|
||
|
*/
|
||
|
|
||
|
// AE
|
||
|
/*
|
||
|
DEFINE_MEMBER_FN(CreateOffensiveEnchantment, EnchantmentItem*, 0x005C1350, tArray<Effect::EffectItem>* effectArray);
|
||
|
DEFINE_MEMBER_FN(CreateDefensiveEnchantment, EnchantmentItem*, 0x005C13F0, tArray<Effect::EffectItem>* effectArray);
|
||
|
DEFINE_MEMBER_FN(CreatePoison, void, 0x005C1540, tArray<Effect::EffectItem>* effectArray, AlchemyItem** poison);
|
||
|
DEFINE_MEMBER_FN(CreatePotion, void, 0x005C1490, AlchemyItem** potion, tArray<Effect>* effectArray);
|
||
|
DEFINE_MEMBER_FN(ScheduleForDeletion, void, 0x005C1870, TESForm*);
|
||
|
*/
|
||
|
|
||
|
void CreatePotion(AlchemyItem** potion, tArray<Effect>* effectArray)
|
||
|
{
|
||
|
using func_t = decltype(&PersistentFormManager::CreatePotion);
|
||
|
REL::Relocation<func_t> func{ RELOCATION_ID(35265, 36167) };
|
||
|
return func(this, potion, effectArray);
|
||
|
}
|
||
|
|
||
|
void ScheduleForDeletion(TESForm* form)
|
||
|
{
|
||
|
using func_t = decltype(&PersistentFormManager::ScheduleForDeletion);
|
||
|
REL::Relocation<func_t> func{ RELOCATION_ID(35269, 36171) };
|
||
|
return func(this, form);
|
||
|
}
|
||
|
};
|
||
|
//STATIC_ASSERT(sizeof(PersistentFormManager) == 0xD0);
|
||
|
}
|