Enable manual selection of which cores are displayed in the 'Standalone Cores' menu (#13722)

This commit is contained in:
jdgleaver 2022-03-09 16:49:16 +00:00 committed by GitHub
parent 3b08d63b23
commit c67806dbd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 518 additions and 58 deletions

View File

@ -478,6 +478,7 @@ static void core_info_copy(core_info_t *src, core_info_t *dst)
dst->database_match_archive_member = src->database_match_archive_member;
dst->is_experimental = src->is_experimental;
dst->is_locked = src->is_locked;
dst->is_standalone_exempt = src->is_standalone_exempt;
dst->is_installed = src->is_installed;
}
@ -574,6 +575,7 @@ static void core_info_transfer(core_info_t *src, core_info_t *dst)
dst->database_match_archive_member = src->database_match_archive_member;
dst->is_experimental = src->is_experimental;
dst->is_locked = src->is_locked;
dst->is_standalone_exempt = src->is_standalone_exempt;
dst->is_installed = src->is_installed;
}
@ -1282,19 +1284,20 @@ typedef struct
{
const char *filename;
uint32_t hash;
} core_lock_file_path_t;
} core_aux_file_path_t;
typedef struct
{
core_lock_file_path_t *list;
core_aux_file_path_t *list;
size_t size;
} core_lock_file_path_list_t;
} core_aux_file_path_list_t;
typedef struct
{
struct string_list *dir_list;
core_file_path_list_t *core_list;
core_lock_file_path_list_t *lock_list;
core_aux_file_path_list_t *lock_list;
core_aux_file_path_list_t *standalone_exempt_list;
} core_path_list_t;
static uint32_t core_info_hash_string(const char *str)
@ -1325,6 +1328,13 @@ static void core_info_path_list_free(core_path_list_t *path_list)
free(path_list->lock_list);
}
if (path_list->standalone_exempt_list)
{
if (path_list->standalone_exempt_list->list)
free(path_list->standalone_exempt_list->list);
free(path_list->standalone_exempt_list);
}
if (path_list->dir_list)
string_list_free(path_list->dir_list);
@ -1352,22 +1362,30 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
goto error;
/* Allocate list containers */
path_list->dir_list = string_list_new();
path_list->core_list = (core_file_path_list_t*)calloc(1,
sizeof(*path_list->core_list));
path_list->lock_list = (core_lock_file_path_list_t*)calloc(1,
sizeof(*path_list->lock_list));
path_list->dir_list = string_list_new();
path_list->core_list = (core_file_path_list_t*)
calloc(1, sizeof(*path_list->core_list));
path_list->lock_list = (core_aux_file_path_list_t*)
calloc(1, sizeof(*path_list->lock_list));
path_list->standalone_exempt_list = (core_aux_file_path_list_t*)
calloc(1, sizeof(*path_list->standalone_exempt_list));
if ( !path_list->dir_list
|| !path_list->core_list
|| !path_list->lock_list)
|| !path_list->lock_list
|| !path_list->standalone_exempt_list)
goto error;
/* Get list of file extensions to include
* (core + lock file) */
fill_pathname_join_delim(exts,
core_exts, FILE_PATH_LOCK_EXTENSION_NO_DOT,
'|', sizeof(exts));
* > core + lock */
strlcpy(exts, core_exts, sizeof(exts));
strlcat(exts, "|" FILE_PATH_LOCK_EXTENSION_NO_DOT,
sizeof(exts));
#if defined(HAVE_DYNAMIC)
/* > 'standalone exempt' */
strlcat(exts, "|" FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT,
sizeof(exts));
#endif
/* Fetch core directory listing */
dir_list_ok = dir_list_append(path_list->dir_list,
@ -1396,15 +1414,19 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
#endif
/* Allocate sub lists */
path_list->core_list->list = (core_file_path_t*)
path_list->core_list->list = (core_file_path_t*)
malloc(path_list->dir_list->size *
sizeof(*path_list->core_list->list));
path_list->lock_list->list = (core_lock_file_path_t*)
path_list->lock_list->list = (core_aux_file_path_t*)
malloc(path_list->dir_list->size *
sizeof(*path_list->lock_list->list));
path_list->standalone_exempt_list->list = (core_aux_file_path_t*)
malloc(path_list->dir_list->size *
sizeof(*path_list->standalone_exempt_list->list));
if (!path_list->core_list->list ||
!path_list->lock_list->list)
!path_list->lock_list->list ||
!path_list->standalone_exempt_list->list)
goto error;
/* Parse directory listing */
@ -1419,7 +1441,8 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
|| !(file_ext = path_get_extension(filename)))
continue;
/* Check whether this is a core or lock file */
/* Check whether this is a core, lock or
* 'standalone exempt' file */
if (string_list_find_elem(core_ext_list, file_ext))
{
path_list->core_list->list[
@ -1436,6 +1459,16 @@ static core_path_list_t *core_info_path_list_new(const char *core_dir,
path_list->lock_list->size].hash = core_info_hash_string(filename);
path_list->lock_list->size++;
}
#if defined(HAVE_DYNAMIC)
else if (string_is_equal(file_ext, FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT))
{
path_list->standalone_exempt_list->list[
path_list->standalone_exempt_list->size].filename = filename;
path_list->standalone_exempt_list->list[
path_list->standalone_exempt_list->size].hash = core_info_hash_string(filename);
path_list->standalone_exempt_list->size++;
}
#endif
}
string_list_free(core_ext_list);
@ -1448,13 +1481,15 @@ error:
}
static bool core_info_path_is_locked(
core_lock_file_path_list_t *lock_list,
core_aux_file_path_list_t *lock_list,
const char *core_file_name)
{
size_t i;
uint32_t hash;
char lock_filename[256];
lock_filename[0] = '\0';
if (lock_list->size < 1)
return false;
@ -1465,7 +1500,7 @@ static bool core_info_path_is_locked(
for (i = 0; i < lock_list->size; i++)
{
core_lock_file_path_t *lock_file = &lock_list->list[i];
core_aux_file_path_t *lock_file = &lock_list->list[i];
if ((lock_file->hash == hash) &&
string_is_equal(lock_file->filename, lock_filename))
@ -1475,6 +1510,37 @@ static bool core_info_path_is_locked(
return false;
}
static bool core_info_path_is_standalone_exempt(
core_aux_file_path_list_t *exempt_list,
const char *core_file_name)
{
size_t i;
uint32_t hash;
char exempt_filename[256];
exempt_filename[0] = '\0';
if (exempt_list->size < 1)
return false;
snprintf(exempt_filename, sizeof(exempt_filename),
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
core_file_name);
hash = core_info_hash_string(exempt_filename);
for (i = 0; i < exempt_list->size; i++)
{
core_aux_file_path_t *exempt_file = &exempt_list->list[i];
if ((exempt_file->hash == hash) &&
string_is_equal(exempt_file->filename, exempt_filename))
return true;
}
return false;
}
static bool core_info_get_file_id(const char *core_filename,
char *core_file_id, size_t len)
{
@ -1987,23 +2053,35 @@ static core_info_list_t *core_info_list_new(const char *path,
if (info_cache)
{
core_info_copy(info_cache, info);
/* Core path is 'dynamic', and cannot
* be cached (i.e. core directory may
* change between runs) */
if (info->path)
free(info->path);
info->path = strdup(base_path);
info->path = strdup(base_path);
/* Core lock status is 'dynamic', and
* cannot be cached */
info->is_locked = core_info_path_is_locked(
path_list->lock_list, core_filename);
/* Core 'standalone exempt' status is 'dynamic',
* and cannot be cached
* > It is also dependent upon whether the core
* supports contentless operation */
info->is_standalone_exempt = info->supports_no_game &&
core_info_path_is_standalone_exempt(
path_list->standalone_exempt_list,
core_filename);
/* 'info_count' is normally incremented inside
* core_info_parse_config_file(). If core entry
* is cached, must instead increment the value
* here */
if (info->has_info)
core_info_list->info_count++;
continue;
}
}
@ -2032,7 +2110,13 @@ static core_info_list_t *core_info_list_new(const char *path,
if (!info->display_name)
info->display_name = strdup(core_filename);
info->is_installed = true;
/* Get core 'standalone exempt' status */
info->is_standalone_exempt = info->supports_no_game &&
core_info_path_is_standalone_exempt(
path_list->standalone_exempt_list,
core_filename);
info->is_installed = true;
/* If info cache is enabled and we reach this
* point, current core is uncached
@ -2205,6 +2289,8 @@ bool core_info_init_current_core(void)
current->database_match_archive_member = false;
current->is_experimental = false;
current->is_locked = false;
current->is_standalone_exempt = false;
current->is_installed = false;
current->firmware_count = 0;
current->savestate_support_level = CORE_INFO_SAVESTATE_DETERMINISTIC;
current->path = NULL;
@ -3087,6 +3173,43 @@ bool core_info_current_supports_runahead(void)
CORE_INFO_SAVESTATE_DETERMINISTIC;
}
static bool core_info_update_core_aux_file(const char *path, bool create)
{
bool aux_file_exists = false;
if (string_is_empty(path))
return false;
/* Check whether aux file exists */
aux_file_exists = path_is_valid(path);
/* Create or delete aux file, as required */
if (create && !aux_file_exists)
{
RFILE *aux_file = filestream_open(path,
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!aux_file)
return false;
/* We have to write something - just output
* a single character */
if (filestream_putc(aux_file, 0) != 0)
{
filestream_close(aux_file);
return false;
}
filestream_close(aux_file);
}
else if (!create && aux_file_exists)
if (filestream_delete(path) != 0)
return false;
return true;
}
/* Sets 'locked' status of specified core
* > Returns true if successful
* > Like all functions that access the cached
@ -3094,9 +3217,10 @@ bool core_info_current_supports_runahead(void)
bool core_info_set_core_lock(const char *core_path, bool lock)
{
core_info_t *core_info = NULL;
bool lock_file_exists = false;
char lock_file_path[PATH_MAX_LENGTH];
lock_file_path[0] = '\0';
#if defined(ANDROID)
/* Play Store builds do not support
* core locking */
@ -3104,47 +3228,19 @@ bool core_info_set_core_lock(const char *core_path, bool lock)
return false;
#endif
if (string_is_empty(core_path))
return false;
/* Search for specified core */
if (!core_info_find(core_path, &core_info))
return false;
if (string_is_empty(core_info->path))
if (string_is_empty(core_path) ||
!core_info_find(core_path, &core_info) ||
string_is_empty(core_info->path))
return false;
/* Get lock file path */
snprintf(lock_file_path, sizeof(lock_file_path),
"%s" FILE_PATH_LOCK_EXTENSION, core_info->path);
/* Check whether lock file exists */
lock_file_exists = path_is_valid(lock_file_path);
/* Create or delete lock file, as required */
if (lock && !lock_file_exists)
{
RFILE *lock_file = filestream_open(
lock_file_path,
RETRO_VFS_FILE_ACCESS_WRITE,
RETRO_VFS_FILE_ACCESS_HINT_NONE);
if (!lock_file)
return false;
/* We have to write something - just output
* a single character */
if (filestream_putc(lock_file, 0) != 0)
{
filestream_close(lock_file);
return false;
}
filestream_close(lock_file);
}
else if (!lock && lock_file_exists)
if (filestream_delete(lock_file_path) != 0)
return false;
if (!core_info_update_core_aux_file(lock_file_path, lock))
return false;
/* File operations were successful - update
* core info entry */
@ -3169,6 +3265,8 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
bool is_locked = false;
char lock_file_path[PATH_MAX_LENGTH];
lock_file_path[0] = '\0';
#if defined(ANDROID)
/* Play Store builds do not support
* core locking */
@ -3209,3 +3307,88 @@ bool core_info_get_core_lock(const char *core_path, bool validate_path)
return is_locked;
}
/* Sets 'standalone exempt' status of specified core
* > A 'standalone exempt' core will not be shown
* in the contentless cores menu when display type
* is set to 'custom'
* > Returns true if successful
* > Returns false if core does not support
* contentless operation
* > *Not* thread safe */
bool core_info_set_core_standalone_exempt(const char *core_path, bool exempt)
{
#if defined(HAVE_DYNAMIC)
core_info_t *core_info = NULL;
char exempt_file_path[PATH_MAX_LENGTH];
exempt_file_path[0] = '\0';
/* Search for specified core */
if (string_is_empty(core_path) ||
!core_info_find(core_path, &core_info) ||
string_is_empty(core_info->path) ||
!core_info->supports_no_game)
return false;
/* Get 'standalone exempt' file path */
snprintf(exempt_file_path, sizeof(exempt_file_path),
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
core_info->path);
/* Create or delete 'standalone exempt' file, as required */
if (!core_info_update_core_aux_file(exempt_file_path, exempt))
return false;
/* File operations were successful - update
* core info entry */
core_info->is_standalone_exempt = exempt;
return true;
#else
/* Static platforms do not support the contentless
* cores menu */
return false;
#endif
}
/* Fetches 'standalone exempt' status of specified core
* > Returns true if core should be excluded from
* the contentless cores menu when display type is
* set to 'custom'
* > *Not* thread safe */
bool core_info_get_core_standalone_exempt(const char *core_path)
{
#if defined(HAVE_DYNAMIC)
core_info_t *core_info = NULL;
bool is_exempt = false;
char exempt_file_path[PATH_MAX_LENGTH];
exempt_file_path[0] = '\0';
/* Search for specified core */
if (string_is_empty(core_path) ||
!core_info_find(core_path, &core_info) ||
string_is_empty(core_info->path) ||
!core_info->supports_no_game)
return false;
/* Get 'standalone exempt' file path */
snprintf(exempt_file_path, sizeof(exempt_file_path),
"%s" FILE_PATH_STANDALONE_EXEMPT_EXTENSION,
core_info->path);
/* Check whether 'standalone exempt' file exists */
is_exempt = path_is_valid(exempt_file_path);
/* Ensure that core info 'is_standalone_exempt'
* field is up to date */
core_info->is_standalone_exempt = is_exempt;
return is_exempt;
#else
/* Static platforms do not support the contentless
* cores menu */
return false;
#endif
}

View File

@ -108,6 +108,7 @@ typedef struct
bool database_match_archive_member;
bool is_experimental;
bool is_locked;
bool is_standalone_exempt;
bool is_installed;
} core_info_t;
@ -229,6 +230,22 @@ bool core_info_set_core_lock(const char *core_path, bool lock);
* must be checked externally */
bool core_info_get_core_lock(const char *core_path, bool validate_path);
/* Sets 'standalone exempt' status of specified core
* > A 'standalone exempt' core will not be shown
* in the contentless cores menu when display type
* is set to 'custom'
* > Returns true if successful
* > Returns false if core does not support
* contentless operation
* > *Not* thread safe */
bool core_info_set_core_standalone_exempt(const char *core_path, bool exempt);
/* Fetches 'standalone exempt' status of specified core
* > Returns true if core should be excluded from
* the contentless cores menu when display type is
* set to 'custom'
* > *Not* thread safe */
bool core_info_get_core_standalone_exempt(const char *core_path);
bool core_info_core_file_id_is_equal(const char *core_path_a, const char *core_path_b);
/* When called, generates a temporary file

View File

@ -107,6 +107,8 @@ RETRO_BEGIN_DECLS
#define FILE_PATH_CORE_BACKUP_EXTENSION_NO_DOT "lcbk"
#define FILE_PATH_LOCK_EXTENSION ".lck"
#define FILE_PATH_LOCK_EXTENSION_NO_DOT "lck"
#define FILE_PATH_STANDALONE_EXEMPT_EXTENSION ".lsae"
#define FILE_PATH_STANDALONE_EXEMPT_EXTENSION_NO_DOT "lsae"
#define FILE_PATH_BACKUP_EXTENSION ".bak"
#if defined(RARCH_MOBILE)
#define FILE_PATH_DEFAULT_OVERLAY "gamepads/neo-retropad/neo-retropad.cfg"

View File

@ -4082,6 +4082,10 @@ MSG_HASH(
MENU_ENUM_LABEL_CORE_LOCK,
"core_lock"
)
MSG_HASH(
MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT,
"core_set_standalone_exempt"
)
MSG_HASH(
MENU_ENUM_LABEL_CORE_DELETE,
"core_delete"

View File

@ -531,6 +531,14 @@ MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_LOCK,
"Prevent modification of the currently installed core. May be used to avoid unwanted updates when content requires a specific core version (e.g. Arcade ROM sets)."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_SET_STANDALONE_EXEMPT,
"Exclude From 'Standalone Cores' Menu"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_CORE_SET_STANDALONE_EXEMPT,
"Prevent this core from being displayed in the 'Standalone Cores' tab/menu. Only applies when display mode is set to 'Custom'."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_CORE_DELETE,
"Delete Core"
@ -4696,7 +4704,7 @@ MSG_HASH(
)
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)"
"Specify the type of core (if any) to show in the 'Standalone Cores' menu. When set to 'Custom', individual core visibility may be toggled via the 'Manage Cores' menu. (Restart Required on Ozone/XMB)"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_ALL,
@ -4706,6 +4714,10 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE,
"Single-Use"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_CUSTOM,
"Custom"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_TIMEDATE_ENABLE,
"Show Date and Time"
@ -12592,6 +12604,14 @@ MSG_HASH(
MSG_CORE_UNLOCK_FAILED,
"Failed to unlock core: "
)
MSG_HASH(
MSG_CORE_SET_STANDALONE_EXEMPT_FAILED,
"Failed to remove core from 'Standalone Cores' list: "
)
MSG_HASH(
MSG_CORE_UNSET_STANDALONE_EXEMPT_FAILED,
"Failed to add core to 'Standalone Cores' list: "
)
MSG_HASH(
MSG_CORE_DELETE_DISABLED,
"Core deletion disabled - core is locked: "

View File

@ -768,6 +768,40 @@ static void menu_action_setting_disp_set_label_core_lock(
*w = (unsigned)strlen(s);
}
static void menu_action_setting_disp_set_label_core_set_standalone_exempt(
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)
{
core_info_t *core_info = NULL;
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);
/* Check whether core is excluded from the
* contentless cores menu
* > Note: We search core_info here instead of
* calling core_info_get_core_standalone_exempt()
* since we don't want to perform disk access
* every frame */
if (core_info_find(path, &core_info) &&
core_info->supports_no_game &&
core_info->is_standalone_exempt)
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON), len);
else
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF), len);
*w = (unsigned)strlen(s);
}
static void menu_action_setting_disp_set_label_input_desc(
file_list_t* list,
unsigned *w, unsigned type, unsigned i,
@ -2221,6 +2255,10 @@ static int menu_cbs_init_bind_get_string_representation_compare_type(
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_lock);
break;
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_set_standalone_exempt);
break;
case 32: /* Recent history entry */
case 65535: /* System info entry */
BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_entry);

