More correct save state and rewind handling.

This commit is contained in:
Themaister 2011-12-27 18:19:45 +01:00
parent f4cc8a4bca
commit bc66841a29
3 changed files with 113 additions and 89 deletions

141
file.c
View File

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

View File

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

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