Save state slot improvements (#18140)
* Stop polluting playlists with redundant entry_slot numbers * Playlist entry_slot improvements and cleanups * Save current state slot to runtime log
This commit is contained in:
parent
ecbc05de6b
commit
405c3476f5
39
command.c
39
command.c
|
@ -1388,7 +1388,6 @@ void command_event_init_cheats(
|
|||
bool command_event_load_entry_state(settings_t *settings)
|
||||
{
|
||||
char entry_state_path[PATH_MAX_LENGTH] = "";
|
||||
int entry_path_stats;
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
bool ret = false;
|
||||
|
||||
|
@ -1417,10 +1416,7 @@ bool command_event_load_entry_state(settings_t *settings)
|
|||
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)
|
||||
if (!path_is_valid(entry_state_path))
|
||||
return false;
|
||||
|
||||
ret = content_load_state(entry_state_path, false, true);
|
||||
|
@ -1430,32 +1426,30 @@ bool command_event_load_entry_state(settings_t *settings)
|
|||
entry_state_path);
|
||||
RARCH_LOG("[State] %s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_LOADING_ENTRY_STATE_FROM),
|
||||
entry_state_path, ret ? "succeeded" : "failed"
|
||||
);
|
||||
|
||||
if (ret)
|
||||
configuration_set_int(settings, settings->ints.state_slot, runloop_st->entry_state_slot);
|
||||
entry_state_path,
|
||||
ret ? "succeeded" : "failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void command_event_load_auto_state(void)
|
||||
bool command_event_load_auto_state(void)
|
||||
{
|
||||
size_t _len;
|
||||
char savestate_name_auto[PATH_MAX_LENGTH];
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
const char *name_savestate = runloop_st->name.savestate;
|
||||
bool ret = false;
|
||||
|
||||
if (!core_info_current_supports_savestate())
|
||||
return;
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if (rcheevos_hardcore_active())
|
||||
return;
|
||||
return false;
|
||||
#endif
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
return;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
_len = strlcpy(savestate_name_auto, name_savestate,
|
||||
|
@ -1464,20 +1458,19 @@ void command_event_load_auto_state(void)
|
|||
sizeof(savestate_name_auto) - _len);
|
||||
|
||||
if (!path_is_valid(savestate_name_auto))
|
||||
return;
|
||||
return false;
|
||||
|
||||
ret = content_load_state(savestate_name_auto, false, true);
|
||||
|
||||
RARCH_LOG("[State] %s \"%s\".\n",
|
||||
msg_hash_to_str(MSG_FOUND_AUTO_SAVESTATE_IN),
|
||||
savestate_name_auto);
|
||||
RARCH_LOG("[State] %s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_AUTOLOADING_SAVESTATE_FROM),
|
||||
savestate_name_auto,
|
||||
ret ? "succeeded" : "failed");
|
||||
|
||||
if ((content_load_state(savestate_name_auto, false, true)))
|
||||
RARCH_LOG("[State] %s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_AUTOLOADING_SAVESTATE_FROM),
|
||||
savestate_name_auto, "succeeded");
|
||||
else
|
||||
RARCH_LOG("[State] %s \"%s\" %s.\n",
|
||||
msg_hash_to_str(MSG_AUTOLOADING_SAVESTATE_FROM),
|
||||
savestate_name_auto, "failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -380,7 +380,7 @@ void command_event_init_controllers(rarch_system_info_t *info,
|
|||
|
||||
bool command_event_load_entry_state(settings_t *settings);
|
||||
|
||||
void command_event_load_auto_state(void);
|
||||
bool command_event_load_auto_state(void);
|
||||
|
||||
void command_event_set_savestate_auto_index(
|
||||
settings_t *settings);
|
||||
|
|
|
@ -2762,8 +2762,6 @@ 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));
|
||||
|
|
|
@ -4665,9 +4665,6 @@ bool menu_driver_init(bool video_is_threaded)
|
|||
settings_t *settings = config_get_ptr();
|
||||
struct menu_state *menu_st = &menu_driver_state;
|
||||
|
||||
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
||||
command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
|
||||
|
||||
if ( menu_st->driver_data
|
||||
|| menu_driver_init_internal(
|
||||
menu_st, p_disp, settings,
|
||||
|
|
32
playlist.c
32
playlist.c
|
@ -1406,7 +1406,9 @@ bool playlist_push(playlist_t *playlist,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (playlist->entries[i].entry_slot != entry->entry_slot)
|
||||
/* Only write non-redundant entry slot numbers */
|
||||
if ( playlist->entries[i].entry_slot != entry->entry_slot
|
||||
&& (int)entry->entry_slot > 0)
|
||||
{
|
||||
playlist->entries[i].entry_slot = entry->entry_slot;
|
||||
entry_updated = true;
|
||||
|
@ -1962,17 +1964,6 @@ void playlist_write_file(playlist_t *playlist)
|
|||
rjsonwriter_add_string(writer, playlist->entries[i].path);
|
||||
rjsonwriter_raw(writer, ",", 1);
|
||||
|
||||
if (playlist->entries[i].entry_slot)
|
||||
{
|
||||
rjsonwriter_raw(writer, "\n", 1);
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "entry_slot");
|
||||
rjsonwriter_raw(writer, ":", 1);
|
||||
rjsonwriter_raw(writer, " ", 1);
|
||||
rjsonwriter_rawf(writer, "%d", (int)playlist->entries[i].entry_slot);
|
||||
rjsonwriter_raw(writer, ",", 1);
|
||||
}
|
||||
|
||||
rjsonwriter_raw(writer, "\n", 1);
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "label");
|
||||
|
@ -2012,6 +2003,23 @@ void playlist_write_file(playlist_t *playlist)
|
|||
rjsonwriter_raw(writer, " ", 1);
|
||||
rjsonwriter_add_string(writer, playlist->entries[i].db_name);
|
||||
|
||||
/* Conditional rows must add "," first */
|
||||
|
||||
/* Typecast required because playlist_entry.entry_slot is unsigned,
|
||||
* and 0 and -1 are redundant, but runloop.entry_state_slot is int16_t
|
||||
* and must be able to be negative, because 0 is a valid slot */
|
||||
if ( (int)playlist->entries[i].entry_slot > 0
|
||||
&& !strstr(playlist->config.path, FILE_PATH_BUILTIN))
|
||||
{
|
||||
rjsonwriter_raw(writer, ",", 1);
|
||||
rjsonwriter_raw(writer, "\n", 1);
|
||||
rjsonwriter_add_spaces(writer, 6);
|
||||
rjsonwriter_add_string(writer, "entry_slot");
|
||||
rjsonwriter_raw(writer, ":", 1);
|
||||
rjsonwriter_raw(writer, " ", 1);
|
||||
rjsonwriter_rawf(writer, "%d", (int)playlist->entries[i].entry_slot);
|
||||
}
|
||||
|
||||
if (!string_is_empty(playlist->entries[i].subsystem_ident))
|
||||
{
|
||||
rjsonwriter_raw(writer, ",", 1);
|
||||
|
|
|
@ -7203,6 +7203,9 @@ static bool retroarch_parse_input_and_config(
|
|||
runloop_st->current_core.flags &= ~(RETRO_CORE_FLAG_HAS_SET_INPUT_DESCRIPTORS
|
||||
| RETRO_CORE_FLAG_HAS_SET_SUBSYSTEMS);
|
||||
|
||||
/* Reset entry slot */
|
||||
runloop_st->entry_state_slot = -1;
|
||||
|
||||
/* Load the config file now that we know what it is */
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (!(p_rarch->flags & RARCH_FLAGS_BLOCK_CONFIG_READ))
|
||||
|
@ -7773,7 +7776,6 @@ bool retroarch_main_init(int argc, char *argv[])
|
|||
input_st->osk_idx = OSK_LOWERCASE_LATIN;
|
||||
video_st->flags |= VIDEO_FLAG_ACTIVE;
|
||||
audio_state_get_ptr()->flags |= AUDIO_FLAG_ACTIVE;
|
||||
runloop_st->entry_state_slot = -1;
|
||||
|
||||
if (setjmp(global->error_sjlj_context) > 0)
|
||||
{
|
||||
|
@ -8105,8 +8107,6 @@ bool retroarch_main_init(int argc, char *argv[])
|
|||
game_ai_init();
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
|
124
runloop.c
124
runloop.c
|
@ -655,6 +655,9 @@ static void runloop_update_runtime_log(
|
|||
/* Update 'last played' entry */
|
||||
runtime_log_set_last_played_now(runtime_log);
|
||||
|
||||
/* Update state slot */
|
||||
runtime_log->state_slot = config_get_ptr()->ints.state_slot;
|
||||
|
||||
/* Save runtime log file */
|
||||
runtime_log_save(runtime_log);
|
||||
|
||||
|
@ -4282,43 +4285,64 @@ static bool event_init_content(
|
|||
|
||||
runloop_path_init_savefile(runloop_st);
|
||||
|
||||
if (!event_load_save_files(runloop_st->flags &
|
||||
RUNLOOP_FLAG_IS_SRAM_LOAD_DISABLED))
|
||||
RARCH_LOG("[SRAM] %s\n",
|
||||
msg_hash_to_str(MSG_SKIPPING_SRAM_LOAD));
|
||||
if (!event_load_save_files(runloop_st->flags & RUNLOOP_FLAG_IS_SRAM_LOAD_DISABLED))
|
||||
RARCH_LOG("[SRAM] %s\n", msg_hash_to_str(MSG_SKIPPING_SRAM_LOAD));
|
||||
|
||||
/*
|
||||
Since the operations are asynchronous we can't
|
||||
guarantee users will not use auto_load_state to cheat on
|
||||
achievements so we forbid auto_load_state from happening
|
||||
if cheevos_enable and cheevos_hardcode_mode_enable
|
||||
are true.
|
||||
*/
|
||||
/* Set entry slot from playlist entry if available */
|
||||
{
|
||||
playlist_t *playlist = playlist_get_cached();
|
||||
|
||||
if (playlist)
|
||||
{
|
||||
struct menu_state *menu_st = menu_state_get_ptr();
|
||||
const struct playlist_entry *entry = NULL;
|
||||
|
||||
if (menu_st->driver_data)
|
||||
playlist_get_index(playlist, menu_st->driver_data->rpl_entry_selection_ptr, &entry);
|
||||
|
||||
if (entry)
|
||||
runloop_st->entry_state_slot = entry->entry_slot;
|
||||
}
|
||||
|
||||
/* Set current active state slot */
|
||||
if (runloop_st->entry_state_slot > -1)
|
||||
configuration_set_int(settings, settings->ints.state_slot, runloop_st->entry_state_slot);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the operations are asynchronous we can't
|
||||
* guarantee users will not use auto_load_state to cheat on
|
||||
* achievements so we forbid auto_load_state from happening
|
||||
* if cheevos_enable and cheevos_hardcode_mode_enable
|
||||
* are true.
|
||||
*/
|
||||
#ifdef HAVE_CHEEVOS
|
||||
if ( !cheevos_enable
|
||||
|| !cheevos_hardcore_mode_enable)
|
||||
#endif
|
||||
{
|
||||
#ifdef HAVE_BSV_MOVIE
|
||||
/* ignore entry state if we're doing bsv playback (we do want it
|
||||
for bsv recording though) */
|
||||
if (!(input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK))
|
||||
/* Ignore entry state if we're doing bsv playback (we do want it
|
||||
for bsv recording though) */
|
||||
if (!(input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK))
|
||||
#endif
|
||||
{
|
||||
if ( runloop_st->entry_state_slot > -1
|
||||
if ( runloop_st->entry_state_slot > -1
|
||||
&& !command_event_load_entry_state(settings))
|
||||
{
|
||||
/* loading the state failed, reset entry slot */
|
||||
/* Loading the state failed, reset entry slot */
|
||||
runloop_st->entry_state_slot = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_BSV_MOVIE
|
||||
/* ignore autoload state if we're doing bsv playback or recording */
|
||||
if (!(input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_START_RECORDING | BSV_FLAG_MOVIE_START_PLAYBACK)))
|
||||
/* Ignore autoload state if we're doing bsv playback or recording */
|
||||
if (!(input_st->bsv_movie_state.flags & (BSV_FLAG_MOVIE_START_RECORDING | BSV_FLAG_MOVIE_START_PLAYBACK)))
|
||||
#endif
|
||||
{
|
||||
if (runloop_st->entry_state_slot < 0 && settings->bools.savestate_auto_load)
|
||||
command_event_load_auto_state();
|
||||
if ( runloop_st->entry_state_slot < 0
|
||||
&& settings->bools.savestate_auto_load)
|
||||
command_event_load_auto_state();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4326,25 +4350,25 @@ static bool event_init_content(
|
|||
movie_stop(input_st);
|
||||
if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_RECORDING)
|
||||
{
|
||||
configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
|
||||
configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
|
||||
#ifndef HAVE_THREADS
|
||||
/* Hack: the regular scheduler doesn't do the right thing here at
|
||||
least in emscripten builds. I would expect that the check in
|
||||
task_movie.c:343 should defer recording until the movie task
|
||||
is done, but maybe that task isn't enqueued again yet when the
|
||||
movie-record task is checked? Or the finder call in
|
||||
content_load_state_in_progress is not correct? Either way,
|
||||
the load happens after the recording starts rather than the
|
||||
right way around.
|
||||
*/
|
||||
task_queue_wait(NULL,NULL);
|
||||
/* Hack: the regular scheduler doesn't do the right thing here at
|
||||
least in emscripten builds. I would expect that the check in
|
||||
task_movie.c:343 should defer recording until the movie task
|
||||
is done, but maybe that task isn't enqueued again yet when the
|
||||
movie-record task is checked? Or the finder call in
|
||||
content_load_state_in_progress is not correct? Either way,
|
||||
the load happens after the recording starts rather than the
|
||||
right way around.
|
||||
*/
|
||||
task_queue_wait(NULL, NULL);
|
||||
#endif
|
||||
movie_start_record(input_st, input_st->bsv_movie_state.movie_start_path);
|
||||
movie_start_record(input_st, input_st->bsv_movie_state.movie_start_path);
|
||||
}
|
||||
else if (input_st->bsv_movie_state.flags & BSV_FLAG_MOVIE_START_PLAYBACK)
|
||||
{
|
||||
configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
|
||||
movie_start_playback(input_st, input_st->bsv_movie_state.movie_start_path);
|
||||
configuration_set_uint(settings, settings->uints.rewind_granularity, 1);
|
||||
movie_start_playback(input_st, input_st->bsv_movie_state.movie_start_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -4355,6 +4379,7 @@ static bool event_init_content(
|
|||
|
||||
static void runloop_runtime_log_init(runloop_state_t *runloop_st)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *content_path = path_get(RARCH_PATH_CONTENT);
|
||||
const char *core_path = path_get(RARCH_PATH_CORE);
|
||||
|
||||
|
@ -4386,6 +4411,19 @@ static void runloop_runtime_log_init(runloop_state_t *runloop_st)
|
|||
strlcpy(runloop_st->runtime_core_path,
|
||||
core_path,
|
||||
sizeof(runloop_st->runtime_core_path));
|
||||
|
||||
if ( !settings->bools.content_runtime_log
|
||||
&& !settings->bools.content_runtime_log_aggregate)
|
||||
return;
|
||||
|
||||
if ( !string_is_empty(content_path)
|
||||
&& !string_is_empty(core_path))
|
||||
runtime_log_init(
|
||||
runloop_st->runtime_content_path,
|
||||
runloop_st->runtime_core_path,
|
||||
settings->paths.directory_runtime_log,
|
||||
settings->paths.directory_playlist,
|
||||
true);
|
||||
}
|
||||
|
||||
void runloop_set_frame_limit(
|
||||
|
@ -4634,16 +4672,9 @@ bool runloop_event_init_core(
|
|||
float fastforward_ratio = 0.0f;
|
||||
rarch_system_info_t *sys_info = &runloop_st->system;
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
{
|
||||
/* We need this in order for core_info_current_supports_netplay
|
||||
to work correctly at init_netplay,
|
||||
called later at event_init_content. */
|
||||
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
||||
command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
|
||||
}
|
||||
#endif
|
||||
/* Init core info files */
|
||||
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
|
||||
command_event(CMD_EVENT_LOAD_CORE_PERSIST, NULL);
|
||||
|
||||
/* Load symbols */
|
||||
if (!runloop_init_libretro_symbols(runloop_st,
|
||||
|
@ -4771,7 +4802,12 @@ bool runloop_event_init_core(
|
|||
runloop_set_frame_limit(&video_st->av_info, fastforward_ratio);
|
||||
runloop_st->frame_limit_last_time = cpu_features_get_time_usec();
|
||||
|
||||
/* Init runtime log and read current state slot */
|
||||
runloop_runtime_log_init(runloop_st);
|
||||
|
||||
if (runloop_st->entry_state_slot > -1)
|
||||
configuration_set_int(settings, settings->ints.state_slot, runloop_st->entry_state_slot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ typedef struct
|
|||
char **current_entry_val;
|
||||
char *runtime_string;
|
||||
char *last_played_string;
|
||||
char *state_slot;
|
||||
} RtlJSONContext;
|
||||
|
||||
static bool RtlJSONObjectMemberHandler(void *ctx, const char *s, size_t len)
|
||||
|
@ -71,6 +72,8 @@ static bool RtlJSONObjectMemberHandler(void *ctx, const char *s, size_t len)
|
|||
p_ctx->current_entry_val = &p_ctx->runtime_string;
|
||||
else if (string_is_equal(s, "last_played"))
|
||||
p_ctx->current_entry_val = &p_ctx->last_played_string;
|
||||
else if (string_is_equal(s, "state_slot"))
|
||||
p_ctx->current_entry_val = &p_ctx->state_slot;
|
||||
/* Ignore unknown members */
|
||||
}
|
||||
|
||||
|
@ -113,6 +116,8 @@ static void runtime_log_read_file(runtime_log_t *runtime_log)
|
|||
unsigned last_played_minute = 0;
|
||||
unsigned last_played_second = 0;
|
||||
|
||||
unsigned state_slot = 0;
|
||||
|
||||
RtlJSONContext context = {0};
|
||||
/* Attempt to open log file */
|
||||
RFILE *file = filestream_open(runtime_log->path,
|
||||
|
@ -193,6 +198,24 @@ static void runtime_log_read_file(runtime_log_t *runtime_log)
|
|||
}
|
||||
}
|
||||
|
||||
/* State slot */
|
||||
if (!string_is_empty(context.state_slot))
|
||||
{
|
||||
if (sscanf(context.state_slot,
|
||||
"%04u",
|
||||
&state_slot) != 1)
|
||||
{
|
||||
RARCH_ERR("[Runtime] Invalid \"state slot\" entry detected: \"%s\".\n", runtime_log->path);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (state_slot > 0)
|
||||
{
|
||||
runloop_state_t *runloop_st = runloop_state_get_ptr();
|
||||
runloop_st->entry_state_slot = state_slot;
|
||||
}
|
||||
|
||||
/* If we reach this point then all is well
|
||||
* > Assign values to runtime_log object */
|
||||
runtime_log->runtime.hours = runtime_hours;
|
||||
|
@ -206,12 +229,16 @@ static void runtime_log_read_file(runtime_log_t *runtime_log)
|
|||
runtime_log->last_played.minute = last_played_minute;
|
||||
runtime_log->last_played.second = last_played_second;
|
||||
|
||||
runtime_log->state_slot = state_slot;
|
||||
|
||||
end:
|
||||
/* Clean up leftover strings */
|
||||
if (context.runtime_string)
|
||||
free(context.runtime_string);
|
||||
if (context.last_played_string)
|
||||
free(context.last_played_string);
|
||||
if (context.state_slot)
|
||||
free(context.state_slot);
|
||||
|
||||
/* Close log file */
|
||||
filestream_close(file);
|
||||
|
@ -317,8 +344,10 @@ runtime_log_t *runtime_log_init(
|
|||
* no content is provided, 'content' is simply
|
||||
* the name of the core itself */
|
||||
if (supports_no_game)
|
||||
fill_pathname(content_name, core_name,
|
||||
".lrtl", sizeof(content_name));
|
||||
fill_pathname(content_name,
|
||||
core_name,
|
||||
FILE_PATH_RUNTIME_EXTENSION,
|
||||
sizeof(content_name));
|
||||
}
|
||||
/* NOTE: TyrQuake requires a specific hack, since all
|
||||
* content has the same name... */
|
||||
|
@ -334,12 +363,16 @@ runtime_log_t *runtime_log_init(
|
|||
strlcpy(tmp_buf,
|
||||
content_path, _len * sizeof(char));
|
||||
fill_pathname(content_name,
|
||||
path_basename(tmp_buf), ".lrtl", sizeof(content_name));
|
||||
path_basename(tmp_buf),
|
||||
FILE_PATH_RUNTIME_EXTENSION,
|
||||
sizeof(content_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
fill_pathname(content_name, path_basename(content_path), ".lrtl",
|
||||
fill_pathname(content_name,
|
||||
path_basename(content_path),
|
||||
FILE_PATH_RUNTIME_EXTENSION,
|
||||
sizeof(content_name));
|
||||
|
||||
if (string_is_empty(content_name))
|
||||
|
@ -369,6 +402,8 @@ runtime_log_t *runtime_log_init(
|
|||
runtime_log->last_played.minute = 0;
|
||||
runtime_log->last_played.second = 0;
|
||||
|
||||
runtime_log->state_slot = 0;
|
||||
|
||||
runtime_log->path[0] = '\0';
|
||||
|
||||
strlcpy(runtime_log->path, log_file_path, sizeof(runtime_log->path));
|
||||
|
@ -468,6 +503,8 @@ void runtime_log_reset(runtime_log_t *runtime_log)
|
|||
runtime_log->last_played.hour = 0;
|
||||
runtime_log->last_played.minute = 0;
|
||||
runtime_log->last_played.second = 0;
|
||||
|
||||
runtime_log->state_slot = 0;
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
|
@ -1112,6 +1149,20 @@ void runtime_log_save(runtime_log_t *runtime_log)
|
|||
rjsonwriter_raw(writer, ":", 1);
|
||||
rjsonwriter_raw(writer, " ", 1);
|
||||
rjsonwriter_add_string(writer, value_string);
|
||||
rjsonwriter_raw(writer, ",", 1);
|
||||
rjsonwriter_raw(writer, "\n", 1);
|
||||
|
||||
/* > Current state slot */
|
||||
value_string[0] = '\0';
|
||||
snprintf(value_string, sizeof(value_string),
|
||||
"%u",
|
||||
runtime_log->state_slot);
|
||||
|
||||
rjsonwriter_add_spaces(writer, 2);
|
||||
rjsonwriter_add_string(writer, "state_slot");
|
||||
rjsonwriter_raw(writer, ":", 1);
|
||||
rjsonwriter_raw(writer, " ", 1);
|
||||
rjsonwriter_add_string(writer, value_string);
|
||||
rjsonwriter_raw(writer, "\n", 1);
|
||||
|
||||
/* > Finalise */
|
||||
|
|
|
@ -57,6 +57,7 @@ typedef struct
|
|||
{
|
||||
rtl_runtime_t runtime; /* unsigned alignment */
|
||||
rtl_last_played_t last_played; /* unsigned alignment */
|
||||
unsigned state_slot;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
} runtime_log_t;
|
||||
|
||||
|
|
Loading…
Reference in New Issue