From 534f8da487a56b258c60c156aebe0d1d2f8f8335 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Wed, 6 Oct 2021 16:42:55 +0100 Subject: [PATCH] Add enviroment callback to enable cores to notify the frontend that a core option value has changed --- core_option_manager.c | 81 ++++++++++++++++++++++++++++-- core_option_manager.h | 21 ++++++++ libretro-common/include/libretro.h | 25 +++++++++ retroarch.c | 54 ++++++++++++++++++++ 4 files changed, 178 insertions(+), 3 deletions(-) diff --git a/core_option_manager.c b/core_option_manager.c index 4e53ca3f37..f6e314e4ef 100644 --- a/core_option_manager.c +++ b/core_option_manager.c @@ -725,13 +725,20 @@ static bool core_option_manager_parse_variable( if (!option->val_labels) goto error; - /* > Loop over values and 'extract' labels */ + /* > Loop over values and: + * - Set value hashes + * - 'Extract' labels */ for (i = 0; i < option->vals->size; i++) { const char *value = option->vals->elems[i].data; + uint32_t *value_hash = (uint32_t *)malloc(sizeof(uint32_t)); const char *value_label = core_option_manager_parse_value_label( value, NULL); + /* Set value hash */ + *value_hash = core_option_manager_hash_string(value); + option->vals->elems[i].userdata = (void*)value_hash; + /* Redundant safely check... */ value_label = string_is_empty(value_label) ? value : value_label; @@ -753,9 +760,15 @@ static bool core_option_manager_parse_variable( /* Set current config value */ if (entry && !string_is_empty(entry->value)) { + uint32_t entry_value_hash = core_option_manager_hash_string(entry->value); + for (i = 0; i < option->vals->size; i++) { - if (string_is_equal(option->vals->elems[i].data, entry->value)) + const char *value = option->vals->elems[i].data; + uint32_t value_hash = *((uint32_t*)option->vals->elems[i].userdata); + + if ((value_hash == entry_value_hash) && + string_is_equal(value, entry->value)) { option->index = i; break; @@ -998,12 +1011,17 @@ static bool core_option_manager_parse_option( for (i = 0; i < num_vals; i++) { const char *value = values[i].value; + uint32_t *value_hash = (uint32_t *)malloc(sizeof(uint32_t)); const char *value_label = values[i].label; /* Append value string * > We know that 'value' is always valid */ string_list_append(option->vals, value, attr); + /* > Set value hash */ + *value_hash = core_option_manager_hash_string(value); + option->vals->elems[option->vals->size - 1].userdata = (void*)value_hash; + /* Value label requires additional processing */ value_label = core_option_manager_parse_value_label( value, value_label); @@ -1034,9 +1052,15 @@ static bool core_option_manager_parse_option( /* Set current config value */ if (entry && !string_is_empty(entry->value)) { + uint32_t entry_value_hash = core_option_manager_hash_string(entry->value); + for (i = 0; i < option->vals->size; i++) { - if (string_is_equal(option->vals->elems[i].data, entry->value)) + const char *value = option->vals->elems[i].data; + uint32_t value_hash = *((uint32_t*)option->vals->elems[i].userdata); + + if ((value_hash == entry_value_hash) && + string_is_equal(value, entry->value)) { option->index = i; break; @@ -1479,6 +1503,57 @@ bool core_option_manager_get_idx(core_option_manager_t *opt, return false; } +/** + * core_option_manager_get_val_idx: + * + * @opt : options manager handle + * @idx : core option index + * @val : string representation of the + * core option value + * @val_idx : index of core option value + * corresponding to @val + * + * Fetches the index of the core option value + * identified by the specified core option @idx + * and @val string. + * + * Returns: true if option value matching the + * specified option index and value string + * was found, otherwise false. + **/ +bool core_option_manager_get_val_idx(core_option_manager_t *opt, + size_t idx, const char *val, size_t *val_idx) +{ + struct core_option *option = NULL; + uint32_t val_hash; + size_t i; + + if (!opt || + (idx >= opt->size) || + string_is_empty(val) || + !val_idx) + return false; + + val_hash = core_option_manager_hash_string(val); + option = (struct core_option*)&opt->opts[idx]; + + for (i = 0; i < option->vals->size; i++) + { + const char *option_val = option->vals->elems[i].data; + uint32_t option_val_hash = *((uint32_t*)option->vals->elems[i].userdata); + + if ((val_hash == option_val_hash) && + !string_is_empty(option_val) && + string_is_equal(val, option_val)) + { + *val_idx = i; + return true; + } + } + + return false; +} + /** * core_option_manager_get_desc: * diff --git a/core_option_manager.h b/core_option_manager.h index 0a29668c9d..d2816699f5 100644 --- a/core_option_manager.h +++ b/core_option_manager.h @@ -278,6 +278,27 @@ bool core_option_manager_get_category_visible(core_option_manager_t *opt, bool core_option_manager_get_idx(core_option_manager_t *opt, const char *key, size_t *idx); +/** + * core_option_manager_get_val_idx: + * + * @opt : options manager handle + * @idx : core option index + * @val : string representation of the + * core option value + * @val_idx : index of core option value + * corresponding to @val + * + * Fetches the index of the core option value + * identified by the specified core option @idx + * and @val string. + * + * Returns: true if option value matching the + * specified option index and value string + * was found, otherwise false. + **/ +bool core_option_manager_get_val_idx(core_option_manager_t *opt, + size_t idx, const char *val, size_t *val_idx); + /** * core_option_manager_get_desc: * diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index d9e28bfc7c..a918461900 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -1722,6 +1722,31 @@ enum retro_mod * Must be called in retro_set_environment(). */ +#define RETRO_ENVIRONMENT_SET_VARIABLE 70 + /* const struct retro_variable * -- + * Allows an implementation to notify the frontend + * that a core option value has changed. + * + * retro_variable::key and retro_variable::value + * must match strings that have been set previously + * via one of the following: + * + * - RETRO_ENVIRONMENT_SET_VARIABLES + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL + * + * After changing a core option value via this + * callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE + * will return true. + * + * If data is NULL, no changes will be registered + * and the callback will return true; an + * implementation may therefore pass NULL in order + * to test whether the callback is supported. + */ + /* VFS functionality */ /* File paths: diff --git a/retroarch.c b/retroarch.c index be5b109ac1..23260a32b5 100644 --- a/retroarch.c +++ b/retroarch.c @@ -10267,6 +10267,60 @@ static bool retroarch_environment_cb(unsigned cmd, void *data) break; + case RETRO_ENVIRONMENT_SET_VARIABLE: + { + unsigned log_level = settings->uints.libretro_log_level; + const struct retro_variable *var = (const struct retro_variable*)data; + size_t opt_idx; + size_t val_idx; + + /* If core passes NULL to the callback, return + * value indicates whether callback is supported */ + if (!var) + return true; + + if (string_is_empty(var->key) || + string_is_empty(var->value)) + return false; + + if (!runloop_state.core_options) + { + RARCH_LOG("[Environ]: SET_VARIABLE %s: not implemented.\n", + var->key); + return false; + } + + /* Check whether key is valid */ + if (!core_option_manager_get_idx(runloop_state.core_options, + var->key, &opt_idx)) + { + RARCH_LOG("[Environ]: SET_VARIABLE %s: invalid key.\n", + var->key); + return false; + } + + /* Check whether value is valid */ + if (!core_option_manager_get_val_idx(runloop_state.core_options, + opt_idx, var->value, &val_idx)) + { + RARCH_LOG("[Environ]: SET_VARIABLE %s: invalid value: %s\n", + var->key, var->value); + return false; + } + + /* Update option value if core-requested value + * is not currently set */ + if (val_idx != runloop_state.core_options->opts[opt_idx].index) + core_option_manager_set_val(runloop_state.core_options, + opt_idx, val_idx, true); + + if (log_level == RETRO_LOG_DEBUG) + RARCH_LOG("[Environ]: SET_VARIABLE %s:\n\t%s\n", + var->key, var->value); + } + + break; + /* SET_VARIABLES: Legacy path */ case RETRO_ENVIRONMENT_SET_VARIABLES: RARCH_LOG("[Environ]: SET_VARIABLES.\n");