Add option to save core options per-core

This commit is contained in:
jdgleaver 2019-08-02 15:44:51 +01:00
parent 56ef7eb229
commit 2fc8f5b0bf
10 changed files with 208 additions and 58 deletions

View File

@ -467,6 +467,7 @@ static bool rgui_extended_ascii = false;
static bool default_game_specific_options = true;
static bool default_auto_overrides_enable = true;
static bool default_auto_remaps_enable = true;
static bool default_global_core_options = true;
static bool default_auto_shaders_enable = true;
static bool default_sort_savefiles_enable = false;

View File

@ -1579,6 +1579,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
SETTING_BOOL("game_specific_options", &settings->bools.game_specific_options, true, default_game_specific_options, false);
SETTING_BOOL("auto_overrides_enable", &settings->bools.auto_overrides_enable, true, default_auto_overrides_enable, false);
SETTING_BOOL("auto_remaps_enable", &settings->bools.auto_remaps_enable, true, default_auto_remaps_enable, false);
SETTING_BOOL("global_core_options", &settings->bools.global_core_options, true, default_global_core_options, false);
SETTING_BOOL("auto_shaders_enable", &settings->bools.auto_shaders_enable, true, default_auto_shaders_enable, false);
SETTING_BOOL("scan_without_core_match", &settings->bools.scan_without_core_match, true, scan_without_core_match, false);
SETTING_BOOL("sort_savefiles_enable", &settings->bools.sort_savefiles_enable, true, default_sort_savefiles_enable, false);

View File

@ -305,6 +305,7 @@ typedef struct settings
bool game_specific_options;
bool auto_overrides_enable;
bool auto_remaps_enable;
bool global_core_options;
bool auto_shaders_enable;
bool sort_savefiles_enable;

View File

@ -76,6 +76,8 @@ MSG_HASH(MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE,
"auto_overrides_enable")
MSG_HASH(MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE,
"auto_remaps_enable")
MSG_HASH(MENU_ENUM_LABEL_GLOBAL_CORE_OPTIONS,
"global_core_options")
MSG_HASH(MENU_ENUM_LABEL_AUTO_SHADERS_ENABLE,
"auto_shaders_enable")
MSG_HASH(MENU_ENUM_LABEL_BLOCK_SRAM_OVERWRITE,

View File

@ -381,6 +381,14 @@ MSG_HASH(
MENU_ENUM_LABEL_VALUE_AUTO_REMAPS_ENABLE,
"Load Remap Files Automatically"
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_GLOBAL_CORE_OPTIONS,
"Use Global Core Options File"
)
MSG_HASH(
MENU_ENUM_SUBLABEL_GLOBAL_CORE_OPTIONS,
"Save all core options to a common settings file (retroarch-core-options.cfg). When disabled, options for each core will be saved to a separate core-specific folder/file in RetroArch's 'Config' directory."
)
MSG_HASH(
MENU_ENUM_LABEL_VALUE_AUTO_SHADERS_ENABLE,
"Load Shader Presets Automatically"

View File

@ -408,6 +408,7 @@ default_sublabel_macro(action_bind_sublabel_menu_filebrowser_open_picker,
default_sublabel_macro(action_bind_sublabel_auto_remaps_enable, MENU_ENUM_SUBLABEL_AUTO_REMAPS_ENABLE)
default_sublabel_macro(action_bind_sublabel_auto_overrides_enable, MENU_ENUM_SUBLABEL_AUTO_OVERRIDES_ENABLE)
default_sublabel_macro(action_bind_sublabel_game_specific_options, MENU_ENUM_SUBLABEL_GAME_SPECIFIC_OPTIONS)
default_sublabel_macro(action_bind_sublabel_global_core_options, MENU_ENUM_SUBLABEL_GLOBAL_CORE_OPTIONS)
default_sublabel_macro(action_bind_sublabel_core_enable, MENU_ENUM_SUBLABEL_CORE_ENABLE)
default_sublabel_macro(action_bind_sublabel_database_manager, MENU_ENUM_SUBLABEL_DATABASE_MANAGER)
default_sublabel_macro(action_bind_sublabel_cursor_manager, MENU_ENUM_SUBLABEL_CURSOR_MANAGER)
@ -1682,6 +1683,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
case MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_game_specific_options);
break;
case MENU_ENUM_LABEL_GLOBAL_CORE_OPTIONS:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_global_core_options);
break;
case MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE:
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_auto_overrides_enable);
break;

