#pragma once #include "RE/S/ScrapHeap.h" namespace RE { namespace CompactingStore { class Store; } class BSSmallBlockAllocator; class IMemoryHeap; class MemoryManager { public: class AutoScrapBuffer { public: AutoScrapBuffer() { ctor(); } ~AutoScrapBuffer() { dtor(); } [[nodiscard]] void* GetPtr() const noexcept { return ptr; } // members void* ptr{ nullptr }; // 0 private: AutoScrapBuffer* ctor() { using func_t = decltype(&AutoScrapBuffer::ctor); static REL::Relocation func{ ID::MemoryManager::AutoScrapBuffer::ctor }; return func(this); } void dtor() { using func_t = decltype(&AutoScrapBuffer::dtor); static REL::Relocation func{ ID::MemoryManager::AutoScrapBuffer::dtor }; return func(this); } }; static_assert(sizeof(AutoScrapBuffer) == 0x8); class ThreadScrapHeap { public: // members ScrapHeap heap; // 00 ThreadScrapHeap* next{ nullptr }; // 90 std::uint32_t owningThread{ REX::W32::GetCurrentThreadId() }; // 98 }; static_assert(sizeof(ThreadScrapHeap) == 0xA0); [[nodiscard]] static MemoryManager& GetSingleton() { using func_t = decltype(&MemoryManager::GetSingleton); const REL::Relocation func{ ID::MemoryManager::Singleton }; return func(); } [[nodiscard]] void* Allocate(std::size_t a_size, std::uint32_t a_alignment, bool a_alignmentRequired) { using func_t = decltype(&MemoryManager::Allocate); const REL::Relocation func{ ID::MemoryManager::Allocate }; return func(this, a_size, a_alignment, a_alignmentRequired); } void Deallocate(void* a_mem, bool a_alignmentRequired) { using func_t = decltype(&MemoryManager::Deallocate); const REL::Relocation func{ ID::MemoryManager::Deallocate }; return func(this, a_mem, a_alignmentRequired); } [[nodiscard]] ScrapHeap* GetThreadScrapHeap() { using func_t = decltype(&MemoryManager::GetThreadScrapHeap); const REL::Relocation func{ ID::MemoryManager::GetThreadScrapHeap }; return func(this); } [[nodiscard]] void* Reallocate(void* a_oldMem, std::size_t a_newSize, std::uint32_t a_alignment, bool a_alignmentRequired) { using func_t = decltype(&MemoryManager::Reallocate); const REL::Relocation func{ ID::MemoryManager::Reallocate }; return func(this, a_oldMem, a_newSize, a_alignment, a_alignmentRequired); } void RegisterMemoryManager() { using func_t = decltype(&MemoryManager::RegisterMemoryManager); const REL::Relocation func{ ID::MemoryManager::RegisterMemoryManager }; return func(this); } // members bool initialized{ false }; // 000 std::uint16_t numHeaps{ 0 }; // 002 std::uint16_t numPhysicalHeaps{ 0 }; // 004 IMemoryHeap** heaps{ nullptr }; // 008 bool* allowOtherContextAllocs{ nullptr }; // 010 IMemoryHeap* heapsByContext[127]{ nullptr }; // 018 ThreadScrapHeap* threadScrapHeap{ nullptr }; // 410 IMemoryHeap** physicalHeaps{ nullptr }; // 418 IMemoryHeap* bigAllocHeap{ nullptr }; // 420 IMemoryHeap* emergencyHeap{ nullptr }; // 428 BSSmallBlockAllocator* smallBlockAllocator{ nullptr }; // 430 CompactingStore::Store* compactingStore{ nullptr }; // 438 IMemoryHeap* externalHavokAllocator{ nullptr }; // 440 bool specialHeaps{ false }; // 448 bool allowPoolUse{ true }; // 449 std::uint32_t sysAllocBytes{ 0 }; // 44C std::uint32_t mallocBytes{ 0 }; // 450 std::uint32_t alignmentForPools{ 4 }; // 454 std::uint32_t mainThreadMemoryProblemPassSignal{ 0 }; // 458 std::size_t failedAllocationSize{ 0 }; // 460 std::uint32_t numMemoryProblemPassesRun{ 0 }; // 468 std::size_t timeOfLastMemoryProblemPass{ 0 }; // 470 IMemoryHeap* defaultHeap{ nullptr }; // 478 }; static_assert(sizeof(MemoryManager) == 0x480); [[nodiscard]] inline void* malloc(std::size_t a_size) { auto& mem = MemoryManager::GetSingleton(); return mem.Allocate(a_size, 0, false); } template [[nodiscard]] T* malloc() { return static_cast(malloc(sizeof(T))); } [[nodiscard]] inline void* aligned_alloc(std::size_t a_alignment, std::size_t a_size) { auto& mem = MemoryManager::GetSingleton(); return mem.Allocate(a_size, static_cast(a_alignment), true); } template [[nodiscard]] T* aligned_alloc() { return static_cast(aligned_alloc(alignof(T), sizeof(T))); } [[nodiscard]] inline void* calloc(std::size_t a_num, std::size_t a_size) { const auto ret = malloc(a_num * a_size); if (ret) { std::memset(ret, 0, a_num * a_size); } return ret; } template [[nodiscard]] T* calloc(std::size_t a_num) { return static_cast(calloc(a_num, sizeof(T))); } [[nodiscard]] inline void* realloc(void* a_ptr, std::size_t a_newSize) { auto& mem = MemoryManager::GetSingleton(); return mem.Reallocate(a_ptr, a_newSize, 0, false); } [[nodiscard]] inline void* aligned_realloc(void* a_ptr, std::size_t a_alignment, std::size_t a_newSize) { auto& mem = MemoryManager::GetSingleton(); return mem.Reallocate(a_ptr, a_newSize, static_cast(a_alignment), true); } inline void free(void* a_ptr) { auto& mem = MemoryManager::GetSingleton(); return mem.Deallocate(a_ptr, false); } inline void aligned_free(void* a_ptr) { auto& mem = MemoryManager::GetSingleton(); return mem.Deallocate(a_ptr, true); } } #define F4_HEAP_REDEFINE_HELPER(...) \ [[nodiscard]] void* operator new(std::size_t a_count, std::align_val_t a_alignment) \ { \ const auto mem = RE::aligned_alloc(static_cast(a_alignment), a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ [[nodiscard]] void* operator new[](std::size_t a_count, std::align_val_t a_alignment) \ { \ const auto mem = RE::aligned_alloc(static_cast(a_alignment), a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ [[nodiscard]] void* operator new(std::size_t, void* a_ptr) noexcept { return a_ptr; } \ [[nodiscard]] void* operator new[](std::size_t, void* a_ptr) noexcept { return a_ptr; } \ [[nodiscard]] void* operator new(std::size_t, std::align_val_t, void* a_ptr) noexcept { return a_ptr; } \ [[nodiscard]] void* operator new[](std::size_t, std::align_val_t, void* a_ptr) noexcept { return a_ptr; } \ \ void operator delete(void*, void*) noexcept { return; } \ void operator delete[](void*, void*) noexcept { return; } \ \ void operator delete(void* a_ptr, std::align_val_t) { RE::aligned_free(a_ptr); } \ void operator delete[](void* a_ptr, std::align_val_t) { RE::aligned_free(a_ptr); } \ void operator delete(void* a_ptr, std::size_t, std::align_val_t) { RE::aligned_free(a_ptr); } \ void operator delete[](void* a_ptr, std::size_t, std::align_val_t) { RE::aligned_free(a_ptr); } #define F4_HEAP_REDEFINE_NEW(...) \ [[nodiscard]] void* operator new(std::size_t a_count) \ { \ const auto mem = RE::malloc(a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ [[nodiscard]] void* operator new[](std::size_t a_count) \ { \ const auto mem = RE::malloc(a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ void operator delete(void* a_ptr) { RE::free(a_ptr); } \ void operator delete[](void* a_ptr) { RE::free(a_ptr); } \ void operator delete(void* a_ptr, std::size_t) { RE::free(a_ptr); } \ void operator delete[](void* a_ptr, std::size_t) { RE::free(a_ptr); } \ \ F4_HEAP_REDEFINE_HELPER(__VA_ARGS__) #define F4_HEAP_REDEFINE_ALIGNED_NEW(...) \ [[nodiscard]] void* operator new(std::size_t a_count) \ { \ const auto mem = RE::aligned_alloc(alignof(__VA_ARGS__), a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ [[nodiscard]] void* operator new[](std::size_t a_count) \ { \ const auto mem = RE::aligned_alloc(alignof(__VA_ARGS__), a_count); \ if (!mem) \ REX::FAIL("out of memory"); \ \ return mem; \ } \ \ void operator delete(void* a_ptr) { RE::aligned_free(a_ptr); } \ void operator delete[](void* a_ptr) { RE::aligned_free(a_ptr); } \ void operator delete(void* a_ptr, std::size_t) { RE::aligned_free(a_ptr); } \ void operator delete[](void* a_ptr, std::size_t) { RE::aligned_free(a_ptr); } \ \ F4_HEAP_REDEFINE_HELPER(__VA_ARGS__)