mirror of
https://github.com/libretro/RetroArch
synced 2024-12-27 15:29:23 +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();
|
settings_t *settings = config_get_ptr();
|
||||||
char buf[PATH_MAX_LENGTH] = {0};
|
char buf[PATH_MAX_LENGTH] = {0};
|
||||||
|
|
||||||
|
#if 0
|
||||||
/* if a save state already exists rename it to .last before saving
|
/* if a save state already exists rename it to .last before saving
|
||||||
* so it can be recovered */
|
* so it can be recovered */
|
||||||
if (path_file_exists(path))
|
if (path_file_exists(path))
|
||||||
@ -1597,6 +1598,7 @@ static void command_event_save_state(const char *path,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!content_save_state(path))
|
if (!content_save_state(path))
|
||||||
{
|
{
|
||||||
|
@ -46,16 +46,20 @@ bool content_load_ram_file(unsigned slot);
|
|||||||
bool content_save_ram_file(unsigned slot);
|
bool content_save_ram_file(unsigned slot);
|
||||||
|
|
||||||
/* Load a state from disk to memory. */
|
/* 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. */
|
/* Save a state from memory to disk. */
|
||||||
bool content_save_state(const char *path);
|
bool content_save_state(const char *path);
|
||||||
|
bool content_save_state_with_backup(const char *path, bool save_to_disk)
|
||||||
|
|
||||||
/* Copy a save state. */
|
/* Copy a save state. */
|
||||||
bool content_rename_state(const char *origin, const char *dest);
|
bool content_rename_state(const char *origin, const char *dest);
|
||||||
|
|
||||||
/* Load a state backup from disk to memory. */
|
/* 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);
|
bool content_does_not_need_content(void);
|
||||||
|
|
||||||
|
@ -30,6 +30,26 @@
|
|||||||
#include "../verbosity.h"
|
#include "../verbosity.h"
|
||||||
#include "tasks_internal.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
|
struct sram_block
|
||||||
{
|
{
|
||||||
unsigned type;
|
unsigned type;
|
||||||
@ -37,6 +57,141 @@ struct sram_block
|
|||||||
size_t size;
|
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 */
|
/* TODO/FIXME - turn this into actual task */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +202,8 @@ struct sram_block
|
|||||||
*
|
*
|
||||||
* Returns: true if successful, false otherwise.
|
* 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_serialize_info_t serial_info;
|
||||||
retro_ctx_size_info_t info;
|
retro_ctx_size_info_t info;
|
||||||
@ -77,8 +233,30 @@ bool content_save_state(const char *path)
|
|||||||
serial_info.size = info.size;
|
serial_info.size = info.size;
|
||||||
ret = core_serialize(&serial_info);
|
ret = core_serialize(&serial_info);
|
||||||
|
|
||||||
if (ret)
|
if (ret) {
|
||||||
ret = filestream_write_file(path, data, info.size);
|
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
|
else
|
||||||
{
|
{
|
||||||
RARCH_ERR("%s \"%s\".\n",
|
RARCH_ERR("%s \"%s\".\n",
|
||||||
@ -99,7 +277,8 @@ bool content_save_state(const char *path)
|
|||||||
*
|
*
|
||||||
* Returns: true if successful, false otherwise.
|
* 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;
|
unsigned i;
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
@ -123,6 +302,24 @@ bool content_load_state(const char *path)
|
|||||||
(unsigned)size,
|
(unsigned)size,
|
||||||
msg_hash_to_str(MSG_BYTES));
|
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
|
if (settings->block_sram_overwrite && global->savefiles
|
||||||
&& global->savefiles->size)
|
&& global->savefiles->size)
|
||||||
{
|
{
|
||||||
@ -174,6 +371,9 @@ bool content_load_state(const char *path)
|
|||||||
|
|
||||||
serial_info.data_const = buf;
|
serial_info.data_const = buf;
|
||||||
serial_info.size = size;
|
serial_info.size = size;
|
||||||
|
|
||||||
|
/* Backup the current state so we can undo this load */
|
||||||
|
content_save_state(NULL, true);
|
||||||
ret = core_unserialize(&serial_info);
|
ret = core_unserialize(&serial_info);
|
||||||
|
|
||||||
/* Flush back. */
|
/* Flush back. */
|
||||||
|
Loading…
Reference in New Issue
Block a user