diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 827754a51a..42436e6d75 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -1325,28 +1325,18 @@ static void menu_action_setting_disp_set_label_core_options( char *s2, size_t len2) { core_option_manager_t *coreopts = NULL; - const char *core_opt = NULL; + const char *coreopt_label = NULL; *s = '\0'; *w = 19; if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts)) { - core_opt = core_option_manager_get_val_label(coreopts, + coreopt_label = 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); - } + if (!string_is_empty(coreopt_label)) + strlcpy(s, coreopt_label, len); } strlcpy(s2, path, len2); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 8be727634f..09cbda4834 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -3440,17 +3440,77 @@ static int action_ok_audio_run(const char *path, int action_ok_core_option_dropdown_list(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { - char core_option_lbl[256]; - char core_option_idx[256]; - snprintf(core_option_lbl, sizeof(core_option_lbl), - "core_option_%d", (int)idx); - snprintf(core_option_idx, sizeof(core_option_idx), "%d", - type); + core_option_manager_t *coreopts = NULL; + struct core_option *option = NULL; + const char *value_label_0 = NULL; + const char *value_label_1 = NULL; + size_t option_index; + char option_path_str[256]; + char option_lbl_str[256]; + option_path_str[0] = '\0'; + option_lbl_str[0] = '\0'; + + /* Boolean options are toggled directly, + * without the use of a drop-down list */ + + /* > Get current option index */ + 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)) + goto push_dropdown_list; + + /* > Get current option, and check whether + * it has exactly 2 values (i.e. on/off) */ + option = (struct core_option*)&coreopts->opts[option_index]; + + if (!option || + (option->vals->size != 2) || + ((option->index != 0) && + (option->index != 1))) + goto push_dropdown_list; + + /* > Check whether option values correspond + * to a boolean toggle */ + value_label_0 = option->val_labels->elems[0].data; + value_label_1 = option->val_labels->elems[1].data; + + if (string_is_empty(value_label_0) || + string_is_empty(value_label_1) || + !((string_is_equal(value_label_0, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)) && + string_is_equal(value_label_1, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF))) || + (string_is_equal(value_label_0, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) && + string_is_equal(value_label_1, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON))))) + goto push_dropdown_list; + + /* > Update value and return */ + core_option_manager_set_val(coreopts, option_index, + (option->index == 0) ? 1 : 0); + + return 0; + +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); + snprintf(option_lbl_str, sizeof(option_lbl_str), + "%d", type); + + /* TODO/FIXME: This should be refactored to make + * use of a core-option-specific drop-down list, + * rather than hijacking the generic one... */ generic_action_ok_displaylist_push( - core_option_lbl, NULL, - core_option_idx, 0, idx, 0, + option_path_str, NULL, + option_lbl_str, 0, idx, 0, ACTION_OK_DL_DROPDOWN_BOX_LIST); + return 0; } diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 120b771289..75d03ec403 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10531,25 +10531,21 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, * toggles the Quick Menu off and on again (returning * to the Core Options menu) the menu must be refreshed * (or undefined behaviour occurs). - * We therefore have to cache the last set menu size, - * and compare this with the new size after processing - * the current core_option_manager_t struct. - * Note: It would be 'nicer' to only refresh the menu - * if the selection marker is at an index higher than - * the new size, but we don't really have access that - * information at this stage (i.e. the selection can - * change after this function is called) */ - static size_t prev_count = 0; + * To prevent the menu selection from going out of bounds, + * we therefore have to check that the current selection + * index is less than the current number of menu entries + * - if not, we reset the navigation pointer */ + size_t selection = menu_navigation_get_selection(); menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, info->list); if (rarch_ctl(RARCH_CTL_HAS_CORE_OPTIONS, NULL)) { - size_t opts = 0; + size_t num_opts = 0; settings_t *settings = config_get_ptr(); bool game_specific_options = settings->bools.game_specific_options; - rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &opts); + rarch_ctl(RARCH_CTL_GET_CORE_OPTION_SIZE, &num_opts); if (game_specific_options) { @@ -10557,9 +10553,9 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, { if (menu_entries_append_enum(info->list, msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE), + MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE), msg_hash_to_str( - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE), + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE), MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_CREATE, MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) count++; @@ -10567,29 +10563,28 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, else if (menu_entries_append_enum(info->list, msg_hash_to_str( - MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE), + MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE), msg_hash_to_str( - MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE), + MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE), MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS_IN_USE, MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0)) count++; } - if (opts != 0) + if (num_opts != 0) { core_option_manager_t *coreopts = NULL; - rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts); - - for (i = 0; i < opts; i++) + if (rarch_ctl(RARCH_CTL_CORE_OPTIONS_LIST_GET, &coreopts)) { - if (core_option_manager_get_visible(coreopts, i)) + for (i = 0; i < num_opts; i++) { - 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++; + 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++; } } } @@ -10602,11 +10597,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, MENU_ENUM_LABEL_NO_CORE_OPTIONS_AVAILABLE, MENU_SETTINGS_CORE_OPTION_NONE, 0, 0); - if (count != prev_count) + if (selection >= count) { info->need_refresh = true; info->need_navigation_clear = true; - prev_count = count; } info->need_push = true; } diff --git a/retroarch.c b/retroarch.c index d590916d81..e1d46ebe1f 100644 --- a/retroarch.c +++ b/retroarch.c @@ -17354,11 +17354,62 @@ int main(int argc, char *argv[]) #endif /* CORE OPTIONS */ +static const char *core_option_manager_parse_value_label( + const char *value, const char *value_label) +{ + /* 'value_label' may be NULL */ + const char *label = string_is_empty(value_label) ? + value : value_label; + + if (string_is_empty(label)) + return NULL; + + /* Any label starting with a digit (or +/-) + * cannot be a boolean string, and requires + * no further processing */ + if (isdigit((unsigned char)*label) || + (*label == '+') || + (*label == '-')) + return label; + + /* Core devs have a habit of using arbitrary + * strings to label boolean values (i.e. enabled, + * Enabled, on, On, ON, true, True, TRUE, disabled, + * Disabled, off, Off, OFF, false, False, FALSE). + * These should all be converted to standard ON/OFF + * strings + * > Note: We require some duplication here + * (e.g. MENU_ENUM_LABEL_ENABLED *and* + * MENU_ENUM_LABEL_VALUE_ENABLED) in order + * to match both localised and non-localised + * strings. This function is not performance + * critical, so these extra comparisons do + * no harm */ + if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_ENABLED)) || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ENABLED)) || + string_is_equal_noncase(label, "on") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON)) || + string_is_equal_noncase(label, "true") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_TRUE))) + label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_ON); + else if (string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_DISABLED)) || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DISABLED)) || + string_is_equal_noncase(label, "off") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF)) || + string_is_equal_noncase(label, "false") || + string_is_equal_noncase(label, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FALSE))) + label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_OFF); + + return label; +} + static bool core_option_manager_parse_variable( core_option_manager_t *opt, size_t idx, const struct retro_variable *var, config_file_t *config_src) { + size_t i; + union string_list_elem_attr attr; const char *val_start = NULL; char *value = NULL; char *desc_end = NULL; @@ -17391,13 +17442,30 @@ 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); + /* Legacy core option interface has no concept + * of value labels + * > Use actual values for display purposes */ + attr.i = 0; + option->val_labels = string_list_new(); if (!option->val_labels) goto error; + /* > Loop over values and 'extract' labels */ + for (i = 0; i < option->vals->size; i++) + { + const char *value = option->vals->elems[i].data; + const char *value_label = core_option_manager_parse_value_label( + value, NULL); + + /* Redundant safely check... */ + value_label = string_is_empty(value_label) ? + value : value_label; + + /* Append value label string */ + string_list_append(option->val_labels, value_label, attr); + } + /* Legacy core option interface always uses first * defined value as the default */ option->default_index = 0; @@ -17411,8 +17479,6 @@ static bool core_option_manager_parse_variable( /* Set current config value */ if (entry && !string_is_empty(entry->value)) { - size_t i; - for (i = 0; i < option->vals->size; i++) { if (string_is_equal(option->vals->elems[i].data, entry->value)) @@ -17475,26 +17541,35 @@ static bool core_option_manager_parse_option( if (!option->vals || !option->val_labels) return false; - /* Initialse default value */ + /* Initialise default value */ option->default_index = 0; option->index = 0; /* 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); + const char *value = values[i].value; + const char *value_label = values[i].label; - /* 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); + /* Append value string + * > We know that 'value' is always valid */ + string_list_append(option->vals, value, attr); + + /* Value label requires additional processing */ + value_label = core_option_manager_parse_value_label( + value, value_label); + + /* > Redundant safely check... */ + value_label = string_is_empty(value_label) ? + value : value_label; + + /* Append value label string */ + string_list_append(option->val_labels, value_label, attr); /* Check whether this value is the default setting */ if (!string_is_empty(option_def->default_value)) { - if (string_is_equal(option_def->default_value, values[i].value)) + if (string_is_equal(option_def->default_value, value)) { option->default_index = i; option->index = i;