From ca1f117865937c9c27a5ca942ea143d078a82b85 Mon Sep 17 00:00:00 2001 From: CLF78 <29682335+CLF78@users.noreply.github.com> Date: Mon, 29 Dec 2025 15:38:23 +0100 Subject: [PATCH 1/4] Match and link mHeap Cleanup to be done --- include/constants/sjis_constants.h | 18 ++ include/game/mLib/m_allocator_dummy_heap.hpp | 6 +- include/game/mLib/m_heap.hpp | 39 ++- include/lib/egg/core/eggAssertHeap.h | 12 + include/lib/egg/core/eggExpHeap.h | 17 +- include/lib/egg/core/eggFrmHeap.h | 16 +- include/lib/egg/core/eggHeap.h | 7 + include/lib/egg/core/eggUnitHeap.h | 13 + include/lib/nw4r/ut/ut_algorithm.h | 2 +- include/lib/revolution/MEM/mem_unitHeap.h | 20 ++ slices/wiimj2d.json | 11 + source/dol/mLib/m_allocator.cpp | 6 +- source/dol/mLib/m_heap.cpp | 243 +++++++++++++++++++ syms.txt | 9 + 14 files changed, 388 insertions(+), 31 deletions(-) create mode 100644 include/lib/egg/core/eggAssertHeap.h create mode 100644 include/lib/egg/core/eggUnitHeap.h create mode 100644 include/lib/revolution/MEM/mem_unitHeap.h create mode 100644 source/dol/mLib/m_heap.cpp diff --git a/include/constants/sjis_constants.h b/include/constants/sjis_constants.h index 4aea42a4..0eb9c1eb 100644 --- a/include/constants/sjis_constants.h +++ b/include/constants/sjis_constants.h @@ -19,3 +19,21 @@ // [Translation: 2D resource heap (d2d::ResAccMultLoader_c::create)] #define D2D_HEAP_NAME "2Dリソース用ヒープ(d2d::ResAccMultLoader_c::create)" + +// [Translation: General-purpose file reading heap (mHeap::archiveHeap)] +#define ARCHIVE_HEAP_NAME "汎用ファイル読み込み用ヒープ(mHeap::archiveHeap)" + +// [Translation: DVD read command heap (mHeap::commandHeap)] +#define COMMAND_HEAP_NAME "DVD読み込みコマンド用ヒープ(mHeap::commandHeap)" + +// [Translation: Dynamic linking heap (mHeap::dylinkHeap)] +#define DYLINK_HEAP_NAME "ダイナミックリンク用ヒープ(mHeap::dylinkHeap)" + +// [Translation: General-purpose heap for games 1(mHeap::gameHeaps[1])] +#define GAME_HEAP_1_NAME "ゲーム用汎用ヒープ1(mHeap::gameHeaps[1])" + +// [Translation: General-purpose heap for games 2(mHeap::gameHeaps[2])] +#define GAME_HEAP_2_NAME "ゲーム用汎用ヒープ2(mHeap::gameHeaps[2])" + +// [Translation: Assert Heap (mHeap::assertHeap)] +#define ASSERT_HEAP_NAME "アサートヒープ(mHeap::assertHeap)" diff --git a/include/game/mLib/m_allocator_dummy_heap.hpp b/include/game/mLib/m_allocator_dummy_heap.hpp index 21ec1283..a0fbab41 100644 --- a/include/game/mLib/m_allocator_dummy_heap.hpp +++ b/include/game/mLib/m_allocator_dummy_heap.hpp @@ -19,11 +19,11 @@ class mAllocatorDummyHeap_c : public EGG::Heap { virtual void free(void *ptr); virtual void destroy(); - virtual void *resizeForMBlock(void *ptr, size_t newSize); + virtual size_t resizeForMBlock(void *ptr, size_t newSize); virtual u32 getTotalFreeSize(); - virtual u32 getAllocatableSize(long alignment); - virtual bool adjust(); + virtual size_t getAllocatableSize(long alignment); + virtual size_t adjust(); static void *AllocatorAllocForDummyHeap(MEMAllocator *, size_t); static void AllocatorFreeForDummyHeap(MEMAllocator *, void *); diff --git a/include/game/mLib/m_heap.hpp b/include/game/mLib/m_heap.hpp index 5bcd4f96..50702b3b 100644 --- a/include/game/mLib/m_heap.hpp +++ b/include/game/mLib/m_heap.hpp @@ -1,25 +1,44 @@ #pragma once -#include +#include #include #include -#include +#include namespace mHeap { + enum AllocOptBit_t { OPT_NONE = 0, + OPT_CLEAR_ALLOC = BIT_FLAG(0), + OPT_DEBUG_FILL = BIT_FLAG(1), OPT_THREAD_SAFE = BIT_FLAG(2) }; - void restoreCurrentHeap(); - EGG::Heap *setCurrentHeap(EGG::Heap *); - size_t frmHeapCost(size_t, size_t); - void destroyFrmHeap(EGG::FrmHeap *); - unsigned long adjustFrmHeap(EGG::FrmHeap *); + u16 GetOptFlag(AllocOptBit_t opt); + + EGG::Heap *setCurrentHeap(EGG::Heap *heap); void saveCurrentHeap(); + void restoreCurrentHeap(); + + size_t expHeapCost(size_t size, ulong align); + size_t frmHeapCost(size_t size, ulong align); + size_t untHeapCost(size_t size, ulong count, ulong align); + + void destroyFrmHeap(EGG::FrmHeap *heap); + size_t adjustFrmHeap(EGG::FrmHeap *heap); + + EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + EGG::Heap *createHeap(size_t size, EGG::Heap *parent, const char *name); - EGG::ExpHeap *createExpHeap(size_t, EGG::Heap *, const char *, size_t, mHeap::AllocOptBit_t); - EGG::FrmHeap *createFrmHeapToCurrent(unsigned long size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); - EGG::FrmHeap *createFrmHeap(unsigned long size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + EGG::Heap *createGameHeap(int idx, size_t size, EGG::Heap* parent); + void createGameHeap1(size_t size, EGG::Heap *parent); + void createGameHeap2(size_t size, EGG::Heap *parent); + void createArchiveHeap(size_t size, EGG::Heap *parent); + void createCommandHeap(size_t size, EGG::Heap *parent); + void createDylinkHeap(size_t size, EGG::Heap *parent); + EGG::Heap *createAssertHeap(EGG::Heap *parent); extern EGG::Heap *g_gameHeaps[3]; }; diff --git a/include/lib/egg/core/eggAssertHeap.h b/include/lib/egg/core/eggAssertHeap.h new file mode 100644 index 00000000..5c2d1188 --- /dev/null +++ b/include/lib/egg/core/eggAssertHeap.h @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace EGG { + +class AssertHeap : public Heap { +public: + static AssertHeap *create(size_t size, Heap *parent); + static size_t getMinSizeForCreate(); +}; + +} // namespace EGG diff --git a/include/lib/egg/core/eggExpHeap.h b/include/lib/egg/core/eggExpHeap.h index cd1ec636..8baf7787 100644 --- a/include/lib/egg/core/eggExpHeap.h +++ b/include/lib/egg/core/eggExpHeap.h @@ -1,12 +1,15 @@ #pragma once - #include +namespace EGG { + // [TODO: extend this] +class ExpHeap : public Heap { +public: + void setAllocMode(u16 mode); -namespace EGG { - class ExpHeap : public Heap { - public: - static EGG::ExpHeap *create(size_t, EGG::Heap *, u16); - }; -} + static EGG::ExpHeap *create(void *buffer, size_t size, u16 flags); + static EGG::ExpHeap *create(size_t, EGG::Heap *, u16); +}; + +} // namespace EGG \ No newline at end of file diff --git a/include/lib/egg/core/eggFrmHeap.h b/include/lib/egg/core/eggFrmHeap.h index a65b98a5..161d80fb 100644 --- a/include/lib/egg/core/eggFrmHeap.h +++ b/include/lib/egg/core/eggFrmHeap.h @@ -1,13 +1,15 @@ #pragma once - #include #include +namespace EGG { + // [TODO: extend this] +class FrmHeap : public Heap { +public: + void free(long); -namespace EGG { - class FrmHeap : public Heap { - public: - void free(long); - }; -} + static FrmHeap *create(void *buffer, size_t size, u16 flags); +}; + +} // namespace EGG diff --git a/include/lib/egg/core/eggHeap.h b/include/lib/egg/core/eggHeap.h index 328d7c3e..11ec0a31 100644 --- a/include/lib/egg/core/eggHeap.h +++ b/include/lib/egg/core/eggHeap.h @@ -23,11 +23,18 @@ class Heap : Disposer { virtual void *alloc(size_t, long) = 0; virtual void free(void *) = 0; virtual void destroy() = 0; + virtual size_t resizeForMBlock(void* block, size_t size) = 0; + virtual u32 getTotalFreeSize(); + virtual size_t getAllocatableSize(long align) = 0; + virtual size_t adjust() = 0; + + void dump(); static void *alloc(size_t, int, EGG::Heap *); static void free(void *, EGG::Heap *); static Heap *findContainHeap(const void *); + Heap *becomeCurrentHeap(); void appendDisposer(Disposer *disposer) { nw4r::ut::List_Append(&mChildren, disposer); } void removeDisposer(Disposer *disposer) { nw4r::ut::List_Remove(&mChildren, disposer); } diff --git a/include/lib/egg/core/eggUnitHeap.h b/include/lib/egg/core/eggUnitHeap.h new file mode 100644 index 00000000..79c41d8b --- /dev/null +++ b/include/lib/egg/core/eggUnitHeap.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +namespace EGG { + +class UnitHeap : public Heap, MEMiUntHeapHead { +public: + static UnitHeap* create(void* block, size_t size, size_t unitSize, long align, u16 flag); + static size_t calcHeapSize(size_t unitSize, ulong count, long align); +}; + +} // namespace EGG diff --git a/include/lib/nw4r/ut/ut_algorithm.h b/include/lib/nw4r/ut/ut_algorithm.h index fa830a92..0bca6549 100644 --- a/include/lib/nw4r/ut/ut_algorithm.h +++ b/include/lib/nw4r/ut/ut_algorithm.h @@ -85,7 +85,7 @@ inline int ComparePtr(const void* pPtr1, const void* pPtr2) { * ******************************************************************************/ template inline T RoundUp(T t, unsigned int alignment) { - return (alignment + t - 1) & ~(alignment - 1); + return (t + (alignment - 1)) & ~(alignment - 1); } template inline void* RoundUp(T* pPtr, unsigned int alignment) { diff --git a/include/lib/revolution/MEM/mem_unitHeap.h b/include/lib/revolution/MEM/mem_unitHeap.h new file mode 100644 index 00000000..647b3028 --- /dev/null +++ b/include/lib/revolution/MEM/mem_unitHeap.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct MEMiUntHeapMBlockHead MEMiUntHeapMBlockHead; +struct MEMiUntHeapMBlockHead { + MEMiUntHeapMBlockHead* succ; +}; + +typedef struct MEMiUntHeapHead { + MEMiUntHeapMBlockHead* free_list; + u32 unit_size; +} MEMiUntHeapHead; + +#ifdef __cplusplus +} +#endif diff --git a/slices/wiimj2d.json b/slices/wiimj2d.json index f3fc205b..07be3278 100644 --- a/slices/wiimj2d.json +++ b/slices/wiimj2d.json @@ -731,6 +731,17 @@ ".data": "0x2b780-0x2b7a8" } }, + { + "source": "dol/mLib/m_heap.cpp", + "memoryRanges": { + ".text": "0x167e70-0x168560", + ".rodata": "0x7d60-0x7d70", + ".data": "0x2b7a8-0x2b8c0", + ".bss": "0x265c8-0x265d8", + ".sdata": "0x1e08-0x1e10", + ".sbss": "0x888-0x8a0" + } + }, { "source": "dol/mLib/m_mtx.cpp", "memoryRanges": { diff --git a/source/dol/mLib/m_allocator.cpp b/source/dol/mLib/m_allocator.cpp index a5add6b2..9e3b5eec 100644 --- a/source/dol/mLib/m_allocator.cpp +++ b/source/dol/mLib/m_allocator.cpp @@ -37,7 +37,7 @@ void mAllocatorDummyHeap_c::free(void *ptr) {} void mAllocatorDummyHeap_c::destroy() {} -void *mAllocatorDummyHeap_c::resizeForMBlock(void *ptr, size_t newSize) { +size_t mAllocatorDummyHeap_c::resizeForMBlock(void *ptr, size_t newSize) { return 0; } @@ -45,11 +45,11 @@ u32 mAllocatorDummyHeap_c::getTotalFreeSize() { return 0; } -u32 mAllocatorDummyHeap_c::getAllocatableSize(long alignment) { +size_t mAllocatorDummyHeap_c::getAllocatableSize(long alignment) { return 0; } -bool mAllocatorDummyHeap_c::adjust() { +size_t mAllocatorDummyHeap_c::adjust() { return 0; } diff --git a/source/dol/mLib/m_heap.cpp b/source/dol/mLib/m_heap.cpp new file mode 100644 index 00000000..d8fcd055 --- /dev/null +++ b/source/dol/mLib/m_heap.cpp @@ -0,0 +1,243 @@ +#include "egg/core/eggAssertHeap.h" +#include +#include + +namespace mHeap { + +u8 g_DefaultGameHeapId = 1; +const char * const s_GameHeapNames[3] = { + nullptr, + GAME_HEAP_1_NAME, + GAME_HEAP_2_NAME, +}; + +EGG::Heap *s_SavedCurrentHeap; +EGG::Heap *g_gameHeaps[3]; +EGG::Heap *g_archiveHeap; +EGG::Heap *g_commandHeap; +EGG::Heap *g_dylinkHeap; +EGG::Heap *g_assertHeap; + +u16 GetOptFlag(AllocOptBit_t opt) { + u16 ret = OPT_NONE; + + if (opt & OPT_CLEAR_ALLOC) { + ret = MEM_HEAP_OPT_CLEAR_ALLOC; + } + + if (opt & OPT_DEBUG_FILL) { + ret |= MEM_HEAP_OPT_DEBUG_FILL; + } + + if (opt & OPT_THREAD_SAFE) { + ret |= MEM_HEAP_OPT_CAN_LOCK; + } + + return ret; +} + +EGG::Heap *setCurrentHeap(EGG::Heap *heap) { + return heap->becomeCurrentHeap(); +} + +EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { + if (parent == nullptr) { + parent = EGG::Heap::sCurrentHeap; + } + + if (align < 0x20) { + align = 0x20; + } + + if (size != -1) { + size = expHeapCost(size, align); + } else { + size = parent->getAllocatableSize(align); + } + + void *buffer = parent->alloc(size, align); + EGG::ExpHeap *heap = nullptr; + + if (buffer != nullptr) { + u16 flags = GetOptFlag(opt); + heap = EGG::ExpHeap::create(buffer, size, flags); + if (heap == nullptr) { + parent->free(buffer); + } else if (name != nullptr) { + heap->mpName = name; + } + } + + return heap; +} + +size_t expHeapCost(size_t size, ulong align) { + return size + nw4r::ut::RoundUp(sizeof(EGG::ExpHeap) + sizeof(MEMiHeapHead) + sizeof(MEMiExpHeapHead), align); +} + +EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { + if (parent == nullptr) { + parent = EGG::Heap::sCurrentHeap; + } + + if (align < 0x20) { + align = 0x20; + } + + if (size != -1) { + size = frmHeapCost(size, align); + } else { + size = parent->getAllocatableSize(align); + } + + void *buffer = parent->alloc(size, align); + EGG::FrmHeap *heap = nullptr; + + if (buffer != nullptr) { + u16 flags = GetOptFlag(opt); + heap = EGG::FrmHeap::create(buffer, size, flags); + if (heap == nullptr) { + parent->free(buffer); + } else if (name != nullptr) { + heap->mpName = name; + } + } + + return heap; +} + +void destroyFrmHeap(EGG::FrmHeap *heap) { + if (heap != nullptr) { + heap->destroy(); + } +} + +size_t adjustFrmHeap(EGG::FrmHeap *heap) { + size_t adjustedSize = 0; + + if (heap != nullptr) { + size_t a = heap->adjust(); + size_t cost = frmHeapCost(0, 4); + if (a >= cost) { + adjustedSize = a - cost; + } + } + + return adjustedSize; +} + +size_t frmHeapCost(size_t size, ulong align) { + return size + nw4r::ut::RoundUp(sizeof(EGG::FrmHeap) + sizeof(MEMiHeapHead) + sizeof(MEMiFrmHeapHead), align); +} + +EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt) { + if (parent == nullptr) { + parent = EGG::Heap::sCurrentHeap; + } + + if (align < 0x20) { + align = 0x20; + } + + size_t totalSize = untHeapCost(size, count, align); + EGG::UnitHeap *heap = nullptr; + void *buffer = parent->alloc(totalSize, align); + + if (buffer != nullptr) { + u16 flags = GetOptFlag(opt); + heap = EGG::UnitHeap::create(buffer, totalSize, size, align, flags); + if (heap == nullptr) { + parent->free(buffer); + } else if (name != nullptr) { + heap->mpName = name; + } + } + + return heap; +} + +size_t untHeapCost(size_t size, ulong count, ulong align) { + return EGG::UnitHeap::calcHeapSize(size, count, align); +} + +EGG::Heap *createHeap(size_t size, EGG::Heap *parent, const char *name) { + + EGG::ExpHeap *heap = EGG::ExpHeap::create(size, parent, MEM_HEAP_OPT_CAN_LOCK); + if (heap != nullptr) { + heap->setAllocMode(MEM_EXP_HEAP_ALLOC_FAST); + if (name != nullptr) { + heap->mpName = name; + } + } else { + parent->dump(); + } + + return heap; +} + +void saveCurrentHeap() { + s_SavedCurrentHeap = EGG::Heap::sCurrentHeap; +} + +void restoreCurrentHeap() { + s_SavedCurrentHeap->becomeCurrentHeap(); + s_SavedCurrentHeap = nullptr; +} + +EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { + EGG::FrmHeap *heap = createFrmHeap(size, parent, name, align, opt); + if (heap != nullptr) { + s_SavedCurrentHeap = setCurrentHeap(heap); + } + + return heap; +} + +EGG::Heap *createGameHeap(int idx, size_t size, EGG::Heap *parent) { + bool isValid = false; + if (1U <= idx && idx <= 2U) { + isValid = true; + } + + if (!isValid) { + return nullptr; + } + + g_gameHeaps[idx] = createHeap(size, parent, s_GameHeapNames[idx]); + + if (idx == g_DefaultGameHeapId) { + g_gameHeaps[0] = g_gameHeaps[idx]; + } + + return g_gameHeaps[idx]; +} + +void createGameHeap1(size_t size, EGG::Heap *parent) { + createGameHeap(1, size, parent); +} + +void createGameHeap2(size_t size, EGG::Heap *parent) { + createGameHeap(2, size, parent); +} + +void createArchiveHeap(size_t size, EGG::Heap *parent) { + g_archiveHeap = createHeap(size, parent, ARCHIVE_HEAP_NAME); +} + +void createCommandHeap(size_t size, EGG::Heap *parent) { + g_commandHeap = createHeap(size, parent, COMMAND_HEAP_NAME); +} + +void createDylinkHeap(size_t size, EGG::Heap *parent) { + g_dylinkHeap = createHeap(size, parent, DYLINK_HEAP_NAME); +} + +EGG::Heap *createAssertHeap(EGG::Heap *parent) { + const char *heapName = ASSERT_HEAP_NAME; + size_t size = EGG::AssertHeap::getMinSizeForCreate(); + g_assertHeap = EGG::AssertHeap::create(size, parent); + g_assertHeap->mpName = heapName; + return g_assertHeap; +} + +} \ No newline at end of file diff --git a/syms.txt b/syms.txt index e6c5edac..084be336 100644 --- a/syms.txt +++ b/syms.txt @@ -478,6 +478,8 @@ __dt__Q23EGG4HeapFv=0x802B8D60 alloc__Q23EGG4HeapFUliPQ23EGG4Heap=0x802B8E00 findContainHeap__Q23EGG4HeapFPCv=0x802B9070 free__Q23EGG4HeapFPvPQ23EGG4Heap=0x802B90B0 +dump__Q23EGG4HeapFv=0x802B91B0 +becomeCurrentHeap__Q23EGG4HeapFv=0x802B92B0 __nw__FUl=0x802B9350 __nw__FUlPv=0x802B9360 __nw__FUlPQ23EGG4Heapi=0x802B9380 @@ -485,8 +487,15 @@ __nwa__FUl=0x802B9390 __nwa__FUlPQ23EGG4Heapi=0x802B93B0 __dl__FPv=0x802B93C0 __dla__FPv=0x802B93D0 +create__Q23EGG7ExpHeapFPvUlUs=0x802B94A0 create__Q23EGG7ExpHeapFUlPQ23EGG4HeapUs=0x802B9570 +setAllocMode__Q23EGG7ExpHeapFUs=0x802B9790 +create__Q23EGG7FrmHeapFPvUlUs=0x802B9980 free__Q23EGG7FrmHeapFl=0x802B9B00 +create__Q23EGG8UnitHeapFPvUlUllUs=0x802B9CC0 +calcHeapSize__Q23EGG8UnitHeapFUlUll=0x802B9F30 +create__Q23EGG10AssertHeapFUlPQ23EGG4Heap=0x802BA140 +getMinSizeForCreate__Q23EGG10AssertHeapFv=0x802BA3F0 sqrt__Q23EGG7MathFf=0x802BE760 normalise__Q23EGG8Vector2fFv=0x802BE930 Initialize__Q23EGG6DrawGXFPQ23EGG4Heap=0x802C1A00 From e0b8d5e518b0011021a1f5a79c03e19b7419b9e1 Mon Sep 17 00:00:00 2001 From: CLF78 <29682335+CLF78@users.noreply.github.com> Date: Mon, 16 Feb 2026 01:30:46 +0100 Subject: [PATCH 2/4] Clean up and document mHeap --- include/game/mLib/m_heap.hpp | 166 ++++++++++++++++++--- include/lib/revolution/MEM/mem_expHeap.h | 5 +- include/lib/revolution/MEM/mem_frameHeap.h | 3 +- source/dol/mLib/m_heap.cpp | 106 ++++++------- 4 files changed, 200 insertions(+), 80 deletions(-) diff --git a/include/game/mLib/m_heap.hpp b/include/game/mLib/m_heap.hpp index 50702b3b..431b07dc 100644 --- a/include/game/mLib/m_heap.hpp +++ b/include/game/mLib/m_heap.hpp @@ -4,41 +4,171 @@ #include #include +/** + * @brief Provides high-level heap management utilities built on top of the @ref EGG heap system. + * @details The mHeap namespace wraps and extends the functionality of the EGG heap framework, providing + * helper functions for creating and managing different heap types used by the game. + * + * It supports: + * - @ref EGG::ExpHeap "Expandable heaps". + * - @ref EGG::FrmHeap "Frame heaps". + * - @ref EGG::UnitHeap "Unit heaps". + * + * It also manages various game-specific heaps: + * - The @ref g_gameHeaps "game heaps", used for most game-related content. One game heap is created in MEM1 and one in MEM2. + * - The @ref g_archiveHeap "archive heap", used for loading resource files. + * - The @ref g_commandHeap "command heap", used for @ref mDvd_command_c "DVD commands". + * - The @ref g_dylinkHeap "dylink heap", used for loading and linking the game's REL files. + * - The @ref g_assertHeap "assert heap". + */ namespace mHeap { + /// @brief Bit flags controlling heap allocation behavior. + /// These flags are translated into internal MEM heap flags via GetOptFlag(). enum AllocOptBit_t { - OPT_NONE = 0, - OPT_CLEAR_ALLOC = BIT_FLAG(0), - OPT_DEBUG_FILL = BIT_FLAG(1), - OPT_THREAD_SAFE = BIT_FLAG(2) + OPT_NONE = 0, ///< No special allocation options. + OPT_CLEAR_ALLOC = BIT_FLAG(0), ///< Memory blocks are cleared upon allocation. + OPT_DEBUG_FILL = BIT_FLAG(1), ///< Memory blocks are filled with different values depending on the heap status. + OPT_THREAD_SAFE = BIT_FLAG(2) ///< Enables thread-safe memory block de/allocation. }; + /// @brief The identifiers for the predefined game heaps. + enum GAME_HEAP_e { + GAME_HEAP_DEFAULT, ///< The default game heap (alias of MEM1 or MEM2). + GAME_HEAP_MEM1, ///< The game heap allocated in MEM1. + GAME_HEAP_MEM2, ///< The game heap allocated in MEM2. + GAME_HEAP_COUNT, ///< The total number of game heaps. + }; + + /// @brief Converts the allocation option bits to internal MEM heap flags. + /// @param opt The allocation option bits. + /// @return The corresponding MEM heap flag mask. u16 GetOptFlag(AllocOptBit_t opt); + /// @brief Sets the specified heap as the current heap. + /// @param heap The heap to become current. + /// @return The previously current heap. EGG::Heap *setCurrentHeap(EGG::Heap *heap); + + /// @brief Saves the currently active heap. + /// The saved heap can later be restored using restoreCurrentHeap(). void saveCurrentHeap(); - void restoreCurrentHeap(); + void restoreCurrentHeap(); ///< Restores the previously saved heap as current. + /// @brief Calculates the total required size for an @ref EGG::ExpHeap "expandable heap", including internal overhead. + /// @param size The requested usable size. + /// @param align The lignment requirement. + /// @return The total allocation size required. size_t expHeapCost(size_t size, ulong align); + + /// @brief Calculates the total required size for a @ref EGG::FrmHeap "frame heap", including internal overhead. + /// @param size The requested usable size. + /// @param align The lignment requirement. + /// @return The total allocation size required. size_t frmHeapCost(size_t size, ulong align); + + /** + * @brief Calculates the total required size for an @ref EGG::UnitHeap "unit heap", including internal overhead. + * @param size The requested usable size. + * @param count The number of units. + * @param align The lignment requirement. + * @return The total allocation size required. + */ size_t untHeapCost(size_t size, ulong count, ulong align); + /// @brief Destroys a frame heap. + /// @param heap The frame heap to destroy, or @p nullptr . void destroyFrmHeap(EGG::FrmHeap *heap); + + /// @brief Adjusts a frame heap to compress unused memory. + /// @param heap The frame heap to adjust, or @p nullptr . + /// @return The total available space in the heap, or @p 0 if the adjust operation failed. size_t adjustFrmHeap(EGG::FrmHeap *heap); - EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); - EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); - EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); - EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt); + /** + * @brief Creates an expandable heap. + * @param size The size of the heap, or -1 to use all space available. + * @param parent The parent heap. + * @param name The heap name, or @p nullptr . + * @param align The heap alignment (minimum 0x20). + * @param opt The allocation options. + * @return A pointer to the created heap, or @p nullptr on failure. + */ + EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt); + + /** + * @brief Creates a frame heap and sets it as current. + * @param size The size of the heap, or -1 to use all space available. + * @param parent The parent heap. + * @param name The heap name, or @p nullptr . + * @param align The heap alignment (minimum 0x20). + * @param opt The allocation options. + * @return A pointer to the created heap, or @p nullptr on failure. + */ + EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt); + + /** + * @brief Creates a frame heap. + * @param size The size of the heap, or -1 to use all space available. + * @param parent The parent heap. + * @param name The heap name, or @p nullptr . + * @param align The heap alignment (minimum 0x20). + * @param opt The allocation options. + * @return A pointer to the created heap, or @p nullptr on failure. + */ + EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt); + + /** + * @brief Creates a unit heap. + * @param size The size of the heap, or -1 to use all space available. + * @param count The number of units. + * @param parent The parent heap. + * @param name The heap name, or @p nullptr . + * @param align The heap alignment (minimum 0x20). + * @param opt The allocation options. + * @return A pointer to the created heap, or @p nullptr on failure. + */ + EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt); + + inline bool isValidGameHeapId(u32 idx) { + return GAME_HEAP_MEM1 <= idx && idx <= GAME_HEAP_MEM2; + } + + /** + * @brief Creates a generic expandable heap, with @ref MEM_EXP_HEAP_ALLOC_FAST "fast allocation mode" and @ref OPT_THREAD_SAFE "thread-safe de/allocation". + * @param size The heap size. + * @param parent The parent heap. + * @param name The heap name, or @p nullptr . + * @return A pointer to the created heap, or @p nullptr on failure. + */ EGG::Heap *createHeap(size_t size, EGG::Heap *parent, const char *name); + /** + * @brief Creates a game heap. + * @param idx The game heap index. + * @param size The heap size. + * @param parent The parent heap. + * @return A pointer to the created heap, or @p nullptr if invalid index. + */ EGG::Heap *createGameHeap(int idx, size_t size, EGG::Heap* parent); - void createGameHeap1(size_t size, EGG::Heap *parent); - void createGameHeap2(size_t size, EGG::Heap *parent); - void createArchiveHeap(size_t size, EGG::Heap *parent); - void createCommandHeap(size_t size, EGG::Heap *parent); - void createDylinkHeap(size_t size, EGG::Heap *parent); - EGG::Heap *createAssertHeap(EGG::Heap *parent); - - extern EGG::Heap *g_gameHeaps[3]; -}; + EGG::Heap *createGameHeap1(size_t size, EGG::Heap *parent); ///< Creates the MEM1 game heap. See createGameHeap(). + EGG::Heap *createGameHeap2(size_t size, EGG::Heap *parent); ///< Creates the MEM2 game heap. See createGameHeap(). + EGG::Heap *createArchiveHeap(size_t size, EGG::Heap *parent); ///< Creates the archive heap. See createHeap(). + EGG::Heap *createCommandHeap(size_t size, EGG::Heap *parent); ///< Creates the DVD command heap. See createHeap(). + EGG::Heap *createDylinkHeap(size_t size, EGG::Heap *parent); ///< Creates the REL linking heap. See createHeap(). + + /// @brief Creates the assert heap. + /// @details The size is determined automatically using EGG::AssertHeap::getMinSizeForCreate(). + EGG::Heap *createAssertHeap(EGG::Heap *parent); + + extern u8 g_DefaultGameHeapId; ///< The default game heap to be used if one isn't specified. + extern const char * const s_GameHeapNames[GAME_HEAP_COUNT]; ///< The game heap names. + + extern EGG::Heap *s_SavedCurrentHeap; ///< The saved current heap. + extern EGG::Heap *g_gameHeaps[GAME_HEAP_COUNT]; ///< The game heaps. + extern EGG::Heap *g_archiveHeap; ///< The archive resource heap. + extern EGG::Heap *g_commandHeap; ///< The DVD command heap. + extern EGG::Heap *g_dylinkHeap; ///< The REL linking heap. + extern EGG::Heap *g_assertHeap; ///< The assert heap. + +}; // namespace mHeap diff --git a/include/lib/revolution/MEM/mem_expHeap.h b/include/lib/revolution/MEM/mem_expHeap.h index 62f37491..043695a0 100644 --- a/include/lib/revolution/MEM/mem_expHeap.h +++ b/include/lib/revolution/MEM/mem_expHeap.h @@ -5,9 +5,8 @@ extern "C" { #endif -#define MEM_EXP_HEAP_MIN_SIZE \ - (sizeof(MEMiHeapHead) + sizeof(MEMiExpHeapHead) + \ - sizeof(MEMiExpHeapMBlock) + 4) +#define MEM_EXP_HEAP_HEAD_SIZE (sizeof(MEMiHeapHead) + sizeof(MEMiExpHeapHead)) +#define MEM_EXP_HEAP_MIN_SIZE (MEM_EXP_HEAP_HEAD_SIZE + sizeof(MEMiExpHeapMBlock) + 4) // Forward declarations typedef struct MEMiHeapHead MEMiHeapHead; diff --git a/include/lib/revolution/MEM/mem_frameHeap.h b/include/lib/revolution/MEM/mem_frameHeap.h index 9cb5a437..b410ba33 100644 --- a/include/lib/revolution/MEM/mem_frameHeap.h +++ b/include/lib/revolution/MEM/mem_frameHeap.h @@ -5,7 +5,8 @@ extern "C" { #endif -#define MEM_FRM_HEAP_MIN_SIZE (sizeof(MEMiHeapHead) + sizeof(MEMiFrmHeapHead)) +#define MEM_FRM_HEAP_HEAD_SIZE (sizeof(MEMiHeapHead) + sizeof(MEMiFrmHeapHead)) +#define MEM_FRM_HEAP_MIN_SIZE MEM_FRM_HEAP_HEAD_SIZE // Forward declarations typedef struct MEMiHeapHead MEMiHeapHead; diff --git a/source/dol/mLib/m_heap.cpp b/source/dol/mLib/m_heap.cpp index d8fcd055..00ade386 100644 --- a/source/dol/mLib/m_heap.cpp +++ b/source/dol/mLib/m_heap.cpp @@ -1,24 +1,22 @@ -#include "egg/core/eggAssertHeap.h" #include #include -namespace mHeap { - -u8 g_DefaultGameHeapId = 1; -const char * const s_GameHeapNames[3] = { +u8 mHeap::g_DefaultGameHeapId = GAME_HEAP_MEM1; +const char * const mHeap::s_GameHeapNames[GAME_HEAP_COUNT] = { nullptr, GAME_HEAP_1_NAME, GAME_HEAP_2_NAME, }; -EGG::Heap *s_SavedCurrentHeap; -EGG::Heap *g_gameHeaps[3]; -EGG::Heap *g_archiveHeap; -EGG::Heap *g_commandHeap; -EGG::Heap *g_dylinkHeap; -EGG::Heap *g_assertHeap; +EGG::Heap *mHeap::s_SavedCurrentHeap; + +EGG::Heap *mHeap::g_gameHeaps[GAME_HEAP_COUNT]; +EGG::Heap *mHeap::g_archiveHeap; +EGG::Heap *mHeap::g_commandHeap; +EGG::Heap *mHeap::g_dylinkHeap; +EGG::Heap *mHeap::g_assertHeap; -u16 GetOptFlag(AllocOptBit_t opt) { +u16 mHeap::GetOptFlag(AllocOptBit_t opt) { u16 ret = OPT_NONE; if (opt & OPT_CLEAR_ALLOC) { @@ -36,11 +34,11 @@ u16 GetOptFlag(AllocOptBit_t opt) { return ret; } -EGG::Heap *setCurrentHeap(EGG::Heap *heap) { +EGG::Heap *mHeap::setCurrentHeap(EGG::Heap *heap) { return heap->becomeCurrentHeap(); } -EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { +EGG::ExpHeap *mHeap::createExpHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { if (parent == nullptr) { parent = EGG::Heap::sCurrentHeap; } @@ -59,8 +57,7 @@ EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ul EGG::ExpHeap *heap = nullptr; if (buffer != nullptr) { - u16 flags = GetOptFlag(opt); - heap = EGG::ExpHeap::create(buffer, size, flags); + heap = EGG::ExpHeap::create(buffer, size, GetOptFlag(opt)); if (heap == nullptr) { parent->free(buffer); } else if (name != nullptr) { @@ -71,11 +68,11 @@ EGG::ExpHeap *createExpHeap(size_t size, EGG::Heap *parent, const char *name, ul return heap; } -size_t expHeapCost(size_t size, ulong align) { - return size + nw4r::ut::RoundUp(sizeof(EGG::ExpHeap) + sizeof(MEMiHeapHead) + sizeof(MEMiExpHeapHead), align); +size_t mHeap::expHeapCost(size_t size, ulong align) { + return size + nw4r::ut::RoundUp(sizeof(EGG::ExpHeap) + MEM_EXP_HEAP_HEAD_SIZE, align); } -EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { +EGG::FrmHeap *mHeap::createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { if (parent == nullptr) { parent = EGG::Heap::sCurrentHeap; } @@ -94,8 +91,7 @@ EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ul EGG::FrmHeap *heap = nullptr; if (buffer != nullptr) { - u16 flags = GetOptFlag(opt); - heap = EGG::FrmHeap::create(buffer, size, flags); + heap = EGG::FrmHeap::create(buffer, size, GetOptFlag(opt)); if (heap == nullptr) { parent->free(buffer); } else if (name != nullptr) { @@ -106,31 +102,31 @@ EGG::FrmHeap *createFrmHeap(size_t size, EGG::Heap *parent, const char *name, ul return heap; } -void destroyFrmHeap(EGG::FrmHeap *heap) { +void mHeap::destroyFrmHeap(EGG::FrmHeap *heap) { if (heap != nullptr) { heap->destroy(); } } -size_t adjustFrmHeap(EGG::FrmHeap *heap) { - size_t adjustedSize = 0; +size_t mHeap::adjustFrmHeap(EGG::FrmHeap *heap) { + size_t totalFreeSpace = 0; if (heap != nullptr) { - size_t a = heap->adjust(); - size_t cost = frmHeapCost(0, 4); - if (a >= cost) { - adjustedSize = a - cost; + size_t freeSpace = heap->adjust(); + size_t minCost = frmHeapCost(0, 4); + if (freeSpace >= minCost) { + totalFreeSpace = freeSpace - minCost; } } - return adjustedSize; + return totalFreeSpace; } -size_t frmHeapCost(size_t size, ulong align) { - return size + nw4r::ut::RoundUp(sizeof(EGG::FrmHeap) + sizeof(MEMiHeapHead) + sizeof(MEMiFrmHeapHead), align); +size_t mHeap::frmHeapCost(size_t size, ulong align) { + return size + nw4r::ut::RoundUp(sizeof(EGG::FrmHeap) + MEM_FRM_HEAP_HEAD_SIZE, align); } -EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, mHeap::AllocOptBit_t opt) { +EGG::UnitHeap *mHeap::createUntHeap(size_t size, ulong count, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { if (parent == nullptr) { parent = EGG::Heap::sCurrentHeap; } @@ -144,8 +140,7 @@ EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const void *buffer = parent->alloc(totalSize, align); if (buffer != nullptr) { - u16 flags = GetOptFlag(opt); - heap = EGG::UnitHeap::create(buffer, totalSize, size, align, flags); + heap = EGG::UnitHeap::create(buffer, totalSize, size, align, GetOptFlag(opt)); if (heap == nullptr) { parent->free(buffer); } else if (name != nullptr) { @@ -156,11 +151,11 @@ EGG::UnitHeap *createUntHeap(size_t size, ulong count, EGG::Heap *parent, const return heap; } -size_t untHeapCost(size_t size, ulong count, ulong align) { +size_t mHeap::untHeapCost(size_t size, ulong count, ulong align) { return EGG::UnitHeap::calcHeapSize(size, count, align); } -EGG::Heap *createHeap(size_t size, EGG::Heap *parent, const char *name) { +EGG::Heap *mHeap::createHeap(size_t size, EGG::Heap *parent, const char *name) { EGG::ExpHeap *heap = EGG::ExpHeap::create(size, parent, MEM_HEAP_OPT_CAN_LOCK); if (heap != nullptr) { @@ -175,16 +170,16 @@ EGG::Heap *createHeap(size_t size, EGG::Heap *parent, const char *name) { return heap; } -void saveCurrentHeap() { +void mHeap::saveCurrentHeap() { s_SavedCurrentHeap = EGG::Heap::sCurrentHeap; } -void restoreCurrentHeap() { +void mHeap::restoreCurrentHeap() { s_SavedCurrentHeap->becomeCurrentHeap(); s_SavedCurrentHeap = nullptr; } -EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { +EGG::FrmHeap *mHeap::createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char *name, ulong align, AllocOptBit_t opt) { EGG::FrmHeap *heap = createFrmHeap(size, parent, name, align, opt); if (heap != nullptr) { s_SavedCurrentHeap = setCurrentHeap(heap); @@ -193,51 +188,46 @@ EGG::FrmHeap *createFrmHeapToCurrent(size_t size, EGG::Heap *parent, const char return heap; } -EGG::Heap *createGameHeap(int idx, size_t size, EGG::Heap *parent) { - bool isValid = false; - if (1U <= idx && idx <= 2U) { - isValid = true; - } - - if (!isValid) { +EGG::Heap *mHeap::createGameHeap(int idx, size_t size, EGG::Heap *parent) { + if (!isValidGameHeapId(idx)) { return nullptr; } g_gameHeaps[idx] = createHeap(size, parent, s_GameHeapNames[idx]); - if (idx == g_DefaultGameHeapId) { - g_gameHeaps[0] = g_gameHeaps[idx]; + g_gameHeaps[GAME_HEAP_DEFAULT] = g_gameHeaps[idx]; } return g_gameHeaps[idx]; } -void createGameHeap1(size_t size, EGG::Heap *parent) { - createGameHeap(1, size, parent); +EGG::Heap *mHeap::createGameHeap1(size_t size, EGG::Heap *parent) { + return createGameHeap(GAME_HEAP_MEM1, size, parent); } -void createGameHeap2(size_t size, EGG::Heap *parent) { - createGameHeap(2, size, parent); +EGG::Heap *mHeap::createGameHeap2(size_t size, EGG::Heap *parent) { + return createGameHeap(GAME_HEAP_MEM2, size, parent); } -void createArchiveHeap(size_t size, EGG::Heap *parent) { +EGG::Heap *mHeap::createArchiveHeap(size_t size, EGG::Heap *parent) { g_archiveHeap = createHeap(size, parent, ARCHIVE_HEAP_NAME); + return g_archiveHeap; } -void createCommandHeap(size_t size, EGG::Heap *parent) { +EGG::Heap *mHeap::createCommandHeap(size_t size, EGG::Heap *parent) { g_commandHeap = createHeap(size, parent, COMMAND_HEAP_NAME); + return g_commandHeap; } -void createDylinkHeap(size_t size, EGG::Heap *parent) { +EGG::Heap *mHeap::createDylinkHeap(size_t size, EGG::Heap *parent) { g_dylinkHeap = createHeap(size, parent, DYLINK_HEAP_NAME); + return g_dylinkHeap; } -EGG::Heap *createAssertHeap(EGG::Heap *parent) { +EGG::Heap *mHeap::createAssertHeap(EGG::Heap *parent) { const char *heapName = ASSERT_HEAP_NAME; size_t size = EGG::AssertHeap::getMinSizeForCreate(); g_assertHeap = EGG::AssertHeap::create(size, parent); g_assertHeap->mpName = heapName; return g_assertHeap; } - -} \ No newline at end of file From 7bea9bac1781d6f5f21c877a3671bc4ef2bf7e5f Mon Sep 17 00:00:00 2001 From: CLF78 <29682335+CLF78@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:21:43 +0100 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Liam Braun <41953062+RootCubed@users.noreply.github.com> --- include/game/mLib/m_heap.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/game/mLib/m_heap.hpp b/include/game/mLib/m_heap.hpp index 431b07dc..b486c21a 100644 --- a/include/game/mLib/m_heap.hpp +++ b/include/game/mLib/m_heap.hpp @@ -33,6 +33,7 @@ namespace mHeap { }; /// @brief The identifiers for the predefined game heaps. + /// @unofficial enum GAME_HEAP_e { GAME_HEAP_DEFAULT, ///< The default game heap (alias of MEM1 or MEM2). GAME_HEAP_MEM1, ///< The game heap allocated in MEM1. @@ -80,7 +81,7 @@ namespace mHeap { /// @param heap The frame heap to destroy, or @p nullptr . void destroyFrmHeap(EGG::FrmHeap *heap); - /// @brief Adjusts a frame heap to compress unused memory. + /// @brief Adjusts a frame heap to release unused memory. /// @param heap The frame heap to adjust, or @p nullptr . /// @return The total available space in the heap, or @p 0 if the adjust operation failed. size_t adjustFrmHeap(EGG::FrmHeap *heap); From db6280e7195a692f1b6982780ab25316259e9554 Mon Sep 17 00:00:00 2001 From: CLF78 <29682335+CLF78@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:22:38 +0100 Subject: [PATCH 4/4] Forgot to commit this one... Co-authored-by: Liam Braun <41953062+RootCubed@users.noreply.github.com> --- source/dol/mLib/m_heap.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/source/dol/mLib/m_heap.cpp b/source/dol/mLib/m_heap.cpp index 00ade386..c440764f 100644 --- a/source/dol/mLib/m_heap.cpp +++ b/source/dol/mLib/m_heap.cpp @@ -156,7 +156,6 @@ size_t mHeap::untHeapCost(size_t size, ulong count, ulong align) { } EGG::Heap *mHeap::createHeap(size_t size, EGG::Heap *parent, const char *name) { - EGG::ExpHeap *heap = EGG::ExpHeap::create(size, parent, MEM_HEAP_OPT_CAN_LOCK); if (heap != nullptr) { heap->setAllocMode(MEM_EXP_HEAP_ALLOC_FAST);