diff --git a/src/config/config.cpp b/src/config/config.cpp index 9f90ab6..5e96d2f 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,7 +1,7 @@ #include "config.h" #include "internal/assembly.h" -#include "internal/heap.h" +#include "internal/mem.h" #include "internal/log.h" #include "internal/tickable.h" #include "patches/custom/party_game_toggle.h" @@ -159,7 +159,7 @@ void parse_config() { if (open_success) { // heap::alloc rounds to a multiple of 32, necessary for DVDReadAsyncPrio config_file_length = (config_file_info.length + 0x1f) & 0xffffffe0; - config_file_buf = static_cast(heap::alloc(config_file_length)); + config_file_buf = static_cast(mem::wsmod_heap.alloc(config_file_length)); u32 read_length = mkb::read_entire_file_using_dvdread_prio_async(&config_file_info, config_file_buf, config_file_length, 0); char* eof = config_file_buf + config_file_info.length; @@ -219,7 +219,7 @@ void parse_config() { } while (file <= eof); } mkb::DVDClose(&config_file_info); - heap::free(config_file_buf); + mem::wsmod_heap.free(config_file_buf); } } diff --git a/src/internal/arena.cpp b/src/internal/arena.cpp new file mode 100644 index 0000000..53caa62 --- /dev/null +++ b/src/internal/arena.cpp @@ -0,0 +1,34 @@ +#include "arena.h" + +#include "log.h" + +// TODO default to 4-byte alignment + +namespace arena { + +void Arena::init(const char* name, void* start, u32 size) { + m_name = name; + m_start = start; + m_capacity = size; + reset(); +} + +void Arena::reset() { + m_occupied = 0; + mkb::memset(m_start, 0, m_capacity); +} + +void* Arena::alloc(u32 size) { + u32 new_occupied = m_occupied + size; + if (new_occupied > m_capacity) { + // Not ideal way to log this + LOG("%s arena out of memory", m_name); + MOD_ASSERT(new_occupied <= m_capacity); + } + void* ret = reinterpret_cast(reinterpret_cast(m_start) + m_occupied); + m_occupied = new_occupied; + return ret; +} + +} // namespace arena + diff --git a/src/internal/arena.h b/src/internal/arena.h new file mode 100644 index 0000000..601ed53 --- /dev/null +++ b/src/internal/arena.h @@ -0,0 +1,36 @@ +#pragma once + +#include "mkb.h" + +namespace arena { + +class Arena { + public: + void init(const char* name, void* start, u32 size); + void reset(); + void* alloc(u32 size); + + template + T* alloc() { + return static_cast(alloc(sizeof(T))); + } + + template + T* alloc(u32 elem_count) { + return static_cast(alloc(sizeof(T) * elem_count)); + } + + void* get_start() { return m_start; } + void* get_pos() { return reinterpret_cast(reinterpret_cast(m_start) + m_occupied); } + u32 get_occupied() { return m_occupied; } + u32 get_free() { return m_capacity - m_occupied; } + + private: + const char* m_name; + void* m_start = nullptr; + u32 m_capacity = 0; + u32 m_occupied = 0; +}; + +} // namespace arena + diff --git a/src/internal/cardio.cpp b/src/internal/cardio.cpp index 103ca03..70faf2a 100644 --- a/src/internal/cardio.cpp +++ b/src/internal/cardio.cpp @@ -1,5 +1,5 @@ #include "cardio.h" -#include "heap.h" +#include "mem.h" #include "mkb.h" #include "modlink.h" #include @@ -28,7 +28,7 @@ static Slot s_current_slot = Slot::None; // We need a 40KB(!) buffer just for the privilege of accessing memory cards, // this sucks! Reminder we only have ~550KB to work with for the entire mod, // including savestates -static void* s_card_work_area; +static u8 s_card_work_area[mkb::CARD_WORKAREA_SIZE] __attribute__((__aligned__(32))); static mkb::CARDFileInfo s_card_file_info; static WriteState s_state = WriteState::Idle; @@ -81,7 +81,7 @@ static mkb::CARDResult read_file_internal(const Slot slot, const char* file_name u32 buf_size = (stat.length + mkb::CARD_READ_SIZE - 1) & ~(mkb::CARD_READ_SIZE - 1); - void* buf = heap::alloc(buf_size); + void* buf = mem::wsmod_heap.alloc(buf_size); if (buf == nullptr) { // Not quite the right error (we're out of memory, not out of card space) mkb::CARDUnmount(static_cast(slot)); @@ -93,7 +93,7 @@ static mkb::CARDResult read_file_internal(const Slot slot, const char* file_name res = mkb::CARDGetResultCode(static_cast(slot)); } while (res == mkb::CARD_RESULT_BUSY); if (res != mkb::CARD_RESULT_READY) { - heap::free(buf); + mem::wsmod_heap.free(buf); mkb::CARDUnmount(static_cast(slot)); return res; } @@ -120,15 +120,6 @@ static void finish_write(mkb::CARDResult res) { } void init() { - // Artificial delay so that the memcard has time to initialize - // Probably not an issue on console, but if read speed emulation is disabled in Dolphin, this can cause issues - // auto current_tick = mkb::OSGetTick(); - // auto end_tick = current_tick + mkb::BUS_CLOCK_SPEED/32; // 0.125s delay - // while (current_tick < end_tick) { - // current_tick = mkb::OSGetTick(); - // } - - s_card_work_area = heap::alloc(mkb::CARD_WORKAREA_SIZE); modlink::set_card_work_area(s_card_work_area); } diff --git a/src/internal/cxx.cpp b/src/internal/cxx.cpp index c04509a..86da5b9 100644 --- a/src/internal/cxx.cpp +++ b/src/internal/cxx.cpp @@ -1,25 +1,25 @@ -#include "heap.h" +#include "mem.h" void* operator new(u32 size) { - return heap::alloc(size); + return mem::wsmod_heap.alloc(size); } void* operator new[](u32 size) { - return heap::alloc(size); + return mem::wsmod_heap.alloc(size); } void operator delete(void* ptr) { - heap::free(ptr); + mem::wsmod_heap.free(ptr); } void operator delete[](void* ptr) { - heap::free(ptr); + mem::wsmod_heap.free(ptr); } void operator delete(void* ptr, u32 size) { - heap::free(ptr); + mem::wsmod_heap.free(ptr); } void operator delete[](void* ptr, u32 size) { - heap::free(ptr); + mem::wsmod_heap.free(ptr); } diff --git a/src/internal/heap.cpp b/src/internal/heap.cpp index 0409542..339dbe8 100644 --- a/src/internal/heap.cpp +++ b/src/internal/heap.cpp @@ -1,28 +1,25 @@ #include "heap.h" -#include "mkb/mkb.h" -#include "relutil.h" +#include #include +#include "log.h" namespace heap { -static mkb::HeapInfo s_heap_info; - -mkb::ChunkInfo* extract_chunk(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { +static mkb::ChunkInfo* extract_chunk(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { if (chunk->next) { chunk->next->prev = chunk->prev; } if (!chunk->prev) { return chunk->next; - } - else { + } else { chunk->prev->next = chunk->next; return list; } } -mkb::ChunkInfo* add_chunk_to_front(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { +static mkb::ChunkInfo* add_chunk_to_front(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { chunk->next = list; chunk->prev = nullptr; @@ -33,7 +30,7 @@ mkb::ChunkInfo* add_chunk_to_front(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) return chunk; } -mkb::ChunkInfo* find_chunk_in_list(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { +static mkb::ChunkInfo* find_chunk_in_list(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) { for (; list; list = list->next) { if (list == chunk) { return list; @@ -42,23 +39,17 @@ mkb::ChunkInfo* find_chunk_in_list(mkb::ChunkInfo* list, mkb::ChunkInfo* chunk) return nullptr; } -static void make_heap() { - u32 start = mkb::OSRoundUp32B(*reinterpret_cast(0x8000452C)); - void* end_ptr = relutil::compute_mainloop_reldata_boundary(reinterpret_cast(start)); - u32 end = mkb::OSRoundDown32B(reinterpret_cast(end_ptr)); - u32 size = end - start; - +void Heap::init(void* start, u32 size) { mkb::memset(reinterpret_cast(start), 0, size); - - s_heap_info.capacity = size; - s_heap_info.first_free = reinterpret_cast(start); - s_heap_info.first_free->next = nullptr; - s_heap_info.first_free->prev = nullptr; - s_heap_info.first_free->size = size; - s_heap_info.first_used = nullptr; + m_heap_info.capacity = size; + m_heap_info.first_free = reinterpret_cast(start); + m_heap_info.first_free->next = nullptr; + m_heap_info.first_free->prev = nullptr; + m_heap_info.first_free->size = size; + m_heap_info.first_used = nullptr; } -void* alloc(u32 size) { +void* Heap::alloc(u32 size) { // Enlarge size to the smallest possible chunk size u32 new_size = size + mkb::OSRoundUp32B(sizeof(mkb::ChunkInfo)); new_size = mkb::OSRoundUp32B(new_size); @@ -66,7 +57,7 @@ void* alloc(u32 size) { mkb::ChunkInfo* temp_chunk = nullptr; // Find a memory area large enough - for (temp_chunk = s_heap_info.first_free; temp_chunk; temp_chunk = temp_chunk->next) { + for (temp_chunk = m_heap_info.first_free; temp_chunk; temp_chunk = temp_chunk->next) { if (new_size <= temp_chunk->size) { break; } @@ -84,9 +75,8 @@ void* alloc(u32 size) { // Check if the current chunk can be split into two pieces if (leftover_size < min_size) { // Too small to split, so just extract it - s_heap_info.first_free = extract_chunk(s_heap_info.first_free, temp_chunk); - } - else { + m_heap_info.first_free = extract_chunk(m_heap_info.first_free, temp_chunk); + } else { // Large enough to split temp_chunk->size = static_cast(new_size); @@ -105,14 +95,13 @@ void* alloc(u32 size) { if (new_chunk->prev) { new_chunk->prev->next = new_chunk; - } - else { - s_heap_info.first_free = new_chunk; + } else { + m_heap_info.first_free = new_chunk; } } // Add the chunk to the allocated list - s_heap_info.first_used = add_chunk_to_front(s_heap_info.first_used, temp_chunk); + m_heap_info.first_used = add_chunk_to_front(m_heap_info.first_used, temp_chunk); // Add the header size to the chunk void* allocated_memory = reinterpret_cast(reinterpret_cast(temp_chunk) + @@ -122,7 +111,7 @@ void* alloc(u32 size) { return allocated_memory; } -bool free(void* ptr) { +bool Heap::free(void* ptr) { u32 ptr_raw = reinterpret_cast(ptr); u32 header_size = mkb::OSRoundUp32B(sizeof(mkb::ChunkInfo)); @@ -131,34 +120,34 @@ bool free(void* ptr) { mkb::ChunkInfo* temp_chunk = reinterpret_cast(ptr_raw - header_size); // Make sure ptr is actually allocated - if (!find_chunk_in_list(s_heap_info.first_used, temp_chunk)) { + if (!find_chunk_in_list(m_heap_info.first_used, temp_chunk)) { return false; } // Extract the chunk from the allocated list - s_heap_info.first_used = extract_chunk(s_heap_info.first_used, temp_chunk); + m_heap_info.first_used = extract_chunk(m_heap_info.first_used, temp_chunk); // Add in sorted order to the free list - s_heap_info.first_free = mkb::DLInsert(s_heap_info.first_free, temp_chunk); + m_heap_info.first_free = mkb::DLInsert(m_heap_info.first_free, temp_chunk); return true; } -u32 get_free_space() { +u32 Heap::get_free_space() { u32 space = 0; - for (mkb::ChunkInfo* chunk = s_heap_info.first_free; chunk; chunk = chunk->next) { - space += chunk->size - 32;// Don't count the ChunkInfo + for (mkb::ChunkInfo* chunk = m_heap_info.first_free; chunk; chunk = chunk->next) { + space += chunk->size - 32; // Don't count the ChunkInfo } return space; } -u32 get_total_space() { return s_heap_info.capacity; } +u32 Heap::get_total_space() { return m_heap_info.capacity; } -void check_integrity() { +void Heap::check_integrity() { bool valid = true; mkb::ChunkInfo* current_chunk = nullptr; mkb::ChunkInfo* prev_chunk = nullptr; - for (current_chunk = s_heap_info.first_used; current_chunk; + for (current_chunk = m_heap_info.first_used; current_chunk; current_chunk = current_chunk->next) { // Check pointer sanity auto check_if_pointer_is_valid = [](void* ptr) { @@ -187,17 +176,12 @@ void check_integrity() { } if (!valid) { - // Print the error message to the console - mkb::OSReport("Heap corrupt at 0x%08" PRIx32 "\n", reinterpret_cast(current_chunk)); + // Not ideal way to log this + LOG("Heap corrupt at 0x%08" PRIx32 "\n", reinterpret_cast(current_chunk)); + MOD_ASSERT(valid); } } -void init() { - make_heap(); -} - -mkb::HeapInfo& get_heap_info() { - return s_heap_info; -} +mkb::HeapInfo& Heap::get_heap_info() { return m_heap_info; } -}// namespace heap +} // namespace heap diff --git a/src/internal/heap.h b/src/internal/heap.h index 9396541..2bae93e 100644 --- a/src/internal/heap.h +++ b/src/internal/heap.h @@ -4,12 +4,30 @@ namespace heap { -void init(); -void* alloc(u32 size); -bool free(void* ptr); -void check_integrity(); -u32 get_free_space(); -u32 get_total_space(); -mkb::HeapInfo& get_heap_info(); - -}// namespace heap +class Heap { + public: + void init(void* start, u32 size); + void* alloc(u32 size); + + template + T* alloc() { + return static_cast(sizeof(T)); + } + + template + T* alloc(u32 elem_count) { + return static_cast(sizeof(T) * elem_count); + } + + bool free(void* ptr); + void check_integrity(); + u32 get_free_space(); + u32 get_total_space(); + mkb::HeapInfo& get_heap_info(); + + private: + mkb::HeapInfo m_heap_info; +}; + +} // namespace heap + diff --git a/src/internal/mem.cpp b/src/internal/mem.cpp new file mode 100644 index 0000000..169d5e6 --- /dev/null +++ b/src/internal/mem.cpp @@ -0,0 +1,8 @@ +#include "mem.h" + +namespace mem { + arena::Arena wsmod_arena; + heap::Heap wsmod_heap; + heap::Heap chainload_heap; +} + diff --git a/src/internal/mem.h b/src/internal/mem.h new file mode 100644 index 0000000..203dc35 --- /dev/null +++ b/src/internal/mem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "arena.h" +#include "heap.h" + +// +// wsmod-wide dynamic memory model, consisting of arenas and heaps +// + +namespace mem { + // Arena spanning the remaining mainloop.rel relocation data free space. + extern arena::Arena wsmod_arena; + + // Separate heap for wsmod's dynamic allocations. Allocated at the start of wsmod_arena. + extern heap::Heap wsmod_heap; + + // Heap allocated at end of remaining space in wsmod arena. This is what additional chainloaded + // mods (like Practice Mod) are loaded in and provided as their own heap. + extern heap::Heap chainload_heap; +} + diff --git a/src/internal/modlink.cpp b/src/internal/modlink.cpp index 746698c..4bc4c8d 100644 --- a/src/internal/modlink.cpp +++ b/src/internal/modlink.cpp @@ -1,6 +1,6 @@ #include "modlink.h" -#include "heap.h" +#include "mem.h" #include "version.h" namespace modlink { @@ -15,8 +15,8 @@ void write() { link->magic = MAGIC; link->modlink_version = {1, 1, 0}; link->wsmod_version = version::WSMOD_VERSION; - link->malloc_func = heap::alloc; - link->heap_info = &heap::get_heap_info(); + link->malloc_func = [](u32 size) { return mem::chainload_heap.alloc(size); }; + link->heap_info = &mem::chainload_heap.get_heap_info(); link->part2 = &s_part2; } diff --git a/src/main.cpp b/src/main.cpp index 4f1636e..db39b55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,9 @@ #include "internal/tickable.h" #include "internal/ui/ui_manager.h" #include "internal/version.h" +#include "internal/mem.h" #include "mkb/mkb.h" +#include "relutil.h" #include "stardust/savedata.h" namespace main { @@ -30,13 +32,34 @@ since it reports every frame, and thus clutters the console */ patch::write_nop(reinterpret_cast(0x80299f54)); } +static constexpr u32 WSMOD_HEAP_SIZE = 1024 * 80; + +static void init_memory_model() { + // Allocate wsmod arena from available mainloop REL space + u32 start = mkb::OSRoundUp32B( + *reinterpret_cast(0x8000452C)); // Set by REL loader to region after wsmod REL/BSS + void* end_ptr = relutil::compute_mainloop_reldata_boundary(reinterpret_cast(start)); + u32 end = mkb::OSRoundDown32B(reinterpret_cast(end_ptr)); + u32 size = end - start; + mem::wsmod_arena.init("wsmod", reinterpret_cast(start), size); + + // Allocate separate heap for wsmod + void* wsmod_heap_start = mem::wsmod_arena.alloc(WSMOD_HEAP_SIZE); + mem::wsmod_heap.init(wsmod_heap_start, WSMOD_HEAP_SIZE); + + // Allocate chainload heap from remaining wsmod arena + u32 chainload_heap_size = mem::wsmod_arena.get_free(); + void* chainload_heap_start = mem::wsmod_arena.alloc(chainload_heap_size); + mem::chainload_heap.init(chainload_heap_start, chainload_heap_size); +} + void init() { LOG("SMB2 Workshop Mod v%d.%d.%d loaded", version::WSMOD_VERSION.major, version::WSMOD_VERSION.minor, version::WSMOD_VERSION.patch); - heap::init(); + init_memory_model(); cardio::init(); modlink::write(); diff --git a/src/patches/extensions/stage_author_names.cpp b/src/patches/extensions/stage_author_names.cpp index a33c8fb..f84c806 100644 --- a/src/patches/extensions/stage_author_names.cpp +++ b/src/patches/extensions/stage_author_names.cpp @@ -1,7 +1,7 @@ #include "stage_author_names.h" -#include "internal/heap.h" #include "internal/log.h" +#include "internal/mem.h" #include "internal/patch.h" #include "internal/tickable.h" #include "mkb/mkb.h" @@ -88,7 +88,7 @@ void init_main_loop() { // Round the length of the author file to a multiple of 32, necessary for DVDReadAsyncPrio author_file_length = (author_file_info.length + 0x1f) & 0xffffffe0; - author_file_buf = static_cast(heap::alloc(author_file_length)); + author_file_buf = static_cast(mem::wsmod_heap.alloc(author_file_length)); author_file_length = mkb::read_entire_file_using_dvdread_prio_async(&author_file_info, author_file_buf, author_file_length, 0); char* eof = author_file_buf + author_file_info.length; diff --git a/src/stardust/galactic_log.cpp b/src/stardust/galactic_log.cpp index 33a9ca6..1398aef 100644 --- a/src/stardust/galactic_log.cpp +++ b/src/stardust/galactic_log.cpp @@ -363,64 +363,39 @@ void create_galactic_log_menu() { // Handler for the 'About' button auto open_about_handler = [](ui::Widget&, void*) { - if (heap::get_free_space() > 40 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); - s_galactic_log_index = menu.get_active_index(); - ui::get_widget_manager().remove(menu); - create_about_screen(); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" - } + auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); + s_galactic_log_index = menu.get_active_index(); + ui::get_widget_manager().remove(menu); + create_about_screen(); }; // Handler for the 'Credits & Special Thanks' button auto open_credits_handler = [](ui::Widget&, void*) { - if (heap::get_free_space() > 40 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); - s_galactic_log_index = menu.get_active_index(); - ui::get_widget_manager().remove(menu); - create_credits_screen(); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" - } + auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); + s_galactic_log_index = menu.get_active_index(); + ui::get_widget_manager().remove(menu); + create_credits_screen(); }; auto open_badge_handler = [](ui::Widget&, void*) { - if (heap::get_free_space() > 80 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); - s_galactic_log_index = menu.get_active_index(); - ui::get_widget_manager().remove(menu); - create_badge_screen(); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" - } + auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); + s_galactic_log_index = menu.get_active_index(); + ui::get_widget_manager().remove(menu); + create_badge_screen(); }; auto open_interstellar_handler = [](ui::Widget&, void*) { - if (heap::get_free_space() > 40 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); - s_galactic_log_index = menu.get_active_index(); - ui::get_widget_manager().remove(menu); - create_interstellar_screen(); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" - } + auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); + s_galactic_log_index = menu.get_active_index(); + ui::get_widget_manager().remove(menu); + create_interstellar_screen(); }; auto open_achievement_handler = [](ui::Widget&, void*) { - if (heap::get_free_space() > 60 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); - s_galactic_log_index = menu.get_active_index(); - ui::get_widget_manager().remove(menu); - create_achievement_screen(); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" - } + auto& menu = static_cast(ui::get_widget_manager().find("galmenu")); + s_galactic_log_index = menu.get_active_index(); + ui::get_widget_manager().remove(menu); + create_achievement_screen(); }; // Handle for 'Close' button, as well as B/Start buttons @@ -453,7 +428,6 @@ void create_galactic_log_menu() { } ui::get_widget_manager().remove("galmenu"); - LOG("After closing free heap: %dkb", heap::get_free_space() / 1024); }; // Hack for making these children widgets appear above the pause menu screen overlay @@ -1184,20 +1158,12 @@ void create_achievement_screen() { void init_main_loop() { patch::hook_function(s_g_create_how_to_sprite_tramp, mkb::create_how_to_sprite, [](void) { - if (heap::get_free_space() > 20 * 1024) {// TEMP FIX: Make sure we're not about to crash! (Practice Mod savestates) - if (mkb::main_mode == mkb::MD_GAME) { - mkb::g_some_pausemenu_var = 4; - mkb::g_some_other_flags = mkb::g_some_other_flags | mkb::OF_GAME_PAUSED; - } - mkb::call_SoundReqID_arg_1(10); - LOG("Heap free before: %dkb", heap::get_free_space() / 1024); - create_galactic_log_menu(); - LOG("Heap free after: %dkb", heap::get_free_space() / 1024); - } - else { - mkb::call_SoundReqID_arg_1(35);// Announcer saying "ZERO!" + if (mkb::main_mode == mkb::MD_GAME) { + mkb::g_some_pausemenu_var = 4; + mkb::g_some_other_flags = mkb::g_some_other_flags | mkb::OF_GAME_PAUSED; } - return; + mkb::call_SoundReqID_arg_1(10); + create_galactic_log_menu(); }); patch::hook_function(s_check_pause_menu_input, mkb::check_pause_menu_input, [](mkb::Sprite* s) { diff --git a/src/stardust/savedata.cpp b/src/stardust/savedata.cpp index 34f4510..1252c48 100644 --- a/src/stardust/savedata.cpp +++ b/src/stardust/savedata.cpp @@ -1,7 +1,6 @@ #include "savedata.h" #include "../internal/cardio.h" -#include "../internal/draw.h" -#include "../internal/heap.h" +#include "../internal/mem.h" #include "../mkb/mkb.h" namespace savedata { @@ -225,7 +224,7 @@ void init() { s32 result_A = cardio::read_file(cardio::Slot::A, FILENAME, reinterpret_cast(&header)); if (result_A == mkb::CARD_RESULT_READY) { to_array(header); - heap::free(header); + mem::wsmod_heap.free(header); // mkb::OSReport("[stardust] Savedata loaded from Slot A!\n"); cardio::set_slot(cardio::Slot::A); return; @@ -236,7 +235,7 @@ void init() { s32 result_B = cardio::read_file(cardio::Slot::B, FILENAME, reinterpret_cast(&header)); if (result_B == mkb::CARD_RESULT_READY) { to_array(header); - heap::free(header); + mem::wsmod_heap.free(header); // mkb::OSReport("[stardust] Savedata loaded from Slot B!\n"); cardio::set_slot(cardio::Slot::B); return;