From 70b1f385e806d82e1f5bea42c91d310d86c6f6a7 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Tue, 7 Jun 2016 18:33:01 +0100 Subject: [PATCH 1/9] 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/9] 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/9] 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/9] 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/9] 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); From 4ad2eba60219edd409d452591c150cceb4bdac9b Mon Sep 17 00:00:00 2001 From: radius Date: Wed, 8 Jun 2016 23:02:19 -0500 Subject: [PATCH 6/9] cleanups --- command.c | 8 +-- content.h | 6 +-- tasks/task_save_state.c | 117 +++++++++++++++++++++------------------- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/command.c b/command.c index d6c5440ef9..d377473d1a 100644 --- a/command.c +++ b/command.c @@ -1236,7 +1236,7 @@ static void command_event_load_auto_state(void) if (!path_file_exists(savestate_name_auto)) return; - ret = content_load_state(savestate_name_auto); + ret = content_load_state(savestate_name_auto, false); RARCH_LOG("Found auto savestate in: %s\n", savestate_name_auto); @@ -1408,7 +1408,7 @@ static bool command_event_save_auto_state(void) fill_pathname_noext(savestate_name_auto, global->name.savestate, ".auto", sizeof(savestate_name_auto)); - ret = content_save_state((const char*)savestate_name_auto); + ret = content_save_state((const char*)savestate_name_auto, true); RARCH_LOG("Auto save state to \"%s\" %s.\n", savestate_name_auto, ret ? "succeeded" : "failed"); @@ -1579,7 +1579,7 @@ static void command_event_save_state(const char *path, { settings_t *settings = config_get_ptr(); - if (!content_save_state(path)) + if (!content_save_state(path, true)) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_SAVE_STATE_TO), @@ -1623,7 +1623,7 @@ static void command_event_load_state(const char *path, char *s, size_t len) { settings_t *settings = config_get_ptr(); - if (!content_load_state(path)) + if (!content_load_state(path, false)) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_LOAD_STATE), diff --git a/content.h b/content.h index 00e2796c29..4292f4e022 100644 --- a/content.h +++ b/content.h @@ -46,12 +46,10 @@ 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_with_backup(const char* path, bool load_to_backup_buffer); +bool content_load_state(const char* path, bool load_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(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 b605988834..496858a743 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -42,18 +42,13 @@ struct save_state_buf 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; +/* Holds the previous saved state + * Can be restored to disk with undo_save_state(). */ +static struct save_state_buf undo_save_buf; -/* -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; +/* Holds the data from before a load_state() operation + * Can be restored with undo_load_state(). */ +static struct save_state_buf undo_load_buf; struct sram_block { @@ -62,9 +57,15 @@ struct sram_block size_t size; }; +/** + * undo_load_state: + * Revert to the state before a state was loaded. + * + * Returns: true if successful, false otherwise. + **/ bool content_undo_load_state() { - if (old_state_buf.data == NULL || old_state_buf.size == 0) + if (undo_load_buf.data == NULL || undo_load_buf.size == 0) return false; unsigned i; @@ -83,7 +84,7 @@ bool content_undo_load_state() RARCH_LOG("%s: %u %s.\n", msg_hash_to_str(MSG_STATE_SIZE), - old_state_buf.size, + undo_load_buf.size, msg_hash_to_str(MSG_BYTES)); @@ -138,16 +139,16 @@ bool content_undo_load_state() } /* 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); + void* temp_data = malloc(undo_load_buf.size); + size_t temp_data_size = undo_load_buf.size; + memcpy(temp_data, undo_load_buf.data, undo_load_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); + content_save_state("RAM", false); bool ret = core_unserialize(&serial_info); /* Clean up the temporary copy */ @@ -185,18 +186,24 @@ bool content_undo_load_state() return ret; } +/** + * undo_save_state: + * Reverts the last save operation + * + * Returns: true if successful, false otherwise. + **/ bool content_undo_save_state() { - bool ret = filestream_write_file(old_save_file.path, old_save_file.data, old_save_file.size); + bool ret = filestream_write_file(undo_save_buf.path, undo_save_buf.data, undo_save_buf.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; + undo_save_buf.path[0] = '\0'; + if (undo_save_buf.data) { + free(undo_save_buf.data); + undo_save_buf.data = NULL; } - old_save_file.data = 0; + undo_save_buf.data = 0; if (!ret) RARCH_ERR("%s \"%s\".\n", @@ -212,13 +219,12 @@ bool content_undo_save_state() /** * save_state: * @path : path of saved state that shall be written to. - * @save_to_disk: If false, saves the state onto old_state_buf. + * @save_to_disk: If false, saves the state onto undo_load_buf. * Save a state from memory to disk. * * Returns: true if successful, false otherwise. **/ -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) +bool content_save_state(const char *path, bool save_to_disk) { retro_ctx_serialize_info_t serial_info; retro_ctx_size_info_t info; @@ -251,26 +257,26 @@ 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_with_backup(path, true); + 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 */ + in undo_load_buf to allow content_undo_load_state() to restore it */ else { - old_state_buf.path[0] = '\0'; + undo_load_buf.path[0] = '\0'; /* 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; + if (undo_load_buf.data) { + free(undo_load_buf.data); + undo_load_buf.data = NULL; } - old_state_buf.data = malloc(info.size); - memcpy(old_state_buf.data, data, info.size); - old_state_buf.size = info.size; + undo_load_buf.data = malloc(info.size); + memcpy(undo_load_buf.data, data, info.size); + undo_load_buf.size = info.size; } } else @@ -288,15 +294,14 @@ bool content_save_state_with_backup(const char *path, bool save_to_disk) /** * content_load_state: * @path : path that state will be loaded from. - * @load_to_backup_buffer: If true, the state will be loaded into old_save_file. + * @load_to_backup_buffer: If true, the state will be loaded into undo_save_buf. * Load a state from disk to memory. * * Returns: true if successful, false otherwise. * * **/ -bool content_load_state(const char* path) { content_load_state_with_backup(path, false); } -bool content_load_state_with_backup(const char *path, bool load_to_backup_buffer) +bool content_load_state(const char *path, bool load_to_backup_buffer) { unsigned i; ssize_t size; @@ -323,18 +328,18 @@ bool content_load_state_with_backup(const char *path, bool load_to_backup_buffer /* This means we're backing up the file in memory, so content_undo_save_state() can restore it */ if (load_to_backup_buffer) { - strcpy(old_save_file.path, path); + strcpy(undo_save_buf.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; + if (undo_save_buf.data) { + free(undo_save_buf.data); + undo_save_buf.data = NULL; } - old_save_file.data = malloc(size); - memcpy(old_save_file.data, buf, size); + undo_save_buf.data = malloc(size); + memcpy(undo_save_buf.data, buf, size); - old_save_file.size = size; + undo_save_buf.size = size; free(buf); return true; @@ -393,7 +398,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("RAM", false); + content_save_state("RAM", false); ret = core_unserialize(&serial_info); /* Flush back. */ @@ -456,23 +461,23 @@ bool content_reset_savestate_backups() { printf("Resetting undo buffers.\n"); - if (old_save_file.data) + if (undo_save_buf.data) { - free(old_save_file.data); - old_save_file.data = NULL; + free(undo_save_buf.data); + undo_save_buf.data = NULL; } - old_save_file.path[0] = '\0'; - old_save_file.size = 0; + undo_save_buf.path[0] = '\0'; + undo_save_buf.size = 0; - if (old_state_buf.data) + if (undo_load_buf.data) { - free(old_state_buf.data); - old_state_buf.data = NULL; + free(undo_load_buf.data); + undo_load_buf.data = NULL; } - old_state_buf.path[0] = '\0'; - old_state_buf.size = 0; + undo_load_buf.path[0] = '\0'; + undo_load_buf.size = 0; return true; } From 83a1d9ac1fb4a1994a8f96cb3a7af04ad1180689 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Thu, 9 Jun 2016 16:14:40 +0100 Subject: [PATCH 7/9] Clean up some stuff, add comments, reduce the places where content_reset_backup_buffers is called, try to make the undo messages nicer. * I'm trying to write to differentiate read/write errors from empty buffer errors. I changed command.c and task_save_state.c so the content_undo funcs are the ones writting the success messages. I was told to use runloop_msg_queue_push() to write to the OSD, but that doesn't seem to be working. --- command.c | 16 ++----------- tasks/task_save_state.c | 51 +++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/command.c b/command.c index d377473d1a..1d06aa6611 100644 --- a/command.c +++ b/command.c @@ -1602,13 +1602,7 @@ 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), "RAM"); - - return; } - - /* TODO/FIXME - use msg_hash_to_str here and there */ - snprintf(s, len, "%s", - "Restored save state."); } /** @@ -1646,11 +1640,7 @@ 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), "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) @@ -1789,7 +1779,6 @@ 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 @@ -2082,6 +2071,7 @@ bool command_event(enum event_command cmd, void *data) break; case CMD_EVENT_CORE_DEINIT: { + content_reset_savestate_backups(); struct retro_hw_render_callback *hwr = video_driver_get_hw_context(); command_event_deinit_core(true); @@ -2089,8 +2079,6 @@ 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: diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 496858a743..78a23bb7b4 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -65,8 +65,17 @@ struct sram_block **/ bool content_undo_load_state() { - if (undo_load_buf.data == NULL || undo_load_buf.size == 0) - return false; + if (undo_load_buf.data == NULL || undo_load_buf.size == 0) { + /* TODO/FIXME - Use msg_hash_to_str in here */ + /* TODO/FIXME - Should we be using runloop_msg_queue_push in here? */ + + RARCH_LOG("%s\n", + "No load state to undo."); + runloop_msg_queue_push("No load state to undo.", 2, 180, true); + + /* Even though there was no undo, signal this as a success */ + return true; + } unsigned i; //ssize_t size; @@ -80,7 +89,7 @@ bool content_undo_load_state() RARCH_LOG("%s: \"%s\".\n", msg_hash_to_str(MSG_LOADING_STATE), - "RAM"); + undo_load_buf.path); RARCH_LOG("%s: %u %s.\n", msg_hash_to_str(MSG_STATE_SIZE), @@ -178,10 +187,13 @@ bool content_undo_load_state() free(blocks[i].data); free(blocks); - if (!ret) + if (!ret) { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE), - "RAM"); + undo_load_buf.path); + } else { + runloop_msg_queue_push("Undid load state.", 2, 180, true); + } return ret; } @@ -194,6 +206,15 @@ bool content_undo_load_state() **/ bool content_undo_save_state() { + if (undo_save_buf.data == NULL || undo_save_buf.size == 0) + { + /* TODO/FIXME - Use msg_hash_to_str in here */ + RARCH_LOG("%s\n", + "No save state to undo."); + runloop_msg_queue_push("No save state to undo.", 2, 180, false); + return true; + } + bool ret = filestream_write_file(undo_save_buf.path, undo_save_buf.data, undo_save_buf.size); /* Wipe the save file buffer as it's intended to be one use only */ @@ -205,10 +226,13 @@ bool content_undo_save_state() undo_save_buf.data = 0; - if (!ret) + if (!ret) { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), - "RAM"); + undo_save_buf.path); + } else { + runloop_msg_queue_push("Undid save state.", 2, 180, true); + } return ret; } @@ -257,16 +281,22 @@ bool content_save_state(const char *path, bool save_to_disk) if (ret) { if (save_to_disk) { if (path_file_exists(path)) { + /* Before overwritting the savestate file, load it into a buffer + to allow undo_save_state() to work */ + /* TODO/FIXME - Use msg_hash_to_str here */ + RARCH_LOG("%s\n", + "File already exists. Saving to backup buffer..."); + 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 undo_load_buf to allow content_undo_load_state() to restore it */ + else { - undo_load_buf.path[0] = '\0'; + /* save_to_disk is false, which means we are saving the state + in undo_load_buf to allow content_undo_load_state() to restore it */ /* If we were holding onto an old state already, clean it up first */ if (undo_load_buf.data) { @@ -277,6 +307,7 @@ bool content_save_state(const char *path, bool save_to_disk) undo_load_buf.data = malloc(info.size); memcpy(undo_load_buf.data, data, info.size); undo_load_buf.size = info.size; + strcpy(undo_load_buf.path, path); } } else From 8e20b9e938ec4b3a1e63d0291668a52e1c3965a2 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Fri, 10 Jun 2016 20:12:43 +0100 Subject: [PATCH 8/9] Both Undo options now write distinct messages in the OSD when buffers are empty, when undoing fails and when undoing is successful. Aside from some TODO/FIXME areas added by this fork, the features in the fork are now complete. --- command.c | 29 ++++++++++++++++++++++++++++- content.h | 4 ++++ tasks/task_save_state.c | 37 +++++++++++-------------------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/command.c b/command.c index 1d06aa6611..f0f71f53ad 100644 --- a/command.c +++ b/command.c @@ -1597,12 +1597,25 @@ static void command_event_save_state(const char *path, static void command_event_undo_save_state(char *s, size_t len) { + if (content_undo_save_buf_is_empty()) + { + /* TODO/FIXME - use msg_hash_to_str here */ + snprintf(s, len, "%s", + "No save state has been overwritten yet."); + return; + } + if (!content_undo_save_state()) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), "RAM"); + return; } + + /* TODO/FIXME - use msg_hash_to_str here */ + snprintf(s, len, "%s", + "Restored old save state."); } /** @@ -1635,12 +1648,26 @@ static void command_event_load_state(const char *path, char *s, size_t len) static void command_event_undo_load_state(char *s, size_t len) { + + if (content_undo_load_buf_is_empty()) + { + /* TODO/FIXME - use msg_hash_to_str here */ + snprintf(s, len, "%s", + "No state has been loaded yet."); + return; + } + if (!content_undo_load_state()) { snprintf(s, len, "%s \"%s\".", msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE), "RAM"); - } + return; + } + + /* TODO/FIXME - use msg_hash_to_str here */ + snprintf(s, len, "%s", + "Undid load state."); } static void command_event_main_state(unsigned cmd) diff --git a/content.h b/content.h index 4292f4e022..c50773aaed 100644 --- a/content.h +++ b/content.h @@ -79,6 +79,10 @@ bool content_init(void); /* Resets the state and savefile backup buffers */ bool content_reset_savestate_backups(); +/* Checks if the buffers are empty */ +bool content_undo_load_buf_is_empty(); +bool content_undo_save_buf_is_empty(); + RETRO_END_DECLS #endif diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 78a23bb7b4..366eb92d62 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -65,18 +65,6 @@ struct sram_block **/ bool content_undo_load_state() { - if (undo_load_buf.data == NULL || undo_load_buf.size == 0) { - /* TODO/FIXME - Use msg_hash_to_str in here */ - /* TODO/FIXME - Should we be using runloop_msg_queue_push in here? */ - - RARCH_LOG("%s\n", - "No load state to undo."); - runloop_msg_queue_push("No load state to undo.", 2, 180, true); - - /* Even though there was no undo, signal this as a success */ - return true; - } - unsigned i; //ssize_t size; retro_ctx_serialize_info_t serial_info; @@ -191,9 +179,7 @@ bool content_undo_load_state() RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_LOAD_STATE), undo_load_buf.path); - } else { - runloop_msg_queue_push("Undid load state.", 2, 180, true); - } + } return ret; } @@ -206,15 +192,6 @@ bool content_undo_load_state() **/ bool content_undo_save_state() { - if (undo_save_buf.data == NULL || undo_save_buf.size == 0) - { - /* TODO/FIXME - Use msg_hash_to_str in here */ - RARCH_LOG("%s\n", - "No save state to undo."); - runloop_msg_queue_push("No save state to undo.", 2, 180, false); - return true; - } - bool ret = filestream_write_file(undo_save_buf.path, undo_save_buf.data, undo_save_buf.size); /* Wipe the save file buffer as it's intended to be one use only */ @@ -230,8 +207,6 @@ bool content_undo_save_state() RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), undo_save_buf.path); - } else { - runloop_msg_queue_push("Undid save state.", 2, 180, true); } return ret; @@ -512,3 +487,13 @@ bool content_reset_savestate_backups() return true; } + +bool content_undo_load_buf_is_empty() +{ + return undo_load_buf.data == NULL || undo_load_buf.size == 0; +} + +bool content_undo_save_buf_is_empty() +{ + return undo_save_buf.data == NULL || undo_save_buf.size == 0; +} From 0d3a866356956cd0f7971ee4629dc77d5d9e04d3 Mon Sep 17 00:00:00 2001 From: Arzed Five Date: Sat, 11 Jun 2016 17:21:09 +0100 Subject: [PATCH 9/9] Corrected a mistake, added some more error checking. --- command.h | 1 + tasks/task_save_state.c | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/command.h b/command.h index fce1ac2392..2725022923 100644 --- a/command.h +++ b/command.h @@ -48,6 +48,7 @@ enum event_command CMD_EVENT_LOAD_CORE_PERSIST, CMD_EVENT_UNLOAD_CORE, CMD_EVENT_LOAD_STATE, + /* Swaps the current state with what's on the undo load buffer */ CMD_EVENT_UNDO_LOAD_STATE, /* Rewrites a savestate on disk */ CMD_EVENT_UNDO_SAVE_STATE, diff --git a/tasks/task_save_state.c b/tasks/task_save_state.c index 366eb92d62..b7384e955e 100644 --- a/tasks/task_save_state.c +++ b/tasks/task_save_state.c @@ -59,7 +59,7 @@ struct sram_block /** * undo_load_state: - * Revert to the state before a state was loaded. + * Revert to the state before a state was loaded. * * Returns: true if successful, false otherwise. **/ @@ -186,7 +186,7 @@ bool content_undo_load_state() /** * undo_save_state: - * Reverts the last save operation + * Reverts the last save operation * * Returns: true if successful, false otherwise. **/ @@ -196,13 +196,12 @@ bool content_undo_save_state() /* Wipe the save file buffer as it's intended to be one use only */ undo_save_buf.path[0] = '\0'; + undo_save_buf.size = 0; if (undo_save_buf.data) { free(undo_save_buf.data); undo_save_buf.data = NULL; } - undo_save_buf.data = 0; - if (!ret) { RARCH_ERR("%s \"%s\".\n", msg_hash_to_str(MSG_FAILED_TO_UNDO_SAVE_STATE), @@ -280,6 +279,11 @@ bool content_save_state(const char *path, bool save_to_disk) } undo_load_buf.data = malloc(info.size); + if (!undo_load_buf.data) { + free(data); + return false; + } + memcpy(undo_load_buf.data, data, info.size); undo_load_buf.size = info.size; strcpy(undo_load_buf.path, path); @@ -334,8 +338,7 @@ bool content_load_state(const char *path, bool load_to_backup_buffer) /* This means we're backing up the file in memory, so content_undo_save_state() can restore it */ if (load_to_backup_buffer) { - strcpy(undo_save_buf.path, path); - + /* If we were previously backing up a file, let go of it first */ if (undo_save_buf.data) { free(undo_save_buf.data); @@ -343,9 +346,12 @@ bool content_load_state(const char *path, bool load_to_backup_buffer) } undo_save_buf.data = malloc(size); - memcpy(undo_save_buf.data, buf, size); + if (!undo_save_buf.data) + goto error; + memcpy(undo_save_buf.data, buf, size); undo_save_buf.size = size; + strcpy(undo_save_buf.path, path); free(buf); return true; @@ -460,7 +466,8 @@ bool content_rename_state(const char *origin, const char *dest) /* * -* TODO/FIXME: Figure out when and where this should be called +* TODO/FIXME: Figure out when and where this should be called. +* As it is, when e.g. closing Gambatte, we get the same printf message 4 times. * */ bool content_reset_savestate_backups()