From 7346ef41480e6bd17fed1ec10cc78139ddd42117 Mon Sep 17 00:00:00 2001 From: jdgleaver Date: Thu, 1 Aug 2019 18:03:19 +0100 Subject: [PATCH] Add enhanced core options --- Makefile.common | 1 + libretro-common/include/libretro.h | 198 ++++++++++- libretro.c | 130 +------- libretro_core_options.c | 520 +++++++++++++++++++++++++++++ libretro_core_options.h | 16 + 5 files changed, 737 insertions(+), 128 deletions(-) create mode 100644 libretro_core_options.c create mode 100644 libretro_core_options.h diff --git a/Makefile.common b/Makefile.common index cbf541e..36bfc59 100644 --- a/Makefile.common +++ b/Makefile.common @@ -16,6 +16,7 @@ endif SOURCES_C := \ $(CORE_DIR)/libretro.c \ + $(CORE_DIR)/libretro_core_options.c \ $(CORE_DIR)/cuefile.c \ $(CORE_DIR)/nvram.c \ $(CORE_DIR)/retro_callbacks.c \ diff --git a/libretro-common/include/libretro.h b/libretro-common/include/libretro.h index 8daec83..d388f0f 100644 --- a/libretro-common/include/libretro.h +++ b/libretro-common/include/libretro.h @@ -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,146 @@ 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}}, NULL } 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_core_option_definition::values is an array of retro_core_option_value + * structs terminated by a { NULL, NULL } element. + * > retro_core_option_definition::values[index].value is an expected option + * value. + * > retro_core_option_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_definition::default_value is the default core option + * setting. It must match one of the expected option values in the + * retro_core_option_definition::values array. If it does not, or the + * 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, + * 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 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 }, + * }, + * "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_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. + * + * NOTE: Default core option values are always taken from the + * retro_core_options_intl::us array. Any default values in + * retro_core_options_intl::local array will be ignored. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY 55 + /* struct retro_core_option_display * -- + * + * Allows an implementation to signal the environment to show + * or hide a variable when displaying core options. This is + * considered a *suggestion*. The frontend is free to ignore + * this callback, and its implementation not considered mandatory. + * + * 'data' points to a retro_core_option_display struct + * + * retro_core_option_display::key is a variable identifier + * which has already been set by SET_VARIABLES/SET_CORE_OPTIONS. + * + * retro_core_option_display::visible is a boolean, specifying + * whether variable should be displayed + * + * Note that all core option variables will be set visible by + * default when calling SET_VARIABLES/SET_CORE_OPTIONS. + */ + /* VFS functionality */ /* File paths: @@ -2351,6 +2491,62 @@ struct retro_variable const char *value; }; +struct retro_core_option_display +{ + /* Variable to configure in RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY */ + const char *key; + + /* Specifies whether variable should be displayed + * when presenting core options to the user */ + bool visible; +}; + +/* Maximum number of values permitted for a core option */ +#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]; + + /* 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_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. diff --git a/libretro.c b/libretro.c index 2aba20c..c234c08 100644 --- a/libretro.c +++ b/libretro.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -44,132 +45,6 @@ static uint32_t ACTIVE_DEVICES; static const freedo_bios_t *BIOS = NULL; static const freedo_bios_t *FONT = NULL; -static -bool -file_exists(const char *path_) -{ - RFILE *fp; - - fp = filestream_open(path_, - RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE); - - if(fp == NULL) - return false; - - filestream_close(fp); - - return true; -} - -static -bool -file_exists_in_system_directory(const char *filename_) -{ - int rv; - char fullpath[PATH_MAX_LENGTH]; - const char *system_path; - - system_path = NULL; - rv = retro_environment_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY,&system_path); - if((rv == 0) || (system_path == NULL)) - return false; - - fill_pathname_join(fullpath,system_path,filename_,PATH_MAX_LENGTH); - - return file_exists(fullpath); -} - -static -void -create_bios_option_list(char *buf_) -{ - int rv; - const freedo_bios_t *bios; - - strcpy(buf_,"BIOS (rom1); "); - for(bios = freedo_bios_begin(); bios != freedo_bios_end(); bios++) - { - rv = file_exists_in_system_directory(bios->filename); - if(rv) - { - strcat(buf_,bios->name); - strcat(buf_,"|"); - } - } - - rv = (strlen(buf_) - 1); - if(buf_[rv] == '|') - buf_[rv] = '\0'; - else - strcat(buf_,"None Found"); -} - -static -void -create_font_option_list(char *buf_) -{ - int rv; - const freedo_bios_t *font; - - strcpy(buf_,"Font (rom2); disabled|"); - for(font = freedo_bios_font_begin(); font != freedo_bios_font_end(); font++) - { - rv = file_exists_in_system_directory(font->filename); - if(rv) - { - strcat(buf_,font->name); - strcat(buf_,"|"); - } - } - - rv = (strlen(buf_) - 1); - buf_[rv] = '\0'; -} - -static -void -retro_environment_set_variables(void) -{ - char bios[1024]; - char font[1024]; - static struct retro_variable vars[] = - { - { "4do_bios", NULL }, - { "4do_font", NULL }, - { "4do_cpu_overclock", "CPU overclock; " - "1.0x (12.50Mhz)|" - "1.1x (13.75Mhz)|" - "1.2x (15.00Mhz)|" - "1.5x (18.75Mhz)|" - "1.6x (20.00Mhz)|" - "1.8x (22.50Mhz)|" - "2.0x (25.00Mhz)" }, -#if THREADED_DSP - { "4do_dsp_threaded", "Threaded DSP; disabled|enabled" }, -#else - { "4do_dsp_threaded", "Threaded DSP; unsupported" }, -#endif - { "4do_high_resolution", "High Resolution; disabled|enabled" }, - { "4do_nvram_storage", "NVRAM Storage; per game|shared" }, - { "4do_active_devices", "Active Devices; 1|2|3|4|5|6|7|8|0" }, - { "4do_hack_timing_1", "Timing Hack 1 (Crash 'n Burn); disabled|enabled" }, - { "4do_hack_timing_3", "Timing Hack 3 (Dinopark Tycoon); disabled|enabled" }, - { "4do_hack_timing_5", "Timing Hack 5 (Microcosm); disabled|enabled" }, - { "4do_hack_timing_6", "Timing Hack 6 (Alone in the Dark); disabled|enabled" }, - { "4do_hack_graphics_step_y", "Graphics Step Y Hack (Samurai Shodown); disabled|enabled" }, - { "4do_madam_matrix_engine", "MADAM Matrix Engine; hardware|software" }, - { "4do_kprint", "3DO debugging output (stderr); disabled|enabled" }, - { NULL, NULL }, - }; - - vars[0].value = bios; - vars[1].value = font; - create_bios_option_list(bios); - create_font_option_list(font); - retro_environment_cb(RETRO_ENVIRONMENT_SET_VARIABLES,(void*)vars); -} - static void retro_environment_set_support_no_game(void) @@ -216,7 +91,8 @@ retro_set_environment(retro_environment_t cb_) retro_set_environment_cb(cb_); retro_environment_set_controller_info(); - retro_environment_set_variables(); + libretro_init_core_options(); + libretro_set_core_options(); retro_environment_set_support_no_game(); } diff --git a/libretro_core_options.c b/libretro_core_options.c new file mode 100644 index 0000000..1bab162 --- /dev/null +++ b/libretro_core_options.c @@ -0,0 +1,520 @@ + +#include +#include +#include + +#include "retro_callbacks.h" +#include "libfreedo/freedo_bios.h" + +#include + +#include +#include + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_ENGLISH */ + +/* Default language: + * - All other languages must include the same keys and values + * - Will be used as a fallback in the event that frontend language + * is not available + * - Will be used as a fallback for any missing entries in + * frontend language definition */ + +static struct retro_core_option_definition option_defs_us[] = { + { + "4do_bios", + "BIOS (rom1)", + "Select which official system BIOS (hardware revision) to use when performing emulation. Only files present in RetroArch's system directory are listed.", + { + { NULL, NULL }, /* This is set dynamically */ + }, + NULL + }, + { + "4do_font", + "Font (rom2)", + "Select which official 'font rom' to use when performing emulation. Required by some Japanese games, otherwise optional. Only files present in RetroArch's system directory are listed.", + { + { NULL, NULL }, /* This is set dynamically */ + }, + NULL + }, + { + "4do_cpu_overclock", + "CPU Overclock", + "Increase clock speed of the emulated 3DO's 12.5MHz ARM60 CPU. Dramatically improves frame rates in many games (e.g. The Need For Speed), but increases performance requirements and in some cases has no effect (or may cause glitches).", + { + { "1.0x (12.50Mhz)", NULL }, + { "1.1x (13.75Mhz)", NULL }, + { "1.2x (15.00Mhz)", NULL }, + { "1.5x (18.75Mhz)", NULL }, + { "1.6x (20.00Mhz)", NULL }, + { "1.8x (22.50Mhz)", NULL }, + { "2.0x (25.00Mhz)", NULL }, + { NULL, NULL }, + }, + "1.0x (12.50Mhz)" + }, +#if THREADED_DSP + { + "4do_dsp_threaded", + "Threaded DSP", + "Run the 3DO's emulated DSP (audio processor) on a separate CPU thread. Improves performance on multi-core systems.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, +#endif + { + "4do_high_resolution", + "High Resolution", + "The 3DO has a default internal resolution of 320x240, but outputs video at 640x480. Enabling 'High Resolution' mode forces content to be rendered at the full 640x480 output resolution, increasing the fidelity of 3D models. Has a significant performance impact.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_nvram_storage", + "NVRAM Storage", + "Selects whether NVRAM saves should be created per-game, or as a single NVRAM file shared between all games.", + { + { "per game", "Per Game" }, + { "shared", "Shared" }, + { NULL, NULL }, + }, + "per game" + }, + { + "4do_active_devices", + "Active Input Devices", + "There exists a bug (perhaps in 4DO itself, but possibly in certain games) where having more than 1 emulated controller causes content to ignore gamepad input. Setting 'Active Input Devices' to 1 provides a workaround for this issue.", + { + { "0", NULL }, + { "1", NULL }, + { "2", NULL }, + { "3", NULL }, + { "4", NULL }, + { "5", NULL }, + { "6", NULL }, + { "7", NULL }, + { "8", NULL }, + { NULL, NULL }, + }, + "1" + }, + { + "4do_hack_timing_1", + "Timing Hack 1 (Crash 'n Burn)", + "This must be enabled for correct operation of the game 'Crash 'n Burn'.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_hack_timing_3", + "Timing Hack 3 (Dinopark Tycoon)", + "This must be enabled for correct operation of the game 'Dinopark Tycoon'.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_hack_timing_5", + "Timing Hack 5 (Microcosm)", + "This must be enabled for correct operation of the game 'Microcosm'.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_hack_timing_6", + "Timing Hack 6 (Alone in the Dark)", + "This must be enabled for correct operation of the game 'Alone in the Dark'.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_hack_graphics_step_y", + "Graphics Step Y Hack (Samurai Showdown)", + "This must be enabled for backgrounds to render correctly when running the game 'Samurai Showdown'.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "4do_madam_matrix_engine", + "MADAM Matrix Engine", + "'MADAM' is the 3DO's graphics accelerator. It contains a custom maths co-processor, used by the 3DO's CPU to offload matrix operations. This corresponds to running in 'Hardware' - but the 3DS could also utilise a built-in ARM 'Software' version of the MADAM matrix engine. 'Hardware' mode is the default, but it has been observed that some games run faster when forcing 'Software' mode (e.g. The Need For Speed).", + { + { "hardware", "Hardware" }, + { "software", "Software" }, + { NULL, NULL }, + }, + "hardware" + }, + { + "4do_kprint", + "3DO Debugging Output (stderr)", + "Output low-level debugging information to the console (terminal). Should be disabled in most cases.", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { NULL, NULL, NULL, {{0}}, NULL }, +}; + +/* RETRO_LANGUAGE_JAPANESE */ + +/* RETRO_LANGUAGE_FRENCH */ + +/* RETRO_LANGUAGE_SPANISH */ + +/* RETRO_LANGUAGE_GERMAN */ + +/* RETRO_LANGUAGE_ITALIAN */ + +/* RETRO_LANGUAGE_DUTCH */ + +/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + +/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + +/* RETRO_LANGUAGE_RUSSIAN */ + +/* RETRO_LANGUAGE_KOREAN */ + +/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + +/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + +/* RETRO_LANGUAGE_ESPERANTO */ + +/* RETRO_LANGUAGE_POLISH */ + +/* RETRO_LANGUAGE_VIETNAMESE */ + +/* RETRO_LANGUAGE_ARABIC */ + +/* RETRO_LANGUAGE_GREEK */ + +/* RETRO_LANGUAGE_TURKISH */ + +/* + ******************************** + * Language Mapping + ******************************** +*/ + +static struct retro_core_option_definition *option_defs_intl[RETRO_LANGUAGE_LAST] = { + option_defs_us, /* RETRO_LANGUAGE_ENGLISH */ + NULL, /* RETRO_LANGUAGE_JAPANESE */ + NULL, /* RETRO_LANGUAGE_FRENCH */ + NULL, /* RETRO_LANGUAGE_SPANISH */ + NULL, /* RETRO_LANGUAGE_GERMAN */ + NULL, /* RETRO_LANGUAGE_ITALIAN */ + NULL, /* RETRO_LANGUAGE_DUTCH */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + NULL, /* RETRO_LANGUAGE_RUSSIAN */ + NULL, /* RETRO_LANGUAGE_KOREAN */ + NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + NULL, /* RETRO_LANGUAGE_ESPERANTO */ + NULL, /* RETRO_LANGUAGE_POLISH */ + NULL, /* RETRO_LANGUAGE_VIETNAMESE */ + NULL, /* RETRO_LANGUAGE_ARABIC */ + NULL, /* RETRO_LANGUAGE_GREEK */ + NULL, /* RETRO_LANGUAGE_TURKISH */ +}; + +/* + ******************************** + * Functions + ******************************** +*/ + +static const char bios_disabled_str[] = "disabled"; + +static bool file_exists_in_system_directory(const char *filename) +{ + int ret; + char fullpath[PATH_MAX_LENGTH]; + const char *system_path = NULL; + + if (!retro_environment_cb) + return false; + + ret = retro_environment_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_path); + + if((ret == 0) || !system_path) + return false; + + fullpath[0] = '\0'; + fill_pathname_join(fullpath, system_path, filename, sizeof(fullpath)); + + return path_is_valid(fullpath); +} + +static void set_bios_values(struct retro_core_option_value *values) +{ + size_t i = 0; + const freedo_bios_t *bios = NULL; + + if (!values) + return; + + /* Loop through all recognised bios files */ + for(bios = freedo_bios_begin(); bios != freedo_bios_end(); bios++) + { + /* Sanity check */ + if (i >= RETRO_NUM_CORE_OPTION_VALUES_MAX - 1) + break; + + if (file_exists_in_system_directory(bios->filename)) + { + values[i].value = bios->name; + values[i].label = NULL; + i++; + } + } + + /* Handle 'no files found' condition */ + if (i == 0) + { + values[i].value = bios_disabled_str; + values[i].label = NULL; + i++; + } + + /* Add proper null termination */ + values[i].value = NULL; + values[i].label = NULL; +} + +static void set_font_values(struct retro_core_option_value *values) +{ + size_t i = 0; + const freedo_bios_t *font = NULL; + + if (!values) + return; + + /* First value is always 'disabled' */ + values[i].value = bios_disabled_str; + values[i].label = NULL; + i++; + + /* Loop through all recognised font files */ + for(font = freedo_bios_font_begin(); font != freedo_bios_font_end(); font++) + { + /* Sanity check */ + if (i >= RETRO_NUM_CORE_OPTION_VALUES_MAX - 1) + break; + + if (file_exists_in_system_directory(font->filename)) + { + values[i].value = font->name; + values[i].label = NULL; + i++; + } + } + + /* Add proper null termination */ + values[i].value = NULL; + values[i].label = NULL; +} + +void libretro_init_core_options(void) +{ + size_t i = 0; + + /* Loop over options until we find the entries + * with dynamic values */ + while (true) + { + const char *key = option_defs_us[i].key; + struct retro_core_option_value *values = option_defs_us[i].values; + + if (key) + { + if (strcmp(key, "4do_bios") == 0) + set_bios_values(values); + else if (strcmp(key, "4do_font") == 0) + set_font_values(values); + i++; + } + else + break; + } +} + +void libretro_set_core_options(void) +{ + unsigned version = 0; + + if (!retro_environment_cb) + return; + + if (retro_environment_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version) && (version == 1)) + { + struct retro_core_options_intl core_options_intl; + unsigned language = 0; + + core_options_intl.us = option_defs_us; + core_options_intl.local = NULL; + + if (retro_environment_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH)) + core_options_intl.local = option_defs_intl[language]; + + retro_environment_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_intl); + } + else + { + size_t i; + size_t num_options = 0; + struct retro_variable *variables = NULL; + char **values_buf = NULL; + + /* Determine number of options */ + while (true) + { + if (option_defs_us[num_options].key) + num_options++; + else + break; + } + + /* Allocate arrays */ + variables = (struct retro_variable *)calloc(num_options + 1, sizeof(struct retro_variable)); + values_buf = (char **)calloc(num_options, sizeof(char *)); + + if (!variables || !values_buf) + goto error; + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + const char *key = option_defs_us[i].key; + const char *desc = option_defs_us[i].desc; + const char *default_value = option_defs_us[i].default_value; + struct retro_core_option_value *values = option_defs_us[i].values; + size_t buf_len = 3; + size_t default_index = 0; + + values_buf[i] = NULL; + + if (desc) + { + size_t num_values = 0; + + /* Determine number of values */ + while (true) + { + if (values[num_values].value) + { + /* Check if this is the default value */ + if (default_value) + if (strcmp(values[num_values].value, default_value) == 0) + default_index = num_values; + + buf_len += strlen(values[num_values].value); + num_values++; + } + else + break; + } + + /* Build values string */ + if (num_values > 1) + { + size_t j; + + buf_len += num_values - 1; + buf_len += strlen(desc); + + values_buf[i] = (char *)calloc(buf_len, sizeof(char)); + if (!values_buf[i]) + goto error; + + strcpy(values_buf[i], desc); + strcat(values_buf[i], "; "); + + /* Default value goes first */ + strcat(values_buf[i], values[default_index].value); + + /* Add remaining values */ + for (j = 0; j < num_values; j++) + { + if (j != default_index) + { + strcat(values_buf[i], "|"); + strcat(values_buf[i], values[j].value); + } + } + } + } + + variables[i].key = key; + variables[i].value = values_buf[i]; + } + + /* Set variables */ + retro_environment_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); + +error: + + /* Clean up */ + if (values_buf) + { + for (i = 0; i < num_options; i++) + { + if (values_buf[i]) + { + free(values_buf[i]); + values_buf[i] = NULL; + } + } + + free(values_buf); + values_buf = NULL; + } + + if (variables) + { + free(variables); + variables = NULL; + } + } +} diff --git a/libretro_core_options.h b/libretro_core_options.h new file mode 100644 index 0000000..76b1a73 --- /dev/null +++ b/libretro_core_options.h @@ -0,0 +1,16 @@ +#ifndef LIBRETRO_CORE_OPTIONS_H__ +#define LIBRETRO_CORE_OPTIONS_H__ + +/* Initialises any dynamic core options values + * Note: Must be called after retro_set_environment_cb() + */ +void libretro_init_core_options(void); + +/* Handles configuration/setting of core options. + * Should be called as early as possible - ideally inside + * retro_set_environment(), and no later than retro_load_game() + * Note: Must be called after retro_set_environment_cb() + */ +void libretro_set_core_options(void); + +#endif