Add core option category support

This commit is contained in:
jdgleaver 2021-08-06 15:32:51 +01:00
parent 7a0dc3c4ca
commit fe1f311a35
16 changed files with 3693 additions and 1129 deletions

View File

@ -277,6 +277,7 @@ OBJ += \
OBJ += \
$(LIBRETRO_COMM_DIR)/lists/linked_list.o \
$(LIBRETRO_COMM_DIR)/lists/nested_list.o \
$(LIBRETRO_COMM_DIR)/queues/generic_queue.o
ifneq ($(findstring Linux,$(OS)),)
@ -336,6 +337,7 @@ endif
OBJ += \
core_info.o \
core_backup.o \
core_option_manager.o \
$(LIBRETRO_COMM_DIR)/file/config_file.o \
$(LIBRETRO_COMM_DIR)/file/config_file_userdata.o \
runtime_file.o \

1752
core_option_manager.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
#include <boolean.h>
#include <retro_common_api.h>
#include <lists/string_list.h>
#include <lists/nested_list.h>
#include "retroarch.h"
@ -30,100 +31,408 @@ RETRO_BEGIN_DECLS
struct core_option
{
char *desc;
char *desc_categorized;
char *info;
char *info_categorized;
char *key;
char *category_key;
struct string_list *vals;
struct string_list *val_labels;
/* opt_idx: option index, used for internal
* bookkeeping */
size_t opt_idx;
/* default_index, index: correspond to
* option *value* indices */
size_t default_index;
size_t index;
bool visible;
};
struct core_catagory
{
char *key;
char *desc;
char *info;
};
/* TODO/FIXME: This struct should be made
* 'private', with restricted access to its
* members via interface functions. This
* requires significant refactoring... */
struct core_option_manager
{
config_file_t *conf;
char conf_path[PATH_MAX_LENGTH];
struct core_catagory *cats;
struct core_option *opts;
nested_list_t *option_map;
size_t cats_size;
size_t size;
bool updated;
};
typedef struct core_option_manager core_option_manager_t;
/*********************/
/* Option Conversion */
/*********************/
/**
* core_option_manager_set_default:
* @opt : pointer to core option manager object.
* @idx : index of core option to be reset to defaults.
* core_option_manager_convert_v1:
*
* Reset core option specified by @idx and sets default value for option.
* @options_v1 : an array of retro_core_option_definition
* structs
*
* Converts an array of core option v1 definitions into
* a v2 core options struct. Returned pointer must be
* freed using core_option_manager_free_converted().
*
* Returns: Valid pointer to a new v2 core options struct
* if successful, otherwise NULL.
**/
void core_option_manager_set_default(core_option_manager_t *opt, size_t idx);
struct retro_core_options_v2 *core_option_manager_convert_v1(
const struct retro_core_option_definition *options_v1);
/**
* core_option_manager_convert_v1_intl:
*
* @options_v1_intl : pointer to a retro_core_options_intl
* struct
*
* Converts a v1 'international' core options definition
* struct into a v2 core options struct. Returned pointer
* must be freed using core_option_manager_free_converted().
*
* Returns: Valid pointer to a new v2 core options struct
* if successful, otherwise NULL.
**/
struct retro_core_options_v2 *core_option_manager_convert_v1_intl(
const struct retro_core_options_intl *options_v1_intl);
/**
* core_option_manager_convert_v2_intl:
*
* @options_v2_intl : pointer to a retro_core_options_v2_intl
* struct
*
* Converts a v2 'international' core options struct
* into a regular v2 core options struct. Returned pointer
* must be freed using core_option_manager_free_converted().
*
* Returns: Valid pointer to a new v2 core options struct
* if successful, otherwise NULL.
**/
struct retro_core_options_v2 *core_option_manager_convert_v2_intl(
const struct retro_core_options_v2_intl *options_v2_intl);
/**
* core_option_manager_free_converted:
*
* @options_v2 : pointer to a retro_core_options_v2
* struct
*
* Frees the pointer returned by any
* core_option_manager_convert_*() function.
**/
void core_option_manager_free_converted(
struct retro_core_options_v2 *options_v2);
/**************************************/
/* Initialisation / De-Initialisation */
/**************************************/
/**
* core_option_manager_new_vars:
*
* @conf_path : Filesystem path to write core option
* config file to
* @src_conf_path : Filesystem path from which to load
* initial config settings.
* @vars : Pointer to core option variable array
* handle
*
* Legacy version of core_option_manager_new().
* Creates and initializes a core manager handle.
*
* Returns: handle to new core manager handle if successful,
* otherwise NULL.
**/
core_option_manager_t *core_option_manager_new_vars(
const char *conf_path, const char *src_conf_path,
const struct retro_variable *vars);
/**
* core_option_manager_new:
*
* @conf_path : Filesystem path to write core option
* config file to
* @src_conf_path : Filesystem path from which to load
* initial config settings.
* @options_v2 : Pointer to retro_core_options_v2 struct
*
* Creates and initializes a core manager handle. Parses
* information from a retro_core_options_v2 struct.
*
* Returns: handle to new core manager handle if successful,
* otherwise NULL.
**/
core_option_manager_t *core_option_manager_new(
const char *conf_path, const char *src_conf_path,
const struct retro_core_options_v2 *options_v2);
/**
* core_option_manager_free:
*
* @opt : options manager handle
*
* Frees specified core options manager handle.
**/
void core_option_manager_free(core_option_manager_t *opt);
/********************/
/* Category Getters */
/********************/
/**
* core_option_manager_get_category_desc:
*
* @opt : options manager handle
* @key : core option category id string
*
* Fetches the 'description' text of the core option
* category identified by @key (used as the
* category label in the menu).
*
* Returns: description string (menu label) of the
* specified option category if successful,
* otherwise NULL.
**/
const char *core_option_manager_get_category_desc(core_option_manager_t *opt,
const char *key);
/**
* core_option_manager_get_category_info:
*
* @opt : options manager handle
* @key : core option category id string
*
* Fetches the 'info' text of the core option
* category identified by @key (used as the category
* sublabel in the menu).
*
* Returns: information string (menu sublabel) of
* the specified option category if successful,
* otherwise NULL.
**/
const char *core_option_manager_get_category_info(core_option_manager_t *opt,
const char *key);
/**
* core_option_manager_get_category_visible:
*
* @opt : options manager handle
* @key : core option category id string
*
* Queries whether the core option category
* identified by @key should be displayed in
* the frontend menu. (A category is deemed to
* be visible if at least one of the options
* in the category is visible)
*
* Returns: true if option category should be
* displayed by the frontend, otherwise false.
**/
bool core_option_manager_get_category_visible(core_option_manager_t *opt,
const char *key);
/******************/
/* Option Getters */
/******************/
/**
* core_option_manager_get_idx:
*
* @opt : options manager handle
* @key : core option key string (variable to query
* in RETRO_ENVIRONMENT_GET_VARIABLE)
* @idx : index of core option corresponding
* to @key
*
* Fetches the index of the core option identified
* by the specified @key.
*
* Returns: true if option matching the specified
* key was found, otherwise false.
**/
bool core_option_manager_get_idx(core_option_manager_t *opt,
const char *key, size_t *idx);
/**
* core_option_manager_get_desc:
*
* @opt : options manager handle
* @idx : idx identifier of the option
* @idx : core option index
* @categorized : flag specifying whether to
* fetch the categorised description
* or the legacy fallback
*
* Gets description for an option.
* Fetches the 'description' of the core option at
* index @idx (used as the option label in the menu).
* If menu has option category support, @categorized
* should be true. (At present, only the Qt interface
* requires @categorized to be false)
*
* Returns: Description for an option.
* Returns: description string (menu label) of the
* specified option if successful, otherwise NULL.
**/
const char *core_option_manager_get_desc(core_option_manager_t *opt,
size_t idx);
size_t idx, bool categorized);
/**
* core_option_manager_get_info:
*
* @opt : options manager handle
* @idx : idx identifier of the option
* @idx : core option index
* @categorized : flag specifying whether to
* fetch the categorised information
* or the legacy fallback
*
* Gets information text for an option.
* Fetches the 'info' text of the core option at
* index @idx (used as the option sublabel in the
* menu). If menu has option category support,
* @categorized should be true. (At present, only
* the Qt interface requires @categorized to be false)
*
* Returns: Information text for an option.
* Returns: information string (menu sublabel) of the
* specified option if successful, otherwise NULL.
**/
const char *core_option_manager_get_info(core_option_manager_t *opt,
size_t idx);
size_t idx, bool categorized);
/**
* core_option_manager_get_val:
*
* @opt : options manager handle
* @idx : idx identifier of the option
* @idx : core option index
*
* Gets value for an option.
* Fetches the string representation of the current
* value of the core option at index @idx.
*
* Returns: Value for an option.
* Returns: core option value string if successful,
* otherwise NULL.
**/
const char *core_option_manager_get_val(core_option_manager_t *opt,
size_t idx);
/**
* core_option_manager_get_val_label:
*
* @opt : options manager handle
* @idx : idx identifier of the option
* @idx : core option index
*
* Gets value label for an option.
* Fetches the 'label' text (used for display purposes
* in the menu) for the current value of the core
* option at index @idx.
*
* Returns: Value label for an option.
* Returns: core option value label string if
* successful, otherwise NULL.
**/
const char *core_option_manager_get_val_label(core_option_manager_t *opt,
size_t idx);
/**
* core_option_manager_get_visible:
*
* @opt : options manager handle
* @idx : idx identifier of the option
* @idx : core option index
*
* Gets whether option should be visible when displaying
* core options in the frontend
* Queries whether the core option at index @idx
* should be displayed in the frontend menu.
*
* Returns: 'true' if option should be displayed by the frontend.
* Returns: true if option should be displayed by
* the frontend, otherwise false.
**/
bool core_option_manager_get_visible(core_option_manager_t *opt,
size_t idx);
/******************/
/* Option Setters */
/******************/
/**
* core_option_manager_set_val:
*
* @opt : options manager handle
* @idx : core option index
* @val_idx : index of the value to set
*
* Sets the core option at index @idx to the
* option value corresponding to @val_idx.
**/
void core_option_manager_set_val(core_option_manager_t *opt,
size_t idx, size_t val_idx);
/**
* core_option_manager_adjust_val:
*
* @opt : options manager handle
* @idx : core option index
* @adjustment : offset to apply from current
* value index
*
* Modifies the value of the core option at index
* @idx by incrementing the current option value index
* by @adjustment.
**/
void core_option_manager_adjust_val(core_option_manager_t* opt,
size_t idx, int adjustment);
/**
* core_option_manager_set_default:
*
* @opt : options manager handle
* @idx : core option index
*
* Resets the core option at index @idx to its
* default value.
**/
void core_option_manager_set_default(core_option_manager_t *opt, size_t idx);
/**
* core_option_manager_set_visible:
*
* @opt : options manager handle
* @key : core option key string (variable to query
* in RETRO_ENVIRONMENT_GET_VARIABLE)
* @visible : flag specifying whether option should
* be shown in the menu
*
* Sets the in-menu visibility of the core option
* identified by the specified @key.
**/
void core_option_manager_set_visible(core_option_manager_t *opt,
const char *key, bool visible);
/**********************/
/* Configuration File */
/**********************/
/**
* core_option_manager_flush:
*
* @opt : options manager handle
* @conf : configuration file handle
*
* Writes all core option key-pair values from the
* specified core option manager handle to the
* specified configuration file struct.
**/
void core_option_manager_flush(core_option_manager_t *opt,
config_file_t *conf);
RETRO_END_DECLS
#endif

