Merge pull request #9092 from jdgleaver/core-option-sublabels-v2

Core Options: Add sublabels + localisation support
This commit is contained in:
Twinaphex 2019-07-12 14:23:00 +02:00 committed by GitHub
commit e8501203fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 687 additions and 26 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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:

View File

@ -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)

View File

@ -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 */