diff --git a/config.def.h b/config.def.h index 4c88623291..bfd8099fd1 100644 --- a/config.def.h +++ b/config.def.h @@ -517,6 +517,7 @@ static bool default_core_specific_config = true; static bool default_core_specific_config = false; #endif +static bool default_game_specific_options = true; static bool default_auto_overrides_enable = false; static bool default_auto_remaps_enable = false; diff --git a/configuration.c b/configuration.c index 06d0a3c92f..f0377d18ab 100644 --- a/configuration.c +++ b/configuration.c @@ -729,6 +729,7 @@ static void config_set_defaults(void) *settings->menu_config_directory = '\0'; #endif settings->core_specific_config = default_core_specific_config; + settings->game_specific_options = default_game_specific_options; settings->auto_overrides_enable = default_auto_overrides_enable; settings->auto_remaps_enable = default_auto_remaps_enable; @@ -1749,6 +1750,7 @@ static bool config_load_file(const char *path, bool set_defaults) config_read_keybinds_conf(conf); CONFIG_GET_BOOL_BASE(conf, settings, core_specific_config, "core_specific_config"); + CONFIG_GET_BOOL_BASE(conf, settings, game_specific_options, "game_specific_options"); CONFIG_GET_BOOL_BASE(conf, settings, auto_overrides_enable, "auto_overrides_enable"); CONFIG_GET_BOOL_BASE(conf, settings, auto_remaps_enable, "auto_remaps_enable"); @@ -2787,6 +2789,8 @@ bool config_save_file(const char *path) config_set_bool(conf, "core_specific_config", settings->core_specific_config); + config_set_bool(conf, "game_specific_options", + settings->game_specific_options); config_set_bool(conf, "auto_overrides_enable", settings->auto_overrides_enable); config_set_bool(conf, "auto_remaps_enable", diff --git a/configuration.h b/configuration.h index 25c824c1c4..c419e991d9 100644 --- a/configuration.h +++ b/configuration.h @@ -361,6 +361,7 @@ typedef struct settings bool load_dummy_on_core_shutdown; bool core_specific_config; + bool game_specific_options; bool auto_overrides_enable; bool auto_remaps_enable; diff --git a/core_options.c b/core_options.c index 9df1aea179..15ba5e63d4 100644 --- a/core_options.c +++ b/core_options.c @@ -237,6 +237,30 @@ bool core_option_flush(core_option_manager_t *opt) return config_file_write(opt->conf, opt->conf_path); } +/** + * core_option_flush_game_specific: + * @opt : options manager handle + * @path : path for the core options file + * + * Writes core option key-pair values to a custom file. + * + * Returns: true (1) if core option values could be + * successfully saved to disk, otherwise false (0). + **/ +bool core_option_flush_game_specific(core_option_manager_t *opt, char* path) +{ + size_t i; + for (i = 0; i < opt->size; i++) + { + struct core_option *option = (struct core_option*)&opt->opts[i]; + + if (option) + config_set_string(opt->conf, option->key, core_option_get_val(opt, i)); +} + + return config_file_write(opt->conf, path); +} + /** * core_option_size: * @opt : options manager handle diff --git a/core_options.h b/core_options.h index 06f8ef28ff..7923bd3a4e 100644 --- a/core_options.h +++ b/core_options.h @@ -62,6 +62,18 @@ bool core_option_updated(core_option_manager_t *opt); **/ bool core_option_flush(core_option_manager_t *opt); +/** + * core_option_flush_game_specific: + * @opt : options manager handle + * @path : path for the core options file + * + * Writes core option key-pair values to a custom file. + * + * Returns: true (1) if core option values could be + * successfully saved to disk, otherwise false (0). + **/ +bool core_option_flush_game_specific(core_option_manager_t *opt, char* path); + /** * core_option_free: * @opt : options manager handle diff --git a/dynamic.c b/dynamic.c index 66571fab55..cc625766c9 100644 --- a/dynamic.c +++ b/dynamic.c @@ -589,6 +589,64 @@ static void rarch_log_libretro(enum retro_log_level level, va_end(vp); } +/** + * rarch_game_specific_options: + * @cmd : Output variable with path to core options file. + * + * Environment callback function implementation. + * + * Returns: true (1) if a game specific core options path has been found, + * otherwise false (0). + **/ +static bool rarch_game_specific_options(char **output) +{ + settings_t *settings = config_get_ptr(); + global_t *global = global_get_ptr(); + rarch_system_info_t *system = rarch_system_info_get_ptr(); + + const char *core_name = NULL; + const char *game_name = NULL; + config_file_t *option_file = NULL; + char game_path[PATH_MAX_LENGTH] = {0}; + char config_directory[PATH_MAX_LENGTH] = {0}; + + /* Config directory: config_directory. + * Try config directory setting first, + * fallback to the location of the current configuration file. */ + + if (settings->menu_config_directory[0] != '\0') + strlcpy(config_directory, settings->menu_config_directory, PATH_MAX_LENGTH); + else if (global->path.config[0] != '\0') + fill_pathname_basedir(config_directory, global->path.config, PATH_MAX_LENGTH); + else + { + RARCH_WARN("Per-game Options: no config directory set\n"); + return false; + } + + core_name = system->info.library_name; + game_name = path_basename(global->name.base); + + RARCH_LOG("Per-game Options: core name: %s\n", core_name); + RARCH_LOG("Per-game Options: game name: %s\n", game_name); + + /* Concatenate strings into full paths for game_path */ + + fill_pathname_join(game_path, config_directory, core_name, PATH_MAX_LENGTH); + fill_pathname_join(game_path, game_path, game_name, PATH_MAX_LENGTH); + strlcat(game_path, ".opt", PATH_MAX_LENGTH); + + option_file = config_file_new(game_path); + if(option_file) + { + RARCH_LOG("Per-game options: game-specific core options found at %s\n", game_path); + *output = game_path; + return true; + } + config_file_free(option_file); + return false; +} + /** * rarch_environment_cb: * @cmd : Identifier of command. @@ -663,8 +721,17 @@ bool rarch_environment_cb(unsigned cmd, void *data) "retroarch-core-options.cfg", sizeof(buf)); options_path = buf; } + + char *game_options_path = NULL; + bool ret = false; + + if (settings->game_specific_options) + ret = rarch_game_specific_options(&game_options_path); - system->core_options = core_option_new(options_path, vars); + if(ret) + system->core_options = core_option_new(game_options_path, vars); + else + system->core_options = core_option_new(options_path, vars); } break; diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 054b413554..e259e8a5ef 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -958,6 +958,7 @@ static void menu_action_setting_disp_set_label(file_list_t* list, { rarch_system_info_t *system = rarch_system_info_get_ptr(); uint32_t hash_label = menu_hash_calculate(label); + global_t *global = global_get_ptr(); *s = '\0'; *w = 19; @@ -1029,6 +1030,14 @@ static void menu_action_setting_disp_set_label(file_list_t* list, strlcpy(s, core_opt ? core_opt : "", len); } + else if (type >= MENU_SETTINGS_CORE_OPTION_CREATE) + { + const char *core_opt = NULL; + if (!system) + return; + + strlcpy(s, global->name.base ? path_basename(global->name.base) : "", len); + } else menu_setting_get_label(list, s, len, w, type, label, entry_label, i); diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 3ce5cf008f..3a64e1422d 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1246,6 +1246,54 @@ static int action_ok_disk_cycle_tray_status(const char *path, return generic_action_ok_command(EVENT_CMD_DISK_EJECT_TOGGLE); } +static int action_ok_option_create(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + /* create folder and core options stub file for subsequent runs */ + settings_t *settings = config_get_ptr(); + global_t *global = global_get_ptr(); + rarch_system_info_t *system = rarch_system_info_get_ptr(); + + const char *core_name = NULL; + const char *game_name = NULL; + char core_path[PATH_MAX_LENGTH] = {0}; + char game_path[PATH_MAX_LENGTH] = {0}; + char config_directory[PATH_MAX_LENGTH] = {0}; + char msg[PATH_MAX_LENGTH] = {0}; + + /* Config directory: config_directory. + * Try config directory setting first, + * fallback to the location of the current configuration file. */ + if (settings->menu_config_directory[0] != '\0') + strlcpy(config_directory, settings->menu_config_directory, PATH_MAX_LENGTH); + else if (global->path.config[0] != '\0') + fill_pathname_basedir(config_directory, global->path.config, PATH_MAX_LENGTH); + else + { + RARCH_WARN("Per-game Options: no config directory set\n"); + return false; + } + + core_name = system->info.library_name; + game_name = path_basename(global->name.base); + + /* Concatenate strings into full paths for game_path */ + fill_pathname_join(core_path, config_directory, core_name, PATH_MAX_LENGTH); + fill_pathname_join(game_path, config_directory, core_name, PATH_MAX_LENGTH); + fill_pathname_join(game_path, game_path, game_name, PATH_MAX_LENGTH); + strlcat(game_path, ".opt", PATH_MAX_LENGTH); + + if (!path_is_directory(core_path)) + path_mkdir(core_path); + + if(core_option_flush_game_specific(system->core_options,game_path)) + menu_display_msg_queue_push("Core options file saved successfully", 1, 100, true); + else + menu_display_msg_queue_push("Error saving core options file", 1, 100, true); + return 0; +} + + static int action_ok_close_content(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { @@ -2316,6 +2364,9 @@ static int menu_cbs_init_bind_ok_compare_type(menu_file_list_cbs_t *cbs, case MENU_SETTINGS_CORE_DISK_OPTIONS_DISK_CYCLE_TRAY_STATUS: BIND_ACTION_OK(cbs, action_ok_disk_cycle_tray_status); break; + case MENU_SETTINGS_CORE_OPTION_CREATE: + BIND_ACTION_OK(cbs, action_ok_option_create); + break; default: return -1; } diff --git a/menu/intl/menu_hash_us.c b/menu/intl/menu_hash_us.c index 8e5aef6374..ddeb048bbf 100644 --- a/menu/intl/menu_hash_us.c +++ b/menu/intl/menu_hash_us.c @@ -312,6 +312,8 @@ static const char *menu_hash_to_str_us_label(uint32_t hash) return "slowmotion_ratio"; case MENU_LABEL_CORE_SPECIFIC_CONFIG: return "core_specific_config"; + case MENU_LABEL_GAME_SPECIFIC_OPTIONS: + return "game_specific_options"; case MENU_LABEL_AUTO_OVERRIDES_ENABLE: return "auto_overrides_enable"; case MENU_LABEL_CONFIG_SAVE_ON_EXIT: @@ -1024,6 +1026,10 @@ const char *menu_hash_to_str_us(uint32_t hash) return "Slow-Motion Ratio"; case MENU_LABEL_VALUE_CORE_SPECIFIC_CONFIG: return "Configuration Per-Core"; + case MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS: + return "Use per-game core options if available"; + case MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE: + return "Create game-options file"; case MENU_LABEL_VALUE_AUTO_OVERRIDES_ENABLE: return "Load Override Files Automatically"; case MENU_LABEL_VALUE_CONFIG_SAVE_ON_EXIT: diff --git a/menu/menu.h b/menu/menu.h index 7bb42f835e..dd406e54bd 100644 --- a/menu/menu.h +++ b/menu/menu.h @@ -53,10 +53,12 @@ #define MENU_SETTINGS_CORE_INFO_NONE 0xffff #define MENU_SETTINGS_CORE_OPTION_NONE 0xffff #define MENU_SETTINGS_CHEEVOS_NONE 0xffff +#define MENU_SETTINGS_CORE_OPTION_CREATE 0x05000 #define MENU_SETTINGS_CORE_OPTION_START 0x10000 #define MENU_SETTINGS_PLAYLIST_ASSOCIATION_START 0x20000 #define MENU_SETTINGS_CHEEVOS_START 0x40000 + #define MENU_KEYBOARD_BIND_TIMEOUT_SECONDS 5 #ifdef __cplusplus diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index a511abafb9..f969456b80 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3000,6 +3000,12 @@ int menu_displaylist_push_list(menu_displaylist_info_t *info, unsigned type) { size_t opts = core_option_size(system->core_options); + if (settings->game_specific_options) + { + menu_entries_push(info->list, + menu_hash_to_str(MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE), "", + MENU_SETTINGS_CORE_OPTION_CREATE, 0, 0); + } if (opts == 0) { menu_entries_push(info->list, diff --git a/menu/menu_hash.h b/menu/menu_hash.h index 463a668f7f..e87f42e12e 100644 --- a/menu/menu_hash.h +++ b/menu/menu_hash.h @@ -304,6 +304,9 @@ extern "C" { #define MENU_LABEL_VALUE_VIDEO_FULLSCREEN 0x232743caU #define MENU_LABEL_CORE_SPECIFIC_CONFIG 0x3c9a55e8U #define MENU_LABEL_VALUE_CORE_SPECIFIC_CONFIG 0x8b8bec5aU +#define MENU_LABEL_GAME_SPECIFIC_OPTIONS 0x142ec90fU +#define MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS 0x6aed8a05U +#define MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE 0xf8d2456cU #define MENU_LABEL_AUTO_OVERRIDES_ENABLE 0x35ff91b6U #define MENU_LABEL_VALUE_AUTO_OVERRIDES_ENABLE 0xc21c3a11U #define MENU_LABEL_AUTO_REMAPS_ENABLE 0x98c8f98bU diff --git a/menu/menu_setting.c b/menu/menu_setting.c index c399bbbb5d..45adf1d2c4 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -3477,6 +3477,20 @@ static bool setting_append_list_configuration_options( general_write_handler, general_read_handler); +CONFIG_BOOL( + settings->game_specific_options, + menu_hash_to_str(MENU_LABEL_GAME_SPECIFIC_OPTIONS), + menu_hash_to_str(MENU_LABEL_VALUE_GAME_SPECIFIC_OPTIONS), + default_game_specific_options, + menu_hash_to_str(MENU_VALUE_OFF), + menu_hash_to_str(MENU_VALUE_ON), + group_info.name, + subgroup_info.name, + parent_group, + general_write_handler, + general_read_handler); + + CONFIG_BOOL( settings->auto_overrides_enable, menu_hash_to_str(MENU_LABEL_AUTO_OVERRIDES_ENABLE),