View File

@ -54,6 +54,7 @@
/* Forward declarations */
int action_ok_core_lock(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx);
int action_ok_core_set_standalone_exempt(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx);
extern struct key_desc key_descriptors[RARCH_MAX_KEYS];
@ -811,6 +812,12 @@ static int action_left_core_lock(unsigned type, const char *label,
return action_ok_core_lock(label, label, type, 0, 0);
}
static int action_left_core_set_standalone_exempt(unsigned type, const char *label,
bool wraparound)
{
return action_ok_core_set_standalone_exempt(label, label, type, 0, 0);
}
static int disk_options_disk_idx_left(unsigned type, const char *label,
bool wraparound)
{
@ -1241,6 +1248,9 @@ static int menu_cbs_init_bind_left_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_ACTION_CORE_LOCK:
BIND_ACTION_LEFT(cbs, action_left_core_lock);
break;
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_LEFT(cbs, action_left_core_set_standalone_exempt);
break;
case MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION:
case MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION_KBD:
BIND_ACTION_LEFT(cbs, action_left_scroll);

View File

@ -7406,6 +7406,64 @@ int action_ok_core_lock(const char *path,
return ret;
}
/* Do not declare this static - it is also used
* in menu_cbs_left.c and menu_cbs_right.c */
int action_ok_core_set_standalone_exempt(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
const char *core_path = path;
bool exempt = false;
int ret = 0;
if (string_is_empty(core_path))
return -1;
/* Simply toggle current 'exempt' status */
exempt = !core_info_get_core_standalone_exempt(core_path);
if (!core_info_set_core_standalone_exempt(core_path, exempt))
{
const char *core_name = NULL;
core_info_t *core_info = NULL;
char msg[PATH_MAX_LENGTH];
msg[0] = '\0';
/* Need to fetch core name for error message */
/* If core is found, use display name */
if (core_info_find(core_path, &core_info) &&
core_info->display_name)
core_name = core_info->display_name;
/* If not, use core file name */
else
core_name = path_basename_nocompression(core_path);
/* Build error message */
strlcpy(
msg,
msg_hash_to_str(exempt ?
MSG_CORE_SET_STANDALONE_EXEMPT_FAILED :
MSG_CORE_UNSET_STANDALONE_EXEMPT_FAILED),
sizeof(msg));
if (!string_is_empty(core_name))
strlcat(msg, core_name, sizeof(msg));
/* Generate log + notification */
RARCH_ERR("%s\n", msg);
runloop_msg_queue_push(
msg,
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
ret = -1;
}
return ret;
}
static int action_ok_core_delete(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
{
@ -8810,6 +8868,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_ACTION_CORE_LOCK:
BIND_ACTION_OK(cbs, action_ok_core_lock);
break;
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_OK(cbs, action_ok_core_set_standalone_exempt);
break;
case MENU_SETTING_ACTION_VIDEO_FILTER_REMOVE:
BIND_ACTION_OK(cbs, action_ok_video_filter_remove);
break;

View File

@ -55,6 +55,7 @@
/* Forward declarations */
int action_ok_core_lock(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx);
int action_ok_core_set_standalone_exempt(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx);
extern struct key_desc key_descriptors[RARCH_MAX_KEYS];
@ -928,6 +929,12 @@ static int action_right_core_lock(unsigned type, const char *label,
return action_ok_core_lock(label, label, type, 0, 0);
}
static int action_right_core_set_standalone_exempt(unsigned type, const char *label,
bool wraparound)
{
return action_ok_core_set_standalone_exempt(label, label, type, 0, 0);
}
static int disk_options_disk_idx_right(unsigned type, const char *label,
bool wraparound)
{
@ -1057,6 +1064,9 @@ static int menu_cbs_init_bind_right_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_ACTION_CORE_LOCK:
BIND_ACTION_RIGHT(cbs, action_right_core_lock);
break;
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_RIGHT(cbs, action_right_core_set_standalone_exempt);
break;
case MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION:
case MENU_SETTING_DROPDOWN_ITEM_INPUT_DESCRIPTION_KBD:
BIND_ACTION_RIGHT(cbs, action_right_scroll);

View File

@ -637,6 +637,62 @@ static int action_start_core_lock(
return ret;
}
static int action_start_core_set_standalone_exempt(
const char *path, const char *label,
unsigned type, size_t idx, size_t entry_idx)
{
const char *core_path = path;
int ret = 0;
if (string_is_empty(core_path))
return -1;
/* Core should not be exempt by default
* > If it is currently 'not exempt', do nothing */
if (!core_info_get_core_standalone_exempt(core_path))
return ret;
/* ...Otherwise, attempt to unset the exempt flag */
if (!core_info_set_core_standalone_exempt(core_path, false))
{
const char *core_name = NULL;
core_info_t *core_info = NULL;
char msg[PATH_MAX_LENGTH];
msg[0] = '\0';
/* Need to fetch core name for error message */
/* If core is found, use display name */
if (core_info_find(core_path, &core_info) &&
core_info->display_name)
core_name = core_info->display_name;
/* If not, use core file name */
else
core_name = path_basename(core_path);
/* Build error message */
strlcpy(msg,
msg_hash_to_str(MSG_CORE_UNSET_STANDALONE_EXEMPT_FAILED),
sizeof(msg));
if (!string_is_empty(core_name))
strlcat(msg, core_name, sizeof(msg));
/* Generate log + notification */
RARCH_ERR("%s\n", msg);
runloop_msg_queue_push(
msg,
1, 100, true,
NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
ret = -1;
}
return ret;
}
static int action_start_lookup_setting(
const char *path, const char *label,
unsigned type, size_t idx, size_t entry_idx)
@ -796,6 +852,9 @@ static int menu_cbs_init_bind_start_compare_type(menu_file_list_cbs_t *cbs,
case MENU_SETTING_ACTION_CORE_LOCK:
BIND_ACTION_START(cbs, action_start_core_lock);
break;
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_START(cbs, action_start_core_set_standalone_exempt);
break;
default:
return -1;
}

View File

@ -1042,6 +1042,7 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_netplay_use_mitm_server,
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_netplay_mitm_server, MENU_ENUM_SUBLABEL_NETPLAY_MITM_SERVER)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_netplay_custom_mitm_server, MENU_ENUM_SUBLABEL_NETPLAY_CUSTOM_MITM_SERVER)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_lock, MENU_ENUM_SUBLABEL_CORE_LOCK)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_set_standalone_exempt, MENU_ENUM_SUBLABEL_CORE_SET_STANDALONE_EXEMPT)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_delete, MENU_ENUM_SUBLABEL_CORE_DELETE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_pause_hardcode_mode, MENU_ENUM_SUBLABEL_ACHIEVEMENT_PAUSE)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_resume_hardcode_mode, MENU_ENUM_SUBLABEL_ACHIEVEMENT_RESUME)
@ -4439,6 +4440,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_CORE_LOCK:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_lock);
break;
case MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_set_standalone_exempt);
break;
case MENU_ENUM_LABEL_CORE_DELETE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_delete);
break;