View File

@ -4815,6 +4815,7 @@ unsigned menu_displaylist_build_list(file_list_t *list, enum menu_displaylist_ct
{MENU_ENUM_LABEL_GAME_SPECIFIC_OPTIONS, PARSE_ONLY_BOOL},
{MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE, PARSE_ONLY_BOOL},
{MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE, PARSE_ONLY_BOOL},
{MENU_ENUM_LABEL_GLOBAL_CORE_OPTIONS, PARSE_ONLY_BOOL},
};
for (i = 0; i < ARRAY_SIZE(build_list); i++)

View File

@ -7469,7 +7469,7 @@ static bool setting_append_list(
case SETTINGS_LIST_CONFIGURATION:
{
uint8_t i;
struct bool_entry bool_entries[6];
struct bool_entry bool_entries[7];
START_GROUP(list, list_info, &group_info,
msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONFIGURATION_SETTINGS), parent_group);
@ -7514,6 +7514,12 @@ static bool setting_append_list(
bool_entries[5].default_value = default_auto_shaders_enable;
bool_entries[5].flags = SD_FLAG_NONE;
bool_entries[6].target = &settings->bools.global_core_options;
bool_entries[6].name_enum_idx = MENU_ENUM_LABEL_GLOBAL_CORE_OPTIONS;
bool_entries[6].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_GLOBAL_CORE_OPTIONS;
bool_entries[6].default_value = default_global_core_options;
bool_entries[6].flags = SD_FLAG_NONE;
for (i = 0; i < ARRAY_SIZE(bool_entries); i++)
{
CONFIG_BOOL(

View File

@ -1394,6 +1394,7 @@ enum msg_hash_enums
MENU_LABEL(GAME_SPECIFIC_OPTIONS_IN_USE),
MENU_LABEL(AUTO_OVERRIDES_ENABLE),
MENU_LABEL(AUTO_REMAPS_ENABLE),
MENU_LABEL(GLOBAL_CORE_OPTIONS),
MENU_LABEL(AUTO_SHADERS_ENABLE),
MENU_LABEL(RGUI_SHOW_START_SCREEN),
MENU_LABEL(SCREENSHOT),

View File

@ -5241,7 +5241,8 @@ int main(int argc, char *argv[])
/* CORE OPTIONS */
static bool core_option_manager_parse_variable(
core_option_manager_t *opt, size_t idx,
const struct retro_variable *var)
const struct retro_variable *var,
config_file_t *config_src)
{
const char *val_start = NULL;
char *value = NULL;
@ -5286,7 +5287,8 @@ static bool core_option_manager_parse_variable(
option->default_index = 0;
option->index = 0;
if (config_get_string(opt->conf, option->key, &config_val))
/* Set current config value */
if (config_get_string(config_src ? config_src : opt->conf, option->key, &config_val))
{
size_t i;
@ -5313,7 +5315,8 @@ error:
static bool core_option_manager_parse_option(
core_option_manager_t *opt, size_t idx,
const struct retro_core_option_definition *option_def)
const struct retro_core_option_definition *option_def,
config_file_t *config_src)
{
size_t i;
union string_list_elem_attr attr;
@ -5382,7 +5385,7 @@ static bool core_option_manager_parse_option(
}
/* Set current config value */
if (config_get_string(opt->conf, option->key, &config_val))
if (config_get_string(config_src ? config_src : opt->conf, option->key, &config_val))
{
for (i = 0; i < option->vals->size; i++)
{
@ -5441,6 +5444,7 @@ static void core_option_manager_free(core_option_manager_t *opt)
/**
* core_option_manager_new_vars:
* @conf_path : Filesystem path to write core option config file to.
* @src_conf_path : Filesystem path from which to load initial config settings.
* @vars : Pointer to variable array handle.
*
* Legacy version of core_option_manager_new().
@ -5448,13 +5452,15 @@ static void core_option_manager_free(core_option_manager_t *opt)
*
* Returns: handle to new core manager handle, otherwise NULL.
**/
static core_option_manager_t *core_option_manager_new_vars(const char *conf_path,
static core_option_manager_t *core_option_manager_new_vars(
const char *conf_path, const char *src_conf_path,
const struct retro_variable *vars)
{
const struct retro_variable *var;
size_t size = 0;
core_option_manager_t *opt = (core_option_manager_t*)
calloc(1, sizeof(*opt));
config_file_t *config_src = NULL;
if (!opt)
return NULL;
@ -5466,6 +5472,10 @@ static core_option_manager_t *core_option_manager_new_vars(const char *conf_path
strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path));
/* Load source config file, if required */
if (!string_is_empty(src_conf_path))
config_src = config_file_new_from_path_to_string(src_conf_path);
for (var = vars; var->key && var->value; var++)
size++;
@ -5481,13 +5491,18 @@ static core_option_manager_t *core_option_manager_new_vars(const char *conf_path
for (var = vars; var->key && var->value; size++, var++)
{
if (!core_option_manager_parse_variable(opt, size, var))
if (!core_option_manager_parse_variable(opt, size, var, config_src))
goto error;
}
if (config_src)
config_file_free(config_src);
return opt;
error:
if (config_src)
config_file_free(config_src);
core_option_manager_free(opt);
return NULL;
}
@ -5495,19 +5510,22 @@ error:
/**
* core_option_manager_new:
* @conf_path : Filesystem path to write core option config file to.
* @src_conf_path : Filesystem path from which to load initial config settings.
* @option_defs : Pointer to variable array handle.
*
* Creates and initializes a core manager handle.
*
* Returns: handle to new core manager handle, otherwise NULL.
**/
static core_option_manager_t *core_option_manager_new(const char *conf_path,
static core_option_manager_t *core_option_manager_new(
const char *conf_path, const char *src_conf_path,
const struct retro_core_option_definition *option_defs)
{
const struct retro_core_option_definition *option_def;
size_t size = 0;
core_option_manager_t *opt = (core_option_manager_t*)
calloc(1, sizeof(*opt));
config_file_t *config_src = NULL;
if (!opt)
return NULL;
@ -5519,6 +5537,10 @@ static core_option_manager_t *core_option_manager_new(const char *conf_path,
strlcpy(opt->conf_path, conf_path, sizeof(opt->conf_path));
/* Load source config file, if required */
if (!string_is_empty(src_conf_path))
config_src = config_file_new_from_path_to_string(src_conf_path);
/* Note: 'option_def->info == NULL' is valid */
for (option_def = option_defs;
option_def->key && option_def->desc && option_def->values[0].value;
@ -5540,13 +5562,18 @@ static core_option_manager_t *core_option_manager_new(const char *conf_path,
option_def->key && option_def->desc && option_def->values[0].value;
size++, option_def++)
{
if (!core_option_manager_parse_option(opt, size, option_def))
if (!core_option_manager_parse_option(opt, size, option_def, config_src))
goto error;
}
if (config_src)
config_file_free(config_src);
return opt;
error:
if (config_src)
config_file_free(config_src);
core_option_manager_free(opt);
return NULL;
}
@ -5610,6 +5637,7 @@ static bool core_option_manager_flush_game_specific(
opt->opts[i].vals->elems[opt->opts[i].index].data);
}
RARCH_LOG("Per-Game Options: game-specific core options saved to \"%s\"\n", path);
ret = config_file_write(conf_tmp, path, true);
config_file_free(conf_tmp);
@ -21416,7 +21444,8 @@ bool retroarch_validate_game_options(char *s, size_t len, bool mkdir)
file_path_str(FILE_PATH_OPT_EXTENSION),
len);
if (mkdir)
/* No need to make a directory if file already exists... */
if (mkdir && !path_is_valid(s))
{
char *core_path = (char*)malloc(str_size);
core_path[0] = '\0';
@ -21434,6 +21463,46 @@ bool retroarch_validate_game_options(char *s, size_t len, bool mkdir)
return true;
}
bool retroarch_validate_per_core_options(char *s, size_t len, bool mkdir)
{
char *config_directory = NULL;
size_t str_size = PATH_MAX_LENGTH * sizeof(char);
const char *core_name = runloop_system.info.library_name;
if (string_is_empty(core_name))
return false;
config_directory = (char*)malloc(str_size);
config_directory[0] = '\0';
fill_pathname_application_special(config_directory,
str_size, APPLICATION_SPECIAL_DIRECTORY_CONFIG);
/* Concatenate strings into full paths for core options path */
fill_pathname_join_special_ext(s,
config_directory, core_name, core_name,
file_path_str(FILE_PATH_OPT_EXTENSION),
len);
/* No need to make a directory if file already exists... */
if (mkdir && !path_is_valid(s))
{
char *core_options_dir = (char*)malloc(str_size);
core_options_dir[0] = '\0';
fill_pathname_join(core_options_dir,
config_directory, core_name, str_size);
if (!path_is_directory(core_options_dir))
path_mkdir(core_options_dir);
free(core_options_dir);
}
free(config_directory);
return true;
}
/* Validates CPU features for given processor architecture.
* Make sure we haven't compiled for something we cannot run.
* Ideally, code would get swapped out depending on CPU support,
@ -21744,7 +21813,7 @@ void retroarch_menu_running_finished(bool quit)
**/
static bool rarch_game_specific_options(char **output)
{
size_t game_path_size = 8192 * sizeof(char);
size_t game_path_size = PATH_MAX_LENGTH * sizeof(char);
char *game_path = (char*)malloc(game_path_size);
game_path[0] ='\0';
@ -21752,7 +21821,7 @@ static bool rarch_game_specific_options(char **output)
if (!retroarch_validate_game_options(game_path,
game_path_size, false))
goto error;
if (!config_file_exists(game_path))
if (!path_is_valid(game_path))
goto error;
RARCH_LOG("%s %s\n",
@ -21787,43 +21856,117 @@ static void runloop_task_msg_queue_push(
runloop_msg_queue_push(msg, prio, duration, flush, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO);
}
static void rarch_init_core_options(
const struct retro_core_option_definition *option_defs)
/* Fetches core options path for current core/content
* - path: path from which options should be read
* from/saved to
* - src_path: in the event that 'path' file does not
* yet exist, provides source path from which initial
* options should be extracted
* */
static void rarch_init_core_options_path(
char *path, size_t len,
char *src_path, size_t src_len)
{
settings_t *settings = configuration_settings;
char *game_options_path = NULL;
settings_t *settings = configuration_settings;
char *game_options_path = NULL;
/* Ensure that 'input' strings are null terminated */
if (len > 0)
path[0] = '\0';
if (src_len > 0)
src_path[0] = '\0';
/* Check whether game-specific options exist */
if (settings->bools.game_specific_options &&
rarch_game_specific_options(&game_options_path))
{
/* Notify system that we have a valid core options
* override */
path_set(RARCH_PATH_CORE_OPTIONS, game_options_path);
runloop_game_options_active = true;
runloop_core_options =
core_option_manager_new(game_options_path, option_defs);
/* Copy options path */
strlcpy(path, game_options_path, len);
free(game_options_path);
}
else
{
char buf[PATH_MAX_LENGTH];
const char *options_path = settings ? settings->paths.path_core_options : NULL;
char global_options_path[PATH_MAX_LENGTH];
char per_core_options_path[PATH_MAX_LENGTH];
bool per_core_options = !settings->bools.global_core_options;
bool per_core_options_exist = false;
buf[0] = '\0';
global_options_path[0] = '\0';
per_core_options_path[0] = '\0';
if (string_is_empty(options_path) && !path_is_empty(RARCH_PATH_CONFIG))
if (per_core_options)
{
fill_pathname_resolve_relative(buf, path_get(RARCH_PATH_CONFIG),
file_path_str(FILE_PATH_CORE_OPTIONS_CONFIG), sizeof(buf));
options_path = buf;
/* Get core-specific options path
* > if retroarch_validate_per_core_options() returns
* false, then per-core options are disabled (due to
* unknown system errors...) */
per_core_options = retroarch_validate_per_core_options(
per_core_options_path, sizeof(per_core_options_path), true);
/* If we can use per-core options, check whether an options
* file already exists */
if (per_core_options)
per_core_options_exist = path_is_valid(per_core_options_path);
}
runloop_game_options_active = false;
/* If not using per-core options, or if a per-core options
* file does not yet exist, must fetch 'global' options path */
if (!per_core_options || !per_core_options_exist)
{
const char *options_path = settings ? settings->paths.path_core_options : NULL;
if (!string_is_empty(options_path))
runloop_core_options =
core_option_manager_new(options_path, option_defs);
if (!string_is_empty(options_path))
strlcpy(global_options_path, options_path, sizeof(global_options_path));
else if (string_is_empty(options_path) && !path_is_empty(RARCH_PATH_CONFIG))
{
fill_pathname_resolve_relative(
global_options_path, path_get(RARCH_PATH_CONFIG),
file_path_str(FILE_PATH_CORE_OPTIONS_CONFIG), sizeof(global_options_path));
}
}
/* Allocate correct path/src_path strings */
if (per_core_options)
{
strlcpy(path, per_core_options_path, len);
if (!per_core_options_exist)
strlcpy(src_path, global_options_path, src_len);
}
else
strlcpy(path, global_options_path, len);
/* Notify system that we *do not* have a valid core options
* options override */
runloop_game_options_active = false;
}
}
static void rarch_init_core_options(
const struct retro_core_option_definition *option_defs)
{
char options_path[PATH_MAX_LENGTH];
char src_options_path[PATH_MAX_LENGTH];
options_path[0] = '\0';
src_options_path[0] = '\0';
/* Get core options file path */
rarch_init_core_options_path(
options_path, sizeof(options_path),
src_options_path, sizeof(src_options_path));
if (!string_is_empty(options_path))
runloop_core_options =
core_option_manager_new(options_path, src_options_path, option_defs);
}
bool rarch_ctl(enum rarch_ctl_state state, void *data)
{
switch(state)
@ -22093,40 +22236,22 @@ bool rarch_ctl(enum rarch_ctl_state state, void *data)
break;
case RARCH_CTL_CORE_VARIABLES_INIT:
{
settings_t *settings = configuration_settings;
char *game_options_path = NULL;
char options_path[PATH_MAX_LENGTH];
char src_options_path[PATH_MAX_LENGTH];
const struct retro_variable *vars =
(const struct retro_variable*)data;
if (settings->bools.game_specific_options &&
rarch_game_specific_options(&game_options_path))
{
path_set(RARCH_PATH_CORE_OPTIONS, game_options_path);
runloop_game_options_active = true;
runloop_core_options =
core_option_manager_new_vars(game_options_path, vars);
free(game_options_path);
}
else
{
char buf[PATH_MAX_LENGTH];
const char *options_path = settings ? settings->paths.path_core_options : NULL;
options_path[0] = '\0';
src_options_path[0] = '\0';
buf[0] = '\0';
/* Get core options file path */
rarch_init_core_options_path(
options_path, sizeof(options_path),
src_options_path, sizeof(src_options_path));
if (string_is_empty(options_path) && !path_is_empty(RARCH_PATH_CONFIG))
{
fill_pathname_resolve_relative(buf, path_get(RARCH_PATH_CONFIG),
file_path_str(FILE_PATH_CORE_OPTIONS_CONFIG), sizeof(buf));
options_path = buf;
}
runloop_game_options_active = false;
if (!string_is_empty(options_path))
runloop_core_options =
core_option_manager_new_vars(options_path, vars);
}
if (!string_is_empty(options_path))
runloop_core_options =
core_option_manager_new_vars(options_path, src_options_path, vars);
}
break;
case RARCH_CTL_CORE_OPTIONS_INIT: