mirror of
https://github.com/libretro/RetroArch
synced 2025-02-20 06:40:18 +00:00
More correct save state and rewind handling.
This commit is contained in:
parent
f4cc8a4bca
commit
bc66841a29
141
file.c
141
file.c
@ -413,11 +413,14 @@ bool save_state(const char *path)
|
||||
}
|
||||
|
||||
SSNES_LOG("State size: %d bytes.\n", (int)size);
|
||||
psnes_serialize((uint8_t*)data, size);
|
||||
bool ret = dump_to_file(path, data, size);
|
||||
free(data);
|
||||
bool ret = psnes_serialize((uint8_t*)data, size);
|
||||
if (ret)
|
||||
ret = dump_to_file(path, data, size);
|
||||
|
||||
if (!ret)
|
||||
SSNES_ERR("Failed to save state to \"%s\".\n", path);
|
||||
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -426,86 +429,86 @@ bool load_state(const char *path)
|
||||
SSNES_LOG("Loading state: \"%s\".\n", path);
|
||||
void *buf = NULL;
|
||||
ssize_t size = read_file(path, &buf);
|
||||
|
||||
if (size < 0)
|
||||
{
|
||||
SSNES_ERR("Failed to load state from \"%s\".\n", path);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
bool ret = true;
|
||||
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("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)
|
||||
{
|
||||
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_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_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_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;
|
||||
}
|
||||
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] = (uint8_t*)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((uint8_t*)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]);
|
||||
}
|
||||
|
||||
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] = (uint8_t*)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]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = psnes_unserialize((uint8_t*)buf, size);
|
||||
|
||||
// Flush back :D
|
||||
for (unsigned i = 0; i < 2 && ret; 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);
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void load_ram_file(const char *path, int type)
|
||||
|
@ -277,6 +277,7 @@ struct global
|
||||
// Rewind support.
|
||||
state_manager_t *state_manager;
|
||||
void *state_buf;
|
||||
size_t state_size;
|
||||
bool frame_is_reverse;
|
||||
|
||||
// Movie playback/recording support.
|
||||
|
60
ssnes.c
60
ssnes.c
@ -1115,16 +1115,34 @@ static void deinit_cheats(void)
|
||||
|
||||
static void init_rewind(void)
|
||||
{
|
||||
if (g_settings.rewind_enable)
|
||||
if (!g_settings.rewind_enable)
|
||||
return;
|
||||
|
||||
g_extern.state_size = psnes_serialize_size();
|
||||
|
||||
// Make sure we allocate at least 4-byte multiple.
|
||||
size_t aligned_state_size = (g_extern.state_size + 3) & ~3;
|
||||
g_extern.state_buf = calloc(1, aligned_state_size);
|
||||
|
||||
if (!g_extern.state_buf)
|
||||
{
|
||||
size_t serial_size = psnes_serialize_size();
|
||||
g_extern.state_buf = calloc(1, (serial_size + 3) & ~3); // Make sure we allocate at least 4-byte multiple.
|
||||
psnes_serialize((uint8_t*)g_extern.state_buf, serial_size);
|
||||
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)(g_settings.rewind_buffer_size / 1000000));
|
||||
g_extern.state_manager = state_manager_new((serial_size + 3) & ~3, g_settings.rewind_buffer_size, g_extern.state_buf);
|
||||
if (!g_extern.state_manager)
|
||||
SSNES_WARN("Failed to init rewind buffer. Rewinding will be disabled!\n");
|
||||
SSNES_ERR("Failed to allocate memory for rewind buffer!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!psnes_serialize((uint8_t*)g_extern.state_buf, g_extern.state_size))
|
||||
{
|
||||
SSNES_ERR("Failed to perform initial serialization for rewind!\n");
|
||||
free(g_extern.state_buf);
|
||||
g_extern.state_buf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
SSNES_LOG("Initing rewind buffer with size: %u MB\n", (unsigned)(g_settings.rewind_buffer_size / 1000000));
|
||||
g_extern.state_manager = state_manager_new(aligned_state_size, g_settings.rewind_buffer_size, g_extern.state_buf);
|
||||
|
||||
if (!g_extern.state_manager)
|
||||
SSNES_WARN("Failed to init rewind buffer. Rewinding will be disabled!\n");
|
||||
}
|
||||
|
||||
static void deinit_rewind(void)
|
||||
@ -1415,17 +1433,18 @@ static void check_savestates(void)
|
||||
else
|
||||
snprintf(save_path, sizeof(save_path), "%s", g_extern.savestate_name);
|
||||
|
||||
if (!save_state(save_path))
|
||||
char msg[512];
|
||||
if (save_state(save_path))
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "Failed to save state to \"%s\"", save_path);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 2, 180);
|
||||
snprintf(msg, sizeof(msg), "Saved state to slot #%u!", g_extern.state_slot);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, "Saved state!", 1, 180);
|
||||
snprintf(msg, sizeof(msg), "Failed to save state to \"%s\"", save_path);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 2, 180);
|
||||
}
|
||||
}
|
||||
old_should_savestate = should_savestate;
|
||||
@ -1441,17 +1460,18 @@ static void check_savestates(void)
|
||||
else
|
||||
snprintf(load_path, sizeof(load_path), "%s", g_extern.savestate_name);
|
||||
|
||||
if (!load_state(load_path))
|
||||
char msg[512];
|
||||
if (load_state(load_path))
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "Failed to load state from \"%s\"", load_path);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 2, 180);
|
||||
snprintf(msg, sizeof(msg), "Loaded state from slot #%u!", g_extern.state_slot);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
msg_queue_push(g_extern.msg_queue, "Loaded state!", 1, 180);
|
||||
snprintf(msg, sizeof(msg), "Failed to load state from \"%s\"", load_path);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 2, 180);
|
||||
}
|
||||
}
|
||||
old_should_loadstate = should_loadstate;
|
||||
@ -1587,7 +1607,7 @@ static void check_rewind(void)
|
||||
setup_rewind_audio();
|
||||
|
||||
msg_queue_push(g_extern.msg_queue, "Rewinding!", 0, g_extern.is_paused ? 1 : 30);
|
||||
psnes_unserialize((uint8_t*)buf, psnes_serialize_size());
|
||||
psnes_unserialize((uint8_t*)buf, g_extern.state_size);
|
||||
|
||||
if (g_extern.bsv.movie)
|
||||
bsv_movie_frame_rewind(g_extern.bsv.movie);
|
||||
@ -1601,7 +1621,7 @@ static void check_rewind(void)
|
||||
cnt = (cnt + 1) % (g_settings.rewind_granularity ? g_settings.rewind_granularity : 1); // Avoid possible SIGFPE.
|
||||
if (cnt == 0 || g_extern.bsv.movie)
|
||||
{
|
||||
psnes_serialize((uint8_t*)g_extern.state_buf, psnes_serialize_size());
|
||||
psnes_serialize((uint8_t*)g_extern.state_buf, g_extern.state_size);
|
||||
state_manager_push(g_extern.state_manager, g_extern.state_buf);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user