mirror of
https://github.com/libretro/RetroArch
synced 2024-12-26 21:29:08 +00:00
Change task_save_state so we save a single state and a single savestate
file in memory, allowing to undo a loadstate/savestate once.
This commit is contained in:
parent
14fce400ce
commit
70b1f385e8
@ -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))
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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. */
|
||||
|
Loading…
Reference in New Issue
Block a user