From 5cab5d44599ddf7cd48afa506fa3187e2be90f1c Mon Sep 17 00:00:00 2001 From: Matt Stanley <5354790+mstan@users.noreply.github.com> Date: Sun, 16 Aug 2026 00:28:57 -0700 Subject: [PATCH] Park MMX password saves sidecar rework --- .../1.0.0/manifest.toml | 8 +-- src/main.c | 8 +-- src/mods/mmx_sram_plugin.c | 49 ++++++++++++++----- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/mods/preloaded/packages/megaman-x.enhancement.sram/1.0.0/manifest.toml b/mods/preloaded/packages/megaman-x.enhancement.sram/1.0.0/manifest.toml index d6cfcf7..6b9f11c 100644 --- a/mods/preloaded/packages/megaman-x.enhancement.sram/1.0.0/manifest.toml +++ b/mods/preloaded/packages/megaman-x.enhancement.sram/1.0.0/manifest.toml @@ -1,9 +1,9 @@ format_version = 1 id = "megaman-x.enhancement.sram" version = "1.0.0" -name = "Mega Man X SRAM" +name = "Mega Man X Password Saves" author = "mstan" -description = "Adds mod-gated battery-backed SRAM storage for Mega Man X password-save enhancements while leaving the stock password game unchanged when disabled." +description = "Adds mod-gated sidecar persistence for Mega Man X password-save data while leaving the stock no-SRAM cartridge map unchanged." license = "MIT" resolver = "declarative" save_compatibility = "shared" @@ -14,8 +14,8 @@ rom_sha256 = "b8f70a6e7fb93819f79693578887e2c11e196bdf1ac6ddc7cb924b1ad0be2d32" [[feature]] id = "sram" -name = "SRAM Saves" -description = "Expose a small battery-backed SRAM area for password-save data. Disable this for stock no-SRAM behavior." +name = "Password Saves" +description = "Persist the latest password in a sidecar save file. Disable this for stock password-only behavior." group = "Saves" default_enabled = true diff --git a/src/main.c b/src/main.c index addb3ae..66e60a5 100644 --- a/src/main.c +++ b/src/main.c @@ -1310,10 +1310,10 @@ int main(int argc, char** argv) { #endif gi.name = MMX_GAME_NAME; gi.region = MMX_GAME_REGION; - gi.sram_path = "saves/save.srm"; /* The SRAM Saves mod allocates this - for password-save enhancements. - With the mod disabled, stock MMX - still has no guest-visible SRAM. */ + gi.sram_path = "saves/save.srm"; /* Password Saves owns this sidecar + file for launcher presentation only; + the stock MMX cartridge still has no + guest-visible SRAM. */ gi.password_sram_path = "saves/save.srm"; gi.password_sram_label = "Last Password"; gi.password_sram_size = 2048; diff --git a/src/mods/mmx_sram_plugin.c b/src/mods/mmx_sram_plugin.c index 969bc11..6568f20 100644 --- a/src/mods/mmx_sram_plugin.c +++ b/src/mods/mmx_sram_plugin.c @@ -5,16 +5,15 @@ #include #define MMX_SRAM_PLUGIN "megaman-x.sram" -#define MMX_SYNTHETIC_SRAM_BYTES 2048u +#define MMX_PASSWORD_SAVE_BYTES 2048u #define MMX_PASSWORD_DIGITS 12u #define MMX_PASSWORD_WRAM 0x1e60u #define MMX_PASSWORD_SRAM_OFFSET 0x100u #define MMX_PASSWORD_PREFILL_GRACE_FRAMES 90 extern uint8_t g_ram[0x20000]; -extern uint8_t *g_sram; -extern int g_sram_size; -void RtlWriteSram(void); +void RtlEnsureSaveDir(void); +void RtlSramFilePath(char *buf, size_t buflen); typedef struct MmxSramPassword { char magic[8]; @@ -27,6 +26,7 @@ static const char kMmxSramMagic[8] = {'M', 'M', 'X', 'P', 'A', 'S', 'S', 0}; static int g_mmx_sram_active; static int g_mmx_password_prefill_frames; +static uint8_t g_mmx_password_save[MMX_PASSWORD_SAVE_BYTES]; static void mmx_sram_frame(void); @@ -35,16 +35,39 @@ int mmx_sram_mod_active(void) { } static void mmx_sram_reset(void) { - g_mmx_sram_active = 0; g_mmx_password_prefill_frames = 0; } -static void mmx_sram_activate(void) { - if (!snes_mod_request_synthetic_sram_c(MMX_SYNTHETIC_SRAM_BYTES)) { - fprintf(stderr, "MMX SRAM mod: failed to request %u bytes\n", - (unsigned)MMX_SYNTHETIC_SRAM_BYTES); +static void mmx_sram_read_file(void) { + char path[128]; + RtlSramFilePath(path, sizeof(path)); + FILE *f = fopen(path, "rb"); + if (!f) + return; + if (fread(g_mmx_password_save, 1, sizeof(g_mmx_password_save), f) != + sizeof(g_mmx_password_save)) + memset(g_mmx_password_save, 0, sizeof(g_mmx_password_save)); + fclose(f); +} + +static void mmx_sram_write_file(void) { + char path[128], bak[140]; + RtlEnsureSaveDir(); + RtlSramFilePath(path, sizeof(path)); + snprintf(bak, sizeof(bak), "%s.bak", path); + rename(path, bak); + FILE *f = fopen(path, "wb"); + if (!f) { + fprintf(stderr, "MMX SRAM mod: unable to write %s\n", path); return; } + fwrite(g_mmx_password_save, 1, sizeof(g_mmx_password_save), f); + fclose(f); +} + +static void mmx_sram_activate(void) { + memset(g_mmx_password_save, 0, sizeof(g_mmx_password_save)); + mmx_sram_read_file(); g_mmx_sram_active = 1; g_mmx_password_prefill_frames = 0; (void)snes_mod_register_frame_callback(mmx_sram_frame); @@ -69,10 +92,10 @@ static int mmx_sram_password_valid(const MmxSramPassword *slot) { } static MmxSramPassword *mmx_sram_password_slot(void) { - if (!g_sram || - g_sram_size < (int)(MMX_PASSWORD_SRAM_OFFSET + sizeof(MmxSramPassword))) + if (sizeof(g_mmx_password_save) < + MMX_PASSWORD_SRAM_OFFSET + sizeof(MmxSramPassword)) return NULL; - return (MmxSramPassword *)(g_sram + MMX_PASSWORD_SRAM_OFFSET); + return (MmxSramPassword *)(g_mmx_password_save + MMX_PASSWORD_SRAM_OFFSET); } static int mmx_password_digits_equal(const uint8_t *a, const uint8_t *b) { @@ -135,7 +158,7 @@ static void mmx_sram_store_password(const uint8_t digits[MMX_PASSWORD_DIGITS]) { slot->version = 1; memcpy(slot->digits, digits, MMX_PASSWORD_DIGITS); slot->checksum = mmx_password_checksum(slot->digits); - RtlWriteSram(); + mmx_sram_write_file(); } static void mmx_sram_prefill_password(const MmxSramPassword *slot) {