From 70b1f385e806d82e1f5bea42c91d310d86c6f6a7 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Tue, 7 Jun 2016 18:33:01 +0100 Subject: [PATCH 1/5] 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. */ From c47b58228f460a16bab9682bee68bf8f069aa32b Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Wed, 8 Jun 2016 17:07:04 +0100 Subject: [PATCH 2/5] Make this compile --- content.h | 4 ++-- tasks/task_save_state.c | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/content.h b/content.h index 9f5bd3e09f..a53ec3cb3c 100644 --- a/content.h +++ b/content.h @@ -47,11 +47,11 @@ 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_with_backup(const char* path, bool save_to_backup_buffer) +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) +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); diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 27a8b15d6c..cc74c3519f 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -236,7 +236,7 @@ bool content_save_state_with_backup(const char *path, bool save_to_disk) if (ret) { if (save_to_disk) { if (path_file_exists(path)) { - content_load_state(path, true); + content_load_state_with_backup(path, true); } ret = filestream_write_file(path, data, info.size); @@ -245,7 +245,7 @@ bool content_save_state_with_backup(const char *path, bool save_to_disk) in old_state_buf to allow content_undo_load_state() to restore it */ else { - old_state_buf.path = NULL; + old_state_buf.path[0] = '\0'; /* If we were holding onto an old state already, clean it up first */ if (old_state_buf.data) { @@ -254,7 +254,8 @@ bool content_save_state_with_backup(const char *path, bool save_to_disk) } old_state_buf.data = malloc(info.size); - memcpy(old_state_buf.data, data); + memcpy(old_state_buf.data, data, info.size); + old_state_buf.size = info.size; } } else @@ -314,7 +315,9 @@ bool content_load_state_with_backup(const char *path, bool save_to_backup_buffer } old_save_file.data = malloc(size); - memcpy(old_save_file.data, buf); + memcpy(old_save_file.data, buf, size); + + old_save_file.size = size; free(buf); return true; @@ -373,7 +376,7 @@ bool content_load_state_with_backup(const char *path, bool save_to_backup_buffer serial_info.size = size; /* Backup the current state so we can undo this load */ - content_save_state(NULL, true); + content_save_state_with_backup(NULL, true); ret = core_unserialize(&serial_info); /* Flush back. */ From 051cc3fe4d0170a6181e5f98dabc81438723ed50 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Wed, 8 Jun 2016 20:27:54 +0100 Subject: [PATCH 3/5] Undo Save State currently seems to be working almost as intended (missing OSD messages). Undo Load State isn't working yet. --- command.c | 98 ++++++++++++----------------------------- content.h | 3 +- tasks/task_save_state.c | 32 +++++++++++++- 3 files changed, 61 insertions(+), 72 deletions(-) diff --git a/command.c b/command.c index 4d351b30fb..3a69e11525 100644 --- a/command.c +++ b/command.c @@ -1578,27 +1578,6 @@ static void command_event_save_state(const char *path, char *s, size_t len) { 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)) - { - strlcpy(buf, path, sizeof(buf)); - snprintf(buf, sizeof(buf), "%s", path); - path_remove_extension(buf); - snprintf(buf, sizeof(buf), "%s.last", buf); - - if (!content_rename_state(path, buf)) - { - snprintf(s, len, "%s \"%s\".", - msg_hash_to_str(MSG_FAILED_TO_SAVE_UNDO), - path); - return; - } - } -#endif if (!content_save_state(path)) { @@ -1616,6 +1595,16 @@ static void command_event_save_state(const char *path, settings->state_slot); } +static void command_event_undo_save_state(char *s, size_t len) +{ + if (!content_undo_save_state()) + { + snprintf(s, len, "%s \"%s\".", + msg_hash_to_str(MSG_FAILED_TO_SAVE_UNDO), + "from internal buffer"); + } +} + /** * event_load_state * @path : Path to state. @@ -1624,29 +1613,9 @@ static void command_event_save_state(const char *path, * * Loads a state with path being @path. **/ -static void command_event_load_state(const char *path, char *s, size_t len, bool undo) +static void command_event_load_state(const char *path, char *s, size_t len) { settings_t *settings = config_get_ptr(); - char buf[PATH_MAX_LENGTH] = {0}; - - /* save a state before loading (unless it's an undo operation already) - * so the state can be recovered - */ - if (!undo) - { - strlcpy(buf, path, sizeof(buf)); - snprintf(buf, sizeof(buf), "%s", path); - path_remove_extension(buf); - snprintf(buf, sizeof(buf), "%s.undo", buf); - - if (!content_save_state(buf)) - { - snprintf(s, len, "%s \"%s\".", - msg_hash_to_str(MSG_FAILED_TO_SAVE_UNDO), - path); - return; - } - } if (!content_load_state(path)) { @@ -1659,18 +1628,25 @@ static void command_event_load_state(const char *path, char *s, size_t len, bool if (settings->state_slot < 0) snprintf(s, len, "%s #-1 (auto).", msg_hash_to_str(MSG_LOADED_STATE_FROM_SLOT)); - else if (!undo) + else snprintf(s, len, "%s #%d.", msg_hash_to_str(MSG_LOADED_STATE_FROM_SLOT), settings->state_slot); - else - snprintf(s, len, "%s #-1 (undo).", msg_hash_to_str(MSG_LOADED_STATE_FROM_SLOT)); +} + +static void command_event_undo_load_state(char *s, size_t len) +{ + if (!content_undo_load_state()) + { + snprintf(s, len, "%s \"%s\".", + msg_hash_to_str(MSG_FAILED_TO_LOAD_UNDO), + "from internal buffer"); + } } static void command_event_main_state(unsigned cmd) { retro_ctx_size_info_t info; char path[PATH_MAX_LENGTH] = {0}; - char buf[PATH_MAX_LENGTH] = {0}; char msg[128] = {0}; global_t *global = global_get_ptr(); settings_t *settings = config_get_ptr(); @@ -1694,33 +1670,13 @@ static void command_event_main_state(unsigned cmd) command_event_save_state(path, msg, sizeof(msg)); break; case CMD_EVENT_LOAD_STATE: - command_event_load_state(path, msg, sizeof(msg), false); + command_event_load_state(path, msg, sizeof(msg)); break; case CMD_EVENT_UNDO_LOAD_STATE: - strlcpy(buf, path, sizeof(buf)); - path_remove_extension(buf); - snprintf(buf, sizeof(buf), "%s.undo", buf); - - if (path_file_exists(buf)) - command_event_load_state(buf, msg, sizeof(msg), true); - else - { - snprintf(msg, sizeof(msg), "%s.", - msg_hash_to_str(MSG_FAILED_TO_LOAD_UNDO)); - } + command_event_undo_load_state(msg, sizeof(msg)); break; case CMD_EVENT_UNDO_SAVE_STATE: - strlcpy(buf, path, sizeof(buf)); - path_remove_extension(buf); - snprintf(buf, sizeof(buf), "%s.last", buf); - - if (path_file_exists(buf)) - command_event_load_state(buf, msg, sizeof(msg), true); - else - { - snprintf(msg, sizeof(msg), "%s.", - msg_hash_to_str(MSG_FAILED_TO_LOAD_UNDO)); - } + command_event_undo_save_state(msg, sizeof(msg)); break; } } @@ -1823,6 +1779,7 @@ bool command_event(enum event_command cmd, void *data) #ifndef HAVE_DYNAMIC command_event(CMD_EVENT_QUIT, NULL); #endif + content_reset_savestate_backups(); break; case CMD_EVENT_LOAD_STATE: /* Immutable - disallow savestate load when @@ -2122,9 +2079,12 @@ bool command_event(enum event_command cmd, void *data) if (hwr) memset(hwr, 0, sizeof(*hwr)); + content_reset_savestate_backups(); + break; } case CMD_EVENT_CORE_INIT: + content_reset_savestate_backups(); if (!command_event_init_core((enum rarch_core_type*)data)) return false; break; diff --git a/content.h b/content.h index a53ec3cb3c..0fbda04fdc 100644 --- a/content.h +++ b/content.h @@ -56,9 +56,10 @@ 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. */ +/* Undoes the last load state operation that was done */ bool content_undo_load_state(); +/* Restores the last savestate file which was overwritten */ 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 bd3c59b0c9..67da9363ae 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -182,7 +182,7 @@ bool content_undo_load_state() bool content_undo_save_state() { - filestream_write_file(old_save_file.path, old_save_file.data, old_save_file.size); + bool ret = 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'; @@ -193,7 +193,7 @@ bool content_undo_save_state() old_save_file.data = 0; - return true; + return ret; } @@ -434,3 +434,31 @@ bool content_rename_state(const char *origin, const char *dest) RARCH_LOG ("Error %d renaming file %s", ret, origin); return false; } + +/* +* Resets the state and savefile backups +* TODO/FIXME: Figure out when and where this should be called +* +*/ +bool content_reset_savestate_backups() +{ + if (old_save_file.data) + { + free(old_save_file.data); + old_save_file.data = NULL; + } + + old_save_file.path[0] = '\0'; + old_save_file.size = 0; + + if (old_state_buf.data) + { + free(old_state_buf.data); + old_state_buf.data = NULL; + } + + old_state_buf.path[0] = '\0'; + old_state_buf.size = 0; + + return true; +} From 8c5238c34927abc5ec4dcb88f1fcc4ec24b39838 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Wed, 8 Jun 2016 23:03:49 +0100 Subject: [PATCH 4/5] Undo Load State and Undo Save State both seem to be working. --- command.c | 4 ++-- internal buffer | Bin 0 -> 83988 bytes intl/msg_hash_us.c | 10 +++++----- msg_hash.h | 4 ++-- tasks/task_save_state.c | 25 ++++++++++++++++--------- 5 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 internal buffer diff --git a/command.c b/command.c index 3a69e11525..63e5ff9d90 100644 --- a/command.c +++ b/command.c @@ -1600,7 +1600,7 @@ static void command_event_undo_save_state(char *s, size_t len) if (!content_undo_save_state()) { snprintf(s, len, "%s \"%s\".", - msg_hash_to_str(MSG_FAILED_TO_SAVE_UNDO), + msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), "from internal buffer"); } } @@ -1638,7 +1638,7 @@ static void command_event_undo_load_state(char *s, size_t len) if (!content_undo_load_state()) { snprintf(s, len, "%s \"%s\".", - msg_hash_to_str(MSG_FAILED_TO_LOAD_UNDO), + msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE), "from internal buffer"); } } diff --git a/internal buffer b/internal buffer new file mode 100644 index 0000000000000000000000000000000000000000..84b907ed8eb914cfb32722a72f8b8517e345225a GIT binary patch literal 83988 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

(n~Mx?%oVF#yZR8 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 Date: Thu, 9 Jun 2016 00:38:27 +0100 Subject: [PATCH 5/5] Both undo options print messages now. Undo Load State can now also be undone ad infinitum. Doing it in succession swaps the current state for the backed up state. --- command.c | 14 ++++++++++++-- command.h | 1 + content.h | 5 ++++- tasks/task_save_state.c | 41 ++++++++++++++++++++++++----------------- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/command.c b/command.c index 63e5ff9d90..d6c5440ef9 100644 --- a/command.c +++ b/command.c @@ -1601,8 +1601,14 @@ static void command_event_undo_save_state(char *s, size_t len) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), - "from internal buffer"); + "RAM"); + + return; } + + /* TODO/FIXME - use msg_hash_to_str here and there */ + snprintf(s, len, "%s", + "Restored save state."); } /** @@ -1639,8 +1645,12 @@ static void command_event_undo_load_state(char *s, size_t len) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE), - "from internal buffer"); + "RAM"); + return; } + /* TODO/FIXME - use msg_hash_to_str here and there */ + snprintf(s, len, "%s", + "Undid load state."); } static void command_event_main_state(unsigned cmd) diff --git a/command.h b/command.h index 205728fde4..fce1ac2392 100644 --- a/command.h +++ b/command.h @@ -49,6 +49,7 @@ enum event_command CMD_EVENT_UNLOAD_CORE, CMD_EVENT_LOAD_STATE, CMD_EVENT_UNDO_LOAD_STATE, + /* Rewrites a savestate on disk */ CMD_EVENT_UNDO_SAVE_STATE, CMD_EVENT_SAVE_STATE, CMD_EVENT_SAVE_STATE_DECREMENT, diff --git a/content.h b/content.h index 0fbda04fdc..00e2796c29 100644 --- a/content.h +++ b/content.h @@ -47,7 +47,7 @@ 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_with_backup(const char* path, bool save_to_backup_buffer); +bool content_load_state_with_backup(const char* path, bool load_to_backup_buffer); /* Save a state from memory to disk. */ bool content_save_state(const char *path); @@ -78,6 +78,9 @@ void content_deinit(void); * selected libretro core. */ bool content_init(void); +/* Resets the state and savefile backup buffers */ +bool content_reset_savestate_backups(); + RETRO_END_DECLS #endif diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 932727c3dc..b605988834 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -64,7 +64,7 @@ struct sram_block bool content_undo_load_state() { - if (old_state_buf.data == NULL) + if (old_state_buf.data == NULL || old_state_buf.size == 0) return false; unsigned i; @@ -79,7 +79,7 @@ bool content_undo_load_state() RARCH_LOG("%s: \"%s\".\n", msg_hash_to_str(MSG_LOADING_STATE), - "from internal buffer"); + "RAM"); RARCH_LOG("%s: %u %s.\n", msg_hash_to_str(MSG_STATE_SIZE), @@ -136,11 +136,25 @@ bool content_undo_load_state() memcpy(blocks[i].data, ptr, blocks[i].size); } } + + /* We need to make a temporary copy of the buffer, to allow the swap below */ + void* temp_data = malloc(old_state_buf.size); + size_t temp_data_size = old_state_buf.size; + memcpy(temp_data, old_state_buf.data, old_state_buf.size); - serial_info.data_const = old_state_buf.data; - serial_info.size = old_state_buf.size; + serial_info.data_const = temp_data; + serial_info.size = temp_data_size; + + /* Swap the current state with the backup state. This way, we can undo + what we're undoing */ + content_save_state_with_backup("RAM", false); bool ret = core_unserialize(&serial_info); + /* Clean up the temporary copy */ + free(temp_data); + temp_data = NULL; + temp_data_size = 0; + /* Flush back. */ for (i = 0; i < num_blocks; i++) { @@ -166,16 +180,7 @@ bool content_undo_load_state() if (!ret) RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_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; + "RAM"); return ret; } @@ -196,7 +201,7 @@ bool content_undo_save_state() if (!ret) RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), - "from internal buffer"); + "RAM"); return ret; } @@ -388,7 +393,7 @@ bool content_load_state_with_backup(const char *path, bool load_to_backup_buffer serial_info.size = size; /* Backup the current state so we can undo this load */ - content_save_state_with_backup("internal buffer", false); + content_save_state_with_backup("RAM", false); ret = core_unserialize(&serial_info); /* Flush back. */ @@ -443,12 +448,14 @@ bool content_rename_state(const char *origin, const char *dest) } /* -* Resets the state and savefile backups +* * TODO/FIXME: Figure out when and where this should be called * */ bool content_reset_savestate_backups() { + printf("Resetting undo buffers.\n"); + if (old_save_file.data) { free(old_save_file.data);