View File

@ -1081,6 +1081,7 @@ FILE
#include "../file_path_special.c"
#include "../libretro-common/lists/dir_list.c"
#include "../libretro-common/lists/string_list.c"
#include "../libretro-common/lists/nested_list.c"
#include "../libretro-common/lists/file_list.c"
#include "../libretro-common/file/retro_dirent.c"
#include "../libretro-common/streams/file_stream.c"
@ -1183,6 +1184,7 @@ FRONTEND
#include "../core_info.c"
#include "../core_backup.c"
#include "../core_option_manager.c"
#if defined(HAVE_NETWORKING)
#include "../core_updater_list.c"

View File

@ -1131,6 +1131,13 @@ enum retro_mod
* retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.
* This allows the core to additionally set option sublabel information
* and/or provide localisation support.
*
* If version is >= 2, core options may instead be set by passing
* a retro_core_options_v2 struct to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2,
* or an array of retro_core_options_v2 structs to
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL. This allows the core
* to additionally set optional core option category information
* for frontends with core option category support.
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53
@ -1172,7 +1179,7 @@ enum retro_mod
* default value is NULL, the first entry in the
* retro_core_option_definition::values array is treated as the default.
*
* The number of possible options should be very limited,
* The number of possible option values should be very limited,
* and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX.
* i.e. it should be feasible to cycle through options
* without a keyboard.
@ -1205,6 +1212,7 @@ enum retro_mod
* This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION
* returns an API version of >= 1.
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
* This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS.
* This should be called the first time as early as
* possible (ideally in retro_set_environment).
* Afterwards it may be called again for the core to communicate
@ -1493,6 +1501,217 @@ enum retro_mod
* retro_load_game_special()
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 67
/* const struct retro_core_options_v2 * --
* Allows an implementation to signal the environment
* which variables it might want to check for later using
* GET_VARIABLE.
* This allows the frontend to present these variables to
* a user dynamically.
* This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION
* returns an API version of >= 2.
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
* This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS.
* This should be called the first time as early as
* possible (ideally in retro_set_environment).
* Afterwards it may be called again for the core to communicate
* updated options to the frontend, but the number of core
* options must not change from the number in the initial call.
* If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API
* version of >= 2, this callback is guaranteed to succeed
* (i.e. callback return value does not indicate success)
* If callback returns true, frontend has core option category
* support.
* If callback returns false, frontend does not have core option
* category support.
*
* 'data' points to a retro_core_options_v2 struct, containing
* of two pointers:
* - retro_core_options_v2::categories is an array of
* retro_core_option_v2_category structs terminated by a
* { NULL, NULL, NULL } element. If retro_core_options_v2::categories
* is NULL, all core options will have no category and will be shown
* at the top level of the frontend core option interface. If frontend
* does not have core option category support, categories array will
* be ignored.
* - retro_core_options_v2::definitions is an array of
* retro_core_option_v2_definition structs terminated by a
* { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }
* element.
*
* >> retro_core_option_v2_category notes:
*
* - retro_core_option_v2_category::key should contain string
* that uniquely identifies the core option category. Valid
* key characters are [a-z, A-Z, 0-9, _, -]
* Namespace collisions with other implementations' category
* keys are permitted.
* - retro_core_option_v2_category::desc should contain a human
* readable description of the category key.
* - retro_core_option_v2_category::info should contain any
* additional human readable information text that a typical
* user may need to understand the nature of the core option
* category.
*
* Example entry:
* {
* "advanced_settings",
* "Advanced",
* "Options affecting low-level emulation performance and accuracy."
* }
*
* >> retro_core_option_v2_definition notes:
*
* - retro_core_option_v2_definition::key should be namespaced to not
* collide with other implementations' keys. e.g. A core called
* 'foo' should use keys named as 'foo_option'. Valid key characters
* are [a-z, A-Z, 0-9, _, -].
* - retro_core_option_v2_definition::desc should contain a human readable
* description of the key. Will be used when the frontend does not
* have core option category support. Examples: "Aspect Ratio" or
* "Video > Aspect Ratio".
* - retro_core_option_v2_definition::desc_categorized should contain a
* human readable description of the key, which will be used when
* frontend has core option category support. Example: "Aspect Ratio",
* where associated retro_core_option_v2_category::desc is "Video".
* If empty or NULL, the string specified by
* retro_core_option_v2_definition::desc will be used instead.
* retro_core_option_v2_definition::desc_categorized will be ignored
* if retro_core_option_v2_definition::category_key is empty or NULL.
* - retro_core_option_v2_definition::info should contain any additional
* human readable information text that a typical user may need to
* understand the functionality of the option.
* - retro_core_option_v2_definition::info_categorized should contain
* any additional human readable information text that a typical user
* may need to understand the functionality of the option, and will be
* used when frontend has core option category support. This is provided
* to accommodate the case where info text references an option by
* name/desc, and the desc/desc_categorized text for that option differ.
* If empty or NULL, the string specified by
* retro_core_option_v2_definition::info will be used instead.
* retro_core_option_v2_definition::info_categorized will be ignored
* if retro_core_option_v2_definition::category_key is empty or NULL.
* - retro_core_option_v2_definition::category_key should contain a
* category identifier (e.g. "video" or "audio") that will be
* assigned to the core option if frontend has core option category
* support. A categorized option will be shown in a subsection/
* submenu of the frontend core option interface. If key is empty
* or NULL, or if key does not match one of the
* retro_core_option_v2_category::key values in the associated
* retro_core_option_v2_category array, option will have no category
* and will be shown at the top level of the frontend core option
* interface.
* - retro_core_option_v2_definition::values is an array of
* retro_core_option_value structs terminated by a { NULL, NULL }
* element.
* --> retro_core_option_v2_definition::values[index].value is an
* expected option value.
* --> retro_core_option_v2_definition::values[index].label is a
* human readable label used when displaying the value on screen.
* If NULL, the value itself is used.
* - retro_core_option_v2_definition::default_value is the default
* core option setting. It must match one of the expected option
* values in the retro_core_option_v2_definition::values array. If
* it does not, or the default value is NULL, the first entry in the
* retro_core_option_v2_definition::values array is treated as the
* default.
*
* The number of possible option values should be very limited,
* and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX.
* i.e. it should be feasible to cycle through options
* without a keyboard.
*
* Example entries:
*
* - Uncategorized:
*
* {
* "foo_option",
* "Speed hack coprocessor X",
* NULL,
* "Provides increased performance at the expense of reduced accuracy.",
* NULL,
* NULL,
* {
* { "false", NULL },
* { "true", NULL },
* { "unstable", "Turbo (Unstable)" },
* { NULL, NULL },
* },
* "false"
* }
*
* - Categorized:
*
* {
* "foo_option",
* "Advanced > Speed hack coprocessor X",
* "Speed hack coprocessor X",
* "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy",
* "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy",
* "advanced_settings",
* {
* { "false", NULL },
* { "true", NULL },
* { "unstable", "Turbo (Unstable)" },
* { NULL, NULL },
* },
* "false"
* }
*
* Only strings are operated on. The possible values will
* generally be displayed and stored as-is by the frontend.
*/
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL 68
/* const struct retro_core_options_v2_intl * --
* Allows an implementation to signal the environment
* which variables it might want to check for later using
* GET_VARIABLE.
* This allows the frontend to present these variables to
* a user dynamically.
* This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION
* returns an API version of >= 2.
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
* This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS.
* This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.
* This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2.
* This should be called the first time as early as
* possible (ideally in retro_set_environment).
* Afterwards it may be called again for the core to communicate
* updated options to the frontend, but the number of core
* options must not change from the number in the initial call.
* If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API
* version of >= 2, this callback is guaranteed to succeed
* (i.e. callback return value does not indicate success)
* If callback returns true, frontend has core option category
* support.
* If callback returns false, frontend does not have core option
* category support.
*
* This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2,
* with the addition of localisation support. The description of the
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 callback should be consulted
* for further details.
*
* 'data' points to a retro_core_options_v2_intl struct.
*
* - retro_core_options_v2_intl::us is a pointer to a
* retro_core_options_v2 struct defining the US English
* core options implementation. It must point to a valid struct.
*
* - retro_core_options_v2_intl::local is a pointer to a
* retro_core_options_v2 struct defining core options for
* the current frontend language. It may be NULL (in which case
* retro_core_options_v2_intl::us is used by the frontend). Any items
* missing from this struct will be read from
* retro_core_options_v2_intl::us instead.
*
* NOTE: Default core option values are always taken from the
* retro_core_options_v2_intl::us struct. Any default values in
* the retro_core_options_v2_intl::local struct will be ignored.
*/
/* VFS functionality */
/* File paths:
@ -3214,6 +3433,124 @@ struct retro_core_options_intl
struct retro_core_option_definition *local;
};
struct retro_core_option_v2_category
{
/* Variable uniquely identifying the
* option category. Valid key characters
* are [a-z, A-Z, 0-9, _, -] */
const char *key;
/* Human-readable category description
* > Used as category menu label when
* frontend has core option category
* support */
const char *desc;
/* Human-readable category information
* > Used as category menu sublabel when
* frontend has core option category
* support
* > Optional (may be NULL or an empty
* string) */
const char *info;
};
struct retro_core_option_v2_definition
{
/* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE.
* Valid key characters are [a-z, A-Z, 0-9, _, -] */
const char *key;
/* Human-readable core option description
* > Used as menu label when frontend does
* not have core option category support
* e.g. "Video > Aspect Ratio" */
const char *desc;
/* Human-readable core option description
* > Used as menu label when frontend has
* core option category support
* e.g. "Aspect Ratio", where associated
* retro_core_option_v2_category::desc
* is "Video"
* > If empty or NULL, the string specified by
* desc will be used as the menu label
* > Will be ignored (and may be set to NULL)
* if category_key is empty or NULL */
const char *desc_categorized;
/* Human-readable core option information
* > Used as menu sublabel */
const char *info;
/* Human-readable core option information
* > Used as menu sublabel when frontend
* has core option category support
* (e.g. may be required when info text
* references an option by name/desc,
* and the desc/desc_categorized text
* for that option differ)
* > If empty or NULL, the string specified by
* info will be used as the menu sublabel
* > Will be ignored (and may be set to NULL)
* if category_key is empty or NULL */
const char *info_categorized;
/* Variable specifying category (e.g. "video",
* "audio") that will be assigned to the option
* if frontend has core option category support.
* > Categorized options will be displayed in a
* subsection/submenu of the frontend core
* option interface
* > Specified string must match one of the
* retro_core_option_v2_category::key values
* in the associated retro_core_option_v2_category
* array; If no match is not found, specified
* string will be considered as NULL
* > If specified string is empty or NULL, option will
* have no category and will be shown at the top
* level of the frontend core option interface */
const char *category_key;
/* Array of retro_core_option_value structs, terminated by NULL */
struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
/* Default core option value. Must match one of the values
* in the retro_core_option_value array, otherwise will be
* ignored */
const char *default_value;
};
struct retro_core_options_v2
{
/* Array of retro_core_option_v2_category structs,
* terminated by NULL
* > If NULL, all entries in definitions array
* will have no category and will be shown at
* the top level of the frontend core option
* interface
* > Will be ignored if frontend does not have
* core option category support */
struct retro_core_option_v2_category *categories;
/* Array of retro_core_option_v2_definition structs,
* terminated by NULL */
struct retro_core_option_v2_definition *definitions;
};
struct retro_core_options_v2_intl
{
/* Pointer to a retro_core_options_v2 struct
* > US English implementation
* > Must point to a valid struct */
struct retro_core_options_v2 *us;
/* Pointer to a retro_core_options_v2 struct
* - Implementation for current frontend language
* - May be NULL */
struct retro_core_options_v2 *local;
};
struct retro_game_info
{
const char *path; /* Path to game, UTF-8 encoded.

View File

@ -0,0 +1,242 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nested_list.h).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __LIBRETRO_SDK_NESTED_LIST_H__
#define __LIBRETRO_SDK_NESTED_LIST_H__
#include <retro_common_api.h>
#include <boolean.h>
#include <stdlib.h>
#include <stddef.h>
RETRO_BEGIN_DECLS
/* Prevent direct access to nested_list_* members */
typedef struct nested_list_item nested_list_item_t;
typedef struct nested_list nested_list_t;
/**************************************/
/* Initialisation / De-Initialisation */
/**************************************/
/**
* nested_list_init:
*
* Creates a new empty nested list. Returned pointer
* must be freed using nested_list_free.
*
* Returns: Valid nested_list_t pointer if successful,
* otherwise NULL.
*/
nested_list_t *nested_list_init(void);
/**
* nested_list_free:
*
* @list : pointer to nested_list_t object
*
* Frees specified nested list.
*/
void nested_list_free(nested_list_t *list);
/***********/
/* Setters */
/***********/
/**
* nested_list_add_item:
*
* @list : pointer to nested_list_t object
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @delim : delimiter to use when splitting @address
* into individual ids
* @value : optional value (user data) associated with
* new list item. This is added to the last
* item specified by @address
*
* Appends a new item to the specified nested list.
* If @delim is NULL, item is added to the top level
* list (@list itself) with id equal to @address.
* Otherwise, @address is split by @delim and each
* id is added as new 'layer'. For example:
*
* > @address = "one:two:three", @delim = ":" will
* produce:
* top_level_list:one
* `- "one" list:two
* `- "two" list:three
* where @value is assigned to the "two" list:three
* item.
*
* Returns: true if successful, otherwise false. Will
* always return false if item specified by @address
* already exists in the nested list.
*/
bool nested_list_add_item(nested_list_t *list,
const char *address, const char *delim, const void *value);
/***********/
/* Getters */
/***********/
/**
* nested_list_get_size:
*
* @list : pointer to nested_list_t object
*
* Fetches the current size (number of items) in
* the specified list.
*
* Returns: list size.
*/
size_t nested_list_get_size(nested_list_t *list);
/**
* nested_list_get_item:
*
* @list : pointer to nested_list_t object
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @delim : delimiter to use when splitting @address
* into individual ids
*
* Searches for (and returns) the list item corresponding
* to @address. If @delim is NULL, the top level list
* (@list itself) is searched for an item with an id
* equal to @address. Otherwise, @address is split by
* @delim and each id is searched for in a subsequent
* list level.
*
* Returns: valid nested_list_item_t pointer if item
* is found, otherwise NULL.
*/
nested_list_item_t *nested_list_get_item(nested_list_t *list,
const char *address, const char *delim);
/**
* nested_list_get_item_idx:
*
* @list : pointer to nested_list_t object
* @idx : item index
*
* Fetches the item corresponding to index @idx in
* the top level list (@list itself) of the specified
* nested list.
*
* Returns: valid nested_list_item_t pointer if item
* exists, otherwise NULL.
*/
nested_list_item_t *nested_list_get_item_idx(nested_list_t *list,
size_t idx);
/**
* nested_list_item_get_parent:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the parent item of the specified nested
* list item. If returned value is NULL, specified
* nested list item belongs to a top level list.
*
* Returns: valid nested_list_item_t pointer if item
* has a parent, otherwise NULL.
*/
nested_list_item_t *nested_list_item_get_parent(nested_list_item_t *list_item);
/**
* nested_list_item_get_parent_list:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches a pointer to the nested list of which the
* specified list item is a direct member.
*
* Returns: valid nested_list_t pointer if successful,
* otherwise NULL.
*/
nested_list_t *nested_list_item_get_parent_list(nested_list_item_t *list_item);
/**
* nested_list_item_get_children:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches a pointer to the nested list of child items
* belonging to the specified list item.
*
* Returns: valid nested_list_t pointer if item has
* children, otherwise NULL.
*/
nested_list_t *nested_list_item_get_children(nested_list_item_t *list_item);
/**
* nested_list_item_get_id:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the id string of the specified list item,
* as set by nested_list_add_item().
*
* Returns: item id if successful, otherwise NULL.
*/
const char *nested_list_item_get_id(nested_list_item_t *list_item);
/**
* nested_list_item_get_address:
*
* @list_item : pointer to nested_list_item_t object
* @delim : delimiter to use when concatenating
* individual item ids into a an @address
* string
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @len : length of supplied @address char array
* Fetches a compound @address string corresponding to
* the specified item's 'position' in the top level
* nested list of which it is a member. The resultant
* @address may be used to find the item when calling
* nested_list_get_item() on the top level nested list.
*
* Returns: true if successful, otherwise false.
*/
bool nested_list_item_get_address(nested_list_item_t *list_item,
const char *delim, char *address, size_t len);
/**
* nested_list_item_get_value:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the value (user data) associated with the
* specified list item.
*
* Returns: pointer to user data if set, otherwise
* NULL.
*/
const void *nested_list_item_get_value(nested_list_item_t *list_item);
RETRO_END_DECLS
#endif

View File

@ -0,0 +1,604 @@
/* Copyright (C) 2010-2020 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (nested_list.c).
* ---------------------------------------------------------------------------------------
*
* Permission is hereby granted, free of charge,
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <string/stdstring.h>
#include <lists/string_list.h>
#include <array/rbuf.h>
#include <array/rhmap.h>
#include <lists/nested_list.h>
struct nested_list_item
{
nested_list_item_t *parent_item;
nested_list_t *parent_list;
nested_list_t *children;
char *id;
const void *value;
};
struct nested_list
{
nested_list_item_t *items;
nested_list_item_t **item_map;
};
/**************************************/
/* Initialisation / De-Initialisation */
/**************************************/
/* Forward declaration - required since
* nested_list_free_list() is recursive */
static void nested_list_free_list(nested_list_t *list);
/* Frees contents of a nested list item */
static void nested_list_free_item(nested_list_item_t *item)
{
if (!item)
return;
item->parent_item = NULL;
item->parent_list = NULL;
if (item->children)
{
nested_list_free_list(item->children);
free(item->children);
item->children = NULL;
}
if (item->id)
{
free(item->id);
item->id = NULL;
}
item->value = NULL;
}
/* Frees contents of a nested list */
static void nested_list_free_list(nested_list_t *list)
{
size_t i;
if (!list)
return;
for (i = 0; i < RBUF_LEN(list->items); i++)
nested_list_free_item(&list->items[i]);
RBUF_FREE(list->items);
RHMAP_FREE(list->item_map);
}
/**
* nested_list_init:
*
* Creates a new empty nested list. Returned pointer
* must be freed using nested_list_free.
*
* Returns: Valid nested_list_t pointer if successful,
* otherwise NULL.
*/
nested_list_t *nested_list_init(void)
{
/* Create nested list */
nested_list_t *list = (nested_list_t*)malloc(sizeof(*list));
if (!list)
return NULL;
/* Initialise members */
list->items = NULL;
list->item_map = NULL;
return list;
}
/**
* nested_list_free:
* @list : pointer to nested_list_t object
*
* Frees specified nested list.
*/
void nested_list_free(nested_list_t *list)
{
nested_list_free_list(list);
free(list);
}
/***********/
/* Setters */
/***********/
/* Creates and adds a new item to the specified
* nested list. Returns NULL if item matching 'id'
* already exists */
static nested_list_item_t *nested_list_add_item_to_list(nested_list_t *list,
nested_list_item_t *parent_item, const char *id, const void *value)
{
size_t num_items = 0;
nested_list_item_t *new_item = NULL;
nested_list_t *child_list = NULL;
if (!list || string_is_empty(id))
goto end;
num_items = RBUF_LEN(list->items);
/* Ensure that item does not already exist */
if (RHMAP_HAS_STR(list->item_map, id))
goto end;
/* Create new empty child list */
child_list = nested_list_init();
if (!child_list)
goto end;
/* Attempt to allocate memory for new item */
if (!RBUF_TRYFIT(list->items, num_items + 1))
goto end;
/* Allocation successful - increment array size */
RBUF_RESIZE(list->items, num_items + 1);
/* Get handle of new entry at end of list */
new_item = &list->items[num_items];
/* Assign members */
new_item->parent_item = parent_item;
new_item->parent_list = list;
new_item->children = child_list;
new_item->id = strdup(id);
new_item->value = value;
/* Update map */
RHMAP_SET_STR(list->item_map, id, new_item);
end:
return new_item;
}
/**
* nested_list_add_item:
*
* @list : pointer to nested_list_t object
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @delim : delimiter to use when splitting @address
* into individual ids
* @value : optional value (user data) associated with
* new list item. This is added to the last
* item specified by @address
*
* Appends a new item to the specified nested list.
* If @delim is NULL, item is added to the top level
* list (@list itself) with id equal to @address.
* Otherwise, @address is split by @delim and each
* id is added as new 'layer'. For example:
*
* > @address = "one:two:three", @delim = ":" will
* produce:
* top_level_list:one
* `- "one" list:two
* `- "two" list:three
* where @value is assigned to the "two" list:three
* item.
*
* Returns: true if successful, otherwise false. Will
* always return false if item specified by @address
* already exists in the nested list.
*/
bool nested_list_add_item(nested_list_t *list,
const char *address, const char *delim, const void *value)
{
struct string_list id_list = {0};
const char *top_id = NULL;
bool success = false;
if (!list || string_is_empty(address))
goto end;
/* If delim is NULL or address contains a single
* token, then we are adding an item to the top
* level list */
if (string_is_empty(delim))
top_id = address;
else
{
string_list_initialize(&id_list);
if (!string_split_noalloc(&id_list, address, delim) ||
(id_list.size < 1))
goto end;
if (id_list.size == 1)
top_id = id_list.elems[0].data;
}
if (!string_is_empty(top_id))
{
if (nested_list_add_item_to_list(list, NULL, top_id, value))
success = true;
}
else
{
nested_list_t *current_list = list;
nested_list_item_t *parent_item = NULL;
nested_list_item_t *next_item = NULL;
size_t i;
/* Loop over list item ids */
for (i = 0; i < id_list.size; i++)
{
const char *id = id_list.elems[i].data;
if (string_is_empty(id))
goto end;
/* If this is the last entry in the id list,
* then we are adding the item itself */
if (i == (id_list.size - 1))
{
if (nested_list_add_item_to_list(current_list,
parent_item, id, value))
success = true;
break;
}
/* Otherwise, id corresponds to a 'category' */
else
{
/* Check whether category item already exists */
next_item = RHMAP_GET_STR(current_list->item_map, id);
/* Create it, if required */
if (!next_item)
next_item = nested_list_add_item_to_list(current_list,
parent_item, id, NULL);
if (!next_item)
break;
/* Update pointers */
parent_item = next_item;
current_list = next_item->children;
}
}
}
end:
string_list_deinitialize(&id_list);
return success;
}
/***********/
/* Getters */
/***********/
/**
* nested_list_get_size:
*
* @list : pointer to nested_list_t object
*
* Fetches the current size (number of items) in
* the specified list.
*
* Returns: list size.
*/
size_t nested_list_get_size(nested_list_t *list)
{
if (!list)
return 0;
return RBUF_LEN(list->items);
}
/**
* nested_list_get_item:
*
* @list : pointer to nested_list_t object
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @delim : delimiter to use when splitting @address
* into individual ids
*
* Searches for (and returns) the list item corresponding
* to @address. If @delim is NULL, the top level list
* (@list itself) is searched for an item with an id
* equal to @address. Otherwise, @address is split by
* @delim and each id is searched for in a subsequent
* list level.
*
* Returns: valid nested_list_item_t pointer if item
* is found, otherwise NULL.
*/
nested_list_item_t *nested_list_get_item(nested_list_t *list,
const char *address, const char *delim)
{
nested_list_item_t *search_item = NULL;
struct string_list id_list = {0};
const char *top_id = NULL;
if (!list || string_is_empty(address))
goto end;
/* If delim is NULL or address contains a single
* token, then we are fetching an item from the
* top level list */
if (string_is_empty(delim))
top_id = address;
else
{
string_list_initialize(&id_list);
if (!string_split_noalloc(&id_list, address, delim) ||
(id_list.size < 1))
goto end;
if (id_list.size == 1)
top_id = id_list.elems[0].data;
}
if (!string_is_empty(top_id))
search_item = RHMAP_GET_STR(list->item_map, top_id);
else
{
/* Otherwise, search 'category' levels */
nested_list_t *current_list = list;
nested_list_item_t *next_item = NULL;
size_t i;
/* Loop over list item ids */
for (i = 0; i < id_list.size; i++)
{
const char *id = id_list.elems[i].data;
if (string_is_empty(id))
goto end;
/* If this is the last entry in the id list,
* then we are searching for the item itself */
if (i == (id_list.size - 1))
{
search_item = RHMAP_GET_STR(current_list->item_map, id);
break;
}
/* Otherwise, id corresponds to a 'category' */
else
{
next_item = RHMAP_GET_STR(current_list->item_map, id);
if (!next_item)
break;
/* Update pointer */
current_list = next_item->children;
}
}
}
end:
string_list_deinitialize(&id_list);
return search_item;
}
/**
* nested_list_get_item_idx:
*
* @list : pointer to nested_list_t object
* @idx : item index
*
* Fetches the item corresponding to index @idx in
* the top level list (@list itself) of the specified
* nested list.
*
* Returns: valid nested_list_item_t pointer if item
* exists, otherwise NULL.
*/
nested_list_item_t *nested_list_get_item_idx(nested_list_t *list,
size_t idx)
{
if (!list || (idx >= RBUF_LEN(list->items)))
return NULL;
return &list->items[idx];
}
/**
* nested_list_item_get_parent:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the parent item of the specified nested
* list item. If returned value is NULL, specified
* nested list item belongs to a top level list.
*
* Returns: valid nested_list_item_t pointer if item
* has a parent, otherwise NULL.
*/
nested_list_item_t *nested_list_item_get_parent(nested_list_item_t *list_item)
{
if (!list_item)
return NULL;
return list_item->parent_item;
}
/**
* nested_list_item_get_parent_list:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches a pointer to the nested list of which the
* specified list item is a direct member.
*
* Returns: valid nested_list_t pointer if successful,
* otherwise NULL.
*/
nested_list_t *nested_list_item_get_parent_list(nested_list_item_t *list_item)
{
if (!list_item)
return NULL;
return list_item->parent_list;
}
/**
* nested_list_item_get_children:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches a pointer to the nested list of child items
* belonging to the specified list item.
*
* Returns: valid nested_list_t pointer if item has
* children, otherwise NULL.
*/
nested_list_t *nested_list_item_get_children(nested_list_item_t *list_item)
{
if (!list_item ||
!list_item->children ||
(RBUF_LEN(list_item->children->items) < 1))
return NULL;
return list_item->children;
}
/**
* nested_list_item_get_id:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the id string of the specified list item,
* as set by nested_list_add_item().
*
* Returns: item id if successful, otherwise NULL.
*/
const char *nested_list_item_get_id(nested_list_item_t *list_item)
{
if (!list_item)
return NULL;
return list_item->id;
}
/**
* nested_list_item_get_address:
*
* @list_item : pointer to nested_list_item_t object
* @delim : delimiter to use when concatenating
* individual item ids into a an @address
* string
* @address : a delimited list of item identifiers,
* corresponding to item 'levels'
* @len : length of supplied @address char array
* Fetches a compound @address string corresponding to
* the specified item's 'position' in the top level
* nested list of which it is a member. The resultant
* @address may be used to find the item when calling
* nested_list_get_item() on the top level nested list.
*
* Returns: true if successful, otherwise false.
*/
bool nested_list_item_get_address(nested_list_item_t *list_item,
const char *delim, char *address, size_t len)
{
nested_list_item_t *current_item = list_item;
struct string_list id_list = {0};
bool success = false;
union string_list_elem_attr attr;
size_t i;
if (!list_item ||
string_is_empty(delim) ||
!address ||
(len < 1))
goto end;
address[0] = '\0';
attr.i = 0;
/* If this is an item of the top level
* list, just copy the item id directly */
if (!list_item->parent_item)
{
strlcpy(address, list_item->id, len);
success = true;
goto end;
}
/* ...otherwise we have to combine the ids
* of the item and all of its 'ancestors' */
string_list_initialize(&id_list);
/* Fetch all ids */
do
{
const char *id = current_item->id;
if (string_is_empty(id) ||
!string_list_append(&id_list, id, attr))
goto end;
current_item = current_item->parent_item;
}
while (current_item);
if (id_list.size < 1)
goto end;
/* Build address string */
for (i = id_list.size; i > 0; i--)
{
const char *id = id_list.elems[i - 1].data;
if (string_is_empty(id))
goto end;
strlcat(address, id, len);
if (i > 1)
strlcat(address, delim, len);
}
success = true;
end:
string_list_deinitialize(&id_list);
return success;
}
/**
* nested_list_item_get_value:
*
* @list_item : pointer to nested_list_item_t object
*
* Fetches the value (user data) associated with the
* specified list item.
*
* Returns: pointer to user data if set, otherwise
* NULL.
*/
const void *nested_list_item_get_value(nested_list_item_t *list_item)
{
if (!list_item)
return NULL;
return list_item->value;
}

View File

@ -1497,6 +1497,41 @@ static void menu_action_setting_disp_set_label_core_options(
char *s, size_t len,
const char *path,
char *s2, size_t len2)
{
const char *category = path;
const char *desc = NULL;
/* Add 'more' value text */
strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_MORE), len);
*w = 19;
/* If this is an options subcategory, fetch
* the category description */
if (!string_is_empty(category))
{
core_option_manager_t *coreopts = NULL;
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
desc = core_option_manager_get_category_desc(
coreopts, category);
}
/* If this isn't a subcategory (or something
* went wrong...), use top level core options
* menu label */
if (string_is_empty(desc))
desc = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_OPTIONS);
strlcpy(s2, desc, len2);
}
static void menu_action_setting_disp_set_label_core_option(
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_option_manager_t *coreopts = NULL;
const char *coreopt_label = NULL;
@ -1783,11 +1818,14 @@ static int menu_cbs_init_bind_get_string_representation_compare_label(
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_menu_video_resolution);
break;
case MENU_ENUM_LABEL_CORE_OPTIONS:
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_options);
break;
case MENU_ENUM_LABEL_PLAYLISTS_TAB:
case MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY:
case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST:
case MENU_ENUM_LABEL_FAVORITES:
case MENU_ENUM_LABEL_CORE_OPTIONS:
case MENU_ENUM_LABEL_CORE_OPTION_OVERRIDE_LIST:
case MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS:
case MENU_ENUM_LABEL_SHADER_OPTIONS:
@ -2176,7 +2214,7 @@ int menu_cbs_init_bind_get_string_representation(menu_file_list_cbs_t *cbs,
(type < MENU_SETTINGS_CHEEVOS_START))
{
BIND_ACTION_GET_VALUE(cbs,
menu_action_setting_disp_set_label_core_options);
menu_action_setting_disp_set_label_core_option);
return 0;
}

