mirror of
https://github.com/libretro/RetroArch
synced 2025-03-30 07:20:36 +00:00
Enable runtime logging for contentless cores (#13671)
This commit is contained in:
parent
e4d62a3b9f
commit
8739264485
@ -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) ;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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) &&
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
137
runtime_file.c
137
runtime_file.c
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user