diff --git a/config.def.h b/config.def.h index 6b7edd6056..1860369a7d 100644 --- a/config.def.h +++ b/config.def.h @@ -212,6 +212,10 @@ static const unsigned autosave_interval = 0; // When being client over netplay, use keybinds for player 1 rather than player 2. static const bool netplay_client_swap_input = true; +// On save state load, block SRAM from being overwritten. +// This could potentially lead to buggy games. +static const bool block_sram_overwrite = false; + //////////////////// // Keybinds, Joypad diff --git a/file.c b/file.c index 941c0a2c05..b4556b7b83 100644 --- a/file.c +++ b/file.c @@ -425,7 +425,74 @@ bool load_state(const char *path) else { SSNES_LOG("State size: %d bytes.\n", (int)size); + + uint8_t *block_buf[2] = {NULL, NULL}; + int block_type[2] = {-1, -1}; + unsigned block_size[2] = {0}; + + if (g_settings.block_sram_overwrite) + { + SSNES_LOG("Blocking SRAM overwrite!\n"); + switch (g_extern.game_type) + { + case SSNES_CART_NORMAL: + block_type[0] = SNES_MEMORY_CARTRIDGE_RAM; + block_type[1] = SNES_MEMORY_CARTRIDGE_RTC; + break; + + case SSNES_CART_BSX: + case SSNES_CART_BSX_SLOTTED: + block_type[0] = SNES_MEMORY_BSX_RAM; + block_type[1] = SNES_MEMORY_BSX_PRAM; + break; + + case SSNES_CART_SUFAMI: + block_type[0] = SNES_MEMORY_SUFAMI_TURBO_A_RAM; + block_type[1] = SNES_MEMORY_SUFAMI_TURBO_B_RAM; + break; + + case SSNES_CART_SGB: + block_type[0] = SNES_MEMORY_GAME_BOY_RAM; + block_type[1] = SNES_MEMORY_GAME_BOY_RTC; + break; + } + } + + for (unsigned i = 0; i < 2; i++) + if (block_type[i] != -1) + block_size[i] = psnes_get_memory_size(block_type[i]); + + for (unsigned i = 0; i < 2; i++) + if (block_size[i]) + block_buf[i] = malloc(block_size[i]); + + // Backup current SRAM which is overwritten by unserialize. + for (unsigned i = 0; i < 2; i++) + { + if (block_buf[i]) + { + const uint8_t *ptr = psnes_get_memory_data(block_type[i]); + if (ptr) + memcpy(block_buf[i], ptr, block_size[i]); + } + } + psnes_unserialize(buf, size); + + // Flush back :D + for (unsigned i = 0; i < 2; i++) + { + if (block_buf[i]) + { + uint8_t *ptr = psnes_get_memory_data(block_type[i]); + if (ptr) + memcpy(ptr, block_buf[i], block_size[i]); + } + } + + for (unsigned i = 0; i < 2; i++) + if (block_buf[i]) + free(block_buf[i]); } free(buf); diff --git a/general.h b/general.h index fb55983144..8464233f49 100644 --- a/general.h +++ b/general.h @@ -148,6 +148,8 @@ struct settings bool pause_nonactive; unsigned autosave_interval; + + bool block_sram_overwrite; }; enum ssnes_game_type diff --git a/settings.c b/settings.c index 1d03a98d7c..5776884a13 100644 --- a/settings.c +++ b/settings.c @@ -171,6 +171,8 @@ static void set_defaults(void) g_settings.pause_nonactive = pause_nonactive; g_settings.autosave_interval = autosave_interval; + g_settings.block_sram_overwrite = block_sram_overwrite; + assert(sizeof(g_settings.input.binds[0]) >= sizeof(snes_keybinds_1)); assert(sizeof(g_settings.input.binds[1]) >= sizeof(snes_keybinds_rest)); memcpy(g_settings.input.binds[0], snes_keybinds_1, sizeof(snes_keybinds_1)); @@ -418,6 +420,8 @@ static void parse_config_file(void) CONFIG_GET_STRING(cheat_database, "cheat_database_path"); CONFIG_GET_STRING(cheat_settings_path, "cheat_settings_path"); + CONFIG_GET_BOOL(block_sram_overwrite, "block_sram_overwrite"); + if (!g_extern.has_set_save_path && config_get_array(conf, "savefile_directory", tmp_str, sizeof(tmp_str))) { if (path_is_directory(tmp_str)) diff --git a/ssnes.cfg b/ssnes.cfg index d7ace7e906..e052762a27 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -316,3 +316,7 @@ # Records video after CPU video filter. # video_post_filter_record = false + +# Block SRAM from being overwritten when loading save states. +# Might potentially lead to buggy games. +# block_sram_overwrite = false