From e881a04f8645166183cfb6dbeed87cdfed4a658a Mon Sep 17 00:00:00 2001 From: EmeraldLockdown <86802223+EmeraldLoc@users.noreply.github.com> Date: Sun, 1 Mar 2026 19:57:43 -0600 Subject: [PATCH] YAAAA completely change up save system AUWEGBSLIUFJSAI --- autogen/convert_functions.py | 3 +- autogen/lua_definitions/constants.lua | 2 +- autogen/lua_definitions/functions.lua | 7 - docs/lua/functions-6.md | 24 ---- docs/lua/functions.md | 1 - include/PR/os_eeprom.h | 6 +- src/buffers/buffers.c | 2 +- src/game/save_file.c | 190 +++----------------------- src/game/save_file.h | 38 ++---- src/pc/configfile.c | 8 +- src/pc/configfile.h | 4 +- src/pc/djui/djui_panel_host_save.c | 9 +- src/pc/fs/fs.h | 4 +- src/pc/lua/smlua_constants_autogen.c | 2 +- src/pc/lua/smlua_functions_autogen.c | 20 --- src/pc/mods/mod_storage.cpp | 4 +- src/pc/mods/mod_storage.h | 2 +- src/pc/network/packets/packet_join.c | 10 +- src/pc/ultra_reimplementation.c | 29 ++-- 19 files changed, 79 insertions(+), 286 deletions(-) diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index 8846db56f..e34a52c2e 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -89,7 +89,7 @@ override_allowed_functions = { "src/pc/djui/djui_popup.h": [ "create" ], "src/pc/djui/djui_language.h": [ "djui_language_get" ], "src/pc/djui/djui_panel_menu.h": [ "djui_menu_get_rainbow_string_color" ], - "src/game/save_file.h": [ "get_level_", "save_file_get_", "save_file_set_flags", "save_file_clear_flags", "save_file_reload", "save_file_erase_current_backup_save", "save_file_set_star_flags", "save_file_is_cannon_unlocked", "save_file_set_cannon_unlocked", "touch_coin_score_age", "save_file_set_course_coin_score", "save_file_do_save", "save_file_remove_star_flags", "save_file_erase" ], + "src/game/save_file.h": [ "get_level_", "save_file_get_", "save_file_set_flags", "save_file_clear_flags", "save_file_reload", "save_file_erase_current_backup_save", "save_file_set_star_flags", "save_file_is_cannon_unlocked", "save_file_set_cannon_unlocked", "save_file_set_course_coin_score", "save_file_do_save", "save_file_remove_star_flags", "save_file_erase" ], "src/pc/lua/utils/smlua_model_utils.h": [ "smlua_model_util_get_id" ], "src/game/object_list_processor.h": [ "set_object_respawn_info_bits" ], "src/game/platform_displacement.h": [ "apply_platform_displacement" ], @@ -119,6 +119,7 @@ override_disallowed_functions = { "src/pc/djui/djui_console.h": [ " djui_console_create", "djui_console_message_create", "djui_console_message_dequeue" ], "src/pc/djui/djui_chat_message.h": [ "create_from" ], "src/game/interaction.h": [ "process_interaction", "_handle_" ], + "src/game/save_file.h": [ "save_file_get_dir" ], "src/game/sound_init.h": [ "_loop_", "thread4_", "set_sound_mode" ], "src/pc/network/network_utils.h": [ "network_get_player_text_color[^_]" ], "src/pc/network/network_player.h": [ "_init", "_connected[^_]", "_shutdown", "_disconnected", "_update", "construct_player_popup", "network_player_name_valid" ], diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index b1fd233f5..7ce77e0fa 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -6797,7 +6797,7 @@ METAL = CAP --- @type PlayerPart --- | `METAL` --- @type integer -NUM_SAVE_FILES = 4 +NUM_SAVE_FILES = 32 SAVE_FILE_A = 0 --- @type SaveFileIndex SAVE_FILE_B = 1 --- @type SaveFileIndex diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index f7b0aa63d..524e2aac0 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -9946,13 +9946,6 @@ function get_level_course_num(levelNum) -- ... end ---- @param fileIndex integer ---- @param courseIndex integer ---- Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress -function touch_coin_score_age(fileIndex, courseIndex) - -- ... -end - --- @param fileIndex integer --- @param forceSave integer --- Saves the current state of the game into a specified save file. Includes data verification and backup management. Useful for maintaining game progress during play or when saving manually diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md index afd96447e..d3070b04a 100644 --- a/docs/lua/functions-6.md +++ b/docs/lua/functions-6.md @@ -4805,30 +4805,6 @@ Gets the level number's corresponding course number
-## [touch_coin_score_age](#touch_coin_score_age) - -### Description -Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress - -### Lua Example -`touch_coin_score_age(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- None - -### C Prototype -`void touch_coin_score_age(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- ## [save_file_do_save](#save_file_do_save) ### Description diff --git a/docs/lua/functions.md b/docs/lua/functions.md index e2f94b46b..9804e8424 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1779,7 +1779,6 @@ - save_file.h - [get_level_num_from_course_num](functions-6.md#get_level_num_from_course_num) - [get_level_course_num](functions-6.md#get_level_course_num) - - [touch_coin_score_age](functions-6.md#touch_coin_score_age) - [save_file_do_save](functions-6.md#save_file_do_save) - [save_file_erase](functions-6.md#save_file_erase) - [save_file_erase_current_backup_save](functions-6.md#save_file_erase_current_backup_save) diff --git a/include/PR/os_eeprom.h b/include/PR/os_eeprom.h index b3bca8144..63a9e14c9 100644 --- a/include/PR/os_eeprom.h +++ b/include/PR/os_eeprom.h @@ -21,7 +21,7 @@ /*---------------------------------------------------------------------* Copyright (C) 1998 Nintendo. (Originated by SGI) - + $RCSfile: os_eeprom.h,v $ $Revision: 1.1 $ $Date: 1998/10/09 08:01:06 $ @@ -94,8 +94,8 @@ extern "C" { extern s32 osEepromProbe(OSMesgQueue *); extern s32 osEepromRead(OSMesgQueue *, u8, u8 *); extern s32 osEepromWrite(OSMesgQueue *, u8, u8 *); -extern s32 osEepromLongRead(OSMesgQueue *, u8, u8 *, int); -extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8 *, int); +extern s32 osEepromLongRead(OSMesgQueue *, u8, u8, u8 *, int); +extern s32 osEepromLongWrite(OSMesgQueue *, u8, u8, u8 *, int); #endif /* defined(_LANGUAGE_C) || defined(_LANGUAGE_C_PLUS_PLUS) */ diff --git a/src/buffers/buffers.c b/src/buffers/buffers.c index 063aa7bdd..fb918d5a2 100644 --- a/src/buffers/buffers.c +++ b/src/buffers/buffers.c @@ -23,7 +23,7 @@ ALIGNED8 u8 gThread6Stack[0x2000]; ALIGNED8 u8 gGfxSPTaskStack[SP_DRAM_STACK_SIZE8] = { 0 }; // 0xc00 bytes for f3dex, 0x900 otherwise ALIGNED8 u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE] = { 0 }; -// 0x200 bytes +// 0x80 bytes ALIGNED8 struct SaveBuffer gSaveBuffer = { 0 }; // 0x190a0 bytes struct GfxPool gGfxPools[GFX_NUM_POOLS] = { 0 }; diff --git a/src/game/save_file.c b/src/game/save_file.c index 91d9350fb..0d692a9e8 100644 --- a/src/game/save_file.c +++ b/src/game/save_file.c @@ -20,7 +20,6 @@ #define bcopy(b1,b2,len) (memmove((b2), (b1), (len)), (void) 0) #endif -#define MENU_DATA_MAGIC 0x4849 #define SAVE_FILE_MAGIC 0x4441 #define INVALID_FILE_INDEX(_fi) ((u32)_fi >= NUM_SAVE_FILES) @@ -29,13 +28,12 @@ #define INVALID_COURSE_STAR_INDEX(_ci) ((u32)_ci >= COURSE_COUNT) #define INVALID_COURSE_COIN_INDEX(_ci) ((u32)_ci >= COURSE_STAGES_COUNT) -STATIC_ASSERT(sizeof(struct SaveBuffer) == EEPROM_SIZE, "eeprom buffer size must match"); +STATIC_ASSERT(sizeof(struct SingleSaveFile) == EEPROM_SIZE, "eeprom buffer size must match"); extern struct SaveBuffer gSaveBuffer; struct WarpCheckpoint gWarpCheckpoint; -s8 gMainMenuDataModified; s8 gSaveFileModified; u8 gLastCompletedCourseNum = COURSE_NONE; @@ -100,10 +98,9 @@ s8 get_level_course_num(s16 levelNum) { return gLevelToCourseNumTable[levelNum]; } -// This was probably used to set progress to 100% for debugging, but -// it was removed from the release ROM. -static void stub_save_file_1(void) { - UNUSED s32 pad; +void save_file_get_dir(int fileIndex, char* outPath, size_t size) { + snprintf(outPath, size, "%s%s%d.bin", SAVE_DIRECTORY, SAVE_FILENAME, fileIndex); + printf("Returning filepath: %s\n", outPath); } /** @@ -114,19 +111,6 @@ static inline void bswap_signature(struct SaveBlockSignature *data) { data->chksum = BSWAP16(data->chksum); // valid as long as the checksum is a literal sum } -/** - * Byteswap all multibyte fields in a MainMenuSaveData. - */ -static inline void bswap_menudata(struct MainMenuSaveData *data) { - for (s32 i = 0; i < NUM_SAVE_FILES; ++i) - data->coinScoreAges[i] = BSWAP32(data->coinScoreAges[i]); - data->soundMode = BSWAP16(data->soundMode); -#ifdef VERSION_EU - data->language = BSWAP16(data->language); -#endif - bswap_signature(&data->signature); -} - /** * Byteswap all multibyte fields in a SaveFile. */ @@ -144,17 +128,17 @@ static inline void bswap_savefile(struct SaveFile *data) { * Try at most 4 times, and return 0 on success. On failure, return the status returned from * osEepromLongRead. It also returns 0 if EEPROM isn't loaded correctly in the system. */ -static s32 read_eeprom_data(void *buffer, s32 size) { +static s32 read_eeprom_data(u8 file, void *buffer, s32 size) { s32 status = 0; if (gEepromProbe != 0) { s32 triesLeft = 4; - u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer) / 8; + u32 offset = (u32)((u8 *) buffer - (u8 *) &gSaveBuffer.files[file]) / 8; do { block_until_rumble_pak_free(); triesLeft--; - status = osEepromLongRead(&gSIEventMesgQueue, offset, buffer, size); + status = osEepromLongRead(&gSIEventMesgQueue, file, offset, buffer, size); release_rumble_pak_control(); } while (triesLeft > 0 && status != 0); } @@ -168,7 +152,7 @@ static s32 read_eeprom_data(void *buffer, s32 size) { * Try at most 4 times, and return 0 on success. On failure, return the status returned from * osEepromLongWrite. Unlike read_eeprom_data, return 1 if EEPROM isn't loaded. */ -static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) { +static s32 write_eeprom_data(u8 file, void *buffer, s32 size, const uintptr_t baseofs) { s32 status = 1; if (gEepromProbe != 0) { @@ -178,7 +162,7 @@ static s32 write_eeprom_data(void *buffer, s32 size, const uintptr_t baseofs) { do { block_until_rumble_pak_free(); triesLeft--; - status = osEepromLongWrite(&gSIEventMesgQueue, offset, buffer, size); + status = osEepromLongWrite(&gSIEventMesgQueue, file, offset, buffer, size); release_rumble_pak_control(); } while (triesLeft > 0 && status != 0); } @@ -194,32 +178,16 @@ static inline s32 write_eeprom_savefile(const u32 file, const u32 slot, const u3 if (INVALID_FILE_INDEX(file)) { return 0; } if (INVALID_SRC_SLOT(slot)) { return 0; } // calculate the EEPROM address using the file number and slot - const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer; + const uintptr_t ofs = (u8*)&gSaveBuffer.files[file][slot] - (u8*)&gSaveBuffer.files[file]; #if IS_BIG_ENDIAN - return write_eeprom_data(&gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs); + return write_eeprom_data(file, &gSaveBuffer.files[file][slot], num * sizeof(struct SaveFile), ofs); #else // byteswap the data and then write it struct SaveFile sf[num]; bcopy(&gSaveBuffer.files[file][slot], sf, num * sizeof(sf[0])); for (u32 i = 0; i < num; ++i) bswap_savefile(&sf[i]); - return write_eeprom_data(&sf, sizeof(sf), ofs); -#endif -} - -static inline s32 write_eeprom_menudata(const u32 slot, const u32 num) { - if (INVALID_SRC_SLOT(slot)) { return 0; } - // calculate the EEPROM address using the slot - const uintptr_t ofs = (u8*)&gSaveBuffer.menuData[slot] - (u8*)&gSaveBuffer; - -#if IS_BIG_ENDIAN - return write_eeprom_data(&gSaveBuffer.menuData[slot], num * sizeof(struct MainMenuSaveData), ofs); -#else - // byteswap the data and then write it - struct MainMenuSaveData md[num]; - bcopy(&gSaveBuffer.menuData[slot], md, num * sizeof(md[0])); - for (u32 i = 0; i < num; ++i) bswap_menudata(&md[i]); - return write_eeprom_data(&md, sizeof(md), ofs); + return write_eeprom_data(file, &sf, sizeof(sf), ofs); #endif } @@ -261,98 +229,6 @@ static void add_save_block_signature(void *buffer, s32 size, u16 magic) { sig->chksum = calc_checksum(buffer, size); } -/** - * Copy main menu data from one backup slot to the other slot. - */ -UNUSED static void restore_main_menu_data(s32 srcSlot) { - if (INVALID_SRC_SLOT(srcSlot)) { return; } - s32 destSlot = srcSlot ^ 1; - if (INVALID_SRC_SLOT(destSlot)) { return; } - - // Compute checksum on source data - add_save_block_signature(&gSaveBuffer.menuData[srcSlot], sizeof(gSaveBuffer.menuData[srcSlot]), MENU_DATA_MAGIC); - - // Copy source data to destination - bcopy(&gSaveBuffer.menuData[srcSlot], &gSaveBuffer.menuData[destSlot], sizeof(gSaveBuffer.menuData[destSlot])); - - // Write destination data to EEPROM - write_eeprom_menudata(destSlot, 1); -} - -static void save_main_menu_data(void) { - if (gMainMenuDataModified) { - // Compute checksum - add_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); - - // Back up data - bcopy(&gSaveBuffer.menuData[0], &gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1])); - - // Write to EEPROM - write_eeprom_menudata(0, 2); - - gMainMenuDataModified = FALSE; - } -} - -UNUSED static void wipe_main_menu_data(void) { - bzero(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0])); - - // Set score ages for all courses to 3, 2, 1, and 0, respectively. - gSaveBuffer.menuData[0].coinScoreAges[0] = 0x3FFFFFFF; - gSaveBuffer.menuData[0].coinScoreAges[1] = 0x2AAAAAAA; - gSaveBuffer.menuData[0].coinScoreAges[2] = 0x15555555; - - gMainMenuDataModified = TRUE; - save_main_menu_data(); -} - -static s32 get_coin_score_age(s32 fileIndex, s32 courseIndex) { - if (INVALID_FILE_INDEX(fileIndex)) { return 0; } - return (gSaveBuffer.menuData[0].coinScoreAges[fileIndex] >> (2 * courseIndex)) & 0x3; -} - -static void set_coin_score_age(s32 fileIndex, s32 courseIndex, s32 age) { - if (INVALID_FILE_INDEX(fileIndex)) { return; } - s32 mask = 0x3 << (2 * courseIndex); - - gSaveBuffer.menuData[0].coinScoreAges[fileIndex] &= ~mask; - gSaveBuffer.menuData[0].coinScoreAges[fileIndex] |= age << (2 * courseIndex); -} - -/** - * Mark a coin score for a save file as the newest out of all save files. - */ -void touch_coin_score_age(s32 fileIndex, s32 courseIndex) { - if (INVALID_FILE_INDEX(fileIndex)) { return; } - s32 i; - u32 age; - u32 currentAge = get_coin_score_age(fileIndex, courseIndex); - - if (currentAge != 0) { - for (i = 0; i < NUM_SAVE_FILES; i++) { - age = get_coin_score_age(i, courseIndex); - if (age < currentAge) { - set_coin_score_age(i, courseIndex, age + 1); - } - } - - set_coin_score_age(fileIndex, courseIndex, 0); - gMainMenuDataModified = TRUE; - } -} - -/** - * Mark all coin scores for a save file as new. - */ -static void touch_high_score_ages(s32 fileIndex) { - if (INVALID_FILE_INDEX(fileIndex)) { return; } - s32 i; - - for (i = 0; i < 15; i++) { - touch_coin_score_age(fileIndex, i); - } -} - /** * Copy save file data from one backup slot to the other slot. */ @@ -380,8 +256,6 @@ UNUSED static void restore_save_file_data(s32 fileIndex, s32 srcSlot) { static u8 save_file_need_bswap(const struct SaveBuffer *buf) { // check all signatures just in case for (s32 i = 0; i < 2; ++i) { - if (buf->menuData[i].signature.magic == BSWAP16(MENU_DATA_MAGIC)) - return TRUE; for (s32 j = 0; j < NUM_SAVE_FILES; ++j) { if (buf->files[j][i].signature.magic == BSWAP16(SAVE_FILE_MAGIC)) return TRUE; @@ -394,8 +268,6 @@ static u8 save_file_need_bswap(const struct SaveBuffer *buf) { * Byteswap all multibyte fields in a SaveBuffer. */ static void save_file_bswap(struct SaveBuffer *buf) { - bswap_menudata(buf->menuData + 0); - bswap_menudata(buf->menuData + 1); for (s32 i = 0; i < NUM_SAVE_FILES; ++i) { bswap_savefile(buf->files[i] + 0); bswap_savefile(buf->files[i] + 1); @@ -426,13 +298,11 @@ void save_file_do_save(s32 fileIndex, s8 forceSave) { gSaveFileModified = FALSE; } - save_main_menu_data(); } void save_file_erase(s32 fileIndex) { if (INVALID_FILE_INDEX(fileIndex)) { return; } - touch_high_score_ages(fileIndex); bzero(&gSaveBuffer.files[fileIndex][0], sizeof(gSaveBuffer.files[fileIndex][0])); bzero(&gSaveBuffer.files[fileIndex][1], sizeof(gSaveBuffer.files[fileIndex][1])); @@ -465,7 +335,6 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) { if (INVALID_FILE_INDEX(srcFileIndex)) { return; } if (INVALID_FILE_INDEX(destFileIndex)) { return; } - touch_high_score_ages(destFileIndex); bcopy(&gSaveBuffer.files[srcFileIndex][0], &gSaveBuffer.files[destFileIndex][0], sizeof(gSaveBuffer.files[destFileIndex][0])); bcopy(&gSaveBuffer.files[srcFileIndex][1], &gSaveBuffer.files[destFileIndex][1], @@ -478,12 +347,13 @@ BAD_RETURN(s32) save_file_copy(s32 srcFileIndex, s32 destFileIndex) { void save_file_load_all(UNUSED u8 reload) { //s32 file; - gMainMenuDataModified = FALSE; gSaveFileModified = FALSE; bzero(&gSaveBuffer, sizeof(gSaveBuffer)); - read_eeprom_data(&gSaveBuffer, sizeof(gSaveBuffer)); + for (int file = 0; file < NUM_SAVE_FILES; file++) { + read_eeprom_data(file, &gSaveBuffer.files[file], sizeof(gSaveBuffer.files[file])); + } if (save_file_need_bswap(&gSaveBuffer)) save_file_bswap(&gSaveBuffer); @@ -491,19 +361,6 @@ void save_file_load_all(UNUSED u8 reload) { // Verify the main menu data and create a backup copy if only one of the slots is valid. /* Disable this so the 'backup' slot can be used s32 validSlots; - validSlots = verify_save_block_signature(&gSaveBuffer.menuData[0], sizeof(gSaveBuffer.menuData[0]), MENU_DATA_MAGIC); - validSlots |= verify_save_block_signature(&gSaveBuffer.menuData[1], sizeof(gSaveBuffer.menuData[1]),MENU_DATA_MAGIC) << 1; - switch (validSlots) { - case 0: // Neither copy is correct - wipe_main_menu_data(); - break; - case 1: // Slot 0 is correct and slot 1 is incorrect - restore_main_menu_data(0); - break; - case 2: // Slot 1 is correct and slot 0 is incorrect - restore_main_menu_data(1); - break; - } for (file = 0; file < NUM_SAVE_FILES; file++) { // Verify the save file and create a backup copy if only one of the slots is valid. @@ -522,7 +379,6 @@ void save_file_load_all(UNUSED u8 reload) { } } */ - stub_save_file_1(); } /** @@ -557,7 +413,6 @@ void save_file_collect_star_or_key(s16 coinScore, s16 starIndex, u8 fromNetwork) if (coinScore > save_file_get_course_coin_score(fileIndex, courseIndex)) { gSaveBuffer.files[fileIndex][gSaveFileUsingBackupSlot].courseCoinScores[courseIndex] = coinScore; - touch_coin_score_age(fileIndex, courseIndex); gGotFileCoinHiScore = TRUE; gSaveFileModified = TRUE; @@ -595,24 +450,21 @@ s32 save_file_exists(s32 fileIndex) { } /** - * Get the maximum coin score across all files for a course. The lower 16 bits + * Get the maximum coin score across all save files for a course. The lower 16 bits * of the returned value are the score, and the upper 16 bits are the file number * of the save file with this score. */ u32 save_file_get_max_coin_score(s32 courseIndex) { s32 fileIndex; s32 maxCoinScore = -1; - s32 maxScoreAge = -1; s32 maxScoreFileNum = 0; for (fileIndex = 0; fileIndex < NUM_SAVE_FILES; fileIndex++) { if (save_file_get_star_flags(fileIndex, courseIndex) != 0) { s32 coinScore = save_file_get_course_coin_score(fileIndex, courseIndex); - s32 scoreAge = get_coin_score_age(fileIndex, courseIndex); - if (coinScore > maxCoinScore || (coinScore == maxCoinScore && scoreAge > maxScoreAge)) { + if (coinScore > maxCoinScore) { maxCoinScore = coinScore; - maxScoreAge = scoreAge; maxScoreFileNum = fileIndex + 1; } } @@ -805,16 +657,16 @@ s32 save_file_get_cap_pos(VEC_OUT Vec3s capPos) { return FALSE; } -void save_file_set_sound_mode(u16 mode) { - set_sound_mode(mode); +void save_file_set_sound_mode(UNUSED u16 mode) { + /*set_sound_mode(mode); gSaveBuffer.menuData[0].soundMode = mode; gMainMenuDataModified = TRUE; - save_main_menu_data(); + save_main_menu_data();*/ } u16 save_file_get_sound_mode(void) { - return gSaveBuffer.menuData[0].soundMode; + return 0; } void save_file_move_cap_to_default_location(void) { diff --git a/src/game/save_file.h b/src/game/save_file.h index a5ac6a59e..806ecd779 100644 --- a/src/game/save_file.h +++ b/src/game/save_file.h @@ -8,8 +8,9 @@ #include "course_table.h" -#define EEPROM_SIZE 0x200 -#define NUM_SAVE_FILES 4 +#define NUM_SAVE_FILES 32 +// size of savebuffer +#define EEPROM_SIZE 128 struct SaveBlockSignature { @@ -45,33 +46,18 @@ enum SaveFileIndex { SAVE_FILE_D }; -struct MainMenuSaveData +struct SingleSaveFile { - // Each save file has a 2 bit "age" for each course. The higher this value, - // the older the high score is. This is used for tie-breaking when displaying - // on the high score screen. - u32 coinScoreAges[NUM_SAVE_FILES]; - u16 soundMode; - -#ifdef VERSION_EU - u16 language; -#define SUBTRAHEND 8 -#else -#define SUBTRAHEND 6 -#endif - - // Pad to match the EEPROM size of 0x200 (10 bytes on JP/US, 8 bytes on EU) - u8 filler[EEPROM_SIZE / 2 - SUBTRAHEND - NUM_SAVE_FILES * (4 + sizeof(struct SaveFile))]; - - struct SaveBlockSignature signature; + // Each save file has two copies. If one is bad, the other is used as a backup. + struct SaveFile files[2]; + // Filler to make a single save file equal the eeprom size + u8 filler[EEPROM_SIZE - (sizeof(struct SaveFile) * 2)]; }; struct SaveBuffer { - // Each of the four save files has two copies. If one is bad, the other is used as a backup. + // For all save files, each save has two copies. If one is bad, the other is used as a backup. struct SaveFile files[NUM_SAVE_FILES][2]; - // The main menu data has two copies. If one is bad, the other is used as a backup. - struct MainMenuSaveData menuData[2]; }; extern u8 gLastCompletedCourseNum; @@ -134,11 +120,7 @@ s8 get_level_num_from_course_num(s16 courseNum); /* |description|Gets the level number's corresponding course number|descriptionEnd| */ s8 get_level_course_num(s16 levelNum); -/* |description| -Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. -Useful for leaderboard tracking or displaying recent progress -|descriptionEnd| */ -void touch_coin_score_age(s32 fileIndex, s32 courseIndex); +void save_file_get_dir(int fileIndex, char* outPath, size_t size); /* |description| Saves the current state of the game into a specified save file. Includes data verification and backup management. diff --git a/src/pc/configfile.c b/src/pc/configfile.c index f2267a7db..847f1d907 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -19,7 +19,6 @@ #include "network/moderator_list.h" #include "debuglog.h" #include "djui/djui_hud_utils.h" -#include "game/save_file.h" #include "pc/network/network_player.h" #include "pc/pc_main.h" @@ -58,12 +57,7 @@ struct FunctionConfigOption { /* *Config options and default values */ -char configSaveNames[4][MAX_SAVE_NAME_STRING] = { - "SM64", - "SM64", - "SM64", - "SM64" -}; +char configSaveNames[NUM_SAVE_FILES][MAX_SAVE_NAME_STRING] = { 0 }; // Video/audio stuff ConfigWindow configWindow = { diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d537970e4..330e2c1f0 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -4,6 +4,8 @@ #include #include #include "game/player_palette.h" +#include "pc/lua/smlua_autogen.h" +#include "game/save_file.h" #define CONFIGFILE_DEFAULT "sm64config.txt" #define CONFIGFILE_BACKUP "sm64config-backup.txt" @@ -43,7 +45,7 @@ enum RefreshRateMode { RRM_MAX }; -extern char configSaveNames[4][MAX_SAVE_NAME_STRING]; +extern char configSaveNames[32][MAX_SAVE_NAME_STRING]; // display settings extern ConfigWindow configWindow; diff --git a/src/pc/djui/djui_panel_host_save.c b/src/pc/djui/djui_panel_host_save.c index aab34efad..4a68b0ba1 100644 --- a/src/pc/djui/djui_panel_host_save.c +++ b/src/pc/djui/djui_panel_host_save.c @@ -12,8 +12,6 @@ static struct DjuiBase* sSaveButtonCaller = NULL; static struct DjuiButton* sSaveButtons[NUM_SAVE_FILES] = { NULL }; static s32 sButtonTag = 0; -static char* sSaveLetters[NUM_SAVE_FILES] = { "A", "B", "C", "D" }; - static void djui_panel_host_save_update_button(struct DjuiButton* button, int slot); static void djui_panel_host_save_save_name_change(UNUSED struct DjuiBase* caller) { @@ -38,7 +36,9 @@ static void djui_panel_edit_create(struct DjuiBase* caller) { struct DjuiRect* rect1 = djui_rect_container_create(body, 32); { char buffer[64] = { 0 }; - djui_language_replace(DLANG(HOST_SAVE, EDIT_NAME), buffer, 64, '@', sSaveLetters[sButtonTag]); + char slotBuffer[4]; + snprintf(slotBuffer, sizeof(slotBuffer), "%d", sButtonTag); + djui_language_replace(DLANG(HOST_SAVE, EDIT_NAME), buffer, 64, '@', slotBuffer); struct DjuiText* text = djui_text_create(&rect1->base, buffer); djui_base_set_size_type(&text->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); djui_base_set_color(&text->base, 220, 220, 220, 255); @@ -65,6 +65,9 @@ static void djui_panel_edit_create(struct DjuiBase* caller) { static void djui_panel_host_save_update_button(struct DjuiButton* button, int slot) { char starString[64] = { 0 }; + if (configSaveNames[slot][0] == '\0') { + snprintf(configSaveNames[slot], MAX_SAVE_NAME_STRING, "SM64"); + } snprintf(starString, 64, "%c x%d - %s", '~' + 1, save_file_get_total_star_count(slot, 0, 24), configSaveNames[slot]); djui_text_set_text(button->text, starString); } diff --git a/src/pc/fs/fs.h b/src/pc/fs/fs.h index a5b9d06ba..c2b784538 100644 --- a/src/pc/fs/fs.h +++ b/src/pc/fs/fs.h @@ -8,7 +8,9 @@ #include "../platform.h" -#define SAVE_FILENAME "sm64_save_file.bin" +#define SAVE_FILENAME_OLD "sm64_save_file.bin" +#define SAVE_DIRECTORY "saves/" +#define SAVE_FILENAME "sm64coopdx_save_file_" extern char fs_writepath[]; diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index c7fde7802..ba33bb751 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2997,7 +2997,7 @@ char gSmluaConstants[] = "" "EMBLEM=7\n" "PLAYER_PART_MAX=8\n" "METAL=CAP\n" -"NUM_SAVE_FILES=4\n" +"NUM_SAVE_FILES=32\n" "SAVE_FILE_A=0\n" "SAVE_FILE_B=1\n" "SAVE_FILE_C=2\n" diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index c4b6b4f0e..e4ac40a77 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -29727,25 +29727,6 @@ int smlua_func_get_level_course_num(lua_State* L) { return 1; } -int smlua_func_touch_coin_score_age(lua_State* L) { - if (L == NULL) { return 0; } - - int top = lua_gettop(L); - if (top != 2) { - LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "touch_coin_score_age", 2, top); - return 0; - } - - s32 fileIndex = smlua_to_integer(L, 1); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "touch_coin_score_age"); return 0; } - s32 courseIndex = smlua_to_integer(L, 2); - if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "touch_coin_score_age"); return 0; } - - touch_coin_score_age(fileIndex, courseIndex); - - return 1; -} - int smlua_func_save_file_do_save(lua_State* L) { if (L == NULL) { return 0; } @@ -38334,7 +38315,6 @@ void smlua_bind_functions_autogen(void) { // save_file.h smlua_bind_function(L, "get_level_num_from_course_num", smlua_func_get_level_num_from_course_num); smlua_bind_function(L, "get_level_course_num", smlua_func_get_level_course_num); - smlua_bind_function(L, "touch_coin_score_age", smlua_func_touch_coin_score_age); smlua_bind_function(L, "save_file_do_save", smlua_func_save_file_do_save); smlua_bind_function(L, "save_file_erase", smlua_func_save_file_erase); smlua_bind_function(L, "save_file_erase_current_backup_save", smlua_func_save_file_erase_current_backup_save); diff --git a/src/pc/mods/mod_storage.cpp b/src/pc/mods/mod_storage.cpp index e64a9ff2a..4e4f42a24 100644 --- a/src/pc/mods/mod_storage.cpp +++ b/src/pc/mods/mod_storage.cpp @@ -61,7 +61,7 @@ static bool char_valid(const char* buffer, bool isKey) { } static void mod_storage_get_filename(char* dest) { - const char* path = fs_get_write_path(SAVE_DIRECTORY); // get user path + const char* path = fs_get_write_path(MS_SAVE_DIRECTORY); // get user path snprintf(dest, SYS_MAX_PATH - 1, "%s/%s", path, gLuaActiveMod->relativePath); // append sav folder strdelete(dest, ".lua"); // delete ".lua" from sav name strcat(dest, SAVE_EXTENSION); // append SAVE_EXTENSION @@ -88,7 +88,7 @@ static bool mod_storage_check_inputs(const char *key, const char *value, char *f if (!char_valid(value, false)) { return false; } // write: ensure savPath exists - const char* savPath = fs_get_write_path(SAVE_DIRECTORY); + const char* savPath = fs_get_write_path(MS_SAVE_DIRECTORY); if (!fs_sys_dir_exists(savPath)) { fs_sys_mkdir(savPath); } } diff --git a/src/pc/mods/mod_storage.h b/src/pc/mods/mod_storage.h index ee41af4a4..0c1d555d0 100644 --- a/src/pc/mods/mod_storage.h +++ b/src/pc/mods/mod_storage.h @@ -10,7 +10,7 @@ extern "C" { #define MAX_KEYS 4096 #define MAX_KEY_VALUE_LENGTH 1024 -#define SAVE_DIRECTORY "sav" +#define MS_SAVE_DIRECTORY "sav" #define SAVE_EXTENSION ".sav" /* |description|Saves a `key` corresponding to a string `value` to mod storage|descriptionEnd| */ diff --git a/src/pc/network/packets/packet_join.c b/src/pc/network/packets/packet_join.c index 0f1602352..29b665e1c 100644 --- a/src/pc/network/packets/packet_join.c +++ b/src/pc/network/packets/packet_join.c @@ -28,7 +28,7 @@ #include "pc/lua/utils/smlua_misc_utils.h" extern u8* gOverrideEeprom; -static u8 eeprom[512] = { 0 }; +static u8 eeprom[EEPROM_SIZE] = { 0 }; static u8 sJoinRequestPlayerModel; static struct PlayerPalette sJoinRequestPlayerPalette; @@ -100,9 +100,9 @@ void network_send_join(struct Packet* joinRequestPacket) { // do connection event network_player_connected(NPT_CLIENT, globalIndex, sJoinRequestPlayerModel, &sJoinRequestPlayerPalette, sJoinRequestPlayerName, sJoinRequestDiscordId); - fs_file_t* fp = fs_open(SAVE_FILENAME); + fs_file_t* fp = fs_open(SAVE_FILENAME_OLD); if (fp != NULL) { - fs_read(fp, eeprom, 512); + fs_read(fp, eeprom, EEPROM_SIZE); fs_close(fp); } @@ -126,7 +126,7 @@ void network_send_join(struct Packet* joinRequestPacket) { packet_write(&p, &gServerSettings.maxPlayers, sizeof(u8)); packet_write(&p, &gServerSettings.pauseAnywhere, sizeof(u8)); packet_write(&p, &gServerSettings.pvpType, sizeof(u8)); - packet_write(&p, eeprom, sizeof(u8) * 512); + packet_write(&p, eeprom, sizeof(u8) * EEPROM_SIZE); network_send_to(globalIndex, &p); LOG_INFO("sending join packet"); @@ -179,7 +179,7 @@ void network_receive_join(struct Packet* p) { packet_read(p, &gServerSettings.maxPlayers, sizeof(u8)); packet_read(p, &gServerSettings.pauseAnywhere, sizeof(u8)); packet_read(p, &gServerSettings.pvpType, sizeof(u8)); - packet_read(p, eeprom, sizeof(u8) * 512); + packet_read(p, eeprom, sizeof(u8) * EEPROM_SIZE); network_player_connected(NPT_SERVER, 0, 0, &DEFAULT_MARIO_PALETTE, "Player", "0"); network_player_connected(NPT_LOCAL, myGlobalIndex, configPlayerModel, &configPlayerPalette, configPlayerName, get_local_discord_id()); diff --git a/src/pc/ultra_reimplementation.c b/src/pc/ultra_reimplementation.c index 230f7e747..9f54e41b0 100644 --- a/src/pc/ultra_reimplementation.c +++ b/src/pc/ultra_reimplementation.c @@ -4,6 +4,7 @@ #include "macros.h" #include "platform.h" #include "fs/fs.h" +#include "game/save_file.h" u8* gOverrideEeprom = NULL; @@ -124,20 +125,22 @@ s32 osEepromProbe(UNUSED OSMesgQueue *mq) { return 1; } -s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) { +s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 fileIndex, u8 address, u8 *buffer, int nbytes) { if (gOverrideEeprom != NULL) { memcpy(buffer, gOverrideEeprom + address * 8, nbytes); return 0; } - u8 content[512]; + u8 content[EEPROM_SIZE]; s32 ret = -1; - fs_file_t *fp = fs_open(SAVE_FILENAME); + char filePath[256]; + save_file_get_dir(fileIndex, filePath, 256); + fs_file_t *fp = fs_open(filePath); if (fp == NULL) { return -1; } - if (fs_read(fp, content, 512) == 512) { + if (fs_read(fp, content, EEPROM_SIZE) == EEPROM_SIZE) { memcpy(buffer, content + address * 8, nbytes); ret = 0; } @@ -146,23 +149,29 @@ s32 osEepromLongRead(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) return ret; } -s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 address, u8 *buffer, int nbytes) { +s32 osEepromLongWrite(UNUSED OSMesgQueue *mq, u8 fileIndex, u8 address, u8 *buffer, int nbytes) { if (gOverrideEeprom != NULL) { memcpy(gOverrideEeprom + address * 8, buffer, nbytes); return 0; } - u8 content[512] = { 0 }; - if (address != 0 || nbytes != 512) { - osEepromLongRead(mq, 0, content, 512); + u8 content[EEPROM_SIZE] = { 0 }; + if (address != 0 || nbytes != EEPROM_SIZE) { + osEepromLongRead(mq, fileIndex, 0, content, EEPROM_SIZE); } memcpy(content + address * 8, buffer, nbytes); - FILE *fp = fopen(fs_get_write_path(SAVE_FILENAME), "wb"); + if (!fs_sys_dir_exists(fs_get_write_path(SAVE_DIRECTORY))) { + fs_sys_mkdir(fs_get_write_path(SAVE_DIRECTORY)); + } + + char filePath[256]; + save_file_get_dir(fileIndex, filePath, 256); + FILE *fp = fopen(fs_get_write_path(filePath), "wb"); if (fp == NULL) { return -1; } - s32 ret = fwrite(content, 1, 512, fp) == 512 ? 0 : -1; + s32 ret = fwrite(content, 1, EEPROM_SIZE, fp) == EEPROM_SIZE ? 0 : -1; fclose(fp); return ret;