View File

@ -10120,6 +10120,7 @@ static void materialui_list_insert(
case FILE_TYPE_CORE:
case MENU_SETTING_ACTION_CORE_MANAGER_OPTIONS:
case MENU_SETTING_ACTION_CORE_LOCK:
case MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT:
case MENU_EXPLORE_TAB:
case MENU_CONTENTLESS_CORES_TAB:
node->icon_texture_index = MUI_TEXTURE_CORES;

View File

@ -1834,6 +1834,7 @@ static uintptr_t ozone_entries_icon_get_texture(ozone_handle_t *ozone,
case MENU_ENUM_LABEL_CORE_OPTIONS_FLUSH:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE];
case MENU_ENUM_LABEL_CORE_LOCK:
case MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_CORE];
case MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS:
return ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_OSD];

View File

@ -2806,6 +2806,7 @@ static uintptr_t xmb_icon_get_id(xmb_handle_t *xmb,
case MENU_ENUM_LABEL_CORE_OPTIONS_FLUSH:
return xmb->textures.list[XMB_TEXTURE_FILE];
case MENU_ENUM_LABEL_CORE_LOCK:
case MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT:
return xmb->textures.list[XMB_TEXTURE_CORE];
case MENU_ENUM_LABEL_ONSCREEN_DISPLAY_SETTINGS:
return xmb->textures.list[XMB_TEXTURE_OSD];

View File

@ -441,6 +441,10 @@ unsigned menu_displaylist_contentless_cores(file_list_t *list, settings_t *setti
core_valid = core_info->supports_no_game &&
core_info->single_purpose;
break;
case MENU_CONTENTLESS_CORES_DISPLAY_CUSTOM:
core_valid = core_info->supports_no_game &&
!core_info->is_standalone_exempt;
break;
default:
break;
}

View File

@ -137,6 +137,7 @@ 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_CUSTOM,
MENU_CONTENTLESS_CORES_DISPLAY_LAST
};

