1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-03 00:38:44 +00:00

Enable runtime logging for contentless cores (#13671)

This commit is contained in:
jdgleaver 2022-02-25 17:23:55 +00:00 committed by GitHub
parent e4d62a3b9f
commit 8739264485
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 556 additions and 66 deletions

View File

@ -96,6 +96,7 @@ static int action_cancel_contentless_core(const char *path,
const char *label, unsigned type, size_t idx)
{
menu_state_get_ptr()->contentless_core_ptr = 0;
menu_contentless_cores_flush_runtime();
return action_cancel_pop_default(path, label, type, idx) ;
}

View File

@ -95,6 +95,111 @@ static int menu_action_sublabel_file_browser_core(file_list_t *list, unsigned ty
return 1;
}
static int menu_action_sublabel_contentless_core(file_list_t *list,
unsigned type, unsigned i, const char *label, const char *path, char *s, size_t len)
{
const char *core_path = path;
core_info_t *core_info = NULL;
const contentless_core_info_entry_t *entry = NULL;
const char *menu_ident = menu_driver_ident();
bool display_licenses = true;
bool display_runtime = true;
settings_t *settings = config_get_ptr();
bool playlist_show_sublabels = settings->bools.playlist_show_sublabels;
unsigned playlist_sublabel_runtime_type = settings->uints.playlist_sublabel_runtime_type;
bool content_runtime_log = settings->bools.content_runtime_log;
bool content_runtime_log_aggregate = settings->bools.content_runtime_log_aggregate;
const char *directory_runtime_log = settings->paths.directory_runtime_log;
const char *directory_playlist = settings->paths.directory_playlist;
enum playlist_sublabel_last_played_style_type
playlist_sublabel_last_played_style =
(enum playlist_sublabel_last_played_style_type)
settings->uints.playlist_sublabel_last_played_style;
enum playlist_sublabel_last_played_date_separator_type
menu_timedate_date_separator =
(enum playlist_sublabel_last_played_date_separator_type)
settings->uints.menu_timedate_date_separator;
if (!playlist_show_sublabels)
return 0;
/* Search for specified core */
if (!core_info_find(core_path, &core_info) ||
!core_info->supports_no_game)
return 1;
/* Get corresponding contentless core info entry */
menu_contentless_cores_get_info(core_info->core_file_id.str,
&entry);
if (!entry)
return 1;
/* Determine which info we need to display */
/* > Runtime info is always omitted when using Ozone
* > Check if required runtime log is enabled */
if (((playlist_sublabel_runtime_type == PLAYLIST_RUNTIME_PER_CORE) &&
!content_runtime_log) ||
((playlist_sublabel_runtime_type == PLAYLIST_RUNTIME_AGGREGATE) &&
!content_runtime_log_aggregate) ||
string_is_equal(menu_ident, "ozone"))
display_runtime = false;
/* > License info is always displayed unless
* we are using GLUI with runtime info enabled */
if (display_runtime && string_is_equal(menu_ident, "glui"))
display_licenses = false;
if (display_licenses)
strlcpy(s, entry->licenses_str, len);
if (display_runtime)
{
/* Check whether runtime info should be loaded
* from log file */
if (entry->runtime.status == CONTENTLESS_CORE_RUNTIME_UNKNOWN)
runtime_update_contentless_core(
core_path,
directory_runtime_log,
directory_playlist,
(playlist_sublabel_runtime_type == PLAYLIST_RUNTIME_PER_CORE),
playlist_sublabel_last_played_style,
menu_timedate_date_separator);
/* Check whether runtime info is valid */
if (entry->runtime.status == CONTENTLESS_CORE_RUNTIME_VALID)
{
size_t n = 0;
char tmp[64];
tmp[0] = '\0';
if (display_licenses)
{
tmp[0 ] = '\n';
tmp[1 ] = '\0';
}
n = strlcat(tmp, entry->runtime.runtime_str, sizeof(tmp));
if (n < 64 - 1)
{
tmp[n ] = '\n';
tmp[n+1] = '\0';
n = strlcat(tmp, entry->runtime.last_played_str, sizeof(tmp));
}
if (n >= 64)
n = 0; /* Silence GCC warnings... */
(void)n;
if (!string_is_empty(tmp))
strlcat(s, tmp, len);
}
}
return 0;
}
#ifdef HAVE_CHEEVOS
static int menu_action_sublabel_achievement_pause_menu(file_list_t* list,
unsigned type, unsigned i, const char* label, const char* path, char* s, size_t len)
@ -1551,17 +1656,18 @@ static int action_bind_sublabel_playlist_entry(
size_t n = 0;
char tmp[64];
tmp[0 ] = '\n';
tmp[1 ] = '\0';
n = strlcat(tmp, entry->runtime_str, sizeof(tmp));
tmp[n ] = '\n';
tmp[n+1] = '\0';
/* Runtime/last played strings are now cached in the
* playlist, so we can add both in one go */
n = strlcat(tmp, entry->last_played_str, sizeof(tmp));
tmp[0 ] = '\n';
tmp[1 ] = '\0';
n = strlcat(tmp, entry->runtime_str, sizeof(tmp));
if (n < 64 - 1)
{
tmp[n ] = '\n';
tmp[n+1] = '\0';
n = strlcat(tmp, entry->last_played_str, sizeof(tmp));
}
if (n >= 64)
n = 0; /* Silence GCC warnings... */
@ -1970,9 +2076,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
{
case MENU_ENUM_LABEL_FILE_BROWSER_CORE:
case MENU_ENUM_LABEL_CORE_MANAGER_ENTRY:
case MENU_ENUM_LABEL_CONTENTLESS_CORE:
BIND_ACTION_SUBLABEL(cbs, menu_action_sublabel_file_browser_core);
break;
case MENU_ENUM_LABEL_CONTENTLESS_CORE:
BIND_ACTION_SUBLABEL(cbs, menu_action_sublabel_contentless_core);
break;
#ifdef HAVE_NETWORKING
case MENU_ENUM_LABEL_CORE_UPDATER_ENTRY:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_entry);

View File

@ -1,5 +1,5 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2011-2020 - Daniel De Matteis
* Copyright (C) 2011-2022 - Daniel De Matteis
* Copyright (C) 2019-2022 - James Leaver
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
@ -37,12 +37,197 @@ typedef struct
typedef struct
{
contentless_core_info_entry_t **info_entries;
contentless_core_icons_t *icons;
bool icons_enabled;
} contentless_cores_state_t;
static contentless_cores_state_t *contentless_cores_state = NULL;
static void contentless_cores_free_runtime_info(
contentless_core_runtime_info_t *runtime_info)
{
if (!runtime_info)
return;
if (runtime_info->runtime_str)
{
free(runtime_info->runtime_str);
runtime_info->runtime_str = NULL;
}
if (runtime_info->last_played_str)
{
free(runtime_info->last_played_str);
runtime_info->last_played_str = NULL;
}
runtime_info->status = CONTENTLESS_CORE_RUNTIME_UNKNOWN;
}
static void contentless_cores_free_info_entries(
contentless_cores_state_t *state)
{
size_t i, cap;
if (!state || !state->info_entries)
return;
for (i = 0, cap = RHMAP_CAP(state->info_entries); i != cap; i++)
{
if (RHMAP_KEY(state->info_entries, i))
{
contentless_core_info_entry_t *entry = state->info_entries[i];
if (!entry)
continue;
if (entry->licenses_str)
free(entry->licenses_str);
contentless_cores_free_runtime_info(&entry->runtime);
free(entry);
}
}
RHMAP_FREE(state->info_entries);
}
static void contentless_cores_init_info_entries(
contentless_cores_state_t *state)
{
core_info_list_t *core_info_list = NULL;
size_t i;
if (!state)
return;
/* Free any existing entries */
contentless_cores_free_info_entries(state);
/* Create an entry for each contentless core */
core_info_get_list(&core_info_list);
if (!core_info_list)
return;
for (i = 0; i < core_info_list->count; i++)
{
core_info_t *core_info = core_info_get(core_info_list, i);
if (core_info &&
core_info->supports_no_game)
{
contentless_core_info_entry_t *entry =
(contentless_core_info_entry_t*)malloc(sizeof(*entry));
char licenses_str[MENU_SUBLABEL_MAX_LENGTH];
licenses_str[0] = '\0';
/* Populate licences string */
if (core_info->licenses_list)
{
char tmp_str[MENU_SUBLABEL_MAX_LENGTH];
tmp_str[0] = '\0';
string_list_join_concat(tmp_str, sizeof(tmp_str),
core_info->licenses_list, ", ");
snprintf(licenses_str, sizeof(licenses_str), "%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES),
tmp_str);
}
/* No license found - set to N/A */
else
snprintf(licenses_str, sizeof(licenses_str), "%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_INFO_LICENSES),
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE));
entry->licenses_str = strdup(licenses_str);
/* Initialise runtime info */
entry->runtime.runtime_str = NULL;
entry->runtime.last_played_str = NULL;
entry->runtime.status = CONTENTLESS_CORE_RUNTIME_UNKNOWN;
/* Add entry to hash map */
RHMAP_SET_STR(state->info_entries, core_info->core_file_id.str, entry);
}
}
}
void menu_contentless_cores_set_runtime(const char *core_id,
const contentless_core_runtime_info_t *runtime_info)
{
contentless_core_info_entry_t *info_entry = NULL;
if (!contentless_cores_state ||
!contentless_cores_state->info_entries ||
!runtime_info ||
string_is_empty(core_id))
return;
info_entry = RHMAP_GET_STR(contentless_cores_state->info_entries, core_id);
if (!info_entry)
return;
if (!string_is_empty(runtime_info->runtime_str))
{
if (info_entry->runtime.runtime_str)
free(info_entry->runtime.runtime_str);
info_entry->runtime.runtime_str = strdup(runtime_info->runtime_str);
}
if (!string_is_empty(runtime_info->last_played_str))
{
if (info_entry->runtime.last_played_str)
free(info_entry->runtime.last_played_str);
info_entry->runtime.last_played_str = strdup(runtime_info->last_played_str);
}
info_entry->runtime.status = runtime_info->status;
}
void menu_contentless_cores_get_info(const char *core_id,
const contentless_core_info_entry_t **info)
{
if (!info)
return;
if (!contentless_cores_state ||
!contentless_cores_state->info_entries ||
string_is_empty(core_id))
*info = NULL;
*info = RHMAP_GET_STR(contentless_cores_state->info_entries, core_id);
}
void menu_contentless_cores_flush_runtime(void)
{
contentless_cores_state_t *state = contentless_cores_state;
size_t i, cap;
if (!state || !state->info_entries)
return;
for (i = 0, cap = RHMAP_CAP(state->info_entries); i != cap; i++)
{
if (RHMAP_KEY(state->info_entries, i))
{
contentless_core_info_entry_t *entry = state->info_entries[i];
if (!entry)
continue;
contentless_cores_free_runtime_info(&entry->runtime);
}
}
}
static void contentless_cores_unload_icons(contentless_cores_state_t *state)
{
size_t i, cap;
@ -213,6 +398,7 @@ void menu_contentless_cores_free(void)
if (!contentless_cores_state)
return;
contentless_cores_free_info_entries(contentless_cores_state);
contentless_cores_unload_icons(contentless_cores_state);
free(contentless_cores_state);
contentless_cores_state = NULL;
@ -276,7 +462,7 @@ unsigned menu_displaylist_contentless_cores(file_list_t *list, settings_t *setti
}
}
/* Initialise icons, if required */
/* Initialise global state, if required */
if (!contentless_cores_state && (count > 0))
{
contentless_cores_state = (contentless_cores_state_t*)calloc(1,
@ -287,6 +473,7 @@ unsigned menu_displaylist_contentless_cores(file_list_t *list, settings_t *setti
contentless_cores_state->icons_enabled =
!string_is_equal(menu_driver_ident(), "rgui");
contentless_cores_init_info_entries(contentless_cores_state);
contentless_cores_load_icons(contentless_cores_state);
}

View File

@ -4186,26 +4186,53 @@ static unsigned menu_displaylist_parse_content_information(
const char *content_path = NULL;
const char *core_path = NULL;
const char *db_name = NULL;
bool playlist_origin = true;
bool playlist_valid = false;
const char *origin_label = NULL;
struct menu_state *menu_st = menu_state_get_ptr();
file_list_t *list = NULL;
unsigned count = 0;
bool content_loaded = !retroarch_ctl(RARCH_CTL_IS_DUMMY_CORE, NULL)
&& !string_is_empty(loaded_content_path)
&& string_is_equal(menu->deferred_path, loaded_content_path);
bool core_supports_no_game = false;
core_name[0] = '\0';
/* If content is currently running, have to make sure
* we have a valid playlist to work with
* (if content is not running, then playlist will always
* be valid provided that playlist_get_cached() does not
* return NULL) */
if (content_loaded)
/* Check the origin menu from which the information
* entry was selected
* > Can only assume a valid playlist if the origin
* was an actual playlist - i.e. cached playlist is
* dubious if information was selected from
* 'Main Menu > Quick Menu' or 'Standalone Cores >
* Quick Menu' */
if (menu_st->entries.list)
list = MENU_LIST_GET(menu_st->entries.list, 0);
if (list && (list->size > 2))
{
if (!string_is_empty(loaded_content_path) && !string_is_empty(loaded_core_path))
file_list_get_at_offset(list, list->size - 3, NULL,
&origin_label, NULL, NULL);
if (string_is_equal(origin_label, msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU)) ||
string_is_equal(origin_label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)) ||
string_is_equal(origin_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST)))
playlist_origin = false;
}
/* If origin menu was a playlist, may rely on
* return value from playlist_get_cached() */
if (playlist_origin)
playlist_valid = !!playlist;
else
{
/* If origin menu was not a playlist, then
* check currently loaded content against
* last cached playlist */
if (content_loaded &&
!string_is_empty(loaded_core_path))
playlist_valid = playlist_index_is_valid(
playlist, idx, loaded_content_path, loaded_core_path);
}
else if (playlist)
playlist_valid = true;
if (playlist_valid)
{
@ -4236,39 +4263,48 @@ static unsigned menu_displaylist_parse_content_information(
core_path = loaded_core_path;
if (core_info_find(core_path, &core_info))
{
core_supports_no_game = core_info->supports_no_game;
if (!string_is_empty(core_info->display_name))
strlcpy(core_name, core_info->display_name, sizeof(core_name));
}
}
/* Content label */
tmp[0] = '\0';
snprintf(tmp, sizeof(tmp),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_LABEL),
!string_is_empty(content_label)
? content_label
: msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
);
if (menu_entries_append_enum(info->list, tmp,
msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_INFO_LABEL),
MENU_ENUM_LABEL_CONTENT_INFO_LABEL,
0, 0, 0))
count++;
/* If content path is empty and core supports
* contentless operation, skip label/path entries */
if (!(core_supports_no_game && string_is_empty(content_path)))
{
/* Content label */
tmp[0] = '\0';
snprintf(tmp, sizeof(tmp),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_LABEL),
!string_is_empty(content_label)
? content_label
: msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
);
if (menu_entries_append_enum(info->list, tmp,
msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_INFO_LABEL),
MENU_ENUM_LABEL_CONTENT_INFO_LABEL,
0, 0, 0))
count++;
/* Content path */
tmp[0] = '\0';
snprintf(tmp, sizeof(tmp),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_PATH),
!string_is_empty(content_path)
? content_path
: msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
);
if (menu_entries_append_enum(info->list, tmp,
msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_INFO_PATH),
MENU_ENUM_LABEL_CONTENT_INFO_PATH,
0, 0, 0))
count++;
/* Content path */
tmp[0] = '\0';
snprintf(tmp, sizeof(tmp),
"%s: %s",
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_PATH),
!string_is_empty(content_path)
? content_path
: msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)
);
if (menu_entries_append_enum(info->list, tmp,
msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_INFO_PATH),
MENU_ENUM_LABEL_CONTENT_INFO_PATH,
0, 0, 0))
count++;
}
/* Core name */
if (!string_is_empty(core_name) &&

View File

@ -1309,6 +1309,7 @@ void menu_list_flush_stack(
file_list_t *menu_list = MENU_LIST_GET(list, (unsigned)idx);
menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh);
menu_contentless_cores_flush_runtime();
if (menu_list && menu_list->size)
file_list_get_at_offset(menu_list, menu_list->size - 1, &path, &label, &type, &entry_idx);
@ -4162,6 +4163,8 @@ int menu_driver_deferred_push_content_list(file_list_t *list)
menu_st->selection_ptr = 0;
menu_st->contentless_core_ptr = 0;
menu_contentless_cores_flush_runtime();
if (!menu_driver_displaylist_push(
menu_st,
settings,
@ -5269,9 +5272,8 @@ bool menu_driver_init(bool video_is_threaded)
const char *menu_driver_ident(void)
{
struct menu_state *menu_st = &menu_driver_state;
if (menu_st->alive)
if (menu_st->driver_ctx && menu_st->driver_ctx->ident)
return menu_st->driver_ctx->ident;
if (menu_st->driver_ctx && menu_st->driver_ctx->ident)
return menu_st->driver_ctx->ident;
return NULL;
}
@ -7031,6 +7033,8 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
menu_st->contentless_core_ptr = 0;
menu_st->scroll.index_size = 0;
menu_contentless_cores_flush_runtime();
for (i = 0; i < SCROLL_INDEX_SIZE; i++)
menu_st->scroll.index_list[i] = 0;

View File

@ -639,10 +639,37 @@ void menu_explore_free(void);
void menu_explore_set_state(explore_state_t *state);
#endif
/* Contentless cores START */
enum contentless_core_runtime_status
{
CONTENTLESS_CORE_RUNTIME_UNKNOWN = 0,
CONTENTLESS_CORE_RUNTIME_MISSING,
CONTENTLESS_CORE_RUNTIME_VALID
};
typedef struct
{
char *runtime_str;
char *last_played_str;
enum contentless_core_runtime_status status;
} contentless_core_runtime_info_t;
typedef struct
{
char *licenses_str;
contentless_core_runtime_info_t runtime;
} contentless_core_info_entry_t;
uintptr_t menu_contentless_cores_get_entry_icon(const char *core_id);
void menu_contentless_cores_context_init(void);
void menu_contentless_cores_context_deinit(void);
void menu_contentless_cores_free(void);
void menu_contentless_cores_set_runtime(const char *core_id,
const contentless_core_runtime_info_t *runtime_info);
void menu_contentless_cores_get_info(const char *core_id,
const contentless_core_info_entry_t **info);
void menu_contentless_cores_flush_runtime(void);
/* Contentless cores END */
/* Returns true if search filter is enabled
* for the specified menu list */

View File

@ -225,7 +225,9 @@ end:
/* Initialise runtime log, loading current parameters
* if log file exists. Returned object must be free()'d.
* Returns NULL if content_path and/or core_path are invalid */
* Returns NULL if core_path is invalid, or content_path
* is invalid and core does not support contentless
* operation */
runtime_log_t *runtime_log_init(
const char *content_path,
const char *core_path,
@ -238,6 +240,7 @@ runtime_log_t *runtime_log_init(
char log_file_dir[PATH_MAX_LENGTH];
char log_file_path[PATH_MAX_LENGTH];
char tmp_buf[PATH_MAX_LENGTH];
bool supports_no_game = false;
core_info_t *core_info = NULL;
runtime_log_t *runtime_log = NULL;
@ -257,18 +260,23 @@ runtime_log_t *runtime_log_init(
if ( string_is_empty(core_path) ||
string_is_equal(core_path, "builtin") ||
string_is_equal(core_path, "DETECT") ||
string_is_empty(content_path))
string_is_equal(core_path, "DETECT"))
return NULL;
/* Get core name
* Note: An annoyance - this is required even when
* we are performing aggregate (not per core) logging,
* since content name is sometimes dependent upon core
/* Get core info:
* - Need to know if core supports contentless operation
* - Need core name in order to generate file path when
* per-core logging is enabled
* Note: An annoyance - core name is required even when
* we are performing aggregate logging, since content
* name is sometimes dependent upon core
* (e.g. see TyrQuake below) */
if (core_info_find(core_path, &core_info) &&
core_info->core_name)
strlcpy(core_name, core_info->core_name, sizeof(core_name));
if (core_info_find(core_path, &core_info))
{
supports_no_game = core_info->supports_no_game;
if (!string_is_empty(core_info->core_name))
strlcpy(core_name, core_info->core_name, sizeof(core_name));
}
if (string_is_empty(core_name))
return NULL;
@ -313,10 +321,18 @@ runtime_log_t *runtime_log_init(
}
}
/* Get content name
* NOTE: TyrQuake requires a specific hack, since all
/* Get content name */
if (string_is_empty(content_path))
{
/* If core supports contentless operation and
* no content is provided, 'content' is simply
* the name of the core itself */
if (supports_no_game)
strlcpy(content_name, core_name, sizeof(content_name));
}
/* NOTE: TyrQuake requires a specific hack, since all
* content has the same name... */
if (string_is_equal(core_name, "TyrQuake"))
else if (string_is_equal(core_name, "TyrQuake"))
{
const char *last_slash = find_last_slash(content_path);
if (last_slash)
@ -1356,3 +1372,98 @@ void runtime_update_playlist(
/* Update playlist */
playlist_update_runtime(playlist, idx, &update_entry, false);
}
#if defined(HAVE_MENU)
/* Contentless cores manipulation */
/* Updates specified contentless core runtime values with
* contents of associated log file */
void runtime_update_contentless_core(
const char *core_path,
const char *dir_runtime_log,
const char *dir_playlist,
bool log_per_core,
enum playlist_sublabel_last_played_style_type timedate_style,
enum playlist_sublabel_last_played_date_separator_type date_separator)
{
char runtime_str[64];
char last_played_str[64];
core_info_t *core_info = NULL;
runtime_log_t *runtime_log = NULL;
contentless_core_runtime_info_t runtime_info = {0};
#if (defined(HAVE_OZONE) || defined(HAVE_MATERIALUI))
const char *menu_ident = menu_driver_ident();
#endif
/* Sanity check */
if (string_is_empty(core_path) ||
!core_info_find(core_path, &core_info) ||
!core_info->supports_no_game)
return;
/* Set fallback runtime status
* (saves 'if' checks later...) */
runtime_info.status = CONTENTLESS_CORE_RUNTIME_MISSING;
/* 'Attach' runtime/last played strings */
runtime_str[0] = '\0';
last_played_str[0] = '\0';
runtime_info.runtime_str = runtime_str;
runtime_info.last_played_str = last_played_str;
/* Attempt to open log file */
runtime_log = runtime_log_init(
NULL,
core_path,
dir_runtime_log,
dir_playlist,
log_per_core);
if (runtime_log)
{
/* Check whether a non-zero runtime has been recorded */
if (runtime_log_has_runtime(runtime_log))
{
/* Read current runtime */
runtime_log_get_runtime_str(runtime_log,
runtime_str, sizeof(runtime_str));
/* Read last played timestamp */
runtime_log_get_last_played_str(runtime_log,
last_played_str, sizeof(last_played_str),
timedate_style, date_separator);
/* Contentless core entry now contains valid runtime data */
runtime_info.status = CONTENTLESS_CORE_RUNTIME_VALID;
}
/* Clean up */
free(runtime_log);
}
#if (defined(HAVE_OZONE) || defined(HAVE_MATERIALUI))
/* Ozone and GLUI require runtime/last played strings
* to be populated even when no runtime is recorded */
if (runtime_info.status != CONTENTLESS_CORE_RUNTIME_VALID)
{
if (string_is_equal(menu_ident, "ozone") ||
string_is_equal(menu_ident, "glui"))
{
runtime_log_get_runtime_str(NULL,
runtime_str, sizeof(runtime_str));
runtime_log_get_last_played_str(NULL,
last_played_str, sizeof(last_played_str),
timedate_style, date_separator);
/* While runtime data does not exist, the contentless
* core entry does now contain valid information... */
runtime_info.status = CONTENTLESS_CORE_RUNTIME_VALID;
}
}
#endif
/* Update contentless core */
menu_contentless_cores_set_runtime(core_info->core_file_id.str,
&runtime_info);
}
#endif

View File

@ -110,7 +110,9 @@ typedef struct
/* Initialise runtime log, loading current parameters
* if log file exists. Returned object must be free()'d.
* Returns NULL if content_path and/or core_path are invalid */
* Returns NULL if core_path is invalid, or content_path
* is invalid and core does not support contentless
* operation */
runtime_log_t *runtime_log_init(
const char *content_path,
const char *core_path,
@ -205,6 +207,20 @@ void runtime_update_playlist(
enum playlist_sublabel_last_played_style_type timedate_style,
enum playlist_sublabel_last_played_date_separator_type date_separator);
#if defined(HAVE_MENU)
/* Contentless cores manipulation */
/* Updates specified contentless core runtime values with
* contents of associated log file */
void runtime_update_contentless_core(
const char *core_path,
const char *dir_runtime_log,
const char *dir_playlist,
bool log_per_core,
enum playlist_sublabel_last_played_style_type timedate_style,
enum playlist_sublabel_last_played_date_separator_type date_separator);
#endif
RETRO_END_DECLS
#endif