diff --git a/Makefile.common b/Makefile.common
index b658fae21b..69ae1a538f 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -1088,7 +1088,8 @@ ifeq ($(HAVE_MENU_COMMON), 1)
menu/cbs/menu_cbs_label.o \
menu/cbs/menu_cbs_sublabel.o \
menu/cbs/menu_cbs_title.o \
- menu/menu_displaylist.o
+ menu/menu_displaylist.o \
+ menu/menu_contentless_cores.o
endif
ifeq ($(HAVE_GFX_WIDGETS), 1)
diff --git a/config.def.h b/config.def.h
index 5f0dafee8d..2dfa1c76d3 100644
--- a/config.def.h
+++ b/config.def.h
@@ -730,6 +730,7 @@ static const bool content_show_playlists = true;
#if defined(HAVE_LIBRETRODB)
#define DEFAULT_MENU_CONTENT_SHOW_EXPLORE true
#endif
+#define DEFAULT_MENU_CONTENT_SHOW_CONTENTLESS_CORES MENU_CONTENTLESS_CORES_DISPLAY_SINGLE_PURPOSE
#ifdef HAVE_XMB
#define DEFAULT_XMB_ANIMATION 0
diff --git a/configuration.c b/configuration.c
index 7c725f2e92..4908d34dc3 100644
--- a/configuration.c
+++ b/configuration.c
@@ -2162,6 +2162,7 @@ static struct config_uint_setting *populate_settings_uint(
SETTING_UINT("menu_ticker_type", &settings->uints.menu_ticker_type, true, DEFAULT_MENU_TICKER_TYPE, false);
SETTING_UINT("menu_scroll_delay", &settings->uints.menu_scroll_delay, true, DEFAULT_MENU_SCROLL_DELAY, false);
SETTING_UINT("content_show_add_entry", &settings->uints.menu_content_show_add_entry, true, DEFAULT_MENU_CONTENT_SHOW_ADD_ENTRY, false);
+ SETTING_UINT("content_show_contentless_cores", &settings->uints.menu_content_show_contentless_cores, true, DEFAULT_MENU_CONTENT_SHOW_CONTENTLESS_CORES, false);
SETTING_UINT("menu_screensaver_timeout", &settings->uints.menu_screensaver_timeout, true, DEFAULT_MENU_SCREENSAVER_TIMEOUT, false);
#if defined(HAVE_MATERIALUI) || defined(HAVE_XMB) || defined(HAVE_OZONE)
SETTING_UINT("menu_screensaver_animation", &settings->uints.menu_screensaver_animation, true, DEFAULT_MENU_SCREENSAVER_ANIMATION, false);
diff --git a/configuration.h b/configuration.h
index a9712d441f..4dfae63259 100644
--- a/configuration.h
+++ b/configuration.h
@@ -295,6 +295,7 @@ typedef struct settings
unsigned menu_ticker_type;
unsigned menu_scroll_delay;
unsigned menu_content_show_add_entry;
+ unsigned menu_content_show_contentless_cores;
unsigned menu_screensaver_timeout;
unsigned menu_screensaver_animation;
diff --git a/core_info.c b/core_info.c
index 1ace5defbb..ce471d700f 100644
--- a/core_info.c
+++ b/core_info.c
@@ -48,7 +48,7 @@
/* Core Info Cache START */
/*************************/
-#define CORE_INFO_CACHE_VERSION "1.1"
+#define CORE_INFO_CACHE_VERSION "1.2"
#define CORE_INFO_CACHE_DEFAULT_CAPACITY 8
/* TODO/FIXME: Apparently rzip compression is an issue on UWP */
@@ -203,6 +203,8 @@ static bool CCJSONObjectMemberHandler(void *context,
}
else if (string_is_equal(pValue, "supports_no_game"))
pCtx->current_entry_bool_val = &pCtx->core_info->supports_no_game;
+ else if (string_is_equal(pValue, "single_purpose"))
+ pCtx->current_entry_bool_val = &pCtx->core_info->single_purpose;
else if (string_is_equal(pValue, "savestate_support_level"))
pCtx->current_entry_uint_val = &pCtx->core_info->savestate_support_level;
break;
@@ -472,6 +474,7 @@ static void core_info_copy(core_info_t *src, core_info_t *dst)
dst->savestate_support_level = src->savestate_support_level;
dst->has_info = src->has_info;
dst->supports_no_game = src->supports_no_game;
+ dst->single_purpose = src->single_purpose;
dst->database_match_archive_member = src->database_match_archive_member;
dst->is_experimental = src->is_experimental;
dst->is_locked = src->is_locked;
@@ -567,6 +570,7 @@ static void core_info_transfer(core_info_t *src, core_info_t *dst)
dst->savestate_support_level = src->savestate_support_level;
dst->has_info = src->has_info;
dst->supports_no_game = src->supports_no_game;
+ dst->single_purpose = src->single_purpose;
dst->database_match_archive_member = src->database_match_archive_member;
dst->is_experimental = src->is_experimental;
dst->is_locked = src->is_locked;
@@ -1118,6 +1122,14 @@ static bool core_info_cache_write(core_info_cache_list_t *list, const char *info
rjsonwriter_add_comma(writer);
rjsonwriter_add_newline(writer);
+ rjsonwriter_add_spaces(writer, 6);
+ rjsonwriter_add_string(writer, "single_purpose");
+ rjsonwriter_add_colon(writer);
+ rjsonwriter_add_space(writer);
+ rjsonwriter_add_bool(writer, info->single_purpose);
+ rjsonwriter_add_comma(writer);
+ rjsonwriter_add_newline(writer);
+
rjsonwriter_add_spaces(writer, 6);
rjsonwriter_add_string(writer, "database_match_archive_member");
rjsonwriter_add_colon(writer);
@@ -1746,6 +1758,10 @@ static void core_info_parse_config_file(
&tmp_bool))
info->supports_no_game = tmp_bool;
+ if (config_get_bool(conf, "single_purpose",
+ &tmp_bool))
+ info->single_purpose = tmp_bool;
+
if (config_get_bool(conf, "database_match_archive_member",
&tmp_bool))
info->database_match_archive_member = tmp_bool;
@@ -2178,6 +2194,7 @@ bool core_info_init_current_core(void)
return false;
current->has_info = false;
current->supports_no_game = false;
+ current->single_purpose = false;
current->database_match_archive_member = false;
current->is_experimental = false;
current->is_locked = false;
diff --git a/core_info.h b/core_info.h
index 4fe4425757..3096c38e12 100644
--- a/core_info.h
+++ b/core_info.h
@@ -104,6 +104,7 @@ typedef struct
uint32_t savestate_support_level;
bool has_info;
bool supports_no_game;
+ bool single_purpose;
bool database_match_archive_member;
bool is_experimental;
bool is_locked;
diff --git a/driver.c b/driver.c
index 99e8cfb788..a7d050e3c4 100644
--- a/driver.c
+++ b/driver.c
@@ -627,6 +627,7 @@ void drivers_init(
#ifdef HAVE_LIBRETRODB
menu_explore_context_init();
#endif
+ menu_contentless_cores_context_init();
}
}
@@ -694,6 +695,7 @@ void driver_uninit(int flags)
#ifdef HAVE_LIBRETRODB
menu_explore_context_deinit();
#endif
+ menu_contentless_cores_context_deinit();
menu_driver_ctl(RARCH_MENU_CTL_DEINIT, NULL);
}
diff --git a/griffin/griffin.c b/griffin/griffin.c
index 2c7490d577..0478e37fec 100644
--- a/griffin/griffin.c
+++ b/griffin/griffin.c
@@ -1416,6 +1416,7 @@ MENU
#include "../menu/cbs/menu_cbs_label.c"
#include "../menu/cbs/menu_cbs_sublabel.c"
#include "../menu/menu_displaylist.c"
+#include "../menu/menu_contentless_cores.c"
#ifdef HAVE_LIBRETRODB
#include "../menu/menu_explore.c"
#include "../tasks/task_menu_explore.c"
diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h
index 496b40b77a..2aa44f98fa 100644
--- a/intl/msg_hash_lbl.h
+++ b/intl/msg_hash_lbl.h
@@ -82,6 +82,14 @@ MSG_HASH(
MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST,
"explore_initialising_list"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB,
+ "contentless_cores_tab"
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_CONTENTLESS_CORE,
+ "contentless_core"
+ )
MSG_HASH(
MENU_ENUM_LABEL_ADD_TAB,
"add_tab"
@@ -768,6 +776,10 @@ MSG_HASH(
MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST,
"deferred_explore_list"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST,
+ "deferred_contentless_cores_list"
+ )
MSG_HASH(
MENU_ENUM_LABEL_DEFERRED_NETPLAY,
"deferred_netplay"
@@ -3752,6 +3764,10 @@ MSG_HASH(
MENU_ENUM_LABEL_CONTENT_SHOW_EXPLORE,
"content_show_explore"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_CONTENT_SHOW_CONTENTLESS_CORES,
+ "content_show_contentless_cores"
+ )
MSG_HASH(
MENU_ENUM_LABEL_CONTENT_SHOW_FAVORITES,
"content_show_favorites"
@@ -4070,6 +4086,10 @@ MSG_HASH(
MENU_ENUM_LABEL_GOTO_EXPLORE,
"goto_explore"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES,
+ "goto_contentless_cores"
+ )
MSG_HASH(
MENU_ENUM_LABEL_MATERIALUI_ICONS_ENABLE,
"materialui_icons_enable"
diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h
index a10cb12b77..a35de8bdd5 100644
--- a/intl/msg_hash_us.h
+++ b/intl/msg_hash_us.h
@@ -44,6 +44,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_EXPLORE_TAB,
"Explore"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CONTENTLESS_CORES_TAB,
+ "Standalone Cores"
+ )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_ADD_TAB,
"Import Content"
@@ -286,6 +290,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_GOTO_EXPLORE,
"Browse all content matching the database via a categorized search interface."
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_GOTO_CONTENTLESS_CORES,
+ "Standalone Cores"
+ )
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_GOTO_CONTENTLESS_CORES,
+ "Installed cores which can operate without loading content will appear here."
+ )
/* Main Menu > Online Updater */
@@ -4620,6 +4632,22 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CONTENT_SHOW_EXPLORE,
"Show the content explorer option. (Restart Required on Ozone/XMB)"
)
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_CONTENTLESS_CORES,
+ "Show 'Standalone Cores'"
+ )
+MSG_HASH(
+ MENU_ENUM_SUBLABEL_CONTENT_SHOW_CONTENTLESS_CORES,
+ "Specify the type of core (if any) to show in the 'Standalone Cores' menu. (Restart Required on Ozone/XMB)"
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_ALL,
+ "All"
+ )
+MSG_HASH(
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE,
+ "Single-Use"
+ )
MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEDATE_ENABLE,
"Show Date and Time"
diff --git a/menu/cbs/menu_cbs_cancel.c b/menu/cbs/menu_cbs_cancel.c
index 7280ae379e..8d253cf919 100644
--- a/menu/cbs/menu_cbs_cancel.c
+++ b/menu/cbs/menu_cbs_cancel.c
@@ -92,6 +92,13 @@ int action_cancel_pop_default(const char *path,
return 0;
}
+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;
+ return action_cancel_pop_default(path, label, type, idx) ;
+}
+
#ifdef HAVE_CHEATS
static int action_cancel_cheat_details(const char *path,
const char *label, unsigned type, size_t idx)
@@ -164,6 +171,11 @@ static int menu_cbs_init_bind_cancel_compare_type(
case FILE_TYPE_DOWNLOAD_CORE:
BIND_ACTION_CANCEL(cbs, action_cancel_core_content);
return 0;
+ case MENU_SETTING_ACTION_CONTENTLESS_CORE_RUN:
+ BIND_ACTION_CANCEL(cbs, action_cancel_contentless_core);
+ return 0;
+ default:
+ break;
}
#ifdef HAVE_CHEATS
diff --git a/menu/cbs/menu_cbs_deferred_push.c b/menu/cbs/menu_cbs_deferred_push.c
index d08093865f..a439abddd9 100644
--- a/menu/cbs/menu_cbs_deferred_push.c
+++ b/menu/cbs/menu_cbs_deferred_push.c
@@ -648,6 +648,7 @@ GENERIC_DEFERRED_PUSH_GENERAL(deferred_music_history_list, PUSH_DEFAULT, DISPLAY
GENERIC_DEFERRED_PUSH_GENERAL(deferred_image_history_list, PUSH_DEFAULT, DISPLAYLIST_IMAGES_HISTORY)
GENERIC_DEFERRED_PUSH_GENERAL(deferred_video_history_list, PUSH_DEFAULT, DISPLAYLIST_VIDEO_HISTORY)
GENERIC_DEFERRED_PUSH_GENERAL(deferred_explore_list, PUSH_DEFAULT, DISPLAYLIST_EXPLORE)
+GENERIC_DEFERRED_PUSH_GENERAL(deferred_contentless_cores_list, PUSH_DEFAULT, DISPLAYLIST_CONTENTLESS_CORES)
GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST)
GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_special, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_SPECIAL)
GENERIC_DEFERRED_PUSH_GENERAL(deferred_push_dropdown_box_list_resolution, PUSH_DEFAULT, DISPLAYLIST_DROPDOWN_LIST_RESOLUTION)
@@ -759,6 +760,7 @@ static int menu_cbs_init_bind_deferred_push_compare_label(
{MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST, deferred_image_history_list},
{MENU_ENUM_LABEL_DEFERRED_VIDEO_LIST, deferred_video_history_list},
{MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST, deferred_explore_list},
+ {MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST, deferred_contentless_cores_list},
{MENU_ENUM_LABEL_DEFERRED_INPUT_SETTINGS_LIST, deferred_push_input_settings_list},
{MENU_ENUM_LABEL_DEFERRED_INPUT_MENU_SETTINGS_LIST, deferred_push_input_menu_settings_list},
{MENU_ENUM_LABEL_DEFERRED_INPUT_TURBO_FIRE_SETTINGS_LIST, deferred_push_input_turbo_fire_settings_list},
diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c
index b8b1e53384..655c2f5b97 100644
--- a/menu/cbs/menu_cbs_get_value.c
+++ b/menu/cbs/menu_cbs_get_value.c
@@ -519,6 +519,25 @@ static void menu_action_setting_disp_set_label_core_manager_entry(
}
}
+static void menu_action_setting_disp_set_label_contentless_core(
+ file_list_t* list,
+ unsigned *w, unsigned type, unsigned i,
+ const char *label,
+ char *s, size_t len,
+ const char *path,
+ char *s2, size_t len2)
+{
+ const char *alt = list->list[i].alt
+ ? list->list[i].alt
+ : list->list[i].path;
+
+ *s = '\0';
+ *w = 0;
+
+ if (alt)
+ strlcpy(s2, alt, len2);
+}
+
#ifndef HAVE_LAKKA_SWITCH
#ifdef HAVE_LAKKA
static void menu_action_setting_disp_cpu_gov_mode(
@@ -1912,6 +1931,10 @@ static int menu_cbs_init_bind_get_string_representation_compare_label(
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_manager_entry);
break;
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
+ BIND_ACTION_GET_VALUE(cbs,
+ menu_action_setting_disp_set_label_contentless_core);
+ break;
case MENU_ENUM_LABEL_CORE_OPTION_OVERRIDE_INFO:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_option_override_info);
@@ -2120,6 +2143,7 @@ static int menu_cbs_init_bind_get_string_representation_compare_type(
case MENU_SETTING_ACTION_DELETE_ENTRY:
case MENU_SETTING_ACTION_CORE_DISK_OPTIONS:
case MENU_EXPLORE_TAB:
+ case MENU_CONTENTLESS_CORES_TAB:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_menu_more);
break;
diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c
index af19734db6..eb346745d3 100644
--- a/menu/cbs/menu_cbs_left.c
+++ b/menu/cbs/menu_cbs_left.c
@@ -1006,6 +1006,7 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_SUBSYSTEM_LOAD:
case MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM:
case MENU_ENUM_LABEL_EXPLORE_ITEM:
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
case MENU_ENUM_LABEL_NO_SETTINGS_FOUND:
BIND_ACTION_LEFT(cbs, action_left_mainmenu);
break;
@@ -1043,6 +1044,7 @@ static int menu_cbs_init_bind_left_compare_label(menu_file_list_cbs_t *cbs,
break;
case MENU_ENUM_LABEL_NO_ITEMS:
case MENU_ENUM_LABEL_NO_PLAYLIST_ENTRIES_AVAILABLE:
+ case MENU_ENUM_LABEL_NO_CORES_AVAILABLE:
case MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST:
if (
string_ends_with_size(menu_label, "_tab",
diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c
index a840795dfb..44e56c3102 100644
--- a/menu/cbs/menu_cbs_ok.c
+++ b/menu/cbs/menu_cbs_ok.c
@@ -612,6 +612,15 @@ int generic_action_ok_displaylist_push(const char *path,
info.enum_idx = MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST;
dl_type = DISPLAYLIST_GENERIC;
break;
+ case ACTION_OK_DL_CONTENTLESS_CORES_LIST:
+ info.type = type;
+ info.directory_ptr = idx;
+ info_path = label;
+ info_label = msg_hash_to_str(
+ MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST);
+ info.enum_idx = MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST;
+ dl_type = DISPLAYLIST_GENERIC;
+ break;
case ACTION_OK_DL_REMAPPINGS_PORT_LIST:
info.type = type;
info.directory_ptr = idx;
@@ -5172,14 +5181,30 @@ int action_ok_close_content(const char *path, const char *label, unsigned type,
/* Unload core */
ret = generic_action_ok_command(CMD_EVENT_UNLOAD_CORE);
- /* If close content was selected via 'Main Menu > Quick Menu',
- * have to flush the menu stack back to 'Main Menu'
+ /* If close content was selected via any means other than
+ * 'Playlist > Quick Menu', have to flush the menu stack
* (otherwise users will be presented with an empty
* 'No items' quick menu, requiring needless backwards
* navigation) */
if (type == MENU_SETTING_ACTION_CLOSE)
{
- menu_entries_flush_stack(msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU), 0);
+ const char *flush_target = msg_hash_to_str(MENU_ENUM_LABEL_MAIN_MENU);
+ const char *parent_label = NULL;
+ struct menu_state *menu_st = menu_state_get_ptr();
+ file_list_t *list = NULL;
+
+ if (menu_st->entries.list)
+ list = MENU_LIST_GET(menu_st->entries.list, 0);
+ if (list && (list->size > 1))
+ {
+ file_list_get_at_offset(list, list->size - 2, NULL, &parent_label, NULL, NULL);
+
+ if (string_is_equal(parent_label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)) ||
+ string_is_equal(parent_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST)))
+ flush_target = parent_label;
+ }
+
+ menu_entries_flush_stack(flush_target, 0);
/* An annoyance - some menu drivers (Ozone...) call
* RARCH_MENU_CTL_SET_PREVENT_POPULATE in awkward
* places, which can cause breakage here when flushing
@@ -5611,6 +5636,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_cdrom_info_list, ACTION_OK_DL_CDROM_INFO_DETAIL
DEFAULT_ACTION_OK_FUNC(action_ok_goto_video, ACTION_OK_DL_VIDEO_LIST)
DEFAULT_ACTION_OK_FUNC(action_ok_goto_music, ACTION_OK_DL_MUSIC_LIST)
DEFAULT_ACTION_OK_FUNC(action_ok_goto_explore, ACTION_OK_DL_EXPLORE_LIST)
+DEFAULT_ACTION_OK_FUNC(action_ok_goto_contentless_cores, ACTION_OK_DL_CONTENTLESS_CORES_LIST)
#if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL)
DEFAULT_ACTION_OK_FUNC(action_ok_shader_preset_save, ACTION_OK_DL_SHADER_PRESET_SAVE)
DEFAULT_ACTION_OK_FUNC(action_ok_shader_preset_remove, ACTION_OK_DL_SHADER_PRESET_REMOVE)
@@ -6670,12 +6696,20 @@ static int action_ok_start_core(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
content_ctx_info_t content_info;
+ menu_ctx_list_t list_info;
content_info.argc = 0;
content_info.argv = NULL;
content_info.args = NULL;
content_info.environ_get = NULL;
+ /* We are going to push a new menu; ensure
+ * that the current one is cached for animation
+ * purposes */
+ list_info.type = MENU_LIST_PLAIN;
+ list_info.action = 0;
+ menu_driver_list_cache(&list_info);
+
path_clear(RARCH_PATH_BASENAME);
if (!task_push_start_current_core(&content_info))
return -1;
@@ -6683,6 +6717,54 @@ static int action_ok_start_core(const char *path,
return 0;
}
+static int action_ok_contentless_core_run(const char *path,
+ const char *label, unsigned type, size_t idx, size_t entry_idx)
+{
+ const char *core_path = path;
+ /* TODO/FIXME: If this function succeeds, the
+ * quick menu will be pushed on the subsequent
+ * frame via the RARCH_MENU_CTL_SET_PENDING_QUICK_MENU
+ * command. The way this is implemented 'breaks' the
+ * menu stack record, so when leaving the quick
+ * menu via a 'cancel' operation, the last selected
+ * menu index is lost. We therefore have to cache
+ * the current selection here, and reapply it manually
+ * when building the contentless cores list... */
+ size_t selection = menu_navigation_get_selection();
+
+ if (string_is_empty(core_path))
+ return menu_cbs_exit();
+
+ /* If core is already running, open quick menu */
+ if (retroarch_ctl(RARCH_CTL_IS_CORE_LOADED, (void*)core_path) &&
+ retroarch_ctl(RARCH_CTL_CORE_IS_RUNNING, NULL))
+ {
+ bool flush_menu = false;
+ menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, &flush_menu);
+ menu_state_get_ptr()->contentless_core_ptr = selection;
+ menu_navigation_set_selection(0);
+ return 0;
+ }
+
+ /* Cache current menu selection *before* attempting
+ * to start the core, to ensure consistent menu
+ * navigation (i.e. running a core will in general
+ * cause a redraw of the menu, so must record current
+ * position even if the operation fails) */
+ menu_state_get_ptr()->contentless_core_ptr = selection;
+
+ /* Load and start core */
+ path_clear(RARCH_PATH_BASENAME);
+ if (!task_push_load_contentless_core_from_menu(core_path))
+ {
+ if (retroarch_ctl(RARCH_CTL_IS_CORE_LOADED, (void*)core_path))
+ generic_action_ok_command(CMD_EVENT_UNLOAD_CORE);
+ return -1;
+ }
+
+ return 0;
+}
+
static int action_ok_load_archive(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
@@ -7384,6 +7466,9 @@ static int action_ok_core_delete(const char *path,
/* Reload core info files */
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
+ /* Force reload of contentless cores icons */
+ menu_contentless_cores_free();
+
/* Return to higher level menu */
return action_cancel_pop_default(NULL, NULL, 0, 0);
}
@@ -7787,6 +7872,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
{MENU_ENUM_LABEL_GOTO_IMAGES, action_ok_goto_images},
{MENU_ENUM_LABEL_GOTO_VIDEO, action_ok_goto_video},
{MENU_ENUM_LABEL_GOTO_EXPLORE, action_ok_goto_explore},
+ {MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES, action_ok_goto_contentless_cores},
{MENU_ENUM_LABEL_BROWSE_START, action_ok_browse_url_start},
{MENU_ENUM_LABEL_FILE_BROWSER_CORE, action_ok_load_core},
{MENU_ENUM_LABEL_FILE_BROWSER_CORE_SELECT_FROM_COLLECTION, action_ok_core_deferred_set},
@@ -8020,6 +8106,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
{MENU_ENUM_LABEL_PLAYLIST_MANAGER_DEFAULT_CORE, action_ok_playlist_default_core},
{MENU_ENUM_LABEL_CORE_MANAGER_LIST, action_ok_push_core_manager_list},
{MENU_ENUM_LABEL_EXPLORE_TAB, action_ok_push_default},
+ {MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB, action_ok_push_default},
};
for (i = 0; i < ARRAY_SIZE(ok_list); i++)
@@ -8652,6 +8739,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_ACTION_AUDIO_DSP_PLUGIN_REMOVE:
BIND_ACTION_OK(cbs, action_ok_audio_dsp_plugin_remove);
break;
+ case MENU_SETTING_ACTION_CONTENTLESS_CORE_RUN:
+ BIND_ACTION_OK(cbs, action_ok_contentless_core_run);
+ break;
default:
return -1;
}
diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c
index c3ff3c7414..9da4edf83d 100644
--- a/menu/cbs/menu_cbs_right.c
+++ b/menu/cbs/menu_cbs_right.c
@@ -1126,6 +1126,7 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_SUBSYSTEM_LOAD:
case MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM:
case MENU_ENUM_LABEL_EXPLORE_ITEM:
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
case MENU_ENUM_LABEL_NO_SETTINGS_FOUND:
BIND_ACTION_RIGHT(cbs, action_right_mainmenu);
break;
@@ -1163,6 +1164,7 @@ static int menu_cbs_init_bind_right_compare_label(menu_file_list_cbs_t *cbs,
break;
case MENU_ENUM_LABEL_NO_ITEMS:
case MENU_ENUM_LABEL_NO_PLAYLIST_ENTRIES_AVAILABLE:
+ case MENU_ENUM_LABEL_NO_CORES_AVAILABLE:
case MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST:
if (
string_ends_with_size(menu_label, "_tab",
diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c
index e4e32cbd22..5e2a787f4a 100644
--- a/menu/cbs/menu_cbs_sublabel.c
+++ b/menu/cbs/menu_cbs_sublabel.c
@@ -693,6 +693,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_goto_images,
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_goto_music, MENU_ENUM_SUBLABEL_GOTO_MUSIC)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_goto_video, MENU_ENUM_SUBLABEL_GOTO_VIDEO)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_goto_explore, MENU_ENUM_SUBLABEL_GOTO_EXPLORE)
+DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_goto_contentless_cores, MENU_ENUM_SUBLABEL_GOTO_CONTENTLESS_CORES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_filebrowser_settings, MENU_ENUM_SUBLABEL_MENU_FILE_BROWSER_SETTINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_filebrowser_open_uwp_permissions, MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_UWP_PERMISSIONS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_filebrowser_open_picker, MENU_ENUM_SUBLABEL_FILE_BROWSER_OPEN_PICKER)
@@ -855,6 +856,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_import_content_tab,
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_import_content_entry, MENU_ENUM_SUBLABEL_CONTENT_SHOW_ADD_ENTRY)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_playlist_tabs, MENU_ENUM_SUBLABEL_CONTENT_SHOW_PLAYLISTS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_explore_tab, MENU_ENUM_SUBLABEL_CONTENT_SHOW_EXPLORE)
+DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_contentless_cores_tab, MENU_ENUM_SUBLABEL_CONTENT_SHOW_CONTENTLESS_CORES)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_main_menu_enable_settings, MENU_ENUM_SUBLABEL_XMB_MAIN_MENU_ENABLE_SETTINGS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_rgui_show_start_screen, MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_menu_header_opacity, MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY)
@@ -1968,6 +1970,7 @@ 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;
#ifdef HAVE_NETWORKING
@@ -2297,6 +2300,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_CONTENT_SHOW_EXPLORE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_explore_tab);
break;
+ case MENU_ENUM_LABEL_CONTENT_SHOW_CONTENTLESS_CORES:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_menu_contentless_cores_tab);
+ break;
case MENU_ENUM_LABEL_XMB_MAIN_MENU_ENABLE_SETTINGS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_main_menu_enable_settings);
break;
@@ -2321,6 +2327,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_GOTO_EXPLORE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_goto_explore);
break;
+ case MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES:
+ BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_goto_contentless_cores);
+ break;
case MENU_ENUM_LABEL_GOTO_FAVORITES:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_goto_favorites);
break;
diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c
index 1d28f891df..9222ab8c13 100644
--- a/menu/cbs/menu_cbs_title.c
+++ b/menu/cbs/menu_cbs_title.c
@@ -754,6 +754,8 @@ DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_title_deferred_favorites_list, MENU
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_title_deferred_images_list, MENU_ENUM_LABEL_VALUE_GOTO_IMAGES)
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_title_deferred_music_list, MENU_ENUM_LABEL_VALUE_GOTO_MUSIC)
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_title_deferred_video_list, MENU_ENUM_LABEL_VALUE_GOTO_VIDEO)
+DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_title_deferred_contentless_cores_list, MENU_ENUM_LABEL_VALUE_GOTO_CONTENTLESS_CORES)
+
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_core_updater_list, MENU_ENUM_LABEL_VALUE_CORE_UPDATER_LIST)
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_core_manager_list, MENU_ENUM_LABEL_VALUE_CORE_MANAGER_LIST)
DEFAULT_TITLE_SEARCH_FILTER_MACRO(action_get_core_cheat_options_list, MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS)
@@ -853,18 +855,19 @@ static int action_get_title_group_settings(const char *path, const char *label,
* tab, but its actual title is set elsewhere - so treat
* it as a generic top-level item */
title_info_list_t info_list[] = {
- {MENU_ENUM_LABEL_MAIN_MENU, MENU_ENUM_LABEL_VALUE_MAIN_MENU, false },
- {MENU_ENUM_LABEL_HISTORY_TAB, MENU_ENUM_LABEL_VALUE_HISTORY_TAB, true },
- {MENU_ENUM_LABEL_FAVORITES_TAB, MENU_ENUM_LABEL_VALUE_FAVORITES_TAB, true },
- {MENU_ENUM_LABEL_IMAGES_TAB, MENU_ENUM_LABEL_VALUE_IMAGES_TAB, true },
- {MENU_ENUM_LABEL_MUSIC_TAB, MENU_ENUM_LABEL_VALUE_MUSIC_TAB, true },
- {MENU_ENUM_LABEL_VIDEO_TAB, MENU_ENUM_LABEL_VALUE_VIDEO_TAB, true },
- {MENU_ENUM_LABEL_SETTINGS_TAB, MENU_ENUM_LABEL_VALUE_SETTINGS_TAB, false },
- {MENU_ENUM_LABEL_PLAYLISTS_TAB, MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB, false },
- {MENU_ENUM_LABEL_ADD_TAB, MENU_ENUM_LABEL_VALUE_ADD_TAB, false },
- {MENU_ENUM_LABEL_EXPLORE_TAB, MENU_ENUM_LABEL_VALUE_EXPLORE_TAB, false },
- {MENU_ENUM_LABEL_NETPLAY_TAB, MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, false },
- {MENU_ENUM_LABEL_HORIZONTAL_MENU, MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU, false },
+ {MENU_ENUM_LABEL_MAIN_MENU, MENU_ENUM_LABEL_VALUE_MAIN_MENU, false },
+ {MENU_ENUM_LABEL_HISTORY_TAB, MENU_ENUM_LABEL_VALUE_HISTORY_TAB, true },
+ {MENU_ENUM_LABEL_FAVORITES_TAB, MENU_ENUM_LABEL_VALUE_FAVORITES_TAB, true },
+ {MENU_ENUM_LABEL_IMAGES_TAB, MENU_ENUM_LABEL_VALUE_IMAGES_TAB, true },
+ {MENU_ENUM_LABEL_MUSIC_TAB, MENU_ENUM_LABEL_VALUE_MUSIC_TAB, true },
+ {MENU_ENUM_LABEL_VIDEO_TAB, MENU_ENUM_LABEL_VALUE_VIDEO_TAB, true },
+ {MENU_ENUM_LABEL_SETTINGS_TAB, MENU_ENUM_LABEL_VALUE_SETTINGS_TAB, false },
+ {MENU_ENUM_LABEL_PLAYLISTS_TAB, MENU_ENUM_LABEL_VALUE_PLAYLISTS_TAB, false },
+ {MENU_ENUM_LABEL_ADD_TAB, MENU_ENUM_LABEL_VALUE_ADD_TAB, false },
+ {MENU_ENUM_LABEL_EXPLORE_TAB, MENU_ENUM_LABEL_VALUE_EXPLORE_TAB, false },
+ {MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB, MENU_ENUM_LABEL_VALUE_CONTENTLESS_CORES_TAB, false },
+ {MENU_ENUM_LABEL_NETPLAY_TAB, MENU_ENUM_LABEL_VALUE_NETPLAY_TAB, false },
+ {MENU_ENUM_LABEL_HORIZONTAL_MENU, MENU_ENUM_LABEL_VALUE_HORIZONTAL_MENU, false },
};
for (i = 0; i < ARRAY_SIZE(info_list); i++)
@@ -989,6 +992,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
{MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST, action_get_title_deferred_images_list},
{MENU_ENUM_LABEL_DEFERRED_MUSIC_LIST, action_get_title_deferred_music_list},
{MENU_ENUM_LABEL_DEFERRED_VIDEO_LIST, action_get_title_deferred_video_list},
+ {MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST, action_get_title_deferred_contentless_cores_list},
{MENU_ENUM_LABEL_DEFERRED_DRIVER_SETTINGS_LIST, action_get_driver_settings_list},
{MENU_ENUM_LABEL_DEFERRED_AUDIO_SETTINGS_LIST, action_get_audio_settings_list},
{MENU_ENUM_LABEL_DEFERRED_AUDIO_RESAMPLER_SETTINGS_LIST, action_get_audio_resampler_settings_list},
diff --git a/menu/drivers/materialui.c b/menu/drivers/materialui.c
index d17e4bcd2c..42c6fa79a3 100644
--- a/menu/drivers/materialui.c
+++ b/menu/drivers/materialui.c
@@ -1303,7 +1303,8 @@ enum materialui_node_icon_type
MUI_ICON_TYPE_NONE = 0,
MUI_ICON_TYPE_INTERNAL,
MUI_ICON_TYPE_MENU_EXPLORE,
- MUI_ICON_TYPE_PLAYLIST
+ MUI_ICON_TYPE_PLAYLIST,
+ MUI_ICON_TYPE_MENU_CONTENTLESS_CORE
};
/* This structure holds auxiliary information for
@@ -2797,6 +2798,7 @@ static void materialui_compute_entries_box_default(
has_icon = mui->textures.list[node->icon_texture_index] != 0;
break;
case MUI_ICON_TYPE_MENU_EXPLORE:
+ case MUI_ICON_TYPE_MENU_CONTENTLESS_CORE:
has_icon = true;
break;
case MUI_ICON_TYPE_PLAYLIST:
@@ -3996,6 +3998,9 @@ static void materialui_render_menu_entry_default(
case MUI_ICON_TYPE_MENU_EXPLORE:
icon_texture = menu_explore_get_entry_icon(entry_type);
break;
+ case MUI_ICON_TYPE_MENU_CONTENTLESS_CORE:
+ icon_texture = menu_contentless_cores_get_entry_icon(entry->label);
+ break;
case MUI_ICON_TYPE_PLAYLIST:
icon_texture = materialui_get_playlist_icon(
mui, node->icon_texture_index);
@@ -10105,6 +10110,7 @@ static void materialui_list_insert(
case MENU_SETTING_ACTION_CORE_MANAGER_OPTIONS:
case MENU_SETTING_ACTION_CORE_LOCK:
case MENU_EXPLORE_TAB:
+ case MENU_CONTENTLESS_CORES_TAB:
node->icon_texture_index = MUI_TEXTURE_CORES;
node->icon_type = MUI_ICON_TYPE_INTERNAL;
break;
@@ -10189,6 +10195,9 @@ static void materialui_list_insert(
node->icon_texture_index = MUI_TEXTURE_FILE;
node->icon_type = MUI_ICON_TYPE_INTERNAL;
break;
+ case MENU_SETTING_ACTION_CONTENTLESS_CORE_RUN:
+ node->icon_type = MUI_ICON_TYPE_MENU_CONTENTLESS_CORE;
+ break;
case FILE_TYPE_RPL_ENTRY:
case MENU_SETTING_DROPDOWN_ITEM:
case MENU_SETTING_DROPDOWN_ITEM_RESOLUTION:
diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c
index 7869e8e2ce..69a10f4e6f 100644
--- a/menu/drivers/ozone.c
+++ b/menu/drivers/ozone.c
@@ -144,6 +144,7 @@ enum
#if defined(HAVE_LIBRETRODB)
OZONE_SYSTEM_TAB_EXPLORE,
#endif
+ OZONE_SYSTEM_TAB_CONTENTLESS_CORES,
/* End of this enum - use the last one to determine num of possible tabs */
OZONE_SYSTEM_TAB_LAST
@@ -181,6 +182,8 @@ enum OZONE_TAB_TEXTURES
OZONE_TAB_TEXTURE_IMAGE,
OZONE_TAB_TEXTURE_NETWORK,
OZONE_TAB_TEXTURE_SCAN_CONTENT,
+ OZONE_TAB_TEXTURE_EXPLORE,
+ OZONE_TAB_TEXTURE_CONTENTLESS_CORES,
OZONE_TAB_TEXTURE_LAST
};
@@ -570,6 +573,7 @@ struct ozone_handle
bool is_db_manager_list;
bool is_file_list;
bool is_quick_menu;
+ bool is_contentless_cores;
bool first_frame;
struct
@@ -604,15 +608,17 @@ static const char *OZONE_TEXTURES_FILES[OZONE_TEXTURE_LAST] = {
};
static const char *OZONE_TAB_TEXTURES_FILES[OZONE_TAB_TEXTURE_LAST] = {
- "retroarch",
- "settings",
- "history",
- "favorites",
- "music",
- "video",
- "image",
- "netplay",
- "add"
+ "retroarch", /* MAIN_MENU */
+ "settings", /* SETTINGS_TAB */
+ "history", /* HISTORY_TAB */
+ "favorites", /* FAVORITES_TAB */
+ "music", /* MUSIC_TAB */
+ "video", /* VIDEO_TAB */
+ "image", /* IMAGES_TAB */
+ "netplay", /* NETPLAY_TAB */
+ "add", /* ADD_TAB */
+ "retroarch", /* EXPLORE_TAB */
+ "retroarch" /* CONTENTLESS_CORES_TAB */
};
static const enum msg_hash_enums ozone_system_tabs_value[OZONE_SYSTEM_TAB_LAST] = {
@@ -630,12 +636,11 @@ static const enum msg_hash_enums ozone_system_tabs_value[OZONE_SYSTEM_TAB_LAST]
#ifdef HAVE_NETWORKING
MENU_ENUM_LABEL_VALUE_NETPLAY_TAB,
#endif
-#ifdef HAVE_LIBRETRODB
MENU_ENUM_LABEL_VALUE_ADD_TAB,
- MENU_ENUM_LABEL_VALUE_EXPLORE_TAB
-#else
- MENU_ENUM_LABEL_VALUE_ADD_TAB
+#ifdef HAVE_LIBRETRODB
+ MENU_ENUM_LABEL_VALUE_EXPLORE_TAB,
#endif
+ MENU_ENUM_LABEL_VALUE_CONTENTLESS_CORES_TAB
};
static const enum menu_settings_type ozone_system_tabs_type[OZONE_SYSTEM_TAB_LAST] = {
@@ -653,12 +658,11 @@ static const enum menu_settings_type ozone_system_tabs_type[OZONE_SYSTEM_TAB_LAS
#ifdef HAVE_NETWORKING
MENU_NETPLAY_TAB,
#endif
-#ifdef HAVE_LIBRETRODB
MENU_ADD_TAB,
- MENU_EXPLORE_TAB
-#else
- MENU_ADD_TAB
+#ifdef HAVE_LIBRETRODB
+ MENU_EXPLORE_TAB,
#endif
+ MENU_CONTENTLESS_CORES_TAB
};
static const enum msg_hash_enums ozone_system_tabs_idx[OZONE_SYSTEM_TAB_LAST] = {
@@ -676,12 +680,11 @@ static const enum msg_hash_enums ozone_system_tabs_idx[OZONE_SYSTEM_TAB_LAST] =
#ifdef HAVE_NETWORKING
MENU_ENUM_LABEL_NETPLAY_TAB,
#endif
-#ifdef HAVE_LIBRETRODB
MENU_ENUM_LABEL_ADD_TAB,
- MENU_ENUM_LABEL_EXPLORE_TAB
-#else
- MENU_ENUM_LABEL_ADD_TAB
+#ifdef HAVE_LIBRETRODB
+ MENU_ENUM_LABEL_EXPLORE_TAB,
#endif
+ MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB
};
static const unsigned ozone_system_tabs_icons[OZONE_SYSTEM_TAB_LAST] = {
@@ -699,7 +702,11 @@ static const unsigned ozone_system_tabs_icons[OZONE_SYSTEM_TAB_LAST] = {
#ifdef HAVE_NETWORKING
OZONE_TAB_TEXTURE_NETWORK,
#endif
- OZONE_TAB_TEXTURE_SCAN_CONTENT
+ OZONE_TAB_TEXTURE_SCAN_CONTENT,
+#ifdef HAVE_LIBRETRODB
+ OZONE_TAB_TEXTURE_EXPLORE,
+#endif
+ OZONE_TAB_TEXTURE_CONTENTLESS_CORES
};
static const char *OZONE_THEME_TEXTURES_FILES[OZONE_THEME_TEXTURE_LAST] = {
@@ -1597,7 +1604,7 @@ static void ozone_set_background_running_opacity(
static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
enum msg_hash_enums enum_idx, const char *enum_path,
- unsigned type, bool active)
+ const char *enum_label, unsigned type, bool active)
{
switch (enum_idx)
{
@@ -1675,6 +1682,8 @@ static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_MUSIC];
case MENU_ENUM_LABEL_GOTO_EXPLORE:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
+ case MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES:
+ return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
/* Menu icons */
case MENU_ENUM_LABEL_CONTENT_SETTINGS:
@@ -1735,6 +1744,8 @@ static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
case MENU_ENUM_LABEL_UPDATE_DATABASES:
case MENU_ENUM_LABEL_DATABASE_MANAGER_LIST:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_RDB];
+ case MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB:
+ return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
case MENU_ENUM_LABEL_CURSOR_MANAGER_LIST:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CURSOR];
case MENU_ENUM_LABEL_HELP_LIST:
@@ -1930,6 +1941,12 @@ static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
break;
}
#endif
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
+ {
+ uintptr_t icon = menu_contentless_cores_get_entry_icon(enum_label);
+ if (icon) return icon;
+ break;
+ }
default:
break;
}
@@ -3288,6 +3305,7 @@ static bool ozone_is_playlist(ozone_handle_t *ozone, bool depth)
#ifdef HAVE_LIBRETRODB
case OZONE_SYSTEM_TAB_EXPLORE:
#endif
+ case OZONE_SYSTEM_TAB_CONTENTLESS_CORES:
is_playlist = false;
break;
case OZONE_SYSTEM_TAB_HISTORY:
@@ -4490,7 +4508,7 @@ static void ozone_compute_entries_position(
if (ozone->is_playlist && entries_end == 1)
{
uintptr_t tex = ozone_entries_icon_get_texture(ozone,
- entry.enum_idx, entry.path, entry.type, false);
+ entry.enum_idx, entry.path, entry.label, entry.type, false);
ozone->empty_playlist = tex == ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE_INFO];
}
else
@@ -4783,7 +4801,7 @@ border_iterate:
MENU_ENTRY_INIT(entry);
entry.path_enabled = false;
- entry.label_enabled = false;
+ entry.label_enabled = ozone->is_contentless_cores;
menu_entry_get(&entry, 0, (unsigned)i, selection_buf, true);
if (entry.enum_idx == MENU_ENUM_LABEL_CHEEVOS_PASSWORD)
@@ -4856,7 +4874,7 @@ border_iterate:
/* Icon */
tex = ozone_entries_icon_get_texture(ozone,
- entry.enum_idx, entry.path, entry.type, entry_selected);
+ entry.enum_idx, entry.path, entry.label, entry.type, entry_selected);
if (tex != ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_SUBSETTING])
{
uintptr_t texture = tex;
@@ -7150,6 +7168,12 @@ static void *ozone_init(void **userdata, bool video_is_threaded)
ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_EXPLORE;
#endif
+#if defined(HAVE_DYNAMIC)
+ if (settings->uints.menu_content_show_contentless_cores !=
+ MENU_CONTENTLESS_CORES_DISPLAY_NONE)
+ ozone->tabs[++ozone->system_tab_end] = OZONE_SYSTEM_TAB_CONTENTLESS_CORES;
+#endif
+
menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
gfx_display_set_width(width);
@@ -9844,14 +9868,16 @@ static void ozone_populate_entries(void *data,
new_depth = (int)ozone_list_get_size(ozone, MENU_LIST_PLAIN);
- animate = new_depth != ozone->depth;
- ozone->fade_direction = new_depth <= ozone->depth;
- ozone->depth = new_depth;
- ozone->is_playlist = ozone_is_playlist(ozone, true);
- ozone->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST));
- ozone->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES));
- ozone->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) ||
- string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS));
+ animate = new_depth != ozone->depth;
+ ozone->fade_direction = new_depth <= ozone->depth;
+ ozone->depth = new_depth;
+ ozone->is_playlist = ozone_is_playlist(ozone, true);
+ ozone->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST));
+ ozone->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES));
+ ozone->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) ||
+ string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS));
+ ozone->is_contentless_cores = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)) ||
+ string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST));
if (animate)
if (ozone->categories_selection_ptr == ozone->categories_active_idx_old)
diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c
index b7f5badcb5..19bacc060a 100644
--- a/menu/drivers/xmb.c
+++ b/menu/drivers/xmb.c
@@ -248,6 +248,7 @@ enum
#if defined(HAVE_LIBRETRODB)
XMB_SYSTEM_TAB_EXPLORE,
#endif
+ XMB_SYSTEM_TAB_CONTENTLESS_CORES,
/* End of this enum - use the last one to determine num of possible tabs */
XMB_SYSTEM_TAB_MAX_LENGTH
@@ -294,6 +295,7 @@ typedef struct xmb_handle
#if defined(HAVE_LIBRETRODB)
xmb_node_t explore_tab_node;
#endif
+ xmb_node_t contentless_cores_tab_node;
xmb_node_t netplay_tab_node;
menu_input_pointer_t pointer;
@@ -399,6 +401,7 @@ typedef struct xmb_handle
/* Favorites, History, Images, Music, Videos, user generated */
bool is_playlist;
bool is_db_manager_list;
+ bool is_contentless_cores;
/* Load Content file browser */
bool is_file_list;
@@ -1978,6 +1981,8 @@ static xmb_node_t* xmb_get_node(xmb_handle_t *xmb, unsigned i)
case XMB_SYSTEM_TAB_EXPLORE:
return &xmb->explore_tab_node;
#endif
+ case XMB_SYSTEM_TAB_CONTENTLESS_CORES:
+ return &xmb->contentless_cores_tab_node;
default:
if (i > xmb->system_tab_end)
return xmb_get_userdata_from_horizontal_list(
@@ -2472,6 +2477,10 @@ static void xmb_populate_entries(void *data,
/* Determine whether this is a database manager list */
xmb->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST));
+ /* Determine whether this is the contentless cores menu */
+ xmb->is_contentless_cores = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)) ||
+ string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST));
+
/* Determine whether this is a 'file list'
* (needed for handling thumbnails when viewing images
* via 'load content')
@@ -2539,7 +2548,8 @@ static void xmb_populate_entries(void *data,
static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
xmb_node_t *core_node, xmb_node_t *node,
enum msg_hash_enums enum_idx, const char *enum_path,
- unsigned type, bool active, bool checked)
+ const char *enum_label, unsigned type, bool active,
+ bool checked)
{
switch (enum_idx)
{
@@ -2635,7 +2645,8 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
return xmb->textures.list[XMB_TEXTURE_MUSIC];
case MENU_ENUM_LABEL_GOTO_EXPLORE:
return xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
-
+ case MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES:
+ return xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
case MENU_ENUM_LABEL_LOAD_DISC:
case MENU_ENUM_LABEL_DUMP_DISC:
#ifdef HAVE_LAKKA
@@ -2865,6 +2876,13 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
break;
}
#endif
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
+ {
+ uintptr_t icon = menu_contentless_cores_get_entry_icon(enum_label);
+ if (icon)
+ return icon;
+ break;
+ }
default:
break;
}
@@ -3224,7 +3242,7 @@ static int xmb_draw_item(
return 0;
MENU_ENTRY_INIT(entry);
- entry.label_enabled = false;
+ entry.label_enabled = xmb->is_contentless_cores;
entry.sublabel_enabled = (i == current);
menu_entry_get(&entry, 0, i, list, true);
entry_type = entry.type;
@@ -3582,7 +3600,8 @@ static int xmb_draw_item(
math_matrix_4x4 mymat_tmp;
gfx_display_ctx_rotate_draw_t rotate_draw;
uintptr_t texture = xmb_icon_get_id(xmb, core_node, node,
- entry.enum_idx, entry.path, entry_type, (i == current), entry.checked);
+ entry.enum_idx, entry.path, entry.label,
+ entry_type, (i == current), entry.checked);
float x = icon_x;
float y = icon_y;
float scale_factor = node->zoom;
@@ -5933,6 +5952,12 @@ static void *xmb_init(void **userdata, bool video_is_threaded)
xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_EXPLORE;
#endif
+#if defined(HAVE_DYNAMIC)
+ if (settings->uints.menu_content_show_contentless_cores !=
+ MENU_CONTENTLESS_CORES_DISPLAY_NONE)
+ xmb->tabs[++xmb->system_tab_end] = XMB_SYSTEM_TAB_CONTENTLESS_CORES;
+#endif
+
menu_driver_ctl(RARCH_MENU_CTL_UNSET_PREVENT_POPULATE, NULL);
/* TODO/FIXME - we don't use framebuffer at all
@@ -6410,6 +6435,10 @@ static void xmb_context_reset_textures(
xmb->explore_tab_node.zoom = xmb->categories_active_zoom;
#endif
+ xmb->contentless_cores_tab_node.icon = xmb->textures.list[XMB_TEXTURE_MAIN_MENU];
+ xmb->contentless_cores_tab_node.alpha = xmb->categories_active_alpha;
+ xmb->contentless_cores_tab_node.zoom = xmb->categories_active_zoom;
+
#ifdef HAVE_NETWORKING
xmb->netplay_tab_node.icon = xmb->textures.list[XMB_TEXTURE_NETPLAY];
xmb->netplay_tab_node.alpha = xmb->categories_active_alpha;
@@ -6871,6 +6900,12 @@ static void xmb_list_cache(void *data, enum menu_list_type type, unsigned action
MENU_EXPLORE_TAB;
break;
#endif
+ case XMB_SYSTEM_TAB_CONTENTLESS_CORES:
+ menu_stack->list[stack_size - 1].label =
+ strdup(msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB));
+ menu_stack->list[stack_size - 1].type =
+ MENU_CONTENTLESS_CORES_TAB;
+ break;
default:
menu_stack->list[stack_size - 1].label =
strdup(msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU));
diff --git a/menu/menu_cbs.h b/menu/menu_cbs.h
index b567a3f678..735cd06a6f 100644
--- a/menu/menu_cbs.h
+++ b/menu/menu_cbs.h
@@ -77,6 +77,7 @@ enum
ACTION_OK_DL_IMAGES_LIST,
ACTION_OK_DL_VIDEO_LIST,
ACTION_OK_DL_EXPLORE_LIST,
+ ACTION_OK_DL_CONTENTLESS_CORES_LIST,
ACTION_OK_DL_MUSIC_LIST,
ACTION_OK_DL_SHADER_PARAMETERS,
ACTION_OK_DL_SHADER_PRESET,
diff --git a/menu/menu_contentless_cores.c b/menu/menu_contentless_cores.c
new file mode 100644
index 0000000000..b170ac2933
--- /dev/null
+++ b/menu/menu_contentless_cores.c
@@ -0,0 +1,302 @@
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2011-2020 - Daniel De Matteis
+ * Copyright (C) 2019-2022 - James Leaver
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "menu_driver.h"
+#include "menu_displaylist.h"
+#include "../retroarch.h"
+#include "../core_info.h"
+#include "../configuration.h"
+
+#define CONTENTLESS_CORE_ICON_DEFAULT "default.png"
+
+typedef struct
+{
+ uintptr_t **system;
+ uintptr_t fallback;
+} contentless_core_icons_t;
+
+typedef struct
+{
+ 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_unload_icons(contentless_cores_state_t *state)
+{
+ size_t i, cap;
+
+ if (!state || !state->icons)
+ return;
+
+ if (state->icons->fallback)
+ video_driver_texture_unload(&state->icons->fallback);
+
+ for (i = 0, cap = RHMAP_CAP(state->icons->system); i != cap; i++)
+ {
+ if (RHMAP_KEY(state->icons->system, i))
+ {
+ uintptr_t *icon = state->icons->system[i];
+
+ if (!icon)
+ continue;
+
+ video_driver_texture_unload(icon);
+ free(icon);
+ }
+ }
+
+ RHMAP_FREE(state->icons->system);
+ free(state->icons);
+ state->icons = NULL;
+}
+
+static void contentless_cores_load_icons(contentless_cores_state_t *state)
+{
+ bool rgba_supported = video_driver_supports_rgba();
+ core_info_list_t *core_info_list = NULL;
+ char icon_directory[PATH_MAX_LENGTH];
+ char icon_path[PATH_MAX_LENGTH];
+ size_t i;
+
+ icon_directory[0] = '\0';
+ icon_path[0] = '\0';
+
+ if (!state)
+ return;
+
+ /* Unload any existing icons */
+ contentless_cores_unload_icons(state);
+
+ if (!state->icons_enabled)
+ return;
+
+ /* Create new icon container */
+ state->icons = (contentless_core_icons_t*)calloc(
+ 1, sizeof(*state->icons));
+
+ /* Get icon directory */
+ fill_pathname_application_special(icon_directory,
+ sizeof(icon_directory),
+ APPLICATION_SPECIAL_DIRECTORY_ASSETS_SYSICONS);
+
+ if (string_is_empty(icon_directory))
+ return;
+
+ /* Load fallback icon */
+ fill_pathname_join(icon_path, icon_directory,
+ CONTENTLESS_CORE_ICON_DEFAULT, sizeof(icon_path));
+
+ if (path_is_valid(icon_path))
+ {
+ struct texture_image ti = {0};
+ ti.supports_rgba = rgba_supported;
+
+ if (image_texture_load(&ti, icon_path))
+ {
+ if (ti.pixels)
+ video_driver_texture_load(&ti,
+ TEXTURE_FILTER_MIPMAP_LINEAR,
+ &state->icons->fallback);
+
+ image_texture_free(&ti);
+ }
+ }
+
+ /* Get icons for all contentless cores */
+ 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);
+
+ /* Icon name is the first entry in the core
+ * info database list */
+ if (core_info &&
+ core_info->supports_no_game &&
+ core_info->databases_list &&
+ (core_info->databases_list->size > 0))
+ {
+ const char *icon_name =
+ core_info->databases_list->elems[0].data;
+ struct texture_image ti = {0};
+ ti.supports_rgba = rgba_supported;
+
+ fill_pathname_join(icon_path, icon_directory,
+ icon_name, sizeof(icon_path));
+ strlcat(icon_path, ".png", sizeof(icon_path));
+
+ if (!path_is_valid(icon_path))
+ continue;
+
+ if (image_texture_load(&ti, icon_path))
+ {
+ if (ti.pixels)
+ {
+ uintptr_t *icon = (uintptr_t*)calloc(1, sizeof(*icon));
+
+ video_driver_texture_load(&ti,
+ TEXTURE_FILTER_MIPMAP_LINEAR,
+ icon);
+
+ /* Add icon to hash map */
+ RHMAP_SET_STR(state->icons->system, core_info->core_file_id.str, icon);
+ }
+
+ image_texture_free(&ti);
+ }
+ }
+ }
+}
+
+uintptr_t menu_contentless_cores_get_entry_icon(const char *core_id)
+{
+ contentless_cores_state_t *state = contentless_cores_state;
+ uintptr_t *icon = NULL;
+
+ if (!state ||
+ !state->icons_enabled ||
+ !state->icons ||
+ string_is_empty(core_id))
+ return 0;
+
+ icon = RHMAP_GET_STR(state->icons->system, core_id);
+
+ if (icon)
+ return *icon;
+
+ return state->icons->fallback;
+}
+
+void menu_contentless_cores_context_init(void)
+{
+ if (!contentless_cores_state)
+ return;
+
+ contentless_cores_load_icons(contentless_cores_state);
+}
+
+void menu_contentless_cores_context_deinit(void)
+{
+ if (!contentless_cores_state)
+ return;
+
+ contentless_cores_unload_icons(contentless_cores_state);
+}
+
+void menu_contentless_cores_free(void)
+{
+ if (!contentless_cores_state)
+ return;
+
+ contentless_cores_unload_icons(contentless_cores_state);
+ free(contentless_cores_state);
+ contentless_cores_state = NULL;
+}
+
+unsigned menu_displaylist_contentless_cores(file_list_t *list, settings_t *settings)
+{
+ unsigned count = 0;
+ enum menu_contentless_cores_display_type
+ core_display_type = (enum menu_contentless_cores_display_type)
+ settings->uints.menu_content_show_contentless_cores;
+ core_info_list_t *core_info_list = NULL;
+
+ /* Get core list */
+ core_info_get_list(&core_info_list);
+
+ if (core_info_list)
+ {
+ size_t menu_index = 0;
+ size_t i;
+
+ /* Sort cores alphabetically */
+ core_info_qsort(core_info_list, CORE_INFO_LIST_SORT_DISPLAY_NAME);
+
+ /* Loop through cores */
+ for (i = 0; i < core_info_list->count; i++)
+ {
+ core_info_t *core_info = core_info_get(core_info_list, i);
+ bool core_valid = false;
+
+ if (core_info)
+ {
+ switch (core_display_type)
+ {
+ case MENU_CONTENTLESS_CORES_DISPLAY_ALL:
+ core_valid = core_info->supports_no_game;
+ break;
+ case MENU_CONTENTLESS_CORES_DISPLAY_SINGLE_PURPOSE:
+ core_valid = core_info->supports_no_game &&
+ core_info->single_purpose;
+ break;
+ default:
+ break;
+ }
+
+ if (core_valid &&
+ menu_entries_append_enum(list,
+ core_info->path,
+ core_info->core_file_id.str,
+ MENU_ENUM_LABEL_CONTENTLESS_CORE,
+ MENU_SETTING_ACTION_CONTENTLESS_CORE_RUN,
+ 0, 0))
+ {
+ file_list_set_alt_at_offset(
+ list, menu_index, core_info->display_name);
+
+ menu_index++;
+ count++;
+ }
+ }
+ }
+ }
+
+ /* Initialise icons, if required */
+ if (!contentless_cores_state && (count > 0))
+ {
+ contentless_cores_state = (contentless_cores_state_t*)calloc(1,
+ sizeof(*contentless_cores_state));
+
+ /* Disable icons when using menu drivers without
+ * icon support */
+ contentless_cores_state->icons_enabled =
+ !string_is_equal(menu_driver_ident(), "rgui");
+
+ contentless_cores_load_icons(contentless_cores_state);
+ }
+
+ if ((count == 0) &&
+ menu_entries_append_enum(list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_CORES_AVAILABLE),
+ msg_hash_to_str(MENU_ENUM_LABEL_NO_CORES_AVAILABLE),
+ MENU_ENUM_LABEL_NO_CORES_AVAILABLE,
+ 0, 0, 0))
+ count++;
+
+ return count;
+}
diff --git a/menu/menu_defines.h b/menu/menu_defines.h
index 80ce9e7a21..ab44808b71 100644
--- a/menu/menu_defines.h
+++ b/menu/menu_defines.h
@@ -130,6 +130,16 @@ enum menu_add_content_entry_display_type
MENU_ADD_CONTENT_ENTRY_DISPLAY_LAST
};
+/* Specifies which type of core will be displayed
+ * in the 'contentless cores' menu */
+enum menu_contentless_cores_display_type
+{
+ MENU_CONTENTLESS_CORES_DISPLAY_NONE = 0,
+ MENU_CONTENTLESS_CORES_DISPLAY_ALL,
+ MENU_CONTENTLESS_CORES_DISPLAY_SINGLE_PURPOSE,
+ MENU_CONTENTLESS_CORES_DISPLAY_LAST
+};
+
enum rgui_color_theme
{
RGUI_THEME_CUSTOM = 0,
diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c
index 63cc6b2121..30c5ed56ce 100644
--- a/menu/menu_displaylist.c
+++ b/menu/menu_displaylist.c
@@ -3586,6 +3586,17 @@ static unsigned menu_displaylist_parse_playlists(
MENU_EXPLORE_TAB, 0, 0))
count++;
#endif
+
+#if defined(HAVE_DYNAMIC)
+ if (settings->uints.menu_content_show_contentless_cores !=
+ MENU_CONTENTLESS_CORES_DISPLAY_NONE)
+ if (menu_entries_append_enum(info->list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_CONTENTLESS_CORES),
+ msg_hash_to_str(MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES),
+ MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES,
+ MENU_CONTENTLESS_CORES_TAB, 0, 0))
+ count++;
+#endif
if (settings->bools.menu_content_show_favorites)
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES),
@@ -6321,6 +6332,17 @@ unsigned menu_displaylist_build_list(
MENU_EXPLORE_TAB, 0, 0))
count++;
#endif
+
+#if defined(HAVE_DYNAMIC)
+ if (settings->uints.menu_content_show_contentless_cores !=
+ MENU_CONTENTLESS_CORES_DISPLAY_NONE)
+ if (menu_entries_append_enum(list,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_CONTENTLESS_CORES),
+ msg_hash_to_str(MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES),
+ MENU_ENUM_LABEL_GOTO_CONTENTLESS_CORES,
+ MENU_CONTENTLESS_CORES_TAB, 0, 0))
+ count++;
+#endif
if (menu_content_show_favorites)
if (menu_entries_append_enum(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_GOTO_FAVORITES),
@@ -8268,6 +8290,9 @@ unsigned menu_displaylist_build_list(
{MENU_ENUM_LABEL_CONTENT_SHOW_SETTINGS, PARSE_ONLY_BOOL, true },
{MENU_ENUM_LABEL_CONTENT_SHOW_SETTINGS_PASSWORD, PARSE_ONLY_STRING, true},
{MENU_ENUM_LABEL_CONTENT_SHOW_EXPLORE, PARSE_ONLY_BOOL, true },
+#if defined(HAVE_DYNAMIC)
+ {MENU_ENUM_LABEL_CONTENT_SHOW_CONTENTLESS_CORES, PARSE_ONLY_UINT, true },
+#endif
{MENU_ENUM_LABEL_CONTENT_SHOW_FAVORITES, PARSE_ONLY_BOOL, true },
{MENU_ENUM_LABEL_CONTENT_SHOW_IMAGES, PARSE_ONLY_BOOL, true },
{MENU_ENUM_LABEL_CONTENT_SHOW_MUSIC, PARSE_ONLY_BOOL, true },
@@ -8288,6 +8313,9 @@ unsigned menu_displaylist_build_list(
for (i = 0; i < ARRAY_SIZE(build_list); i++)
{
+ if (!build_list[i].checked && !include_everything)
+ continue;
+
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
build_list[i].enum_idx, build_list[i].parse_type,
false) == 0)
@@ -11687,6 +11715,30 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
info->need_push = true;
}
break;
+ case DISPLAYLIST_CONTENTLESS_CORES:
+ {
+ size_t contentless_core_ptr =
+ menu_state_get_ptr()->contentless_core_ptr;
+
+ menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
+ count = menu_displaylist_contentless_cores(info->list, settings);
+
+ /* TODO/FIXME: Selecting an entry in the
+ * contentless cores list will cause the
+ * quick menu to be pushed on the subsequent
+ * frame via the RARCH_MENU_CTL_SET_PENDING_QUICK_MENU
+ * command. The way this is implemented 'breaks' the
+ * menu stack record, so when leaving the quick
+ * menu via a 'cancel' operation, the last selected
+ * menu index is lost. We therefore have to apply
+ * a cached index value after rebuilding the list... */
+ if (contentless_core_ptr < count)
+ menu_navigation_set_selection(contentless_core_ptr);
+
+ info->need_sort = false;
+ info->need_push = true;
+ }
+ break;
case DISPLAYLIST_CORE_OPTIONS:
{
/* Number of displayed options is dynamic. If user opens
diff --git a/menu/menu_displaylist.h b/menu/menu_displaylist.h
index f7013163a3..e9f2e49961 100644
--- a/menu/menu_displaylist.h
+++ b/menu/menu_displaylist.h
@@ -83,6 +83,7 @@ enum menu_displaylist_ctl_state
DISPLAYLIST_HORIZONTAL_CONTENT_ACTIONS,
DISPLAYLIST_HISTORY,
DISPLAYLIST_EXPLORE,
+ DISPLAYLIST_CONTENTLESS_CORES,
DISPLAYLIST_FAVORITES,
DISPLAYLIST_PLAYLIST,
DISPLAYLIST_VIDEO_HISTORY,
@@ -344,6 +345,7 @@ bool menu_displaylist_has_subsystems(void);
#if defined(HAVE_LIBRETRODB)
unsigned menu_displaylist_explore(file_list_t *list, settings_t *settings);
#endif
+unsigned menu_displaylist_contentless_cores(file_list_t *list, settings_t *settings);
enum filebrowser_enums filebrowser_get_type(void);
diff --git a/menu/menu_driver.c b/menu/menu_driver.c
index 3387cadaa4..9b3b573606 100644
--- a/menu/menu_driver.c
+++ b/menu/menu_driver.c
@@ -2334,6 +2334,11 @@ static bool menu_driver_displaylist_push_internal(
return true;
}
#endif
+ else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)))
+ {
+ if (menu_displaylist_ctl(DISPLAYLIST_CONTENTLESS_CORES, info, settings))
+ return true;
+ }
else if (string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)))
{
if (menu_displaylist_ctl(DISPLAYLIST_NETPLAY_ROOM_LIST, info, settings))
@@ -4155,6 +4160,7 @@ int menu_driver_deferred_push_content_list(file_list_t *list)
file_list_t *selection_buf = MENU_LIST_GET_SELECTION(menu_list, (unsigned)0);
menu_st->selection_ptr = 0;
+ menu_st->contentless_core_ptr = 0;
if (!menu_driver_displaylist_push(
menu_st,
@@ -4518,6 +4524,7 @@ bool menu_entries_append_enum(
if ( enum_idx != MENU_ENUM_LABEL_PLAYLIST_ENTRY
&& enum_idx != MENU_ENUM_LABEL_PLAYLIST_COLLECTION_ENTRY
&& enum_idx != MENU_ENUM_LABEL_EXPLORE_ITEM
+ && enum_idx != MENU_ENUM_LABEL_CONTENTLESS_CORE
&& enum_idx != MENU_ENUM_LABEL_RDB_ENTRY)
cbs->setting = menu_setting_find_enum(enum_idx);
@@ -6974,8 +6981,12 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
switch (state)
{
case RARCH_MENU_CTL_SET_PENDING_QUICK_MENU:
- menu_entries_flush_stack(NULL, MENU_SETTINGS);
- menu_st->pending_quick_menu = true;
+ {
+ bool flush_stack = !data ? true : *((bool *)data);
+ if (flush_stack)
+ menu_entries_flush_stack(NULL, MENU_SETTINGS);
+ menu_st->pending_quick_menu = true;
+ }
break;
case RARCH_MENU_CTL_SET_PREVENT_POPULATE:
menu_st->prevent_populate = true;
@@ -7000,21 +7011,25 @@ bool menu_driver_ctl(enum rarch_menu_ctl_state state, void *data)
#ifdef HAVE_NETWORKING
core_updater_list_free_cached();
#endif
-#if defined(HAVE_MENU) && defined(HAVE_LIBRETRODB)
+#if defined(HAVE_MENU)
+#if defined(HAVE_LIBRETRODB)
/* Before freeing the explore menu, we
* must wait for any explore menu initialisation
* tasks to complete */
menu_explore_wait_for_init_task();
menu_explore_free();
#endif
+ menu_contentless_cores_free();
+#endif
if (menu_st->driver_data)
{
unsigned i;
- menu_st->scroll.acceleration = 0;
- menu_st->selection_ptr = 0;
- menu_st->scroll.index_size = 0;
+ menu_st->scroll.acceleration = 0;
+ menu_st->selection_ptr = 0;
+ menu_st->contentless_core_ptr = 0;
+ menu_st->scroll.index_size = 0;
for (i = 0; i < SCROLL_INDEX_SIZE; i++)
menu_st->scroll.index_list[i] = 0;
@@ -7517,6 +7532,7 @@ static int generic_menu_iterate(
break;
#endif
case MENU_ENUM_LABEL_CORE_MANAGER_ENTRY:
+ case MENU_ENUM_LABEL_CONTENTLESS_CORE:
{
core_info_t *core_info = NULL;
const char *path = selection_buf->list[selection].path;
diff --git a/menu/menu_driver.h b/menu/menu_driver.h
index 9375041e6d..bdcef9e4ce 100644
--- a/menu/menu_driver.h
+++ b/menu/menu_driver.h
@@ -90,6 +90,7 @@ enum menu_settings_type
MENU_IMAGES_TAB,
MENU_NETPLAY_TAB,
MENU_EXPLORE_TAB,
+ MENU_CONTENTLESS_CORES_TAB,
MENU_ADD_TAB,
MENU_PLAYLISTS_TAB,
MENU_SETTING_DROPDOWN_ITEM,
@@ -268,6 +269,8 @@ enum menu_settings_type
MENU_SETTING_ACTION_CORE_OPTIONS_RESET,
MENU_SETTING_ACTION_CORE_OPTIONS_FLUSH,
+ MENU_SETTING_ACTION_CONTENTLESS_CORE_RUN,
+
MENU_SETTINGS_LAST
};
@@ -454,6 +457,7 @@ struct menu_state
size_t begin;
} entries;
size_t selection_ptr;
+ size_t contentless_core_ptr;
/* Quick jumping indices with L/R.
* Rebuilt when parsing directory. */
@@ -635,6 +639,11 @@ void menu_explore_free(void);
void menu_explore_set_state(explore_state_t *state);
#endif
+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);
+
/* Returns true if search filter is enabled
* for the specified menu list */
bool menu_driver_search_filter_enabled(const char *label, unsigned type);
diff --git a/menu/menu_setting.c b/menu/menu_setting.c
index 13a97f6002..5e03a6407c 100644
--- a/menu/menu_setting.c
+++ b/menu/menu_setting.c
@@ -3674,6 +3674,36 @@ static void setting_get_string_representation_uint_menu_add_content_entry_displa
}
}
+static void setting_get_string_representation_uint_menu_contentless_cores_display_type(
+ rarch_setting_t *setting,
+ char *s, size_t len)
+{
+ if (!setting)
+ return;
+
+ switch (*setting->value.target.unsigned_integer)
+ {
+ case MENU_CONTENTLESS_CORES_DISPLAY_NONE:
+ strlcpy(s,
+ msg_hash_to_str(
+ MENU_ENUM_LABEL_VALUE_OFF),
+ len);
+ break;
+ case MENU_CONTENTLESS_CORES_DISPLAY_ALL:
+ strlcpy(s,
+ msg_hash_to_str(
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_ALL),
+ len);
+ break;
+ case MENU_CONTENTLESS_CORES_DISPLAY_SINGLE_PURPOSE:
+ strlcpy(s,
+ msg_hash_to_str(
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE),
+ len);
+ break;
+ }
+}
+
static void setting_get_string_representation_uint_rgui_menu_color_theme(
rarch_setting_t *setting,
char *s, size_t len)
@@ -16711,6 +16741,23 @@ static bool setting_append_list(
general_read_handler,
SD_FLAG_NONE);
#endif
+ CONFIG_UINT(
+ list, list_info,
+ &settings->uints.menu_content_show_contentless_cores,
+ MENU_ENUM_LABEL_CONTENT_SHOW_CONTENTLESS_CORES,
+ MENU_ENUM_LABEL_VALUE_CONTENT_SHOW_CONTENTLESS_CORES,
+ DEFAULT_MENU_CONTENT_SHOW_CONTENTLESS_CORES,
+ &group_info,
+ &subgroup_info,
+ parent_group,
+ general_write_handler,
+ general_read_handler);
+ (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
+ (*list)[list_info->index - 1].get_string_representation =
+ &setting_get_string_representation_uint_menu_contentless_cores_display_type;
+ menu_settings_list_current_add_range(list, list_info, 0, MENU_CONTENTLESS_CORES_DISPLAY_LAST-1, 1, true, true);
+ (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX;
+ SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_LAKKA_ADVANCED);
#ifdef HAVE_MATERIALUI
if (string_is_equal(settings->arrays.menu_driver, "glui"))
diff --git a/msg_hash.h b/msg_hash.h
index 9de5cb63b5..a70e8f5c35 100644
--- a/msg_hash.h
+++ b/msg_hash.h
@@ -1224,6 +1224,9 @@ enum msg_hash_enums
MENU_LABEL(CONTENT_SHOW_ADD_ENTRY),
MENU_LABEL(CONTENT_SHOW_PLAYLISTS),
MENU_LABEL(CONTENT_SHOW_EXPLORE),
+ MENU_LABEL(CONTENT_SHOW_CONTENTLESS_CORES),
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_ALL,
+ MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE,
MENU_LABEL(XMB_RIBBON_ENABLE),
MENU_LABEL(THUMBNAILS),
MENU_LABEL(THUMBNAILS_RGUI),
@@ -1368,6 +1371,7 @@ enum msg_hash_enums
MENU_LABEL(GOTO_IMAGES),
MENU_LABEL(GOTO_VIDEO),
MENU_LABEL(GOTO_EXPLORE),
+ MENU_LABEL(GOTO_CONTENTLESS_CORES),
MENU_LABEL(ADD_TO_FAVORITES),
MENU_LABEL(ADD_TO_FAVORITES_PLAYLIST),
MENU_LABEL(SET_CORE_ASSOCIATION),
@@ -1524,6 +1528,7 @@ enum msg_hash_enums
MENU_ENUM_LABEL_DEFERRED_MUSIC_LIST,
MENU_ENUM_LABEL_DEFERRED_VIDEO_LIST,
MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST,
+ MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST,
MENU_ENUM_LABEL_DEFERRED_NETPLAY,
MENU_ENUM_LABEL_DEFERRED_MUSIC,
MENU_ENUM_LABEL_DEFERRED_BROWSE_URL_START,
@@ -2159,6 +2164,8 @@ enum msg_hash_enums
MENU_ENUM_LABEL_EXPLORE_TAB,
MENU_ENUM_LABEL_EXPLORE_ITEM,
MENU_ENUM_LABEL_VALUE_EXPLORE_TAB,
+ MENU_LABEL(CONTENTLESS_CORES_TAB),
+ MENU_LABEL(CONTENTLESS_CORE),
MENU_LABEL(ADD_TAB),
MENU_LABEL(NETPLAY_TAB),
MENU_LABEL(PLAYLISTS_TAB),
diff --git a/runloop.c b/runloop.c
index 8c7ae6afc3..e0d03928f1 100644
--- a/runloop.c
+++ b/runloop.c
@@ -6678,13 +6678,20 @@ static enum runloop_state_enum runloop_check_state(
/* Iterate the menu driver for one frame. */
+ /* If the user had requested that the Quick Menu
+ * be spawned during the previous frame, do this now
+ * and exit the function to go to the next frame. */
if (menu_st->pending_quick_menu)
{
- /* If the user had requested that the Quick Menu
- * be spawned during the previous frame, do this now
- * and exit the function to go to the next frame.
- */
- menu_entries_flush_stack(NULL, MENU_SETTINGS);
+ menu_ctx_list_t list_info;
+
+ /* We are going to push a new menu; ensure
+ * that the current one is cached for animation
+ * purposes */
+ list_info.type = MENU_LIST_PLAIN;
+ list_info.action = 0;
+ menu_driver_list_cache(&list_info);
+
p_disp->msg_force = true;
generic_action_ok_displaylist_push("", NULL,
diff --git a/tasks/task_content.c b/tasks/task_content.c
index f804fc996c..98319646da 100644
--- a/tasks/task_content.c
+++ b/tasks/task_content.c
@@ -2096,8 +2096,14 @@ bool task_push_start_current_core(content_ctx_info_t *content_info)
if (firmware_update_status(&content_ctx))
goto end;
- /* Loads content into currently selected core. */
- if (!(ret = content_load(content_info, p_content)))
+ /* Loads content into currently selected core.
+ * Note that 'content_load()' can fail and yet still
+ * return 'true'... In this case, the dummy core
+ * will be loaded; the 'start core' operation can
+ * therefore only be considered successful if the
+ * dummy core is not running following 'content_load()' */
+ if (!(ret = content_load(content_info, p_content)) ||
+ !(ret = (runloop_st->current_core_type != CORE_TYPE_DUMMY)))
{
if (error_string)
{
@@ -2159,6 +2165,101 @@ bool task_push_load_new_core(
}
#ifdef HAVE_MENU
+bool task_push_load_contentless_core_from_menu(
+ const char *core_path)
+{
+ content_ctx_info_t content_info = {0};
+ content_information_ctx_t content_ctx = {0};
+ content_state_t *p_content = content_state_get_ptr();
+ bool ret = true;
+ char *error_string = NULL;
+ runloop_state_t *runloop_st = runloop_state_get_ptr();
+ settings_t *settings = config_get_ptr();
+ const char *path_dir_system = settings->paths.directory_system;
+ bool check_firmware_before_loading = settings->bools.check_firmware_before_loading;
+ bool flush_menu = true;
+ const char *menu_label = NULL;
+
+ if (string_is_empty(core_path))
+ return false;
+
+ content_info.environ_get = menu_content_environment_get;
+
+ content_ctx.check_firmware_before_loading = check_firmware_before_loading;
+ content_ctx.bios_is_missing = retroarch_ctl(RARCH_CTL_IS_MISSING_BIOS, NULL);
+ if (!string_is_empty(path_dir_system))
+ content_ctx.directory_system = strdup(path_dir_system);
+
+ /* Set core path */
+ path_set(RARCH_PATH_CORE, core_path);
+
+ /* Clear content path */
+ path_clear(RARCH_PATH_CONTENT);
+
+#if defined(HAVE_DYNAMIC)
+ /* Load core */
+ command_event(CMD_EVENT_LOAD_CORE, NULL);
+
+ runloop_set_current_core_type(CORE_TYPE_PLAIN, true);
+
+ if (firmware_update_status(&content_ctx))
+ goto end;
+
+ /* Loads content into currently selected core.
+ * Note that 'content_load()' can fail and yet still
+ * return 'true'... In this case, the dummy core
+ * will be loaded; the 'start core' operation can
+ * therefore only be considered successful if the
+ * dummy core is not running following 'content_load()' */
+ if (!(ret = content_load(&content_info, p_content)) ||
+ !(ret = (runloop_st->current_core_type != CORE_TYPE_DUMMY)))
+ {
+ if (error_string)
+ {
+ runloop_msg_queue_push(error_string, 2, 90,
+ true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
+ MESSAGE_QUEUE_CATEGORY_INFO);
+ RARCH_ERR("[Content]: %s\n", error_string);
+ free(error_string);
+ }
+
+ retroarch_menu_running();
+ goto end;
+ }
+#else
+ /* TODO/FIXME: Static builds do not support running
+ * a core directly from the 'command line' without
+ * supplying a content path, so this *will not work*.
+ * In order to support this functionality, the '-L'
+ * command line argument must be enabled for static
+ * builds to inform the frontend that the core should
+ * run automatically on launch. We will leave this
+ * non-functional code here as a place-marker for
+ * future devs who may wish to implement this... */
+ command_event_cmd_exec(p_content,
+ path_get(RARCH_PATH_CONTENT), &content_ctx,
+ false, &error_string);
+ command_event(CMD_EVENT_QUIT, NULL);
+#endif
+
+ /* Push quick menu onto menu stack */
+ menu_entries_get_last_stack(NULL, &menu_label, NULL, NULL, NULL);
+
+ if (string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENTLESS_CORES_TAB)) ||
+ string_is_equal(menu_label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_CONTENTLESS_CORES_LIST)))
+ flush_menu = false;
+
+ menu_driver_ctl(RARCH_MENU_CTL_SET_PENDING_QUICK_MENU, &flush_menu);
+
+#ifdef HAVE_DYNAMIC
+end:
+#endif
+ if (content_ctx.directory_system)
+ free(content_ctx.directory_system);
+
+ return ret;
+}
+
bool task_push_load_content_with_new_core_from_menu(
const char *core_path,
const char *fullpath,
diff --git a/tasks/task_content.h b/tasks/task_content.h
index 34289e24dc..98500ca19b 100644
--- a/tasks/task_content.h
+++ b/tasks/task_content.h
@@ -99,6 +99,9 @@ bool task_push_load_content_from_playlist_from_menu(
content_ctx_info_t *content_info,
retro_task_callback_t cb,
void *user_data);
+
+bool task_push_load_contentless_core_from_menu(
+ const char *core_path);
#endif
bool task_push_load_content_with_core(
diff --git a/tasks/task_core_backup.c b/tasks/task_core_backup.c
index 4f601aa48d..5ae41b2300 100644
--- a/tasks/task_core_backup.c
+++ b/tasks/task_core_backup.c
@@ -36,6 +36,10 @@
#include "../core_info.h"
#include "../core_backup.h"
+#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
+#include "../menu/menu_driver.h"
+#endif
+
#if defined(ANDROID)
#include "../play_feature_delivery/play_feature_delivery.h"
#endif
@@ -649,6 +653,11 @@ static void cb_task_core_restore(
/* Reload core info files
* > This must be done on the main thread */
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
+
+#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
+ /* Force reload of contentless cores icons */
+ menu_contentless_cores_free();
+#endif
}
static void task_core_restore_handler(retro_task_t *task)
diff --git a/tasks/task_core_updater.c b/tasks/task_core_updater.c
index e1f9b30000..2481e22315 100644
--- a/tasks/task_core_updater.c
+++ b/tasks/task_core_updater.c
@@ -45,6 +45,7 @@
#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
#include "../menu/menu_entries.h"
+#include "../menu/menu_driver.h"
#endif
/* Get core updater list */
@@ -521,6 +522,11 @@ static void cb_task_core_updater_download(
/* Reload core info files
* > This must be done on the main thread */
command_event(CMD_EVENT_CORE_INFO_INIT, NULL);
+
+#if defined(RARCH_INTERNAL) && defined(HAVE_MENU)
+ /* Force reload of contentless cores icons */
+ menu_contentless_cores_free();
+#endif
}
static void cb_decompress_task_core_updater_download(