YAAAA completely change up save system AUWEGBSLIUFJSAI

This commit is contained in:
EmeraldLockdown 2026-03-01 19:57:43 -06:00
parent fa3bad7177
commit e881a04f86
19 changed files with 79 additions and 286 deletions

View file

@ -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" ],

View file

@ -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

View file

@ -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

View file

@ -4805,30 +4805,6 @@ Gets the level number's corresponding course number
<br />
## [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:](#)
<br />
## [save_file_do_save](#save_file_do_save)
### Description

View file

@ -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)

View file

@ -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) */

View file

@ -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 };

View file

@ -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) {

View file

@ -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.

View file

@ -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 = {

View file

@ -4,6 +4,8 @@
#include <stdbool.h>
#include <PR/ultratypes.h>
#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;

View file

@ -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);
}

View file

@ -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[];

View file

@ -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"

View file

@ -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);

View file

@ -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); }
}

View file

@ -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| */

View file

@ -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());

View file

@ -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;