View File

@ -473,6 +473,11 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info,
#if defined(HAVE_NETWORKING) && defined(HAVE_ONLINE_UPDATER)
bool menu_show_core_updater = settings->bools.menu_show_core_updater;
#endif
#endif
#if defined(HAVE_DYNAMIC)
enum menu_contentless_cores_display_type
contentless_display_type = (enum menu_contentless_cores_display_type)
settings->uints.menu_content_show_contentless_cores;
#endif
tmp[0] = '\0';
@ -730,6 +735,34 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info,
end:
#if defined(HAVE_DYNAMIC)
/* Exclude core from contentless cores menu */
if ((contentless_display_type ==
MENU_CONTENTLESS_CORES_DISPLAY_CUSTOM) &&
core_info &&
core_info->supports_no_game &&
!string_is_empty(core_path) &&
!kiosk_mode_enable)
{
/* Note: Have to set core_path as both the
* 'path' and 'label' parameters (otherwise
* cannot access it in menu_cbs_get_value.c
* or menu_cbs_left/right.c), which means
* entry name must be set as 'alt' text */
if (menu_entries_append_enum(info->list,
core_path,
core_path,
MENU_ENUM_LABEL_CORE_SET_STANDALONE_EXEMPT,
MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT, 0, 0))
{
file_list_set_alt_at_offset(
info->list, count,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_SET_STANDALONE_EXEMPT));
count++;
}
}
#endif
#if !(defined(__WINRT__) || defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
if (!string_is_empty(core_path) && !kiosk_mode_enable)
{

View File

@ -144,6 +144,7 @@ enum menu_settings_type
MENU_SETTING_ACTION_DELETE_ENTRY,
MENU_SETTING_ACTION_RESET,
MENU_SETTING_ACTION_CORE_LOCK,
MENU_SETTING_ACTION_CORE_SET_STANDALONE_EXEMPT,
MENU_SETTING_ACTION_CORE_DELETE,
MENU_SETTING_ACTION_FAVORITES_DIR, /* "Start Directory" */
MENU_SETTING_STRING_OPTIONS,

View File

@ -3701,6 +3701,12 @@ static void setting_get_string_representation_uint_menu_contentless_cores_displa
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE),
len);
break;
case MENU_CONTENTLESS_CORES_DISPLAY_CUSTOM:
strlcpy(s,
msg_hash_to_str(
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_CUSTOM),
len);
break;
}
}

View File

@ -1234,6 +1234,7 @@ enum msg_hash_enums
MENU_LABEL(CONTENT_SHOW_CONTENTLESS_CORES),
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_ALL,
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_SINGLE_PURPOSE,
MENU_ENUM_LABEL_VALUE_SHOW_CONTENTLESS_CORES_CUSTOM,
MENU_LABEL(XMB_RIBBON_ENABLE),
MENU_LABEL(THUMBNAILS),
MENU_LABEL(THUMBNAILS_RGUI),
@ -2319,10 +2320,13 @@ enum msg_hash_enums
MENU_LABEL(CORE_INFORMATION),
MENU_LABEL(DISC_INFORMATION),
MENU_LABEL(CORE_LOCK),
MENU_LABEL(CORE_SET_STANDALONE_EXEMPT),
MENU_LABEL(CORE_DELETE),
MSG_CORE_LOCK_FAILED,
MSG_CORE_UNLOCK_FAILED,
MSG_CORE_SET_STANDALONE_EXEMPT_FAILED,
MSG_CORE_UNSET_STANDALONE_EXEMPT_FAILED,
MSG_CORE_DELETE_DISABLED,
/* Core updater */