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