mirror of
https://github.com/libretro/RetroArch.git
synced 2024-06-02 19:57:24 -04:00
[New Feature] Load save states from command-line or playlist (#13354)
* load save states from command-line or playlist * load save states from command-line or playlist * removed some brackets
This commit is contained in:
parent
430baf7c21
commit
682bbab233
42
command.c
42
command.c
|
@ -1082,6 +1082,8 @@ bool command_event_save_auto_state(
|
|||
bool ret = false;
|
||||
char savestate_name_auto[PATH_MAX_LENGTH];
|
||||
|
||||
if (runloop_st->entry_state_slot)
|
||||
return false;
|
||||
if (!savestate_auto_save)
|
||||
return false;
|
||||
if (current_core_type == CORE_TYPE_DUMMY)
|
||||
|
@ -1139,6 +1141,46 @@ void command_event_init_cheats(
|
|||
}
|
||||
#endif
|
||||
|
||||
bool command_event_load_entry_state(void)
|
||||
{
|
||||
char entry_state_path[PATH_MAX_LENGTH];
|
||||
int entry_path_stats;
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool ret = false;
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if (rcheevos_hardcore_active())
|
||||
return false;
|
||||
#endif
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
entry_state_path[0] = '\0';
|
||||
|
||||
if (!retroarch_get_entry_state_path(entry_state_path, sizeof(entry_state_path),
|
||||
runloop_st->entry_state_slot))
|
||||
return false;
|
||||
|
||||
entry_path_stats = path_stat(entry_state_path);
|
||||
|
||||
if ((entry_path_stats & RETRO_VFS_STAT_IS_VALID) == 0
|
||||
|| (entry_path_stats & RETRO_VFS_STAT_IS_DIRECTORY) != 0)
|
||||
return false;
|
||||
|
||||
ret = content_load_state(entry_state_path, false, true);
|
||||
|
||||
RARCH_LOG("%s: %s\n%s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_FOUND_ENTRY_STATE_IN),
|
||||
entry_state_path,
|
||||
msg_hash_to_str(MSG_LOADING_ENTRY_STATE_FROM),
|
||||
entry_state_path, ret ? "succeeded" : "failed"
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void command_event_load_auto_state(void)
|
||||
{
|
||||
char savestate_name_auto[PATH_MAX_LENGTH];
|
||||
|
|
|
@ -362,6 +362,8 @@ void command_event_set_volume(
|
|||
void command_event_init_controllers(rarch_system_info_t *info,
|
||||
settings_t *settings, unsigned num_active_users);
|
||||
|
||||
bool command_event_load_entry_state(void);
|
||||
|
||||
void command_event_load_auto_state(void);
|
||||
|
||||
void command_event_set_savestate_auto_index(
|
||||
|
|
|
@ -12907,3 +12907,11 @@ MSG_HASH(
|
|||
"Scan Finished.<br><br>\nIn order for content to be correctly scanned, you must:\n<ul><li>have a compatible core already downloaded</li>\n<li>have \"Core Info Files\" updated via Online Updater</li>\n<li>have \"Databases\" updated via Online Updater</li>\n<li>restart RetroArch if any of the above was just done</li></ul>\nFinally, the content must match existing databases from <a href=\"https://docs.libretro.com/guides/roms-playlists-thumbnails/#sources\">here</a>. If it is still not working, consider <a href=\"https://www.github.com/libretro/RetroArch/issues\">submitting a bug report</a>."
|
||||
)
|
||||
#endif
|
||||
MSG_HASH(
|
||||
MSG_FOUND_ENTRY_STATE_IN,
|
||||
"Found entry state in"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_LOADING_ENTRY_STATE_FROM,
|
||||
"Loading entry state from"
|
||||
)
|
||||
|
|
|
@ -1394,12 +1394,13 @@ void manual_content_scan_add_content_to_playlist(
|
|||
/* Configure playlist entry
|
||||
* > The push function reads our entry as const,
|
||||
* so these casts are safe */
|
||||
entry.path = (char*)playlist_content_path;
|
||||
entry.label = label;
|
||||
entry.core_path = (char*)FILE_PATH_DETECT;
|
||||
entry.core_name = (char*)FILE_PATH_DETECT;
|
||||
entry.crc32 = (char*)"00000000|crc";
|
||||
entry.db_name = task_config->database_name;
|
||||
entry.path = (char*)playlist_content_path;
|
||||
entry.entry_slot = 0;
|
||||
entry.label = label;
|
||||
entry.core_path = (char*)FILE_PATH_DETECT;
|
||||
entry.core_name = (char*)FILE_PATH_DETECT;
|
||||
entry.crc32 = (char*)"00000000|crc";
|
||||
entry.db_name = task_config->database_name;
|
||||
|
||||
/* Add entry to playlist */
|
||||
playlist_push(playlist, &entry);
|
||||
|
|
|
@ -2349,6 +2349,7 @@ static int action_ok_playlist_entry_collection(const char *path,
|
|||
bool core_is_builtin = false;
|
||||
menu_handle_t *menu = menu_state_get_ptr()->driver_data;
|
||||
settings_t *settings = config_get_ptr();
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool playlist_sort_alphabetical = settings->bools.playlist_sort_alphabetical;
|
||||
const char *path_content_history = settings->paths.path_content_history;
|
||||
const char *path_content_image_history = settings->paths.path_content_image_history;
|
||||
|
@ -2416,6 +2417,8 @@ static int action_ok_playlist_entry_collection(const char *path,
|
|||
playlist_resolve_path(PLAYLIST_LOAD, false, content_path, sizeof(content_path));
|
||||
}
|
||||
|
||||
runloop_st->entry_state_slot = entry->entry_slot;
|
||||
|
||||
/* Cache entry label */
|
||||
if (!string_is_empty(entry->label))
|
||||
strlcpy(content_label, entry->label, sizeof(content_label));
|
||||
|
|
|
@ -499,6 +499,8 @@ enum msg_hash_enums
|
|||
#endif
|
||||
MSG_UNSUPPORTED_VIDEO_MODE,
|
||||
MSG_CORE_INFO_CACHE_UNSUPPORTED,
|
||||
MSG_LOADING_ENTRY_STATE_FROM,
|
||||
MSG_FOUND_ENTRY_STATE_IN,
|
||||
|
||||
MENU_LABEL(MENU_XMB_ANIMATION_HORIZONTAL_HIGHLIGHT),
|
||||
MENU_LABEL(MENU_XMB_ANIMATION_MOVE_UP_DOWN),
|
||||
|
|
35
playlist.c
35
playlist.c
|
@ -1356,24 +1356,30 @@ bool playlist_push(playlist_t *playlist,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (playlist->entries[i].entry_slot != entry->entry_slot)
|
||||
{
|
||||
playlist->entries[i].entry_slot = entry->entry_slot;
|
||||
entry_updated = true;
|
||||
}
|
||||
|
||||
/* If content was previously loaded via file browser
|
||||
* or command line, certain entry values will be missing.
|
||||
* If we are now loading the same content from a playlist,
|
||||
* fill in any blanks */
|
||||
if (!playlist->entries[i].label && !string_is_empty(entry->label))
|
||||
{
|
||||
playlist->entries[i].label = strdup(entry->label);
|
||||
entry_updated = true;
|
||||
playlist->entries[i].label = strdup(entry->label);
|
||||
entry_updated = true;
|
||||
}
|
||||
if (!playlist->entries[i].crc32 && !string_is_empty(entry->crc32))
|
||||
{
|
||||
playlist->entries[i].crc32 = strdup(entry->crc32);
|
||||
entry_updated = true;
|
||||
playlist->entries[i].crc32 = strdup(entry->crc32);
|
||||
entry_updated = true;
|
||||
}
|
||||
if (!playlist->entries[i].db_name && !string_is_empty(entry->db_name))
|
||||
{
|
||||
playlist->entries[i].db_name = strdup(entry->db_name);
|
||||
entry_updated = true;
|
||||
playlist->entries[i].db_name = strdup(entry->db_name);
|
||||
entry_updated = true;
|
||||
}
|
||||
|
||||
/* If top entry, we don't want to push a new entry since
|
||||
|
@ -1445,6 +1451,8 @@ bool playlist_push(playlist_t *playlist,
|
|||
playlist->entries[0].path_id = path_id;
|
||||
path_id = NULL;
|
||||
|
||||
playlist->entries[0].entry_slot = entry->entry_slot;
|
||||
|
||||
if (!string_is_empty(entry->label))
|
||||
playlist->entries[0].label = strdup(entry->label);
|
||||
if (!string_is_empty(real_core_path))
|
||||
|
@ -1873,6 +1881,17 @@ void playlist_write_file(playlist_t *playlist)
|
|||
rjsonwriter_add_string(writer, playlist->entries[i].path);
|
||||
rjsonwriter_add_comma(writer);
|
||||
|
||||
if (playlist->entries[i].entry_slot)
|
||||
{
|
||||
rjsonwriter_add_newline(writer);
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "entry_slot");
|
||||
rjsonwriter_add_colon(writer);
|
||||
rjsonwriter_add_space(writer);
|
||||
rjsonwriter_add_int(writer, (int)playlist->entries[i].entry_slot);
|
||||
rjsonwriter_add_comma(writer);
|
||||
}
|
||||
|
||||
rjsonwriter_add_newline(writer);
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "label");
|
||||
|
@ -2328,6 +2347,10 @@ static bool JSONObjectMemberHandler(void *context, const char *pValue, size_t le
|
|||
if (string_is_equal(pValue, "db_name"))
|
||||
pCtx->current_string_val = &pCtx->current_entry->db_name;
|
||||
break;
|
||||
case 'e':
|
||||
if (string_is_equal(pValue, "entry_slot"))
|
||||
pCtx->current_entry_uint_val = &pCtx->current_entry->entry_slot;
|
||||
break;
|
||||
case 'l':
|
||||
if (string_is_equal(pValue, "label"))
|
||||
pCtx->current_string_val = &pCtx->current_entry->label;
|
||||
|
|
|
@ -101,6 +101,7 @@ typedef struct
|
|||
struct playlist_entry
|
||||
{
|
||||
char *path;
|
||||
unsigned entry_slot;
|
||||
char *label;
|
||||
char *core_path;
|
||||
char *core_name;
|
||||
|
|
21
retroarch.c
21
retroarch.c
|
@ -4214,6 +4214,8 @@ static void retroarch_print_help(const char *arg0)
|
|||
#endif
|
||||
strlcat(buf, " --load-menu-on-error\n"
|
||||
" Open menu instead of quitting if specified core or content fails to load.\n", sizeof(buf));
|
||||
strlcat(buf, " -e, --entryslot=NUMBER\n"
|
||||
" Slot from which to load an entry state\n", sizeof(buf));
|
||||
puts(buf);
|
||||
}
|
||||
}
|
||||
|
@ -4302,6 +4304,7 @@ static bool retroarch_parse_input_and_config(
|
|||
{ "log-file", 1, NULL, RA_OPT_LOG_FILE },
|
||||
{ "accessibility", 0, NULL, RA_OPT_ACCESSIBILITY},
|
||||
{ "load-menu-on-error", 0, NULL, RA_OPT_LOAD_MENU_ON_ERROR },
|
||||
{ "entryslot", 1, NULL, 'e' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
@ -4372,7 +4375,7 @@ static bool retroarch_parse_input_and_config(
|
|||
|
||||
/* Make sure we can call retroarch_parse_input several times ... */
|
||||
optind = 0;
|
||||
optstring = "hs:fvS:A:U:DN:d:"
|
||||
optstring = "hs:fvS:A:U:DN:d:e:"
|
||||
BSV_MOVIE_ARG NETPLAY_ARG DYNAMIC_ARG FFMPEG_RECORD_ARG CONFIG_FILE_ARG;
|
||||
|
||||
#if defined(ORBIS)
|
||||
|
@ -4872,6 +4875,17 @@ static bool retroarch_parse_input_and_config(
|
|||
case RA_OPT_LOAD_MENU_ON_ERROR:
|
||||
global->cli_load_menu_on_error = true;
|
||||
break;
|
||||
case 'e':
|
||||
{
|
||||
unsigned entry_state_slot = (unsigned)strtoul(optarg, NULL, 0);
|
||||
|
||||
if (entry_state_slot)
|
||||
runloop_st->entry_state_slot = entry_state_slot;
|
||||
else
|
||||
RARCH_WARN("--entryslot argument \"%s\" is not a valid "
|
||||
"entry state slot index. Ignoring.\n", optarg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
RARCH_ERR("%s\n", msg_hash_to_str(MSG_ERROR_PARSING_ARGUMENTS));
|
||||
retroarch_fail(1, "retroarch_parse_input()");
|
||||
|
@ -4920,6 +4934,11 @@ static bool retroarch_parse_input_and_config(
|
|||
* command line interface */
|
||||
cli_content_set = true;
|
||||
}
|
||||
else if (runloop_st->entry_state_slot)
|
||||
{
|
||||
runloop_st->entry_state_slot = 0;
|
||||
RARCH_WARN("Trying to load entry state without content. Ignoring.\n");
|
||||
}
|
||||
|
||||
/* Check whether a core has been set via the
|
||||
* command line interface */
|
||||
|
|
|
@ -197,6 +197,8 @@ typedef enum apple_view_type
|
|||
|
||||
bool retroarch_get_current_savestate_path(char *path, size_t len);
|
||||
|
||||
bool retroarch_get_entry_state_path(char *path, size_t len, unsigned slot);
|
||||
|
||||
/**
|
||||
* retroarch_fail:
|
||||
* @error_code : Error code.
|
||||
|
|
23
runloop.c
23
runloop.c
|
@ -5105,8 +5105,12 @@ static bool event_init_content(
|
|||
#ifdef HAVE_CHEEVOS
|
||||
if (!cheevos_enable || !cheevos_hardcore_mode_enable)
|
||||
#endif
|
||||
if (settings->bools.savestate_auto_load)
|
||||
{
|
||||
if (runloop_st->entry_state_slot && !command_event_load_entry_state())
|
||||
runloop_st->entry_state_slot = 0;
|
||||
if (!runloop_st->entry_state_slot && settings->bools.savestate_auto_load)
|
||||
command_event_load_auto_state();
|
||||
}
|
||||
|
||||
#ifdef HAVE_BSV_MOVIE
|
||||
bsv_movie_deinit(input_st);
|
||||
|
@ -7829,6 +7833,23 @@ bool retroarch_get_current_savestate_path(char *path, size_t len)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool retroarch_get_entry_state_path(char *path, size_t len, unsigned slot)
|
||||
{
|
||||
runloop_state_t *runloop_st = &runloop_state;
|
||||
const char *name_savestate = NULL;
|
||||
|
||||
if (!path || !slot)
|
||||
return false;
|
||||
|
||||
name_savestate = runloop_st->name.savestate;
|
||||
if (string_is_empty(name_savestate))
|
||||
return false;
|
||||
|
||||
snprintf(path, len, "%s%d%s", name_savestate, slot, ".entry");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void runloop_set_current_core_type(
|
||||
enum rarch_core_type type, bool explicitly_set)
|
||||
{
|
||||
|
|
|
@ -218,6 +218,7 @@ struct runloop
|
|||
unsigned fastforward_after_frames;
|
||||
unsigned perf_ptr_libretro;
|
||||
unsigned subsystem_current_count;
|
||||
unsigned entry_state_slot;
|
||||
|
||||
fastmotion_overrides_t fastmotion_override; /* float alignment */
|
||||
|
||||
|
|
|
@ -1678,10 +1678,10 @@ static void task_push_to_history_list(
|
|||
subsystem_name[0] = '\0';
|
||||
|
||||
content_get_subsystem_friendly_name(path_get(RARCH_PATH_SUBSYSTEM), subsystem_name, sizeof(subsystem_name));
|
||||
|
||||
/* The push function reads our entry as const,
|
||||
* so these casts are safe */
|
||||
entry.path = (char*)tmp;
|
||||
entry.entry_slot = runloop_st->entry_state_slot;
|
||||
entry.label = (char*)label;
|
||||
entry.core_path = (char*)core_path;
|
||||
entry.core_name = (char*)core_name;
|
||||
|
|
Loading…
Reference in a new issue