diff --git a/dynamic.c b/dynamic.c index a6190943c8..8b29930857 100644 --- a/dynamic.c +++ b/dynamic.c @@ -335,6 +335,50 @@ static bool environment_cb(unsigned cmd, void *data) SSNES_LOG("Environ GET_CAN_REWIND: %s\n", g_settings.rewind_enable ? "true" : "false"); break; + case SNES_ENVIRONMENT_GET_VARIABLE: + { + struct snes_variable *var = (struct snes_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; + + SSNES_LOG("Environ GET_VARIABLE: %s=%s\n", + var->key ? var->key : "null", + var->value ? var->value : "null"); + + break; + } + + case SNES_ENVIRONMENT_SET_VARIABLES: + { + SSNES_LOG("Environ SET_VARIABLES:\n"); + SSNES_LOG("=======================\n"); + const struct snes_variable *vars = (const struct snes_variable*)data; + while (vars->key) + { + SSNES_LOG("\t%s :: %s\n", + vars->key, + vars->value ? vars->value : "N/A"); + + vars++; + } + SSNES_LOG("=======================\n"); + break; + } + default: SSNES_LOG("Environ UNSUPPORTED (#%u)!\n", cmd); return false; @@ -377,5 +421,9 @@ static void set_environment_defaults(void) g_extern.system.geom.base_height = 224; g_extern.system.geom.max_width = 512; g_extern.system.geom.max_height = 512; + + // Split up environment variables beforehand. + if (g_extern.system.environment_split && strtok(g_extern.system.environment_split, ";")) + while (strtok(NULL, ";")); } diff --git a/general.h b/general.h index 5561ca652f..3439111da8 100644 --- a/general.h +++ b/general.h @@ -264,6 +264,9 @@ struct global struct snes_system_timing timing; bool timing_set; bool need_fullpath; + + char *environment; + char *environment_split; } system; struct diff --git a/input/sdl_input.c b/input/sdl_input.c index 5b80087c12..3ce051f393 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -150,7 +150,7 @@ static void *sdl_input_init(void) if (g_settings.input.joypad_map[i] < 0) continue; - if (sdl->num_joysticks > g_settings.input.joypad_map[i]) + if (sdl->num_joysticks > (unsigned)g_settings.input.joypad_map[i]) { sdl->joysticks[i] = SDL_JoystickOpen(g_settings.input.joypad_map[i]); if (!sdl->joysticks[i]) diff --git a/libsnes.hpp b/libsnes.hpp index 77160f76a1..0116d4f85e 100755 --- a/libsnes.hpp +++ b/libsnes.hpp @@ -99,6 +99,27 @@ extern "C" { // Boolean value telling if SSNES is able to rewind. // Some implementations might need to take extra precautions // to allow this as smoothly as possible. + // +#define SNES_ENVIRONMENT_GET_VARIABLE 8 // struct snes_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 SNES_ENVIRONMENT_SET_VARIABLES 9 // const struct snes_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 snes_variable structs terminated by a { NULL, NULL } element. + // snes_variable::value should contain a human readable description of the key. + +struct snes_variable +{ + const char *key; // Variable to query in SNES_ENVIRONMENT_GET_VARIABLE. + // If NULL, obtains the complete environment string if more complex parsing is necessary. + // The environment string is formatted as key-value pairs delimited by semicolons as so: + // "key1=value1;key2=value2;..." + const char *value; // Value to be obtained. If key does not exist, it is set to NULL. +}; struct snes_geometry { diff --git a/settings.c b/settings.c index 8dc22daab1..4261074186 100644 --- a/settings.c +++ b/settings.c @@ -216,8 +216,8 @@ void config_set_defaults(void) memcpy(g_settings.input.binds[i], snes_keybinds_rest, sizeof(snes_keybinds_rest)); // Verify that binds are in proper order. - for (unsigned i = 0; i < MAX_PLAYERS; i++) - for (unsigned j = 0; j < SSNES_BIND_LIST_END; j++) + for (int i = 0; i < MAX_PLAYERS; i++) + for (int j = 0; j < SSNES_BIND_LIST_END; j++) if (g_settings.input.binds[i][j].valid) ssnes_assert(j == g_settings.input.binds[i][j].id); @@ -466,6 +466,18 @@ bool config_load_file(const char *path) CONFIG_GET_BOOL(block_sram_overwrite, "block_sram_overwrite"); CONFIG_GET_BOOL(savestate_auto_index, "savestate_auto_index"); + 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) + { + SSNES_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_array(conf, "savefile_directory", tmp_str, sizeof(tmp_str))) { if (path_is_directory(tmp_str)) diff --git a/ssnes.c b/ssnes.c index 9a96d762e4..2ba4142d42 100644 --- a/ssnes.c +++ b/ssnes.c @@ -2079,10 +2079,15 @@ static void init_state(void) void ssnes_main_clear_state(void) { memset(&g_settings, 0, sizeof(g_settings)); + + free(g_extern.system.environment); + free(g_extern.system.environment_split); memset(&g_extern, 0, sizeof(g_extern)); + #ifdef SSNES_CONSOLE memset(&g_console, 0, sizeof(g_console)); #endif + init_state(); } @@ -2274,6 +2279,7 @@ int main(int argc, char *argv[]) if ((init_ret = ssnes_main_init(argc, argv))) return init_ret; while (ssnes_main_iterate()); ssnes_main_deinit(); + ssnes_main_clear_state(); return 0; } diff --git a/ssnes.cfg b/ssnes.cfg index cb7a6b6490..6045182034 100644 --- a/ssnes.cfg +++ b/ssnes.cfg @@ -8,9 +8,18 @@ # This will be overridden by explicit command line options. # savestate_directory = -## If enabled, load libsnes from a dynamic location. +# If enabled, load libsnes from a dynamic location. # libsnes_path = "/path/to/libsnes.so" +# Environment variables internally in SSNES. +# 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 = + #### Video # Video driver to use. "gl", "xvideo", "sdl" or "ext" (external API driver)