diff --git a/Makefile b/Makefile index 9ca46ec96c..93411aa690 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ OBJ = frontend/frontend.o \ input/overlay.o \ patch.o \ fifo_buffer.o \ + core_options.o \ compat/compat.o \ cheats.o \ conf/config_file.o \ diff --git a/Makefile.win b/Makefile.win index 496e69276b..71a73bf00c 100644 --- a/Makefile.win +++ b/Makefile.win @@ -15,6 +15,7 @@ OBJ = frontend/frontend.o \ movie.o \ gfx/gfx_common.o \ input/input_common.o \ + core_options.o \ patch.o \ compat/compat.o \ screenshot.o \ diff --git a/core_options.c b/core_options.c new file mode 100644 index 0000000000..50ef1ed921 --- /dev/null +++ b/core_options.c @@ -0,0 +1,208 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "core_options.h" +#include "general.h" +#include "file.h" +#include "compat/posix_string.h" + +struct core_option +{ + char *desc; + char *key; + struct string_list *vals; + size_t index; +}; + +struct core_option_manager +{ + config_file_t *conf; + char conf_path[PATH_MAX]; + + struct core_option *opts; + size_t size; + bool updated; +}; + +void core_option_free(core_option_manager_t *opt) +{ + if (!opt) + return; + + for (size_t i = 0; i < opt->size; i++) + { + free(opt->opts[i].desc); + free(opt->opts[i].key); + string_list_free(opt->opts[i].vals); + } + + if (opt->conf) + config_file_free(opt->conf); + free(opt->opts); + free(opt); +} + +void core_option_get(core_option_manager_t *opt, struct retro_variable *var) +{ + for (size_t i = 0; i < opt->size; i++) + { + if (strcmp(opt->opts[i].key, var->key) == 0) + { + var->value = core_option_get_val(opt, i); + return; + } + } + + var->value = NULL; +} + +static bool parse_variable(core_option_manager_t *opt, size_t index, const struct retro_variable *var) +{ + struct core_option *option = &opt->opts[index]; + option->key = strdup(var->key); + + char *value = strdup(var->value); + char *desc_end = strstr(value, "; "); + + if (!desc_end) + { + free(value); + return false; + } + + *desc_end = '\0'; + option->desc = strdup(value); + + const char *val_start = desc_end + 2; + option->vals = string_split(val_start, "|"); + + if (!option->vals) + { + free(value); + return false; + } + + char *config_val = NULL; + if (config_get_string(opt->conf, option->key, &config_val)) + { + for (size_t i = 0; i < option->vals->size; i++) + { + if (strcmp(option->vals->elems[i].data, config_val) == 0) + { + option->index = i; + break; + } + } + + free(config_val); + } + + free(value); + return true; +} + +core_option_manager_t *core_option_new(const char *conf_path, const struct retro_variable *vars) +{ + core_option_manager_t *opt = (core_option_manager_t*)calloc(1, sizeof(*opt)); + if (!opt) + return NULL; + + size_t size = 0; + + if (*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; + + for (const struct retro_variable *var = vars; var->key && var->value; var++) + size++; + + opt->opts = (struct core_option*)calloc(size, sizeof(*opt->opts)); + if (!opt->opts) + goto error; + + opt->size = size; + + size = 0; + for (const struct retro_variable *var = vars; var->key && var->value; size++, var++) + { + if (!parse_variable(opt, size, var)) + goto error; + } + + return opt; + +error: + core_option_free(opt); + return NULL; +} + +bool core_option_updated(core_option_manager_t *opt) +{ + return opt->updated; +} + +void core_option_flush(core_option_manager_t *opt) +{ + for (size_t i = 0; i < opt->size; i++) + { + struct core_option *option = &opt->opts[i]; + config_set_string(opt->conf, option->key, core_option_get_val(opt, i)); + } + config_file_write(opt->conf, opt->conf_path); +} + +size_t core_option_size(core_option_manager_t *opt) +{ + return opt->size; +} + +const char *core_option_get_desc(core_option_manager_t *opt, size_t index) +{ + return opt->opts[index].desc; +} + +const char *core_option_get_val(core_option_manager_t *opt, size_t index) +{ + struct core_option *option = &opt->opts[index]; + return option->vals->elems[option->index].data; +} + +void core_option_next(core_option_manager_t *opt, size_t index) +{ + struct core_option *option = &opt->opts[index]; + option->index = (option->index + 1) % option->vals->size; + opt->updated = true; +} + +void core_option_prev(core_option_manager_t *opt, size_t index) +{ + struct core_option *option = &opt->opts[index]; + option->index = (option->index + option->vals->size - 1) % option->vals->size; + opt->updated = true; +} + +void core_option_set_default(core_option_manager_t *opt, size_t index) +{ + opt->opts[index].index = 0; + opt->updated = true; +} + + diff --git a/core_options.h b/core_options.h new file mode 100644 index 0000000000..7c6c5649b0 --- /dev/null +++ b/core_options.h @@ -0,0 +1,47 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef CORE_OPTIONS_H__ +#define CORE_OPTIONS_H__ + +#include "boolean.h" +#include "libretro.h" +#include + +typedef struct core_option_manager core_option_manager_t; + +core_option_manager_t *core_option_new(const char *conf_path, const struct retro_variable *vars); +bool core_option_updated(core_option_manager_t *opt); +void core_option_flush(core_option_manager_t *opt); +void core_option_free(core_option_manager_t *opt); + +void core_option_get(core_option_manager_t *opt, struct retro_variable *var); + +// Returns total number of options. +size_t core_option_size(core_option_manager_t *opt); + +// Gets description and current value for an option. +const char *core_option_get_desc(core_option_manager_t *opt, size_t index); +const char *core_option_get_val(core_option_manager_t *opt, size_t index); + +// Cycles through options for an option. Options wrap around. +void core_option_next(core_option_manager_t *opt, size_t index); +void core_option_prev(core_option_manager_t *opt, size_t index); + +// Sets default val for an option. +void core_option_set_default(core_option_manager_t *opt, size_t index); + +#endif + diff --git a/dynamic.c b/dynamic.c index eee7d4a85a..c097120497 100644 --- a/dynamic.c +++ b/dynamic.c @@ -90,7 +90,6 @@ void *(*pretro_get_memory_data)(unsigned); size_t (*pretro_get_memory_size)(unsigned); static void set_environment(void); -static void set_environment_defaults(void); #ifdef HAVE_DYNAMIC #if defined(__APPLE__) @@ -310,8 +309,6 @@ void init_libretro_sym(void) #endif load_symbols(); - - set_environment_defaults(); set_environment(); } @@ -393,44 +390,34 @@ static bool environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_GET_VARIABLE: { struct retro_variable *var = (struct retro_variable*)data; - if (var->key) - { - // Split string has '\0' delimiters so we have to find the position in original string, - // then pass the corresponding offset into the split string. - const char *key = strstr(g_extern.system.environment, var->key); - size_t key_len = strlen(var->key); - if (key && key[key_len] == '=') - { - ptrdiff_t offset = key - g_extern.system.environment; - var->value = &g_extern.system.environment_split[offset + key_len + 1]; - } - else - var->value = NULL; - } - else - var->value = g_extern.system.environment; + RARCH_LOG("Environ GET_VARIABLE %s:\n", var->key); - RARCH_LOG("Environ GET_VARIABLE: %s=%s\n", - var->key ? var->key : "null", - var->value ? var->value : "null"); + if (g_extern.system.core_options) + core_option_get(g_extern.system.core_options, var); + else + var->value = NULL; break; } + case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: + *(bool*)data = g_extern.system.core_options ? + core_option_updated(g_extern.system.core_options) : false; + break; + case RETRO_ENVIRONMENT_SET_VARIABLES: { - RARCH_LOG("Environ SET_VARIABLES:\n"); - RARCH_LOG("=======================\n"); - const struct retro_variable *vars = (const struct retro_variable*)data; - while (vars->key) - { - RARCH_LOG("\t%s :: %s\n", - vars->key, - vars->value ? vars->value : "N/A"); + RARCH_LOG("Environ SET_VARIABLES.\n"); - vars++; + if (g_extern.system.core_options) + { + core_option_flush(g_extern.system.core_options); + core_option_free(g_extern.system.core_options); } - RARCH_LOG("=======================\n"); + + const struct retro_variable *vars = (const struct retro_variable*)data; + g_extern.system.core_options = core_option_new(g_settings.core_options_path, vars); + break; } @@ -609,13 +596,3 @@ static void set_environment(void) pretro_set_environment(environment_cb); } -static void set_environment_defaults(void) -{ - char *save; - - // Split up environment variables beforehand. - if (g_extern.system.environment_split && - strtok_r(g_extern.system.environment_split, ";", &save)) - while (strtok_r(NULL, ";", &save)); -} - diff --git a/general.h b/general.h index ba9a92d6ff..e0b4cdd295 100644 --- a/general.h +++ b/general.h @@ -32,6 +32,7 @@ #include "audio/ext/rarch_dsp.h" #include "compat/strl.h" #include "performance.h" +#include "core_options.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -260,6 +261,8 @@ struct settings float overlay_opacity; } input; + char core_options_path[PATH_MAX]; + char libretro[PATH_MAX]; char cheat_database[PATH_MAX]; char cheat_settings_path[PATH_MAX]; @@ -375,9 +378,6 @@ struct global struct retro_system_av_info av_info; float aspect_ratio; - char *environment; - char *environment_split; - unsigned rotation; bool shutdown; unsigned performance_level; @@ -393,6 +393,8 @@ struct global struct retro_disk_control_callback disk_control; struct retro_hw_render_callback hw_render_callback; + + core_option_manager_t *core_options; } system; struct diff --git a/libretro.h b/libretro.h index c36389f9e8..b02855ae1f 100755 --- a/libretro.h +++ b/libretro.h @@ -350,18 +350,7 @@ enum retro_mod // Boolean value whether or not frontend supports frame duping, // passing NULL to video frame callback. // -#define RETRO_ENVIRONMENT_GET_VARIABLE 4 // struct retro_variable * -- - // Interface to aquire user-defined information from environment - // that cannot feasibly be supported in a multi-system way. - // Mostly used for obscure, - // specific features that the user can tap into when neseccary. - // -#define RETRO_ENVIRONMENT_SET_VARIABLES 5 // const struct retro_variable * -- - // Allows an implementation to signal the environment - // which variables it might want to check for later using GET_VARIABLE. - // 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element. - // retro_variable::value should contain a human readable description of the key. - // +// Environ 4, 5 are no longer supported (GET_VARIABLE / SET_VARIABLES), and reserved to avoid possible ABI clash. #define RETRO_ENVIRONMENT_SET_MESSAGE 6 // const struct retro_message * -- // Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'. // Should not be used for trivial messages, which should simply be logged to stderr. @@ -432,7 +421,37 @@ enum retro_mod // If successful, libretro cores will be able to render to a frontend-provided framebuffer. // The size of this framebuffer will be at least as large as max_width/max_height provided in get_av_info(). // If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or NULL to retro_video_refresh_t. - +#define RETRO_ENVIRONMENT_GET_VARIABLE 15 + // struct retro_variable * -- + // Interface to aquire user-defined information from environment + // that cannot feasibly be supported in a multi-system way. + // 'key' should be set to a key which has already been set by SET_VARIABLES. + // 'data' will be set to a value or NULL. + // +#define RETRO_ENVIRONMENT_SET_VARIABLES 16 + // const struct retro_variable * -- + // 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 be called as early as possible (ideally in retro_set_environment). + // + // 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element. + // retro_variable::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_variable::value should contain a human readable description of the key as well as a '|' delimited list of expected values. + // The number of possible options should be very limited, 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; false|true" } + // + // Text before first ';' is description. This ';' must be followed by a space, and followed by a list of possible values split up with '|'. + // Only strings are operated on. The possible values will generally be displayed and stored as-is by the frontend. + // +#define RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE 17 + // bool * -- + // Result is set to true if some variables are updated by + // frontend since last call to RETRO_ENVIRONMENT_GET_VARIABLE. + // Variables should be queried with GET_VARIABLE. // Pass this to retro_video_refresh_t if rendering to hardware. // Passing NULL to retro_video_refresh_t is still a frame dupe as normal. diff --git a/retroarch.c b/retroarch.c index 26c167872e..5317d2c4c1 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2702,9 +2702,6 @@ void rarch_main_clear_state(void) { memset(&g_settings, 0, sizeof(g_settings)); - free(g_extern.system.environment); - free(g_extern.system.environment_split); - if (g_extern.log_file) fclose(g_extern.log_file); @@ -3039,6 +3036,13 @@ void rarch_main_deinit(void) g_extern.rom_file_temporary = false; } + if (g_extern.system.core_options) + { + core_option_flush(g_extern.system.core_options); + core_option_free(g_extern.system.core_options); + } + g_extern.system.core_options = NULL; + g_extern.main_is_init = false; } diff --git a/retroarch.cfg b/retroarch.cfg index 9810367ae9..d1610d4d50 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -22,14 +22,10 @@ # This could fail if ROM extensions overlap. # libretro_path = "/path/to/libretro.so" -# Environment variables internally in RetroArch. -# Implementations can tap into this user-specificed information to enable functionality -# that is deemed too obscure to expose directly. -# Some variables might be "standardized" at a later time if needed. -# The string is formatted as key value pairs delimited by a semicolon ';'. -# Any white space between the delimiter ';' and the '=' is significant. -# I.e.: "key1=value1;key2=value2;..." -# environment_variables = +# Path to core options config file. +# This config file is used to expose core-specific options. +# It will be written to by RetroArch. +# core_options_path = # Sets the "system" directory. # Implementations can query for this directory to load BIOSes, system-specific configs, etc. diff --git a/settings.c b/settings.c index 24b394ec5f..77f7ef4b27 100644 --- a/settings.c +++ b/settings.c @@ -670,6 +670,7 @@ bool config_load_file(const char *path) if (!*g_settings.libretro) CONFIG_GET_PATH(libretro, "libretro_path"); + CONFIG_GET_PATH(core_options_path, "core_options_path"); CONFIG_GET_PATH(screenshot_directory, "screenshot_directory"); if (*g_settings.screenshot_directory && !path_is_directory(g_settings.screenshot_directory)) { @@ -723,18 +724,6 @@ bool config_load_file(const char *path) CONFIG_GET_INT(input.icade_profile[3], "input_autodetect_icade_profile_pad4"); #endif - if (config_get_string(conf, "environment_variables", - &g_extern.system.environment)) - { - g_extern.system.environment_split = strdup(g_extern.system.environment); - if (!g_extern.system.environment_split) - { - RARCH_ERR("Failed to allocate environment variables. Will ignore them.\n"); - free(g_extern.system.environment); - g_extern.system.environment = NULL; - } - } - if (!g_extern.has_set_save_path && config_get_path(conf, "savefile_directory", tmp_str, sizeof(tmp_str))) { if (path_is_directory(tmp_str))