From 70b1f385e806d82e1f5bea42c91d310d86c6f6a7 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Tue, 7 Jun 2016 18:33:01 +0100 Subject: [PATCH] Change task_save_state so we save a single state and a single savestate file in memory, allowing to undo a loadstate/savestate once. --- command.c | 2 + content.h | 8 +- tasks/task_save_state.c | 208 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 212 insertions(+), 6 deletions(-) diff --git a/command.c b/command.c index 74449fc5d1..4d351b30fb 100644 --- a/command.c +++ b/command.c @@ -1580,6 +1580,7 @@ static void command_event_save_state(const char *path, settings_t *settings = config_get_ptr(); char buf[PATH_MAX_LENGTH] = {0}; +#if 0 /* if a save state already exists rename it to .last before saving * so it can be recovered */ if (path_file_exists(path)) @@ -1597,6 +1598,7 @@ static void command_event_save_state(const char *path, return; } } +#endif if (!content_save_state(path)) { diff --git a/content.h b/content.h index 432fbbd36e..9f5bd3e09f 100644 --- a/content.h +++ b/content.h @@ -46,16 +46,20 @@ bool content_load_ram_file(unsigned slot); bool content_save_ram_file(unsigned slot); /* Load a state from disk to memory. */ -bool content_load_state(const char *path); +bool content_load_state(const char* path); +bool content_load_state_with_backup(const char* path, bool save_to_backup_buffer) /* Save a state from memory to disk. */ bool content_save_state(const char *path); +bool content_save_state_with_backup(const char *path, bool save_to_disk) /* Copy a save state. */ bool content_rename_state(const char *origin, const char *dest); /* Load a state backup from disk to memory. */ -bool content_undo_load_state(const char *path); +bool content_undo_load_state(); + +bool content_undo_save_state(); bool content_does_not_need_content(void); diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 9422d1b88c..27a8b15d6c 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -30,6 +30,26 @@ #include "../verbosity.h" #include "tasks_internal.h" +struct save_state_buf +{ + void* data; + char path[PATH_MAX_LENGTH]; + size_t size; +}; + +/* +Holds a savestate which was stored on disk and was lost when +content_save_state() wrote over it. +Can be restored to disk with undo_save_state(). +*/ +static struct save_state_buf old_save_file; + +/* +Represents the state which was lost when load_state() was called. +Can be restored with undo_load_state(). +*/ +static struct save_state_buf old_state_buf; + struct sram_block { unsigned type; @@ -37,6 +57,141 @@ struct sram_block size_t size; }; +bool content_undo_load_state() +{ + unsigned i; + //ssize_t size; + retro_ctx_serialize_info_t serial_info; + unsigned num_blocks = 0; + //void *buf = NULL; + struct sram_block *blocks = NULL; + settings_t *settings = config_get_ptr(); + global_t *global = global_get_ptr(); + //bool ret = filestream_read_file(path, &buf, &size); + + RARCH_LOG("%s: \"%s\".\n", + msg_hash_to_str(MSG_LOADING_STATE), + "from internal buffer"); + + if (old_state_buf.size == 0) + return true; + + RARCH_LOG("%s: %u %s.\n", + msg_hash_to_str(MSG_STATE_SIZE), + old_state_buf.size, + msg_hash_to_str(MSG_BYTES)); + + + /* TODO/FIXME - This checking of SRAM overwrite, the backing up of it and + its flushing could all be in their own functions... */ + if (settings->block_sram_overwrite && global->savefiles + && global->savefiles->size) + { + RARCH_LOG("%s.\n", + msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE)); + blocks = (struct sram_block*) + calloc(global->savefiles->size, sizeof(*blocks)); + + if (blocks) + { + num_blocks = global->savefiles->size; + for (i = 0; i < num_blocks; i++) + blocks[i].type = global->savefiles->elems[i].attr.i; + } + } + + for (i = 0; i < num_blocks; i++) + { + retro_ctx_memory_info_t mem_info; + + mem_info.id = blocks[i].type; + core_get_memory(&mem_info); + + blocks[i].size = mem_info.size; + } + + for (i = 0; i < num_blocks; i++) + if (blocks[i].size) + blocks[i].data = malloc(blocks[i].size); + + /* Backup current SRAM which is overwritten by unserialize. */ + for (i = 0; i < num_blocks; i++) + { + if (blocks[i].data) + { + retro_ctx_memory_info_t mem_info; + const void *ptr = NULL; + + mem_info.id = blocks[i].type; + + core_get_memory(&mem_info); + + ptr = mem_info.data; + if (ptr) + memcpy(blocks[i].data, ptr, blocks[i].size); + } + } + + serial_info.data_const = old_state_buf.data; + serial_info.size = old_state_buf.size; + bool ret = core_unserialize(&serial_info); + + /* Flush back. */ + for (i = 0; i < num_blocks; i++) + { + if (blocks[i].data) + { + retro_ctx_memory_info_t mem_info; + void *ptr = NULL; + + mem_info.id = blocks[i].type; + + core_get_memory(&mem_info); + + ptr = mem_info.data; + if (ptr) + memcpy(ptr, blocks[i].data, blocks[i].size); + } + } + + for (i = 0; i < num_blocks; i++) + free(blocks[i].data); + free(blocks); + + if (!ret) + RARCH_ERR("%s \"%s\".\n", + msg_hash_to_str(MSG_FAILED_TO_LOAD_STATE), + "from internal buffer"); + + /* Wipe the old state buffer, it's meant to be one use only */ + old_state_buf.path[0] = '\0'; + if (old_state_buf.data) { + free(old_state_buf.data); + old_state_buf.data = NULL; + } + + old_state_buf.data = 0; + + return ret; +} + +bool content_undo_save_state() +{ + filestream_write_file(old_save_file.path, old_save_file.data, old_save_file.size); + + /* Wipe the save file buffer as it's intended to be one use only */ + old_save_file.path[0] = '\0'; + if (old_save_file.data) { + free(old_save_file.data); + old_save_file.data = NULL; + } + + old_save_file.data = 0; + + return true; +} + + /* TODO/FIXME - turn this into actual task */ /** @@ -47,7 +202,8 @@ struct sram_block * * Returns: true if successful, false otherwise. **/ -bool content_save_state(const char *path) +bool content_save_state(const char *path) { content_save_state_with_backup(path, true);} +bool content_save_state_with_backup(const char *path, bool save_to_disk) { retro_ctx_serialize_info_t serial_info; retro_ctx_size_info_t info; @@ -77,8 +233,30 @@ bool content_save_state(const char *path) serial_info.size = info.size; ret = core_serialize(&serial_info); - if (ret) - ret = filestream_write_file(path, data, info.size); + if (ret) { + if (save_to_disk) { + if (path_file_exists(path)) { + content_load_state(path, true); + } + + ret = filestream_write_file(path, data, info.size); + } + /* save_to_disk is false, which means we are saving the state + in old_state_buf to allow content_undo_load_state() to restore it */ + else + { + old_state_buf.path = NULL; + + /* If we were holding onto an old state already, clean it up first */ + if (old_state_buf.data) { + free(old_state_buf.data); + old_state_buf.data = NULL; + } + + old_state_buf.data = malloc(info.size); + memcpy(old_state_buf.data, data); + } + } else { RARCH_ERR("%s \"%s\".\n", @@ -99,7 +277,8 @@ bool content_save_state(const char *path) * * Returns: true if successful, false otherwise. **/ -bool content_load_state(const char *path) +bool content_load_state(const char* path) { content_load_state_with_backup(path, false); } +bool content_load_state_with_backup(const char *path, bool save_to_backup_buffer) { unsigned i; ssize_t size; @@ -123,6 +302,24 @@ bool content_load_state(const char *path) (unsigned)size, msg_hash_to_str(MSG_BYTES)); + /* This means we're backing up the file in memory, so content_undo_save_state() + can restore it */ + if (save_to_backup_buffer) { + strcpy(old_save_file.path, path); + + /* If we were previously backing up a file, let go of it first */ + if (old_save_file.data) { + free(old_save_file.data); + old_save_file.data = NULL; + } + + old_save_file.data = malloc(size); + memcpy(old_save_file.data, buf); + + free(buf); + return true; + } + if (settings->block_sram_overwrite && global->savefiles && global->savefiles->size) { @@ -174,6 +371,9 @@ bool content_load_state(const char *path) serial_info.data_const = buf; serial_info.size = size; + + /* Backup the current state so we can undo this load */ + content_save_state(NULL, true); ret = core_unserialize(&serial_info); /* Flush back. */