mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 03:32:46 +00:00
Add core option category support
This commit is contained in:
parent
7a0dc3c4ca
commit
fe1f311a35
@ -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
1752
core_option_manager.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*
|
||||
* Gets description for an option.
|
||||
* @opt : options manager handle
|
||||
* @idx : core option index
|
||||
* @categorized : flag specifying whether to
|
||||
* fetch the categorised description
|
||||
* or the legacy fallback
|
||||
*
|
||||
* Returns: 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 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
|
||||
*
|
||||
* Gets information text for an option.
|
||||
* @opt : options manager handle
|
||||
* @idx : core option index
|
||||
* @categorized : flag specifying whether to
|
||||
* fetch the categorised information
|
||||
* or the legacy fallback
|
||||
*
|
||||
* Returns: 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 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
|
||||
*
|
||||
* Gets value for an option.
|
||||
* @opt : options manager handle
|
||||
* @idx : core option index
|
||||
*
|
||||
* Returns: Value for an option.
|
||||
* Fetches the string representation of the current
|
||||
* value of the core option at index @idx.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Gets value label for an option.
|
||||
* @opt : options manager handle
|
||||
* @idx : core option index
|
||||
*
|
||||
* Returns: 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: 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
|
||||
*
|
||||
* Gets whether option should be visible when displaying
|
||||
* core options in the frontend
|
||||
* @opt : options manager handle
|
||||
* @idx : core option index
|
||||
*
|
||||
* Returns: 'true' if option should be displayed by 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, 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
|
||||
|
@ -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"
|
||||
|
@ -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.
|
||||
|
242
libretro-common/include/lists/nested_list.h
Normal file
242
libretro-common/include/lists/nested_list.h
Normal 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
|
604
libretro-common/lists/nested_list.c
Normal file
604
libretro-common/lists/nested_list.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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);
|
||||
|
@ -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,61 +243,32 @@ 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};
|
||||
int ret = 0;
|
||||
struct string_list tmp_str_list = {0};
|
||||
core_option_manager_t *coreopts = NULL;
|
||||
int ret = 0;
|
||||
|
||||
string_list_initialize(&tmp_str_list);
|
||||
string_split_noalloc(&tmp_str_list, path, "_");
|
||||
|
||||
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;
|
||||
const char *title = core_option_manager_get_desc(
|
||||
coreopts, option_index, true);
|
||||
|
||||
/* Convert menu index to option index */
|
||||
if (game_specific_options)
|
||||
menu_index--;
|
||||
|
||||
for (i = 0; i < coreopts->size; i++)
|
||||
if (s && !string_is_empty(title))
|
||||
{
|
||||
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);
|
||||
|
||||
if (s && !string_is_empty(title))
|
||||
{
|
||||
SANITIZE_TO_STRING(s, title, len);
|
||||
ret = 1;
|
||||
}
|
||||
strlcpy(s, title, len);
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
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,20 +11296,73 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
MENU_SETTING_ACTION_CORE_OPTION_OVERRIDE_LIST, 0, 0))
|
||||
count++;
|
||||
|
||||
if (num_opts != 0)
|
||||
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
|
||||
{
|
||||
core_option_manager_t *coreopts = NULL;
|
||||
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;
|
||||
|
||||
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
|
||||
/* Empty 'category' string signifies top
|
||||
* level core options menu */
|
||||
if (!is_category)
|
||||
option_list = coreopts->option_map;
|
||||
else
|
||||
{
|
||||
for (i = 0; i < num_opts; 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++)
|
||||
{
|
||||
if (core_option_manager_get_visible(coreopts, i))
|
||||
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))
|
||||
count++;
|
||||
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, 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12473,107 +12618,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
{
|
||||
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);
|
||||
}
|
||||
STRLEN_CONST("core_option_")))
|
||||
count = menu_displaylist_parse_core_option_dropdown_list(info);
|
||||
else
|
||||
{
|
||||
enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path);
|
||||
@ -12930,106 +12976,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list);
|
||||
|
||||
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);
|
||||
}
|
||||
STRLEN_CONST("core_option_")))
|
||||
count = menu_displaylist_parse_core_option_dropdown_list(info);
|
||||
else
|
||||
{
|
||||
enum msg_hash_enums enum_idx = (enum msg_hash_enums)atoi(info->path);
|
||||
|
941
retroarch.c
941
retroarch.c
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user