diff --git a/Makefile.common b/Makefile.common
index d76be0515e..c10890968a 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -269,6 +269,7 @@ ifeq ($(HAVE_PATCH), 1)
endif
OBJ += \
+ save.o \
tasks/task_save.o \
tasks/task_movie.o \
tasks/task_file_transfer.o \
diff --git a/command.c b/command.c
index f95084407c..aa17586b35 100644
--- a/command.c
+++ b/command.c
@@ -1258,9 +1258,7 @@ bool command_event_resize_windowed_scale(settings_t *settings,
return true;
}
-bool command_event_save_auto_state(
- bool savestate_auto_save,
- const enum rarch_core_type current_core_type)
+bool command_event_save_auto_state(void)
{
size_t _len;
runloop_state_t *runloop_st = runloop_state_get_ptr();
@@ -1268,10 +1266,6 @@ bool command_event_save_auto_state(
if (runloop_st->entry_state_slot)
return false;
- if (!savestate_auto_save)
- return false;
- if (current_core_type == CORE_TYPE_DUMMY)
- return false;
if (!core_info_current_supports_savestate())
return false;
if (string_is_empty(path_basename(path_get(RARCH_PATH_BASENAME))))
diff --git a/command.h b/command.h
index 59ed89395c..2d690a9929 100644
--- a/command.h
+++ b/command.h
@@ -349,9 +349,7 @@ void command_event_set_mixer_volume(
bool command_event_resize_windowed_scale(settings_t *settings,
unsigned window_scale);
-bool command_event_save_auto_state(
- bool savestate_auto_save,
- const enum rarch_core_type current_core_type);
+bool command_event_save_auto_state(void);
/**
* event_set_volume:
diff --git a/content.h b/content.h
index 70da680aa8..64b2557b97 100644
--- a/content.h
+++ b/content.h
@@ -39,12 +39,6 @@ typedef struct content_ctx_info
int argc; /* Argument count. */
} content_ctx_info_t;
-/* Load a RAM state from disk to memory. */
-bool content_load_ram_file(unsigned slot);
-
-/* Save a RAM state from memory to disk. */
-bool content_save_ram_file(unsigned slot, bool compress);
-
/* Load a state from memory. */
bool content_load_state_from_ram(void);
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 8475b48788..044820c698 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -1274,6 +1274,7 @@ DATA RUNLOOP
#endif
#endif
#endif
+#include "../save.c"
#include "../tasks/task_save.c"
#include "../tasks/task_movie.c"
#include "../tasks/task_image.c"
diff --git a/retroarch.c b/retroarch.c
index cd5b4fe7a2..440667f38a 100644
--- a/retroarch.c
+++ b/retroarch.c
@@ -3600,9 +3600,9 @@ bool command_event(enum event_command cmd, void *data)
settings->bools.content_runtime_log_aggregate,
settings->paths.directory_runtime_log,
settings->paths.directory_playlist);
- command_event_save_auto_state(
- settings->bools.savestate_auto_save,
- runloop_st->current_core_type);
+ if (settings->bools.savestate_auto_save &&
+ runloop_st->current_core_type != CORE_TYPE_DUMMY)
+ command_event_save_auto_state();
if ( (runloop_st->flags & RUNLOOP_FLAG_REMAPS_CORE_ACTIVE)
|| (runloop_st->flags & RUNLOOP_FLAG_REMAPS_CONTENT_DIR_ACTIVE)
@@ -8102,9 +8102,9 @@ bool retroarch_main_quit(void)
command_event(CMD_EVENT_MENU_SAVE_CURRENT_CONFIG, NULL);
#endif
- command_event_save_auto_state(
- settings->bools.savestate_auto_save,
- runloop_st->current_core_type);
+ if (settings->bools.savestate_auto_save &&
+ runloop_st->current_core_type != CORE_TYPE_DUMMY)
+ command_event_save_auto_state();
/* If any save states are in progress, wait
* until all tasks are complete (otherwise
diff --git a/save.c b/save.c
new file mode 100644
index 0000000000..b6b72a2b92
--- /dev/null
+++ b/save.c
@@ -0,0 +1,588 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2016-2019 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include "../content.h"
+#include "../core.h"
+#include "../core_info.h"
+#include "../file_path_special.h"
+#include "../configuration.h"
+#include "../msg_hash.h"
+#include "../runloop.h"
+#include "../verbosity.h"
+#ifdef HAVE_CHEATS
+#include "../cheat_manager.h"
+#endif
+
+struct ram_type
+{
+ const char *path;
+ int type;
+};
+
+static struct string_list *task_save_files = NULL;
+
+#ifdef HAVE_THREADS
+typedef struct autosave autosave_t;
+
+/* Autosave support. */
+struct autosave_st
+{
+ autosave_t **list;
+ unsigned num;
+};
+
+enum autosave_flags
+{
+ AUTOSAVE_FLAG_QUIT = (1 << 0),
+ AUTOSAVE_FLAG_COMPRESS_FILES = (1 << 1)
+};
+
+struct autosave
+{
+ void *buffer;
+ const void *retro_buffer;
+ const char *path;
+ slock_t *lock;
+ slock_t *cond_lock;
+ scond_t *cond;
+ sthread_t *thread;
+ size_t bufsize;
+ unsigned interval;
+ uint8_t flags;
+};
+
+static struct autosave_st autosave_state;
+
+
+/**
+ * autosave_thread:
+ * @data : pointer to autosave object
+ *
+ * Callback function for (threaded) autosave.
+ **/
+static void autosave_thread(void *data)
+{
+ autosave_t *save = (autosave_t*)data;
+
+ for (;;)
+ {
+ bool differ;
+
+ slock_lock(save->lock);
+ differ = memcmp(save->buffer, save->retro_buffer,
+ save->bufsize) != 0;
+ if (differ)
+ memcpy(save->buffer, save->retro_buffer, save->bufsize);
+ slock_unlock(save->lock);
+
+ if (differ)
+ {
+ intfstream_t *file = NULL;
+
+ /* Should probably deal with this more elegantly. */
+ if (save->flags & AUTOSAVE_FLAG_COMPRESS_FILES)
+ file = intfstream_open_rzip_file(save->path,
+ RETRO_VFS_FILE_ACCESS_WRITE);
+ else
+ file = intfstream_open_file(save->path,
+ RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
+
+ if (file)
+ {
+ intfstream_write(file, save->buffer, save->bufsize);
+ intfstream_flush(file);
+ intfstream_close(file);
+ free(file);
+ }
+ }
+
+ slock_lock(save->cond_lock);
+
+ if (save->flags & AUTOSAVE_FLAG_QUIT)
+ {
+ slock_unlock(save->cond_lock);
+ break;
+ }
+
+ scond_wait_timeout(save->cond,
+ save->cond_lock,
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+ save->interval * 1000000
+#else
+ save->interval * 1000000LL
+#endif
+ );
+
+ slock_unlock(save->cond_lock);
+ }
+}
+
+/**
+ * autosave_new:
+ * @path : path to autosave file
+ * @data : pointer to buffer
+ * @size : size of @data buffer
+ * @interval : interval at which saves should be performed.
+ *
+ * Create and initialize autosave object.
+ *
+ * @return Pointer to new autosave_t object if successful, otherwise
+ * NULL.
+ **/
+static autosave_t *autosave_new(const char *path,
+ const void *data, size_t size,
+ unsigned interval, bool compress)
+{
+ void *buf = NULL;
+ autosave_t *handle = (autosave_t*)malloc(sizeof(*handle));
+ if (!handle)
+ return NULL;
+
+ handle->flags = 0;
+ handle->bufsize = size;
+ handle->interval = interval;
+ if (compress)
+ handle->flags |= AUTOSAVE_FLAG_COMPRESS_FILES;
+ handle->retro_buffer = data;
+ handle->path = path;
+
+ if (!(buf = malloc(size)))
+ {
+ free(handle);
+ return NULL;
+ }
+
+ handle->buffer = buf;
+
+ memcpy(handle->buffer, handle->retro_buffer, handle->bufsize);
+
+ handle->lock = slock_new();
+ handle->cond_lock = slock_new();
+ handle->cond = scond_new();
+ handle->thread = sthread_create(autosave_thread, handle);
+
+ return handle;
+}
+
+/**
+ * autosave_free:
+ * @handle : pointer to autosave object
+ *
+ * Frees autosave object.
+ **/
+static void autosave_free(autosave_t *handle)
+{
+ slock_lock(handle->cond_lock);
+ handle->flags |= AUTOSAVE_FLAG_QUIT;
+ slock_unlock(handle->cond_lock);
+ scond_signal(handle->cond);
+ sthread_join(handle->thread);
+
+ slock_free(handle->lock);
+ slock_free(handle->cond_lock);
+ scond_free(handle->cond);
+
+ if (handle->buffer)
+ free(handle->buffer);
+ handle->buffer = NULL;
+}
+
+bool autosave_init(void)
+{
+ unsigned i;
+ autosave_t **list = NULL;
+ settings_t *settings = config_get_ptr();
+ unsigned autosave_interval = settings->uints.autosave_interval;
+#if defined(HAVE_ZLIB)
+ bool compress_files = settings->bools.save_file_compression;
+#else
+ bool compress_files = false;
+#endif
+
+ if (autosave_interval < 1 || !task_save_files)
+ return false;
+
+ if (!(list = (autosave_t**)
+ calloc(task_save_files->size,
+ sizeof(*autosave_state.list))))
+ return false;
+
+ autosave_state.list = list;
+ autosave_state.num = (unsigned)task_save_files->size;
+
+ for (i = 0; i < task_save_files->size; i++)
+ {
+ retro_ctx_memory_info_t mem_info;
+ autosave_t *auto_st = NULL;
+ const char *path = task_save_files->elems[i].data;
+ unsigned type = task_save_files->elems[i].attr.i;
+
+ mem_info.id = type;
+
+ core_get_memory(&mem_info);
+
+ if (mem_info.size <= 0)
+ continue;
+
+ if (!(auto_st = autosave_new(path,
+ mem_info.data,
+ mem_info.size,
+ autosave_interval,
+ compress_files)))
+ {
+ RARCH_WARN("%s\n", msg_hash_to_str(MSG_AUTOSAVE_FAILED));
+ continue;
+ }
+
+ autosave_state.list[i] = auto_st;
+ }
+
+ return true;
+}
+
+void autosave_deinit(void)
+{
+ unsigned i;
+
+ for (i = 0; i < autosave_state.num; i++)
+ {
+ autosave_t *handle = autosave_state.list[i];
+ if (handle)
+ {
+ autosave_free(handle);
+ free(autosave_state.list[i]);
+ }
+ autosave_state.list[i] = NULL;
+ }
+
+ free(autosave_state.list);
+
+ autosave_state.list = NULL;
+ autosave_state.num = 0;
+}
+
+/**
+ * autosave_lock:
+ *
+ * Lock autosave.
+ **/
+void autosave_lock(void)
+{
+ unsigned i;
+
+ for (i = 0; i < autosave_state.num; i++)
+ {
+ autosave_t *handle = autosave_state.list[i];
+ if (handle)
+ slock_lock(handle->lock);
+ }
+}
+
+/**
+ * autosave_unlock:
+ *
+ * Unlocks autosave.
+ **/
+void autosave_unlock(void)
+{
+ unsigned i;
+
+ for (i = 0; i < autosave_state.num; i++)
+ {
+ autosave_t *handle = autosave_state.list[i];
+ if (handle)
+ slock_unlock(handle->lock);
+ }
+}
+#endif
+
+static bool content_get_memory(retro_ctx_memory_info_t *mem_info,
+ struct ram_type *ram, unsigned slot)
+{
+ ram->type = task_save_files->elems[slot].attr.i;
+ ram->path = task_save_files->elems[slot].data;
+ mem_info->id = ram->type;
+
+ core_get_memory(mem_info);
+
+ if (!mem_info->data || mem_info->size == 0)
+ return false;
+
+ return true;
+}
+
+/**
+ * content_load_ram_file:
+ * @path : path of RAM state that will be loaded from.
+ * @type : type of memory
+ *
+ * Load a RAM state from disk to memory.
+ */
+static bool content_load_ram_file(unsigned slot)
+{
+ int64_t rc;
+ struct ram_type ram;
+ retro_ctx_memory_info_t mem_info;
+ void *buf = NULL;
+
+ if (!content_get_memory(&mem_info, &ram, slot))
+ return false;
+
+ /* On first run of content, SRAM file will
+ * not exist. This is a common enough occurrence
+ * that we should check before attempting to
+ * invoke the relevant read_file() function */
+ if ( string_is_empty(ram.path)
+ || !path_is_valid(ram.path))
+ return false;
+
+#if defined(HAVE_ZLIB)
+ /* Always use RZIP interface when reading SRAM
+ * files - this will automatically handle uncompressed
+ * data */
+ if (!rzipstream_read_file(ram.path, &buf, &rc))
+#else
+ if (!filestream_read_file(ram.path, &buf, &rc))
+#endif
+ return false;
+
+ if (rc > 0)
+ {
+ if (rc > (ssize_t)mem_info.size)
+ {
+ RARCH_WARN("[SRAM]: SRAM is larger than implementation expects, "
+ "doing partial load (truncating %u %s %s %u).\n",
+ (unsigned)rc,
+ msg_hash_to_str(MSG_BYTES),
+ msg_hash_to_str(MSG_TO),
+ (unsigned)mem_info.size);
+ rc = mem_info.size;
+ }
+ memcpy(mem_info.data, buf, (size_t)rc);
+ }
+
+ if (buf)
+ free(buf);
+
+ return true;
+}
+
+/**
+ * dump_to_file_desperate:
+ * @data : pointer to data buffer.
+ * @size : size of @data.
+ * @type : type of file to be saved.
+ *
+ * Attempt to save valuable RAM data somewhere.
+ **/
+static bool dump_to_file_desperate(const void *data,
+ size_t size, unsigned type)
+{
+ char path[PATH_MAX_LENGTH + 256 + 32];
+ path [0] = '\0';
+
+ if (fill_pathname_application_data(path,
+ sizeof(path)))
+ {
+ size_t _len;
+ time_t time_;
+ struct tm tm_;
+ char timebuf[256];
+ timebuf [0] = '\0';
+ time(&time_);
+
+ rtime_localtime(&time_, &tm_);
+
+ strftime(timebuf, 256 * sizeof(char),
+ "%Y-%m-%d-%H-%M-%S", &tm_);
+
+ _len = strlcat(path, "/RetroArch-recovery-", sizeof(path));
+
+ snprintf(path + _len, sizeof(path) - _len,
+ "%u%s", type, timebuf);
+
+ /* Fallback (emergency) saves are always
+ * uncompressed
+ * > If a regular save fails, then the host
+ * system is experiencing serious technical
+ * difficulties (most likely some kind of
+ * hardware failure)
+ * > In this case, we don't want to further
+ * complicate matters by introducing zlib
+ * compression overheads */
+ if (filestream_write_file(path, data, size))
+ {
+ RARCH_WARN("[SRAM]: Succeeded in saving RAM data to \"%s\".\n", path);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * content_save_ram_file:
+ * @path : path of RAM state that shall be written to.
+ * @type : type of memory
+ *
+ * Save a RAM state from memory to disk.
+ *
+ */
+static bool content_save_ram_file(unsigned slot, bool compress)
+{
+ struct ram_type ram;
+ retro_ctx_memory_info_t mem_info;
+
+ if (!content_get_memory(&mem_info, &ram, slot))
+ return false;
+
+ RARCH_LOG("[SRAM]: %s #%u %s \"%s\".\n",
+ msg_hash_to_str(MSG_SAVING_RAM_TYPE),
+ ram.type,
+ msg_hash_to_str(MSG_TO),
+ ram.path);
+
+#if defined(HAVE_ZLIB)
+ if (compress)
+ {
+ if (!rzipstream_write_file(
+ ram.path, mem_info.data, mem_info.size))
+ goto fail;
+ }
+ else
+#endif
+ {
+ if (!filestream_write_file(
+ ram.path, mem_info.data, mem_info.size))
+ goto fail;
+ }
+
+ RARCH_LOG("[SRAM]: %s \"%s\".\n",
+ msg_hash_to_str(MSG_SAVED_SUCCESSFULLY_TO),
+ ram.path);
+
+ return true;
+
+fail:
+ RARCH_ERR("[SRAM]: %s.\n",
+ msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
+ RARCH_WARN("[SRAM]: Attempting to recover ...\n");
+
+ /* In case the file could not be written to,
+ * the fallback function 'dump_to_file_desperate'
+ * will be called. */
+ if (!dump_to_file_desperate(
+ mem_info.data, mem_info.size, ram.type))
+ RARCH_WARN("[SRAM]: Failed ... Cannot recover save file.\n");
+ return false;
+}
+
+bool event_save_files(bool is_sram_used)
+{
+ unsigned i;
+ settings_t *settings = config_get_ptr();
+#ifdef HAVE_CHEATS
+ const char *path_cheat_database = settings->paths.path_cheat_database;
+#endif
+#if defined(HAVE_ZLIB)
+ bool compress_files = settings->bools.save_file_compression;
+#else
+ bool compress_files = false;
+#endif
+
+#ifdef HAVE_CHEATS
+ cheat_manager_save_game_specific_cheats(
+ path_cheat_database);
+#endif
+ if (!task_save_files || !is_sram_used)
+ return false;
+
+ for (i = 0; i < task_save_files->size; i++)
+ {
+ content_save_ram_file(i, compress_files);
+ }
+
+ return true;
+}
+
+bool event_load_save_files(bool is_sram_load_disabled)
+{
+ unsigned i;
+ bool success = false;
+
+ if (!task_save_files || is_sram_load_disabled)
+ return false;
+
+ /* Report a successful load operation if
+ * any type of RAM file is found and
+ * processed correctly */
+ for (i = 0; i < task_save_files->size; i++)
+ success |= content_load_ram_file(i);
+
+ return success;
+}
+
+void path_init_savefile_rtc(const char *savefile_path)
+{
+ union string_list_elem_attr attr;
+ char savefile_name_rtc[PATH_MAX_LENGTH];
+
+ attr.i = RETRO_MEMORY_SAVE_RAM;
+ string_list_append(task_save_files, savefile_path, attr);
+
+ /* Infer .rtc save path from save RAM path. */
+ attr.i = RETRO_MEMORY_RTC;
+ fill_pathname(savefile_name_rtc,
+ savefile_path, ".rtc",
+ sizeof(savefile_name_rtc));
+ string_list_append(task_save_files, savefile_name_rtc, attr);
+}
+
+void path_deinit_savefile(void)
+{
+ if (task_save_files)
+ string_list_free(task_save_files);
+ task_save_files = NULL;
+}
+
+void path_init_savefile_new(void)
+{
+ task_save_files = string_list_new();
+}
+
+void *savefile_ptr_get(void)
+{
+ return task_save_files;
+}
+
diff --git a/tasks/task_save.c b/tasks/task_save.c
index fd4b197507..a874196658 100644
--- a/tasks/task_save.c
+++ b/tasks/task_save.c
@@ -19,12 +19,6 @@
#include
#include
-#ifdef _WIN32
-#include
-#else
-#include
-#endif
-
#include
#include
#include
@@ -40,10 +34,6 @@
#include "../config.h"
#endif
-#ifdef HAVE_NETWORKING
-#include "../network/netplay/netplay.h"
-#endif
-
#ifdef HAVE_CHEEVOS
#include "../cheevos/cheevos.h"
#endif
@@ -58,9 +48,6 @@
#include "../runloop.h"
#include "../verbosity.h"
#include "tasks_internal.h"
-#ifdef HAVE_CHEATS
-#include "../cheat_manager.h"
-#endif
#ifdef EMSCRIPTEN
/* Filesystem is in-memory anyway, use huge chunks since each
@@ -78,12 +65,6 @@
#define RASTATE_REPLAY_BLOCK "RPLY"
#define RASTATE_END_BLOCK "END "
-struct ram_type
-{
- const char *path;
- int type;
-};
-
struct save_state_buf
{
void* data;
@@ -130,37 +111,6 @@ typedef struct
char path[PATH_MAX_LENGTH];
} save_task_state_t;
-#ifdef HAVE_THREADS
-typedef struct autosave autosave_t;
-
-/* Autosave support. */
-struct autosave_st
-{
- autosave_t **list;
- unsigned num;
-};
-
-enum autosave_flags
-{
- AUTOSAVE_FLAG_QUIT = (1 << 0),
- AUTOSAVE_FLAG_COMPRESS_FILES = (1 << 1)
-};
-
-struct autosave
-{
- void *buffer;
- const void *retro_buffer;
- const char *path;
- slock_t *lock;
- slock_t *cond_lock;
- scond_t *cond;
- sthread_t *thread;
- size_t bufsize;
- unsigned interval;
- uint8_t flags;
-};
-#endif
-
typedef save_task_state_t load_task_data_t;
/* Holds the previous saved state
@@ -175,12 +125,7 @@ static struct save_state_buf undo_load_buf;
* This is useful for devices with slow I/O. */
static struct ram_save_state_buf ram_buf;
-#ifdef HAVE_THREADS
-static struct autosave_st autosave_state;
-#endif
-
static bool save_state_in_background = false;
-static struct string_list *task_save_files = NULL;
typedef struct rastate_size_info
{
@@ -194,248 +139,6 @@ typedef struct rastate_size_info
#endif
} rastate_size_info_t;
-#ifdef HAVE_THREADS
-/**
- * autosave_thread:
- * @data : pointer to autosave object
- *
- * Callback function for (threaded) autosave.
- **/
-static void autosave_thread(void *data)
-{
- autosave_t *save = (autosave_t*)data;
-
- for (;;)
- {
- bool differ;
-
- slock_lock(save->lock);
- differ = memcmp(save->buffer, save->retro_buffer,
- save->bufsize) != 0;
- if (differ)
- memcpy(save->buffer, save->retro_buffer, save->bufsize);
- slock_unlock(save->lock);
-
- if (differ)
- {
- intfstream_t *file = NULL;
-
- /* Should probably deal with this more elegantly. */
- if (save->flags & AUTOSAVE_FLAG_COMPRESS_FILES)
- file = intfstream_open_rzip_file(save->path,
- RETRO_VFS_FILE_ACCESS_WRITE);
- else
- file = intfstream_open_file(save->path,
- RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
-
- if (file)
- {
- intfstream_write(file, save->buffer, save->bufsize);
- intfstream_flush(file);
- intfstream_close(file);
- free(file);
- }
- }
-
- slock_lock(save->cond_lock);
-
- if (save->flags & AUTOSAVE_FLAG_QUIT)
- {
- slock_unlock(save->cond_lock);
- break;
- }
-
- scond_wait_timeout(save->cond,
- save->cond_lock,
-#if defined(_MSC_VER) && _MSC_VER <= 1200
- save->interval * 1000000
-#else
- save->interval * 1000000LL
-#endif
- );
-
- slock_unlock(save->cond_lock);
- }
-}
-
-/**
- * autosave_new:
- * @path : path to autosave file
- * @data : pointer to buffer
- * @size : size of @data buffer
- * @interval : interval at which saves should be performed.
- *
- * Create and initialize autosave object.
- *
- * @return Pointer to new autosave_t object if successful, otherwise
- * NULL.
- **/
-static autosave_t *autosave_new(const char *path,
- const void *data, size_t size,
- unsigned interval, bool compress)
-{
- void *buf = NULL;
- autosave_t *handle = (autosave_t*)malloc(sizeof(*handle));
- if (!handle)
- return NULL;
-
- handle->flags = 0;
- handle->bufsize = size;
- handle->interval = interval;
- if (compress)
- handle->flags |= AUTOSAVE_FLAG_COMPRESS_FILES;
- handle->retro_buffer = data;
- handle->path = path;
-
- if (!(buf = malloc(size)))
- {
- free(handle);
- return NULL;
- }
-
- handle->buffer = buf;
-
- memcpy(handle->buffer, handle->retro_buffer, handle->bufsize);
-
- handle->lock = slock_new();
- handle->cond_lock = slock_new();
- handle->cond = scond_new();
- handle->thread = sthread_create(autosave_thread, handle);
-
- return handle;
-}
-
-/**
- * autosave_free:
- * @handle : pointer to autosave object
- *
- * Frees autosave object.
- **/
-static void autosave_free(autosave_t *handle)
-{
- slock_lock(handle->cond_lock);
- handle->flags |= AUTOSAVE_FLAG_QUIT;
- slock_unlock(handle->cond_lock);
- scond_signal(handle->cond);
- sthread_join(handle->thread);
-
- slock_free(handle->lock);
- slock_free(handle->cond_lock);
- scond_free(handle->cond);
-
- if (handle->buffer)
- free(handle->buffer);
- handle->buffer = NULL;
-}
-
-bool autosave_init(void)
-{
- unsigned i;
- autosave_t **list = NULL;
- settings_t *settings = config_get_ptr();
- unsigned autosave_interval = settings->uints.autosave_interval;
-#if defined(HAVE_ZLIB)
- bool compress_files = settings->bools.save_file_compression;
-#else
- bool compress_files = false;
-#endif
-
- if (autosave_interval < 1 || !task_save_files)
- return false;
-
- if (!(list = (autosave_t**)
- calloc(task_save_files->size,
- sizeof(*autosave_state.list))))
- return false;
-
- autosave_state.list = list;
- autosave_state.num = (unsigned)task_save_files->size;
-
- for (i = 0; i < task_save_files->size; i++)
- {
- retro_ctx_memory_info_t mem_info;
- autosave_t *auto_st = NULL;
- const char *path = task_save_files->elems[i].data;
- unsigned type = task_save_files->elems[i].attr.i;
-
- mem_info.id = type;
-
- core_get_memory(&mem_info);
-
- if (mem_info.size <= 0)
- continue;
-
- if (!(auto_st = autosave_new(path,
- mem_info.data,
- mem_info.size,
- autosave_interval,
- compress_files)))
- {
- RARCH_WARN("%s\n", msg_hash_to_str(MSG_AUTOSAVE_FAILED));
- continue;
- }
-
- autosave_state.list[i] = auto_st;
- }
-
- return true;
-}
-
-void autosave_deinit(void)
-{
- unsigned i;
-
- for (i = 0; i < autosave_state.num; i++)
- {
- autosave_t *handle = autosave_state.list[i];
- if (handle)
- {
- autosave_free(handle);
- free(autosave_state.list[i]);
- }
- autosave_state.list[i] = NULL;
- }
-
- free(autosave_state.list);
-
- autosave_state.list = NULL;
- autosave_state.num = 0;
-}
-
-/**
- * autosave_lock:
- *
- * Lock autosave.
- **/
-void autosave_lock(void)
-{
- unsigned i;
-
- for (i = 0; i < autosave_state.num; i++)
- {
- autosave_t *handle = autosave_state.list[i];
- if (handle)
- slock_lock(handle->lock);
- }
-}
-
-/**
- * autosave_unlock:
- *
- * Unlocks autosave.
- **/
-void autosave_unlock(void)
-{
- unsigned i;
-
- for (i = 0; i < autosave_state.num; i++)
- {
- autosave_t *handle = autosave_state.list[i];
- if (handle)
- slock_unlock(handle->lock);
- }
-}
-#endif
/**
* undo_load_state:
@@ -451,6 +154,7 @@ bool content_undo_load_state(void)
unsigned num_blocks = 0;
void* temp_data = NULL;
struct sram_block *blocks = NULL;
+ struct string_list *savefile_list = (struct string_list*)savefile_ptr_get();
if (!core_info_current_supports_savestate())
{
@@ -470,18 +174,18 @@ bool content_undo_load_state(void)
* its flushing could all be in their
* own functions... */
if ( config_get_ptr()->bools.block_sram_overwrite
- && task_save_files
- && task_save_files->size)
+ && savefile_list
+ && savefile_list->size)
{
RARCH_LOG("[SRAM]: %s.\n",
msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE));
if ((blocks = (struct sram_block*)
- calloc(task_save_files->size, sizeof(*blocks))))
+ calloc(savefile_list->size, sizeof(*blocks))))
{
- num_blocks = (unsigned)task_save_files->size;
+ num_blocks = (unsigned)savefile_list->size;
for (i = 0; i < num_blocks; i++)
- blocks[i].type = task_save_files->elems[i].attr.i;
+ blocks[i].type = savefile_list->elems[i].attr.i;
}
}
@@ -1282,6 +986,7 @@ static void content_load_state_cb(retro_task_t *task,
struct sram_block *blocks = NULL;
settings_t *settings = config_get_ptr();
bool block_sram_overwrite = settings->bools.block_sram_overwrite;
+ struct string_list *savefile_list = (struct string_list*)savefile_ptr_get();
#ifdef HAVE_CHEEVOS
if (rcheevos_hardcore_active())
@@ -1321,18 +1026,17 @@ static void content_load_state_cb(retro_task_t *task,
return;
}
- if (block_sram_overwrite && task_save_files
- && task_save_files->size)
+ if (block_sram_overwrite && savefile_list && savefile_list->size)
{
RARCH_LOG("[SRAM]: %s.\n",
msg_hash_to_str(MSG_BLOCKING_SRAM_OVERWRITE));
if ((blocks = (struct sram_block*)
- calloc(task_save_files->size, sizeof(*blocks))))
+ calloc(savefile_list->size, sizeof(*blocks))))
{
- num_blocks = (unsigned)task_save_files->size;
+ num_blocks = (unsigned)savefile_list->size;
for (i = 0; i < num_blocks; i++)
- blocks[i].type = task_save_files->elems[i].attr.i;
+ blocks[i].type = savefile_list->elems[i].attr.i;
}
}
@@ -1607,6 +1311,7 @@ static void task_push_load_and_save_state(const char *path, void *data,
* content_save_state:
* @path : path of saved state that shall be written to.
* @save_to_disk: If false, saves the state onto undo_load_buf.
+ * @autosave: If the save is triggered automatically (ie. at core unload).
* Save a state from memory to disk.
*
* Returns: true if successful, false otherwise.
@@ -1874,130 +1579,6 @@ bool content_undo_save_buf_is_empty(void)
return undo_save_buf.data == NULL || undo_save_buf.size == 0;
}
-static bool content_get_memory(retro_ctx_memory_info_t *mem_info,
- struct ram_type *ram, unsigned slot)
-{
- ram->type = task_save_files->elems[slot].attr.i;
- ram->path = task_save_files->elems[slot].data;
- mem_info->id = ram->type;
-
- core_get_memory(mem_info);
-
- if (!mem_info->data || mem_info->size == 0)
- return false;
-
- return true;
-}
-
-/**
- * content_load_ram_file:
- * @path : path of RAM state that will be loaded from.
- * @type : type of memory
- *
- * Load a RAM state from disk to memory.
- */
-bool content_load_ram_file(unsigned slot)
-{
- int64_t rc;
- struct ram_type ram;
- retro_ctx_memory_info_t mem_info;
- void *buf = NULL;
-
- if (!content_get_memory(&mem_info, &ram, slot))
- return false;
-
- /* On first run of content, SRAM file will
- * not exist. This is a common enough occurrence
- * that we should check before attempting to
- * invoke the relevant read_file() function */
- if ( string_is_empty(ram.path)
- || !path_is_valid(ram.path))
- return false;
-
-#if defined(HAVE_ZLIB)
- /* Always use RZIP interface when reading SRAM
- * files - this will automatically handle uncompressed
- * data */
- if (!rzipstream_read_file(ram.path, &buf, &rc))
-#else
- if (!filestream_read_file(ram.path, &buf, &rc))
-#endif
- return false;
-
- if (rc > 0)
- {
- if (rc > (ssize_t)mem_info.size)
- {
- RARCH_WARN("[SRAM]: SRAM is larger than implementation expects, "
- "doing partial load (truncating %u %s %s %u).\n",
- (unsigned)rc,
- msg_hash_to_str(MSG_BYTES),
- msg_hash_to_str(MSG_TO),
- (unsigned)mem_info.size);
- rc = mem_info.size;
- }
- memcpy(mem_info.data, buf, (size_t)rc);
- }
-
- if (buf)
- free(buf);
-
- return true;
-}
-
-/**
- * dump_to_file_desperate:
- * @data : pointer to data buffer.
- * @size : size of @data.
- * @type : type of file to be saved.
- *
- * Attempt to save valuable RAM data somewhere.
- **/
-static bool dump_to_file_desperate(const void *data,
- size_t size, unsigned type)
-{
- char path[PATH_MAX_LENGTH + 256 + 32];
- path [0] = '\0';
-
- if (fill_pathname_application_data(path,
- sizeof(path)))
- {
- size_t _len;
- time_t time_;
- struct tm tm_;
- char timebuf[256];
- timebuf [0] = '\0';
- time(&time_);
-
- rtime_localtime(&time_, &tm_);
-
- strftime(timebuf, 256 * sizeof(char),
- "%Y-%m-%d-%H-%M-%S", &tm_);
-
- _len = strlcat(path, "/RetroArch-recovery-", sizeof(path));
-
- snprintf(path + _len, sizeof(path) - _len,
- "%u%s", type, timebuf);
-
- /* Fallback (emergency) saves are always
- * uncompressed
- * > If a regular save fails, then the host
- * system is experiencing serious technical
- * difficulties (most likely some kind of
- * hardware failure)
- * > In this case, we don't want to further
- * complicate matters by introducing zlib
- * compression overheads */
- if (filestream_write_file(path, data, size))
- {
- RARCH_WARN("[SRAM]: Succeeded in saving RAM data to \"%s\".\n", path);
- return true;
- }
- }
-
- return false;
-}
-
/**
* content_load_state_from_ram:
* Load a state from RAM.
@@ -2157,143 +1738,6 @@ success:
return true;
}
-/**
- * content_save_ram_file:
- * @path : path of RAM state that shall be written to.
- * @type : type of memory
- *
- * Save a RAM state from memory to disk.
- *
- */
-bool content_save_ram_file(unsigned slot, bool compress)
-{
- struct ram_type ram;
- retro_ctx_memory_info_t mem_info;
-
- if (!content_get_memory(&mem_info, &ram, slot))
- return false;
-
- RARCH_LOG("[SRAM]: %s #%u %s \"%s\".\n",
- msg_hash_to_str(MSG_SAVING_RAM_TYPE),
- ram.type,
- msg_hash_to_str(MSG_TO),
- ram.path);
-
-#if defined(HAVE_ZLIB)
- if (compress)
- {
- if (!rzipstream_write_file(
- ram.path, mem_info.data, mem_info.size))
- goto fail;
- }
- else
-#endif
- {
- if (!filestream_write_file(
- ram.path, mem_info.data, mem_info.size))
- goto fail;
- }
-
- RARCH_LOG("[SRAM]: %s \"%s\".\n",
- msg_hash_to_str(MSG_SAVED_SUCCESSFULLY_TO),
- ram.path);
-
- return true;
-
-fail:
- RARCH_ERR("[SRAM]: %s.\n",
- msg_hash_to_str(MSG_FAILED_TO_SAVE_SRAM));
- RARCH_WARN("[SRAM]: Attempting to recover ...\n");
-
- /* In case the file could not be written to,
- * the fallback function 'dump_to_file_desperate'
- * will be called. */
- if (!dump_to_file_desperate(
- mem_info.data, mem_info.size, ram.type))
- RARCH_WARN("[SRAM]: Failed ... Cannot recover save file.\n");
- return false;
-}
-
-bool event_save_files(bool is_sram_used)
-{
- unsigned i;
- settings_t *settings = config_get_ptr();
-#ifdef HAVE_CHEATS
- const char *path_cheat_database = settings->paths.path_cheat_database;
-#endif
-#if defined(HAVE_ZLIB)
- bool compress_files = settings->bools.save_file_compression;
-#endif
-
-#ifdef HAVE_CHEATS
- cheat_manager_save_game_specific_cheats(
- path_cheat_database);
-#endif
- if (!task_save_files || !is_sram_used)
- return false;
-
- for (i = 0; i < task_save_files->size; i++)
- {
-#if defined(HAVE_ZLIB)
- content_save_ram_file(i, compress_files);
-#else
- content_save_ram_file(i, false);
-#endif
- }
-
- return true;
-}
-
-bool event_load_save_files(bool is_sram_load_disabled)
-{
- unsigned i;
- bool success = false;
-
- if (!task_save_files || is_sram_load_disabled)
- return false;
-
- /* Report a successful load operation if
- * any type of RAM file is found and
- * processed correctly */
- for (i = 0; i < task_save_files->size; i++)
- success |= content_load_ram_file(i);
-
- return success;
-}
-
-void path_init_savefile_rtc(const char *savefile_path)
-{
- union string_list_elem_attr attr;
- char savefile_name_rtc[PATH_MAX_LENGTH];
-
- attr.i = RETRO_MEMORY_SAVE_RAM;
- string_list_append(task_save_files, savefile_path, attr);
-
- /* Infer .rtc save path from save RAM path. */
- attr.i = RETRO_MEMORY_RTC;
- fill_pathname(savefile_name_rtc,
- savefile_path, ".rtc",
- sizeof(savefile_name_rtc));
- string_list_append(task_save_files, savefile_name_rtc, attr);
-}
-
-void path_deinit_savefile(void)
-{
- if (task_save_files)
- string_list_free(task_save_files);
- task_save_files = NULL;
-}
-
-void path_init_savefile_new(void)
-{
- task_save_files = string_list_new();
-}
-
-void *savefile_ptr_get(void)
-{
- return task_save_files;
-}
-
void set_save_state_in_background(bool state)
{
save_state_in_background = state;