mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 16:39:43 +00:00
Merge pull request #9092 from jdgleaver/core-option-sublabels-v2
Core Options: Add sublabels + localisation support
This commit is contained in:
commit
e8501203fb
@ -614,7 +614,7 @@ enum retro_mod
|
||||
* Afterward 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.
|
||||
*
|
||||
*
|
||||
* 'data' points to an array of retro_variable structs
|
||||
* terminated by a { NULL, NULL } element.
|
||||
* retro_variable::key should be namespaced to not collide
|
||||
@ -1106,6 +1106,118 @@ enum retro_mod
|
||||
* It will return a bitmask of all the digital buttons.
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION 52
|
||||
/* unsigned * --
|
||||
* Unsigned value is the API version number of the core options
|
||||
* interface supported by the frontend. If callback return false,
|
||||
* API version is assumed to be 0.
|
||||
*
|
||||
* In legacy code, core options are set by passing an array of
|
||||
* retro_variable structs to RETRO_ENVIRONMENT_SET_VARIABLES.
|
||||
* This may be still be done regardless of the core options
|
||||
* interface version.
|
||||
*
|
||||
* If version is 1 however, core options may instead be set by
|
||||
* passing an array of retro_core_option_definition structs to
|
||||
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS, or a 2D array of
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53
|
||||
/* const struct retro_core_option_definition ** --
|
||||
* 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_ENHANCED_CORE_OPTIONS
|
||||
* returns an API version of 1.
|
||||
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
|
||||
* 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.
|
||||
*
|
||||
* 'data' points to an array of retro_core_option_definition structs
|
||||
* terminated by a { NULL, NULL, NULL, {{0}} } element.
|
||||
* retro_core_option_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'.
|
||||
* retro_core_option_definition::desc should contain a human readable
|
||||
* description of the key.
|
||||
* retro_core_option_definition::info should contain any additional human
|
||||
* readable information text that a typical user may need to
|
||||
* understand the functionality of the option.
|
||||
* retro_variable::values is an array of retro_core_option_value
|
||||
* structs terminated by a { NULL, NULL } element.
|
||||
* > retro_variable::values[index].value is an expected option
|
||||
* value.
|
||||
* > retro_variable::values[index].label is a human readable
|
||||
* label used when displaying the value on screen. If NULL,
|
||||
* the value itself is used.
|
||||
*
|
||||
* The number of possible options 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.
|
||||
*
|
||||
* First entry should be treated as a default.
|
||||
*
|
||||
* Example entry:
|
||||
* {
|
||||
* "foo_option",
|
||||
* "Speed hack coprocessor X",
|
||||
* "Provides increased performance at the expense of reduced accuracy",
|
||||
* {
|
||||
* { "false", NULL },
|
||||
* { "true", NULL },
|
||||
* { "unstable", "Turbo (Unstable)" },
|
||||
* { NULL, NULL },
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* 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_INTL 54
|
||||
/* const struct retro_core_options_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_ENHANCED_CORE_OPTIONS
|
||||
* returns an API version of 1.
|
||||
* This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES.
|
||||
* 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.
|
||||
*
|
||||
* This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS,
|
||||
* with the addition of localisation support. The description of the
|
||||
* RETRO_ENVIRONMENT_SET_CORE_OPTIONS callback should be consulted
|
||||
* for further details.
|
||||
*
|
||||
* 'data' points to a retro_core_options_intl struct.
|
||||
*
|
||||
* retro_core_options_intl::us is a pointer to an array of
|
||||
* retro_core_option_definition structs defining the US English
|
||||
* core options implementation. It must point to a valid array.
|
||||
*
|
||||
* retro_core_options_intl::local is a pointer to an array of
|
||||
* retro_core_option_definition structs defining core options for
|
||||
* the current frontend language. It may be NULL (in which case
|
||||
* retro_core_options_intl::us is used by the frontend). Any items
|
||||
* missing from this array will be read from retro_core_options_intl::us
|
||||
* instead.
|
||||
*/
|
||||
|
||||
/* VFS functionality */
|
||||
|
||||
/* File paths:
|
||||
@ -2351,6 +2463,49 @@ struct retro_variable
|
||||
const char *value;
|
||||
};
|
||||
|
||||
/* Maximum number of values permitted for a core option
|
||||
* NOTE: This may be increased on a core-by-core basis
|
||||
* if required (doing so has no effect on the frontend) */
|
||||
#define RETRO_NUM_CORE_OPTION_VALUES_MAX 128
|
||||
|
||||
struct retro_core_option_value
|
||||
{
|
||||
/* Expected option value */
|
||||
const char *value;
|
||||
|
||||
/* Human-readable value label. If NULL, value itself
|
||||
* will be displayed by the frontend */
|
||||
const char *label;
|
||||
};
|
||||
|
||||
struct retro_core_option_definition
|
||||
{
|
||||
/* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. */
|
||||
const char *key;
|
||||
|
||||
/* Human-readable core option description (used as menu label) */
|
||||
const char *desc;
|
||||
|
||||
/* Human-readable core option information (used as menu sublabel) */
|
||||
const char *info;
|
||||
|
||||
/* Array of retro_core_option_value structs, terminated by NULL */
|
||||
struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
|
||||
};
|
||||
|
||||
struct retro_core_options_intl
|
||||
{
|
||||
/* Pointer to an array of retro_core_option_definition structs
|
||||
* - US English implementation
|
||||
* - Must point to a valid array */
|
||||
struct retro_core_option_definition *us;
|
||||
|
||||
/* Pointer to an array of retro_core_option_definition structs
|
||||
* - Implementation for current frontend language
|
||||
* - May be NULL */
|
||||
struct retro_core_option_definition *local;
|
||||
};
|
||||
|
||||
struct retro_game_info
|
||||
{
|
||||
const char *path; /* Path to game, UTF-8 encoded.
|
||||
|
@ -29,20 +29,22 @@ RETRO_BEGIN_DECLS
|
||||
|
||||
struct core_option
|
||||
{
|
||||
char *desc;
|
||||
char *key;
|
||||
struct string_list *vals;
|
||||
size_t index;
|
||||
char *desc;
|
||||
char *info;
|
||||
char *key;
|
||||
struct string_list *vals;
|
||||
struct string_list *val_labels;
|
||||
size_t index;
|
||||
};
|
||||
|
||||
struct core_option_manager
|
||||
{
|
||||
config_file_t *conf;
|
||||
char conf_path[PATH_MAX_LENGTH];
|
||||
config_file_t *conf;
|
||||
char conf_path[PATH_MAX_LENGTH];
|
||||
|
||||
struct core_option *opts;
|
||||
size_t size;
|
||||
bool updated;
|
||||
struct core_option *opts;
|
||||
size_t size;
|
||||
bool updated;
|
||||
};
|
||||
|
||||
typedef struct core_option_manager core_option_manager_t;
|
||||
@ -68,6 +70,18 @@ void core_option_manager_set_default(core_option_manager_t *opt, size_t idx);
|
||||
const char *core_option_manager_get_desc(core_option_manager_t *opt,
|
||||
size_t idx);
|
||||
|
||||
/**
|
||||
* core_option_manager_get_info:
|
||||
* @opt : options manager handle
|
||||
* @idx : idx identifier of the option
|
||||
*
|
||||
* Gets information text for an option.
|
||||
*
|
||||
* Returns: Information text for an option.
|
||||
**/
|
||||
const char *core_option_manager_get_info(core_option_manager_t *opt,
|
||||
size_t idx);
|
||||
|
||||
/**
|
||||
* core_option_manager_get_val:
|
||||
* @opt : options manager handle
|
||||
@ -80,6 +94,18 @@ const char *core_option_manager_get_desc(core_option_manager_t *opt,
|
||||
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.
|
||||
*
|
||||
* Returns: Value label for an option.
|
||||
**/
|
||||
const char *core_option_manager_get_val_label(core_option_manager_t *opt,
|
||||
size_t idx);
|
||||
|
||||
void core_option_manager_set_val(core_option_manager_t *opt,
|
||||
size_t idx, size_t val_idx);
|
||||
|
||||
|
@ -1109,13 +1109,19 @@ static void menu_action_setting_disp_set_label_core_options(file_list_t* list,
|
||||
|
||||
if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts))
|
||||
{
|
||||
core_opt = core_option_manager_get_val(coreopts,
|
||||
core_opt = core_option_manager_get_val_label(coreopts,
|
||||
type - MENU_SETTINGS_CORE_OPTION_START);
|
||||
|
||||
strlcpy(s, "", len);
|
||||
|
||||
if (core_opt)
|
||||
{
|
||||
if (string_is_equal(core_opt, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)))
|
||||
core_opt = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON);
|
||||
else if (string_is_equal(core_opt, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)))
|
||||
core_opt = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF);
|
||||
strlcpy(s, core_opt, len);
|
||||
}
|
||||
}
|
||||
|
||||
strlcpy(s2, path, len2);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../menu_cbs.h"
|
||||
|
||||
#include "../../retroarch.h"
|
||||
#include "../../managers/core_option_manager.h"
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../../cheevos-new/cheevos.h"
|
||||
@ -1014,6 +1015,26 @@ static int action_bind_sublabel_playlist_entry(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_bind_sublabel_core_option(
|
||||
file_list_t *list,
|
||||
unsigned type, unsigned i,
|
||||
const char *label, const char *path,
|
||||
char *s, size_t len)
|
||||
{
|
||||
core_option_manager_t *opt = NULL;
|
||||
const char *info = NULL;
|
||||
|
||||
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);
|
||||
|
||||
if (!string_is_empty(info))
|
||||
strlcpy(s, info, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int action_bind_sublabel_generic(
|
||||
file_list_t *list,
|
||||
unsigned type, unsigned i,
|
||||
@ -1102,6 +1123,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
}
|
||||
#endif
|
||||
|
||||
if (type >= MENU_SETTINGS_CORE_OPTION_START)
|
||||
{
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_option);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cbs->enum_idx != MSG_UNKNOWN)
|
||||
{
|
||||
settings_t *settings; /* config_get_ptr is called only when needed */
|
||||
|
@ -8050,6 +8050,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
bool checked_found = false;
|
||||
unsigned checked = 0;
|
||||
|
||||
/* Note: Although we display value labels here,
|
||||
* most logic is performed using values. This seems
|
||||
* more appropriate somehow... */
|
||||
|
||||
if (settings->bools.game_specific_options)
|
||||
{
|
||||
val = core_option_manager_get_val(coreopts, i-1);
|
||||
@ -8065,19 +8069,26 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
unsigned k;
|
||||
for (k = 0; k < option->vals->size; k++)
|
||||
{
|
||||
const char *str = option->vals->elems[k].data;
|
||||
const char *val_str = option->vals->elems[k].data;
|
||||
const char *val_label_str = option->val_labels->elems[k].data;
|
||||
|
||||
if (!string_is_empty(str))
|
||||
if (!string_is_empty(val_label_str))
|
||||
{
|
||||
char val_d[256];
|
||||
snprintf(val_d, sizeof(val_d), "%d", i);
|
||||
|
||||
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);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
str,
|
||||
val_label_str,
|
||||
val_d,
|
||||
MENU_ENUM_LABEL_NO_ITEMS,
|
||||
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM, k, 0);
|
||||
|
||||
if (!checked_found && string_is_equal(str, val))
|
||||
if (!checked_found && string_is_equal(val_str, val))
|
||||
{
|
||||
checked = k;
|
||||
checked_found = true;
|
||||
@ -8093,6 +8104,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp_str_list)
|
||||
string_list_free(tmp_str_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -8135,6 +8148,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
if (checked_found)
|
||||
menu_entries_set_checked(info->list, checked, true);
|
||||
}
|
||||
|
||||
if (tmp_str_list)
|
||||
string_list_free(tmp_str_list);
|
||||
}
|
||||
break;
|
||||
case ST_INT:
|
||||
@ -8373,6 +8389,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
unsigned checked = 0;
|
||||
const char *val = core_option_manager_get_val(coreopts, i-1);
|
||||
|
||||
/* Note: Although we display value labels here,
|
||||
* most logic is performed using values. This seems
|
||||
* more appropriate somehow... */
|
||||
|
||||
i--;
|
||||
|
||||
option = (struct core_option*)&coreopts->opts[i];
|
||||
@ -8382,20 +8402,26 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
unsigned k;
|
||||
for (k = 0; k < option->vals->size; k++)
|
||||
{
|
||||
const char *str = option->vals->elems[k].data;
|
||||
const char *val_str = option->vals->elems[k].data;
|
||||
const char *val_label_str = option->val_labels->elems[k].data;
|
||||
|
||||
if (!string_is_empty(str))
|
||||
if (!string_is_empty(val_label_str))
|
||||
{
|
||||
char val_d[256];
|
||||
snprintf(val_d, sizeof(val_d), "%d", i);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
menu_entries_append_enum(info->list,
|
||||
str,
|
||||
val_label_str,
|
||||
val_d,
|
||||
MENU_ENUM_LABEL_NO_ITEMS,
|
||||
MENU_SETTING_DROPDOWN_SETTING_CORE_OPTIONS_ITEM_SPECIAL, k, 0);
|
||||
|
||||
if (!checked_found && string_is_equal(str, val))
|
||||
if (!checked_found && string_is_equal(val_str, val))
|
||||
{
|
||||
checked = k;
|
||||
checked_found = true;
|
||||
@ -8409,6 +8435,8 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
}
|
||||
}
|
||||
|
||||
if (tmp_str_list)
|
||||
string_list_free(tmp_str_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -8450,6 +8478,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type,
|
||||
if (checked_found)
|
||||
menu_entries_set_checked(info->list, checked, true);
|
||||
}
|
||||
|
||||
if (tmp_str_list)
|
||||
string_list_free(tmp_str_list);
|
||||
}
|
||||
break;
|
||||
case ST_INT:
|
||||
|
426
retroarch.c
426
retroarch.c
@ -1713,6 +1713,13 @@ static bool core_option_manager_parse_variable(
|
||||
if (!option->vals)
|
||||
goto error;
|
||||
|
||||
/* Legacy core option interface has no concept of
|
||||
* value labels - use actual values for display purposes */
|
||||
option->val_labels = string_list_clone(option->vals);
|
||||
|
||||
if (!option->val_labels)
|
||||
goto error;
|
||||
|
||||
if (config_get_string(opt->conf, option->key, &config_val))
|
||||
{
|
||||
size_t i;
|
||||
@ -1738,6 +1745,77 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool core_option_manager_parse_option(
|
||||
core_option_manager_t *opt, size_t idx,
|
||||
const struct retro_core_option_definition *option_def)
|
||||
{
|
||||
size_t i;
|
||||
union string_list_elem_attr attr;
|
||||
size_t num_vals = 0;
|
||||
char *config_val = NULL;
|
||||
struct core_option *option = (struct core_option*)&opt->opts[idx];
|
||||
const struct retro_core_option_value *values = option_def->values;
|
||||
|
||||
if (!string_is_empty(option_def->key))
|
||||
option->key = strdup(option_def->key);
|
||||
|
||||
if (!string_is_empty(option_def->desc))
|
||||
option->desc = strdup(option_def->desc);
|
||||
|
||||
if (!string_is_empty(option_def->info))
|
||||
option->info = strdup(option_def->info);
|
||||
|
||||
/* Get number of values */
|
||||
while (true)
|
||||
{
|
||||
if (!string_is_empty(values[num_vals].value))
|
||||
num_vals++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_vals < 1)
|
||||
return false;
|
||||
|
||||
/* Initialise string lists */
|
||||
attr.i = 0;
|
||||
option->vals = string_list_new();
|
||||
option->val_labels = string_list_new();
|
||||
|
||||
if (!option->vals || !option->val_labels)
|
||||
return false;
|
||||
|
||||
/* Extract value/label pairs */
|
||||
for (i = 0; i < num_vals; i++)
|
||||
{
|
||||
/* We know that 'value' is valid */
|
||||
string_list_append(option->vals, values[i].value, attr);
|
||||
|
||||
/* Value 'label' may be NULL */
|
||||
if (!string_is_empty(values[i].label))
|
||||
string_list_append(option->val_labels, values[i].label, attr);
|
||||
else
|
||||
string_list_append(option->val_labels, values[i].value, attr);
|
||||
}
|
||||
|
||||
/* Set current config value */
|
||||
if (config_get_string(opt->conf, option->key, &config_val))
|
||||
{
|
||||
for (i = 0; i < option->vals->size; i++)
|
||||
{
|
||||
if (string_is_equal(option->vals->elems[i].data, config_val))
|
||||
{
|
||||
option->index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(config_val);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_free:
|
||||
* @opt : options manager handle
|
||||
@ -1755,13 +1833,18 @@ static void core_option_manager_free(core_option_manager_t *opt)
|
||||
{
|
||||
if (opt->opts[i].desc)
|
||||
free(opt->opts[i].desc);
|
||||
if (opt->opts[i].info)
|
||||
free(opt->opts[i].info);
|
||||
if (opt->opts[i].key)
|
||||
free(opt->opts[i].key);
|
||||
|
||||
if (opt->opts[i].vals)
|
||||
string_list_free(opt->opts[i].vals);
|
||||
if (opt->opts[i].val_labels)
|
||||
string_list_free(opt->opts[i].val_labels);
|
||||
|
||||
opt->opts[i].desc = NULL;
|
||||
opt->opts[i].info = NULL;
|
||||
opt->opts[i].key = NULL;
|
||||
opt->opts[i].vals = NULL;
|
||||
}
|
||||
@ -1800,15 +1883,16 @@ static void core_option_manager_get(core_option_manager_t *opt,
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_new:
|
||||
* core_option_manager_new_vars:
|
||||
* @conf_path : Filesystem path to write core option config file to.
|
||||
* @vars : Pointer to variable array handle.
|
||||
*
|
||||
* Legacy version of core_option_manager_new().
|
||||
* Creates and initializes a core manager handle.
|
||||
*
|
||||
* Returns: handle to new core manager handle, otherwise NULL.
|
||||
**/
|
||||
static core_option_manager_t *core_option_manager_new(const char *conf_path,
|
||||
static core_option_manager_t *core_option_manager_new_vars(const char *conf_path,
|
||||
const struct retro_variable *vars)
|
||||
{
|
||||
const struct retro_variable *var;
|
||||
@ -1855,6 +1939,68 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_new:
|
||||
* @conf_path : Filesystem path to write core option config file to.
|
||||
* @option_defs : Pointer to variable array handle.
|
||||
*
|
||||
* Creates and initializes a core manager handle.
|
||||
*
|
||||
* Returns: handle to new core manager handle, otherwise NULL.
|
||||
**/
|
||||
static core_option_manager_t *core_option_manager_new(const char *conf_path,
|
||||
const struct retro_core_option_definition *option_defs)
|
||||
{
|
||||
const struct retro_core_option_definition *option_def;
|
||||
size_t size = 0;
|
||||
core_option_manager_t *opt = (core_option_manager_t*)
|
||||
calloc(1, sizeof(*opt));
|
||||
|
||||
if (!opt)
|
||||
return NULL;
|
||||
|
||||
if (!string_is_empty(conf_path))
|
||||
opt->conf = config_file_new(conf_path);
|
||||
if (!opt->conf)
|
||||
opt->conf = config_file_new(NULL);
|
||||
|
||||
strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path));
|
||||
|
||||
if (!opt->conf)
|
||||
goto error;
|
||||
|
||||
/* Note: 'option_def->info == NULL' is valid */
|
||||
for (option_def = option_defs;
|
||||
option_def->key && option_def->desc && option_def->values[0].value;
|
||||
option_def++)
|
||||
size++;
|
||||
|
||||
if (size == 0)
|
||||
goto error;
|
||||
|
||||
opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts));
|
||||
if (!opt->opts)
|
||||
goto error;
|
||||
|
||||
opt->size = size;
|
||||
size = 0;
|
||||
|
||||
/* Note: 'option_def->info == NULL' is valid */
|
||||
for (option_def = option_defs;
|
||||
option_def->key && option_def->desc && option_def->values[0].value;
|
||||
size++, option_def++)
|
||||
{
|
||||
if (!core_option_manager_parse_option(opt, size, option_def))
|
||||
goto error;
|
||||
}
|
||||
|
||||
return opt;
|
||||
|
||||
error:
|
||||
core_option_manager_free(opt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_flush:
|
||||
* @opt : options manager handle
|
||||
@ -1921,9 +2067,30 @@ const char *core_option_manager_get_desc(
|
||||
{
|
||||
if (!opt)
|
||||
return NULL;
|
||||
if (idx >= opt->size)
|
||||
return NULL;
|
||||
return opt->opts[idx].desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_get_info:
|
||||
* @opt : options manager handle
|
||||
* @idx : idx identifier of the option
|
||||
*
|
||||
* Gets information text for an option.
|
||||
*
|
||||
* Returns: Information text for an option.
|
||||
**/
|
||||
const char *core_option_manager_get_info(
|
||||
core_option_manager_t *opt, size_t idx)
|
||||
{
|
||||
if (!opt)
|
||||
return NULL;
|
||||
if (idx >= opt->size)
|
||||
return NULL;
|
||||
return opt->opts[idx].info;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_get_val:
|
||||
* @opt : options manager handle
|
||||
@ -1938,10 +2105,32 @@ const char *core_option_manager_get_val(core_option_manager_t *opt, size_t idx)
|
||||
struct core_option *option = NULL;
|
||||
if (!opt)
|
||||
return NULL;
|
||||
if (idx >= opt->size)
|
||||
return NULL;
|
||||
option = (struct core_option*)&opt->opts[idx];
|
||||
return option->vals->elems[option->index].data;
|
||||
}
|
||||
|
||||
/**
|
||||
* core_option_manager_get_val_label:
|
||||
* @opt : options manager handle
|
||||
* @idx : idx identifier of the option
|
||||
*
|
||||
* Gets value label for an option.
|
||||
*
|
||||
* Returns: Value label for an option.
|
||||
**/
|
||||
const char *core_option_manager_get_val_label(core_option_manager_t *opt, size_t idx)
|
||||
{
|
||||
struct core_option *option = NULL;
|
||||
if (!opt)
|
||||
return NULL;
|
||||
if (idx >= opt->size)
|
||||
return NULL;
|
||||
option = (struct core_option*)&opt->opts[idx];
|
||||
return option->val_labels->elems[option->index].data;
|
||||
}
|
||||
|
||||
void core_option_manager_set_val(core_option_manager_t *opt,
|
||||
size_t idx, size_t val_idx)
|
||||
{
|
||||
@ -1949,6 +2138,8 @@ void core_option_manager_set_val(core_option_manager_t *opt,
|
||||
|
||||
if (!opt)
|
||||
return;
|
||||
if (idx >= opt->size)
|
||||
return;
|
||||
|
||||
option = (struct core_option*)&opt->opts[idx];
|
||||
option->index = val_idx % option->vals->size;
|
||||
@ -1967,11 +2158,148 @@ void core_option_manager_set_default(core_option_manager_t *opt, size_t idx)
|
||||
{
|
||||
if (!opt)
|
||||
return;
|
||||
if (idx >= opt->size)
|
||||
return;
|
||||
|
||||
opt->opts[idx].index = 0;
|
||||
opt->updated = true;
|
||||
}
|
||||
|
||||
static struct retro_core_option_definition *core_option_manager_get_definitions(
|
||||
const struct retro_core_options_intl *core_options_intl)
|
||||
{
|
||||
size_t i;
|
||||
size_t num_options = 0;
|
||||
struct retro_core_option_definition *option_defs_us = NULL;
|
||||
struct retro_core_option_definition *option_defs_local = NULL;
|
||||
struct retro_core_option_definition *option_defs = NULL;
|
||||
|
||||
if (!core_options_intl)
|
||||
return NULL;
|
||||
|
||||
option_defs_us = core_options_intl->us;
|
||||
option_defs_local = core_options_intl->local;
|
||||
|
||||
if (!option_defs_us)
|
||||
return NULL;
|
||||
|
||||
/* Determine number of options */
|
||||
while (true)
|
||||
{
|
||||
if (!string_is_empty(option_defs_us[num_options].key))
|
||||
num_options++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_options < 1)
|
||||
return NULL;
|
||||
|
||||
/* Allocate output option_defs array
|
||||
* > One extra entry required for terminating NULL entry
|
||||
* > Note that calloc() sets terminating NULL entry and
|
||||
* correctly 'nullifies' each values array */
|
||||
option_defs = (struct retro_core_option_definition *)calloc(
|
||||
num_options + 1, sizeof(struct retro_core_option_definition));
|
||||
|
||||
if (!option_defs)
|
||||
return NULL;
|
||||
|
||||
/* Loop through options... */
|
||||
for (i = 0; i < num_options; i++)
|
||||
{
|
||||
size_t j;
|
||||
size_t num_values = 0;
|
||||
const char *key = option_defs_us[i].key;
|
||||
const char *local_desc = NULL;
|
||||
const char *local_info = NULL;
|
||||
struct retro_core_option_value *local_values = NULL;
|
||||
|
||||
/* Key is always taken from us english defs */
|
||||
option_defs[i].key = key;
|
||||
|
||||
/* Try to find corresponding entry in local defs array */
|
||||
if (option_defs_local)
|
||||
{
|
||||
size_t index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const char *local_key = option_defs_local[index].key;
|
||||
|
||||
if (!string_is_empty(local_key))
|
||||
{
|
||||
if (string_is_equal(key, local_key))
|
||||
{
|
||||
local_desc = option_defs_local[index].desc;
|
||||
local_info = option_defs_local[index].info;
|
||||
local_values = option_defs_local[index].values;
|
||||
break;
|
||||
}
|
||||
else
|
||||
index++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set desc and info strings */
|
||||
option_defs[i].desc = string_is_empty(local_desc) ? option_defs_us[i].desc : local_desc;
|
||||
option_defs[i].info = string_is_empty(local_info) ? option_defs_us[i].info : local_info;
|
||||
|
||||
/* Determine number of values
|
||||
* (always taken from us english defs) */
|
||||
while (true)
|
||||
{
|
||||
if (!string_is_empty(option_defs_us[i].values[num_values].value))
|
||||
num_values++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Copy values */
|
||||
for (j = 0; j < num_values; j++)
|
||||
{
|
||||
const char *value = option_defs_us[i].values[j].value;
|
||||
const char *local_label = NULL;
|
||||
|
||||
/* Value string is always taken from us english defs */
|
||||
option_defs[i].values[j].value = value;
|
||||
|
||||
/* Try to find corresponding entry in local defs values array */
|
||||
if (local_values)
|
||||
{
|
||||
size_t value_index = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
const char *local_value = local_values[value_index].value;
|
||||
|
||||
if (!string_is_empty(local_value))
|
||||
{
|
||||
if (string_is_equal(value, local_value))
|
||||
{
|
||||
local_label = local_values[value_index].label;
|
||||
break;
|
||||
}
|
||||
else
|
||||
value_index++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set value label string */
|
||||
option_defs[i].values[j].label = string_is_empty(local_label) ?
|
||||
option_defs_us[i].values[j].label : local_label;
|
||||
}
|
||||
}
|
||||
|
||||
return option_defs;
|
||||
}
|
||||
|
||||
|
||||
/* DYNAMIC LIBRETRO CORE */
|
||||
|
||||
@ -2569,14 +2897,31 @@ bool rarch_environment_cb(unsigned cmd, void *data)
|
||||
*(bool*)data = false;
|
||||
break;
|
||||
|
||||
/* SET_VARIABLES: Legacy path */
|
||||
case RETRO_ENVIRONMENT_SET_VARIABLES:
|
||||
RARCH_LOG("Environ SET_VARIABLES.\n");
|
||||
|
||||
rarch_ctl(RARCH_CTL_CORE_OPTIONS_DEINIT, NULL);
|
||||
rarch_ctl(RARCH_CTL_CORE_VARIABLES_INIT, data);
|
||||
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS:
|
||||
RARCH_LOG("Environ SET_CORE_OPTIONS.\n");
|
||||
|
||||
rarch_ctl(RARCH_CTL_CORE_OPTIONS_DEINIT, NULL);
|
||||
rarch_ctl(RARCH_CTL_CORE_OPTIONS_INIT, data);
|
||||
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL:
|
||||
RARCH_LOG("Environ RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL.\n");
|
||||
|
||||
rarch_ctl(RARCH_CTL_CORE_OPTIONS_DEINIT, NULL);
|
||||
rarch_ctl(RARCH_CTL_CORE_OPTIONS_INTL_INIT, data);
|
||||
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_MESSAGE:
|
||||
{
|
||||
const struct retro_message *msg = (const struct retro_message*)data;
|
||||
@ -3432,6 +3777,11 @@ bool rarch_environment_cb(unsigned cmd, void *data)
|
||||
/* Just falldown, the function will return true */
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION:
|
||||
/* Current API version is 1 */
|
||||
*(unsigned *)data = 1;
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_GET_TARGET_REFRESH_RATE:
|
||||
{
|
||||
/* Try to use the polled refresh rate first. */
|
||||
@ -18045,6 +18395,42 @@ static void runloop_task_msg_queue_push(
|
||||
runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
|
||||
}
|
||||
|
||||
static void rarch_init_core_options(
|
||||
const struct retro_core_option_definition *option_defs)
|
||||
{
|
||||
settings_t *settings = configuration_settings;
|
||||
char *game_options_path = NULL;
|
||||
|
||||
if (settings->bools.game_specific_options &&
|
||||
rarch_game_specific_options(&game_options_path))
|
||||
{
|
||||
runloop_game_options_active = true;
|
||||
runloop_core_options =
|
||||
core_option_manager_new(game_options_path, option_defs);
|
||||
free(game_options_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
char buf[PATH_MAX_LENGTH];
|
||||
const char *options_path = settings ? settings->paths.path_core_options : NULL;
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
if (string_is_empty(options_path) && !path_is_empty(RARCH_PATH_CONFIG))
|
||||
{
|
||||
fill_pathname_resolve_relative(buf, path_get(RARCH_PATH_CONFIG),
|
||||
file_path_str(FILE_PATH_CORE_OPTIONS_CONFIG), sizeof(buf));
|
||||
options_path = buf;
|
||||
}
|
||||
|
||||
runloop_game_options_active = false;
|
||||
|
||||
if (!string_is_empty(options_path))
|
||||
runloop_core_options =
|
||||
core_option_manager_new(options_path, option_defs);
|
||||
}
|
||||
}
|
||||
|
||||
bool rarch_ctl(enum rarch_ctl_state state, void *data)
|
||||
{
|
||||
static bool has_set_username = false;
|
||||
@ -18539,7 +18925,7 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RARCH_CTL_CORE_OPTIONS_INIT:
|
||||
case RARCH_CTL_CORE_VARIABLES_INIT:
|
||||
{
|
||||
settings_t *settings = configuration_settings;
|
||||
char *game_options_path = NULL;
|
||||
@ -18551,7 +18937,7 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
|
||||
{
|
||||
runloop_game_options_active = true;
|
||||
runloop_core_options =
|
||||
core_option_manager_new(game_options_path, vars);
|
||||
core_option_manager_new_vars(game_options_path, vars);
|
||||
free(game_options_path);
|
||||
}
|
||||
else
|
||||
@ -18572,11 +18958,39 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
|
||||
|
||||
if (!string_is_empty(options_path))
|
||||
runloop_core_options =
|
||||
core_option_manager_new(options_path, vars);
|
||||
core_option_manager_new_vars(options_path, vars);
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
case RARCH_CTL_CORE_OPTIONS_INIT:
|
||||
{
|
||||
const struct retro_core_option_definition *option_defs =
|
||||
(const struct retro_core_option_definition*)data;
|
||||
|
||||
rarch_init_core_options(option_defs);
|
||||
}
|
||||
break;
|
||||
|
||||
case RARCH_CTL_CORE_OPTIONS_INTL_INIT:
|
||||
{
|
||||
const struct retro_core_options_intl *core_options_intl =
|
||||
(const struct retro_core_options_intl*)data;
|
||||
|
||||
/* Parse core_options_intl to create option definitions array */
|
||||
struct retro_core_option_definition *option_defs =
|
||||
core_option_manager_get_definitions(core_options_intl);
|
||||
|
||||
if (option_defs)
|
||||
{
|
||||
/* Initialise core options */
|
||||
rarch_init_core_options(option_defs);
|
||||
|
||||
/* Clean up */
|
||||
free(option_defs);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RARCH_CTL_CORE_OPTIONS_DEINIT:
|
||||
{
|
||||
if (!runloop_core_options)
|
||||
|
@ -180,7 +180,9 @@ enum rarch_ctl_state
|
||||
RARCH_CTL_CORE_OPTIONS_LIST_GET,
|
||||
RARCH_CTL_CORE_OPTION_PREV,
|
||||
RARCH_CTL_CORE_OPTION_NEXT,
|
||||
RARCH_CTL_CORE_VARIABLES_INIT,
|
||||
RARCH_CTL_CORE_OPTIONS_INIT,
|
||||
RARCH_CTL_CORE_OPTIONS_INTL_INIT,
|
||||
RARCH_CTL_CORE_OPTIONS_DEINIT,
|
||||
|
||||
/* System info */
|
||||
|
Loading…
Reference in New Issue
Block a user