View File

@ -1172,6 +1172,13 @@ int generic_action_ok_displaylist_push(const char *path,
info_label = label;
dl_type = DISPLAYLIST_FILE_BROWSER_SELECT_FILE;
break;
case ACTION_OK_DL_CORE_OPTIONS_LIST:
info.type = type;
info.directory_ptr = idx;
info_path = path;
info_label = label;
dl_type = DISPLAYLIST_GENERIC;
break;
case ACTION_OK_DL_CONTENT_COLLECTION_LIST:
info.type = type;
info.directory_ptr = idx;
@ -3774,6 +3781,8 @@ int action_ok_core_option_dropdown_list(const char *path,
option_path_str[0] = '\0';
option_lbl_str[0] = '\0';
option_index = type - MENU_SETTINGS_CORE_OPTION_START;
/* Boolean options are toggled directly,
* without the use of a drop-down list */
@ -3781,8 +3790,6 @@ int action_ok_core_option_dropdown_list(const char *path,
if (type < MENU_SETTINGS_CORE_OPTION_START)
goto push_dropdown_list;
option_index = type - MENU_SETTINGS_CORE_OPTION_START;
/* > Get core options struct */
if (!rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts) ||
(option_index >= coreopts->size))
@ -3822,7 +3829,7 @@ push_dropdown_list:
/* If this option is not a boolean toggle,
* push drop-down list */
snprintf(option_path_str, sizeof(option_path_str),
"core_option_%d", (int)idx);
"core_option_%d", (int)option_index);
snprintf(option_lbl_str, sizeof(option_lbl_str),
"%d", type);
@ -5628,6 +5635,7 @@ DEFAULT_ACTION_OK_FUNC(action_ok_push_manual_content_scan_list, ACTION_OK_DL_MAN
DEFAULT_ACTION_OK_FUNC(action_ok_manual_content_scan_dat_file, ACTION_OK_DL_MANUAL_CONTENT_SCAN_DAT_FILE)
DEFAULT_ACTION_OK_FUNC(action_ok_push_core_manager_list, ACTION_OK_DL_CORE_MANAGER_LIST)
DEFAULT_ACTION_OK_FUNC(action_ok_push_core_option_override_list, ACTION_OK_DL_CORE_OPTION_OVERRIDE_LIST)
DEFAULT_ACTION_OK_FUNC(action_ok_push_core_options_list, ACTION_OK_DL_CORE_OPTIONS_LIST)
static int action_ok_open_uwp_permission_settings(const char *path,
const char *label, unsigned type, size_t idx, size_t entry_idx)
@ -7722,7 +7730,7 @@ static int menu_cbs_init_bind_ok_compare_label(menu_file_list_cbs_t *cbs,
{MENU_ENUM_LABEL_DUMP_DISC, action_ok_push_dump_disc_list},
{MENU_ENUM_LABEL_LOAD_DISC, action_ok_push_load_disc_list},
{MENU_ENUM_LABEL_SHADER_OPTIONS, action_ok_push_default},
{MENU_ENUM_LABEL_CORE_OPTIONS, action_ok_push_default},
{MENU_ENUM_LABEL_CORE_OPTIONS, action_ok_push_core_options_list},
{MENU_ENUM_LABEL_CORE_OPTION_OVERRIDE_LIST, action_ok_push_core_option_override_list},
{MENU_ENUM_LABEL_CORE_CHEAT_OPTIONS, action_ok_push_default},
{MENU_ENUM_LABEL_CORE_INPUT_REMAPPING_OPTIONS, action_ok_push_default},

View File

@ -681,7 +681,6 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_save_current_config_override_content
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_save_current_config_override_game, MENU_ENUM_SUBLABEL_SAVE_CURRENT_CONFIG_OVERRIDE_GAME)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_shader_options, MENU_ENUM_SUBLABEL_SHADER_OPTIONS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_input_remapping_options, MENU_ENUM_SUBLABEL_CORE_INPUT_REMAPPING_OPTIONS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_options, MENU_ENUM_SUBLABEL_CORE_OPTIONS)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_option_override_list, MENU_ENUM_SUBLABEL_CORE_OPTION_OVERRIDE_LIST)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_options_reset, MENU_ENUM_SUBLABEL_CORE_OPTIONS_RESET)
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_show_advanced_settings, MENU_ENUM_SUBLABEL_SHOW_ADVANCED_SETTINGS)
@ -1506,6 +1505,41 @@ static int action_bind_sublabel_playlist_entry(
return 0;
}
static int action_bind_sublabel_core_options(
file_list_t *list,
unsigned type, unsigned i,
const char *label, const char *path,
char *s, size_t len)
{
const char *category = path;
const char *info = NULL;
/* If this is an options subcategory, fetch
* the category info string */
if (!string_is_empty(category))
{
core_option_manager_t *coreopts = NULL;
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
info = core_option_manager_get_category_info(
coreopts, category);
}
/* If this isn't a subcategory (or something
* went wrong...), use top level core options
* menu sublabel */
if (string_is_empty(info))
info = msg_hash_to_str(MENU_ENUM_SUBLABEL_CORE_OPTIONS);
if (!string_is_empty(info))
{
strlcpy(s, info, len);
return 1;
}
return 0;
}
static int action_bind_sublabel_core_option(
file_list_t *list,
unsigned type, unsigned i,
@ -1518,7 +1552,8 @@ static int action_bind_sublabel_core_option(
if (!rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &opt))
return 0;
info = core_option_manager_get_info(opt, type - MENU_SETTINGS_CORE_OPTION_START);
info = core_option_manager_get_info(opt,
type - MENU_SETTINGS_CORE_OPTION_START, true);
if (!string_is_empty(info))
strlcpy(s, info, len);

View File

@ -202,6 +202,39 @@ static int action_get_title_left_thumbnails(
return 0;
}
static int action_get_title_core_options_list(
const char *path, const char *label, unsigned menu_type,
char *s, size_t len)
{
const char *category = path;
const char *title = NULL;
/* If this is an options subcategory, fetch
* the category description */
if (!string_is_empty(category))
{
core_option_manager_t *coreopts = NULL;
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
title = core_option_manager_get_category_desc(
coreopts, category);
}
/* If this isn't a subcategory (or something
* went wrong...), use top level core options
* menu label */
if (string_is_empty(title))
title = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_OPTIONS);
if (s && !string_is_empty(title))
{
strlcpy(s, title, len);
return 1;
}
return 0;
}
static int action_get_title_dropdown_item(
const char *path, const char *label, unsigned menu_type,
char *s, size_t len)
@ -210,10 +243,12 @@ static int action_get_title_dropdown_item(
if (string_is_empty(path))
return 0;
if (string_starts_with_size(path, "core_option_", STRLEN_CONST("core_option_")))
if (string_starts_with_size(path, "core_option_",
STRLEN_CONST("core_option_")))
{
/* This is a core options item */
struct string_list tmp_str_list = {0};
core_option_manager_t *coreopts = NULL;
int ret = 0;
string_list_initialize(&tmp_str_list);
@ -221,53 +256,22 @@ static int action_get_title_dropdown_item(
if (tmp_str_list.size > 0)
{
core_option_manager_t *coreopts = NULL;
rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
if (coreopts)
{
unsigned i;
unsigned menu_index = string_to_unsigned(
unsigned option_index = string_to_unsigned(
tmp_str_list.elems[(unsigned)tmp_str_list.size - 1].data);
unsigned visible_index = 0;
unsigned option_index = 0;
bool option_found = false;
settings_t *settings = config_get_ptr();
bool game_specific_options = settings->bools.game_specific_options;
/* Convert menu index to option index */
if (game_specific_options)
menu_index--;
for (i = 0; i < coreopts->size; i++)
{
if (core_option_manager_get_visible(coreopts, i))
{
if (visible_index == menu_index)
{
option_found = true;
option_index = i;
break;
}
visible_index++;
}
}
/* If option was found, title == option description */
if (option_found)
{
const char *title = core_option_manager_get_desc(
coreopts, option_index);
coreopts, option_index, true);
if (s && !string_is_empty(title))
{
SANITIZE_TO_STRING(s, title, len);
strlcpy(s, title, len);
ret = 1;
}
}
}
}
/* Clean up */
string_list_deinitialize(&tmp_str_list);
@ -568,7 +572,6 @@ DEFAULT_TITLE_MACRO(action_get_online_thumbnails_updater_list, MENU_ENUM_LABEL_
DEFAULT_TITLE_MACRO(action_get_online_pl_thumbnails_updater_list, MENU_ENUM_LABEL_VALUE_PL_THUMBNAILS_UPDATER_LIST)
DEFAULT_TITLE_MACRO(action_get_add_content_list, MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST)
DEFAULT_TITLE_MACRO(action_get_configurations_list, MENU_ENUM_LABEL_VALUE_CONFIGURATIONS_LIST)
DEFAULT_TITLE_MACRO(action_get_core_options_list, MENU_ENUM_LABEL_VALUE_CORE_OPTIONS)
DEFAULT_TITLE_MACRO(action_get_core_option_override_list, MENU_ENUM_LABEL_VALUE_CORE_OPTION_OVERRIDE_LIST)
DEFAULT_TITLE_MACRO(action_get_quick_menu_list, MENU_ENUM_LABEL_VALUE_CONTENT_SETTINGS)
DEFAULT_TITLE_MACRO(action_get_input_remapping_options_list, MENU_ENUM_LABEL_VALUE_CORE_INPUT_REMAPPING_OPTIONS)
@ -1130,7 +1133,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
{MENU_ENUM_LABEL_ADD_CONTENT_LIST,
action_get_add_content_list},
{MENU_ENUM_LABEL_CORE_OPTIONS,
action_get_core_options_list},
action_get_title_core_options_list},
{MENU_ENUM_LABEL_DEFERRED_CORE_OPTION_OVERRIDE_LIST,
action_get_core_option_override_list},
{MENU_ENUM_LABEL_CONTENT_SETTINGS,
@ -1451,7 +1454,7 @@ static int menu_cbs_init_bind_title_compare_label(menu_file_list_cbs_t *cbs,
BIND_ACTION_GET_TITLE(cbs, action_get_configurations_list);
break;
case MENU_ENUM_LABEL_CORE_OPTIONS:
BIND_ACTION_GET_TITLE(cbs, action_get_core_options_list);
BIND_ACTION_GET_TITLE(cbs, action_get_title_core_options_list);
break;
case MENU_ENUM_LABEL_DEFERRED_CORE_OPTION_OVERRIDE_LIST:
BIND_ACTION_GET_TITLE(cbs, action_get_core_option_override_list);

View File

@ -214,7 +214,8 @@ enum
ACTION_OK_DL_MANUAL_CONTENT_SCAN_DAT_FILE,
ACTION_OK_DL_CORE_RESTORE_BACKUP_LIST,
ACTION_OK_DL_CORE_DELETE_BACKUP_LIST,
ACTION_OK_DL_CORE_OPTION_OVERRIDE_LIST
ACTION_OK_DL_CORE_OPTION_OVERRIDE_LIST,
ACTION_OK_DL_CORE_OPTIONS_LIST
};
/* Function callbacks */

View File

@ -932,6 +932,96 @@ static unsigned menu_displaylist_parse_core_manager_list(
return count;
}
static unsigned menu_displaylist_parse_core_option_dropdown_list(
menu_displaylist_info_t *info)
{
unsigned count = 0;
struct string_list tmp_str_list = {0};
unsigned option_index = 0;
unsigned checked = 0;
bool checked_found = false;
core_option_manager_t *coreopts = NULL;
struct core_option *option = NULL;
const char *val = NULL;
unsigned i, j;
/* Fetch options */
rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
if (!coreopts)
goto end;
/* Path string has the format core_option_<opt_idx>
* > Extract option index */
if (string_is_empty(info->path))
goto end;
string_list_initialize(&tmp_str_list);
string_split_noalloc(&tmp_str_list, info->path, "_");
if (tmp_str_list.size < 1)
goto end;
option_index = string_to_unsigned(
tmp_str_list.elems[tmp_str_list.size - 1].data);
/* Get option itself + current value */
option = (struct core_option*)&coreopts->opts[option_index];
val = core_option_manager_get_val(coreopts, option_index);
if (!option ||
string_is_empty(val))
goto end;
/* Loop over all option values */
for (j = 0; j < option->vals->size; j++)
{
const char *val_str = option->vals->elems[j].data;
const char *val_label_str = option->val_labels->elems[j].data;
if (!string_is_empty(val_label_str))
{
char val_d[256];
val_d[0] = '\0';
snprintf(val_d, sizeof(val_d), "%d", option_index);
if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON);
else if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
if (menu_entries_append_enum(info->list,
val_label_str,
val_d,
MENU_ENUM_LABEL_NO_ITEMS,
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM, j, 0))
count++;
if (!checked_found && string_is_equal(val_str, val))
{
checked = j;
checked_found = true;
}
}
}
if (checked_found)
{
menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)
info->list->list[checked].actiondata;
if (cbs)
cbs->checked = true;
menu_navigation_set_selection(checked);
}
end:
string_list_deinitialize(&tmp_str_list);
return count;
}
static unsigned menu_displaylist_parse_core_option_override_list(
menu_displaylist_info_t *info)
{
@ -2929,8 +3019,10 @@ static int menu_displaylist_parse_load_content_settings(
if (settings->bools.quick_menu_show_options && !settings->bools.kiosk_mode_enable)
{
/* Empty 'path' string signifies top level
* core options menu */
if (menu_entries_append_enum(list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_OPTIONS),
"",
msg_hash_to_str(MENU_ENUM_LABEL_CORE_OPTIONS),
MENU_ENUM_LABEL_CORE_OPTIONS,
MENU_SETTING_ACTION_CORE_OPTIONS, 0, 0))
@ -11191,12 +11283,12 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
if (rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL))
{
size_t num_opts = 0;
bool game_specific_options = settings->bools.game_specific_options;
const char *category = info->path;
bool is_category = !string_is_empty(category);
core_option_manager_t *coreopts = NULL;
rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &num_opts);
if (game_specific_options)
if (game_specific_options && !is_category)
if (menu_entries_append_enum(info->list,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CORE_OPTION_OVERRIDE_LIST),
msg_hash_to_str(MENU_ENUM_LABEL_CORE_OPTION_OVERRIDE_LIST),
@ -11204,21 +11296,74 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
MENU_SETTING_ACTION_CORE_OPTION_OVERRIDE_LIST, 0, 0))
count++;
if (num_opts != 0)
{
core_option_manager_t *coreopts = NULL;
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
{
for (i = 0; i < num_opts; i++)
nested_list_item_t *category_item = NULL;
nested_list_t *option_list = NULL;
nested_list_item_t *option_item = NULL;
const struct core_option *option = NULL;
size_t i;
/* Empty 'category' string signifies top
* level core options menu */
if (!is_category)
option_list = coreopts->option_map;
else
{
if (core_option_manager_get_visible(coreopts, i))
category_item = nested_list_get_item(coreopts->option_map,
category, NULL);
if (category_item)
option_list = nested_list_item_get_children(category_item);
}
if (option_list)
{
/* Loop over child options */
for (i = 0; i < nested_list_get_size(option_list); i++)
{
option_item = nested_list_get_item_idx(option_list, i);
option = (const struct core_option *)
nested_list_item_get_value(option_item);
/* Check whether this is an option or a
* subcategory */
if (option)
{
/* This is a regular option */
size_t opt_idx = option->opt_idx;
if (core_option_manager_get_visible(coreopts, opt_idx))
if (menu_entries_append_enum(info->list,
core_option_manager_get_desc(coreopts, i), "",
MENU_ENUM_LABEL_CORE_OPTION_ENTRY,
(unsigned)(MENU_SETTINGS_CORE_OPTION_START + i), 0, 0))
core_option_manager_get_desc(coreopts, opt_idx, true),
"", MENU_ENUM_LABEL_CORE_OPTION_ENTRY,
(unsigned)(MENU_SETTINGS_CORE_OPTION_START + opt_idx),
0, 0))
count++;
}
else if (option_item)
{
/* This is a subcategory */
const char *catgory_id = nested_list_item_get_id(option_item);
bool category_visible = core_option_manager_get_category_visible(
coreopts, catgory_id);
/* Note: We use nested_list_item_get_id() because we
* guarantee that the list can only be two levels
* deep. If we supported arbitrary nesting, would
* have to use nested_list_item_get_address() here */
if (category_visible &&
!string_is_empty(catgory_id))
{
if (menu_entries_append_enum(info->list,
catgory_id,
msg_hash_to_str(MENU_ENUM_LABEL_CORE_OPTIONS),
MENU_ENUM_LABEL_CORE_OPTIONS,
MENU_SETTING_ACTION_CORE_OPTIONS, 0, 0))
count++;
}
}
}
}
}
}
@ -12474,106 +12619,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
{
if (string_starts_with_size(info->path, "core_option_",
STRLEN_CONST("core_option_")))
{
struct string_list tmp_str_list = {0};
string_list_initialize(&tmp_str_list);
string_split_noalloc(&tmp_str_list, info->path, "_");
if (tmp_str_list.size > 0)
{
core_option_manager_t *coreopts = NULL;
const char *val = NULL;
rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
if (coreopts)
{
unsigned i;
unsigned size = (unsigned)
tmp_str_list.size;
unsigned menu_index = atoi(tmp_str_list.elems[size-1].data);
unsigned visible_index = 0;
unsigned option_index = 0;
bool option_found = false;
struct core_option *option = NULL;
bool checked_found = false;
unsigned checked = 0;
bool game_specific_options = settings->bools.game_specific_options;
/* Note: Although we display value labels here,
* most logic is performed using values. This seems
* more appropriate somehow... */
/* Convert menu index to option index */
if (game_specific_options)
menu_index--;
for (i = 0; i < coreopts->size; i++)
{
if (core_option_manager_get_visible(coreopts, i))
{
if (visible_index == menu_index)
{
option_found = true;
option_index = i;
break;
}
visible_index++;
}
}
if (option_found)
{
val = core_option_manager_get_val(coreopts, option_index);
option = (struct core_option*)&coreopts->opts[option_index];
}
if (option)
{
unsigned k;
for (k = 0; k < option->vals->size; k++)
{
const char *val_str = option->vals->elems[k].data;
const char *val_label_str = option->val_labels->elems[k].data;
if (!string_is_empty(val_label_str))
{
char val_d[256];
snprintf(val_d, sizeof(val_d), "%d", option_index);
if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON);
else if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
if (menu_entries_append_enum(info->list,
val_label_str,
val_d,
MENU_ENUM_LABEL_NO_ITEMS,
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM, k, 0))
count++;
if (!checked_found && string_is_equal(val_str, val))
{
checked = k;
checked_found = true;
}
}
}
if (checked_found)
{
menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[checked].actiondata;
if (cbs)
cbs->checked = true;
menu_navigation_set_selection(checked);
}
}
}
}
string_list_deinitialize(&tmp_str_list);
}
count = menu_displaylist_parse_core_option_dropdown_list(info);
else
{
enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path);
@ -12931,105 +12977,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
if (string_starts_with_size(info->path, "core_option_",
STRLEN_CONST("core_option_")))
{
struct string_list tmp_str_list = {0};
string_list_initialize(&tmp_str_list);
string_split_noalloc(&tmp_str_list, info->path, "_");
if (tmp_str_list.size > 0)
{
core_option_manager_t *coreopts = NULL;
const char *val = NULL;
rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts);
if (coreopts)
{
unsigned size = (unsigned)
tmp_str_list.size;
unsigned menu_index = atoi(tmp_str_list.elems[size-1].data);
unsigned visible_index = 0;
unsigned option_index = 0;
bool option_found = false;
struct core_option *option = NULL;
bool checked_found = false;
unsigned checked = 0;
unsigned i;
/* Note: Although we display value labels here,
* most logic is performed using values. This seems
* more appropriate somehow... */
/* Convert menu index to option index */
menu_index--;
for (i = 0; i < coreopts->size; i++)
{
if (core_option_manager_get_visible(coreopts, i))
{
if (visible_index == menu_index)
{
option_found = true;
option_index = i;
break;
}
visible_index++;
}
}
if (option_found)
{
val = core_option_manager_get_val(coreopts, option_index);
option = (struct core_option*)&coreopts->opts[option_index];
}
if (option)
{
unsigned k;
for (k = 0; k < option->vals->size; k++)
{
const char *val_str = option->vals->elems[k].data;
const char *val_label_str = option->val_labels->elems[k].data;
if (!string_is_empty(val_label_str))
{
char val_d[256];
snprintf(val_d, sizeof(val_d), "%d", option_index);
if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON);
else if (string_is_equal(val_label_str, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)))
val_label_str = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
if (menu_entries_append_enum(info->list,
val_label_str,
val_d,
MENU_ENUM_LABEL_NO_ITEMS,
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM_SPECIAL, k, 0))
count++;
if (!checked_found && string_is_equal(val_str, val))
{
checked = k;
checked_found = true;
}
}
}
if (checked_found)
{
menu_file_list_cbs_t *cbs = (menu_file_list_cbs_t*)info->list->list[checked].actiondata;
if (cbs)
cbs->checked = true;
menu_navigation_set_selection(checked);
}
}
}
}
string_list_deinitialize(&tmp_str_list);
}
count = menu_displaylist_parse_core_option_dropdown_list(info);
else
{
enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path);

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ static void retroarch_init_core_variables(
const struct retro_variable *vars);
static void rarch_init_core_options(
struct rarch_state *p_rarch,
const struct retro_core_option_definition *option_defs);
const struct retro_core_options_v2 *options_v2);
#ifdef HAVE_RUNAHEAD
#if defined(HAVE_DYNAMIC) || defined(HAVE_DYLIB)
static bool secondary_core_create(struct rarch_state *p_rarch,

View File

@ -1163,7 +1163,7 @@ void CoreOptionsDialog::buildLayout()
for (j = 0; j < opts; j++)
{
QString desc =
core_option_manager_get_desc(coreopts, j);
core_option_manager_get_desc(coreopts, j, false);
QString val =
core_option_manager_get_val(coreopts, j);
QComboBox *combo_box = NULL;