From 70bf90c5d8a20ed6d59f12cc7f05807420f44cd7 Mon Sep 17 00:00:00 2001 From: HyperspaceMadness Date: Sun, 1 Nov 2020 09:18:40 -0500 Subject: [PATCH] Shader_Preset_Simple_Save_Cleaned_2020_11_01 --- config.def.h | 4 + configuration.c | 1 + configuration.h | 1 + gfx/video_shader_parse.c | 724 ++++++++++++++++----- gfx/video_shader_parse.h | 6 + intl/msg_hash_lbl.h | 4 + intl/msg_hash_us.h | 10 + libretro-common/file/config_file.c | 83 ++- libretro-common/include/file/config_file.h | 3 + menu/cbs/menu_cbs_ok.c | 5 + menu/menu_displaylist.c | 4 + menu/menu_setting.c | 15 + msg_hash.h | 3 +- retroarch.c | 20 +- 14 files changed, 691 insertions(+), 192 deletions(-) diff --git a/config.def.h b/config.def.h index 04c2cb9fc3..a64e474db5 100644 --- a/config.def.h +++ b/config.def.h @@ -343,6 +343,10 @@ #define DEFAULT_SHADER_ENABLE false #endif +/* When presets are saved they will be saved using the #reference + * directive by default */ +#define DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE true + #define DEFAULT_SHADER_DELAY 0 /* Only scale in integer steps. diff --git a/configuration.c b/configuration.c index d20e13becd..f31c5101f1 100644 --- a/configuration.c +++ b/configuration.c @@ -1455,6 +1455,7 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("video_shader_enable", &settings->bools.video_shader_enable, true, DEFAULT_SHADER_ENABLE, false); SETTING_BOOL("video_shader_watch_files", &settings->bools.video_shader_watch_files, true, DEFAULT_VIDEO_SHADER_WATCH_FILES, false); SETTING_BOOL("video_shader_remember_last_dir", &settings->bools.video_shader_remember_last_dir, true, DEFAULT_VIDEO_SHADER_REMEMBER_LAST_DIR, false); + SETTING_BOOL("video_shader_preset_save_reference_enable", &settings->bools.video_shader_preset_save_reference_enable, true, DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE, false); /* Let implementation decide if automatic, or 1:1 PAR. */ SETTING_BOOL("video_aspect_ratio_auto", &settings->bools.video_aspect_ratio_auto, true, DEFAULT_ASPECT_RATIO_AUTO, false); diff --git a/configuration.h b/configuration.h index b94aff244f..7206d32862 100644 --- a/configuration.h +++ b/configuration.h @@ -462,6 +462,7 @@ typedef struct settings bool video_shader_enable; bool video_shader_watch_files; bool video_shader_remember_last_dir; + bool video_shader_preset_save_reference_enable; bool video_threaded; bool video_font_enable; bool video_disable_composition; diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index e5e1c7941c..60c495a658 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -417,6 +417,7 @@ static struct video_shader_parameter *video_shader_parse_find_parameter( * @shader : Shader passes handle. * * Reads the current value for all parameters from config file. + * Checks for parameters in the parameter list which don't have a value set * * Returns: true (1) if successful, otherwise false (0). **/ @@ -440,6 +441,7 @@ bool video_shader_resolve_current_parameters(config_file_t *conf, for ( id = strtok_r(parameters, ";", &save); id; id = strtok_r(NULL, ";", &save)) { + /* Get the parameter in the shader matching this name */ struct video_shader_parameter *parameter = (struct video_shader_parameter*) video_shader_parse_find_parameter( @@ -465,6 +467,8 @@ bool video_shader_resolve_current_parameters(config_file_t *conf, * @shader : Shader passes handle. * * Resolves all shader parameters belonging to shaders. + * Fills the parameter definition list of the shader + * Does not read any of the config parameter values * * Returns: true (1) if successful, otherwise false (0). **/ @@ -571,6 +575,375 @@ bool video_shader_resolve_parameters(config_file_t *conf, return video_shader_resolve_current_parameters(conf, shader); } +#ifdef _WIN32 +static void make_relative_path_portable(char* path) +{ + /* use '/' instead of '\' for maximum portability */ + char* p; + for (p = path; *p; p++) + if (*p == '\\') + *p = '/'; +} +#endif + +/** + * video_shader_write_referenced_preset: + * @path : File to write to + * @shader : Shader preset to write + * + * Writes a referenced preset to disk + * A referenced preset is a preset which includes the #reference directive + * as it's first line to specify a root preset and can also include parameter + * and texture values to override the values of the root preset + * Returns false if a referenced preset cannot be saved + * See: video_shader_read_preset + **/ +bool video_shader_write_referenced_preset(const char *path, + const char *shader_dir, + const struct video_shader *shader) +{ + unsigned i; + bool ret = false; + bool continue_saving_reference = true; + char preset_dir[PATH_MAX_LENGTH]; + config_file_t *conf; + char *absolute_root_preset_path = (char*)malloc(PATH_MAX_LENGTH); + char *absolute_new_preset_basedir = (char*)malloc(PATH_MAX_LENGTH); + char *relative_root_preset_path = (char*)malloc(PATH_MAX_LENGTH); + + preset_dir[0] = '\0'; + absolute_new_preset_basedir = strdup(path); + path_basedir(absolute_new_preset_basedir); + absolute_root_preset_path[0] = '\0'; + relative_root_preset_path[0] = '\0'; + + /* Get the absolute path to the root preset, this is the one which is used in the #reference directive */ + strlcpy(absolute_root_preset_path, shader->path, PATH_MAX_LENGTH); + if (string_is_empty(shader->path)) + { + RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because the loaded Shader " + "does not have a path to a previously loaded preset file on disk.\n"); + continue_saving_reference = false; + } + + /* We can't save a reference to ourselves */ + if (string_is_equal(shader->path, path)) + { + RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a preset " + "which would reference itself.\n"); + continue_saving_reference = false; + } + + /* Auto-shaders can be written as copies or references. + * If we write a reference to a copy, we could then overwrite the copy + * with any reference, thus creating a reference to a reference. + * To prevent this, we disallow saving references to auto-shaders. */ + fill_pathname_join(preset_dir, + shader_dir, + "presets", + sizeof(preset_dir)); + if (continue_saving_reference && !strncmp(preset_dir, absolute_root_preset_path, strlen(preset_dir))) + { + RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a " + "reference to an auto-loaded shader (E.G. Game Preset, Core Preset).\n"); + continue_saving_reference = false; + } + + /* Don't ever create a reference to the ever-changing retroarch preset + * TODO remove once we don't write this preset anymore */ + if (continue_saving_reference && !strncmp(path_basename(absolute_root_preset_path), "retroarch", STRLEN_CONST("retroarch"))) + { + continue_saving_reference = false; + RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we can't save a reference " + "to the ever-changing retroarch preset.\n"); + } + + if (continue_saving_reference) + { + config_file_t *root_conf; + + path_relative_to(relative_root_preset_path, + absolute_root_preset_path, + absolute_new_preset_basedir, + PATH_MAX_LENGTH); +#ifdef _WIN32 + if (!path_is_absolute(relative_root_preset_path)) + make_relative_path_portable(relative_root_preset_path); +#endif + + /* Create a new EMPTY config */ + conf = config_file_new_alloc(); + if (!(conf)) + return false; + conf->path = strdup(path); + + /* Add the reference path to the config */ + config_file_set_reference_path(conf, relative_root_preset_path); + + /* Set modified to true so when you run config_file_write it will save a file */ + conf->modified = true; + + /* Get a config from root preset which was defined by the #reference directive + * to compare the current shader against */ + root_conf = config_file_new_from_path_to_string(absolute_root_preset_path); + + /* root_conf could be NULL if the file was not found */ + if (root_conf == NULL) + RARCH_WARN("[Shaders-Save Reference]: Saving Full Preset because we could not " + "load the Root Preset: %s.\n", + absolute_root_preset_path); + else + { + /* + Compare the shader to a shader created from the root config to see if + we can save a referenced preset and what parameters and textures of the + root_config are overridden + */ + + struct video_shader *root_shader = NULL; + root_shader = (struct video_shader*) calloc(1, sizeof(*root_shader)); + video_shader_read_conf_preset(root_conf, root_shader); + + /* Check number of passes match */ + if (shader->passes != root_shader->passes) + { + RARCH_WARN("[Shaders-Save Reference]: passes (Number of Passes)"); + continue_saving_reference = false; + } + + /* + Compare all passes from the shader + if anything is different then we should not save a reference + and save instead safe a full preset instead + */ + if (continue_saving_reference) + { + /* Step through each pass comparing all the properties to make sure they match */ + for (i = 0; (i < shader->passes && continue_saving_reference == true); i++) + { + const struct video_shader_pass *pass = &shader->pass[i]; + const struct video_shader_pass *root_pass = &root_shader->pass[i]; + const struct gfx_fbo_scale *fbo = &pass->fbo; + const struct gfx_fbo_scale *root_fbo = &root_pass->fbo; + + if (!string_is_equal(pass->source.path, root_pass->source.path)) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u path", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && pass->filter != root_pass->filter) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u filter", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && pass->wrap != root_pass->wrap) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u wrap", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && pass->frame_count_mod != root_pass->frame_count_mod) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u frame_count", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && pass->mipmap != root_pass->mipmap) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u mipmap", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && !string_is_equal(pass->alias, root_pass->alias)) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u alias", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->type_x != root_fbo->type_x) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u type_x", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->type_y != root_fbo->type_y) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u type_y", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->scale_x != root_fbo->scale_x) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u scale_x", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->scale_y != root_fbo->scale_y) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u scale_y", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->fp_fbo != root_fbo->fp_fbo) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u fp_fbo", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->srgb_fbo != root_fbo->srgb_fbo) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u srgb_fbo", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->valid != root_fbo->valid) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u valid", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->abs_x != root_fbo->abs_x) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u abs_x", i); + continue_saving_reference = false; + } + + if (continue_saving_reference && fbo->abs_y != root_fbo->abs_y) + { + RARCH_WARN("[Shaders-Save Reference]: Pass %u abs_y", i); + continue_saving_reference = false; + } + + if (!continue_saving_reference) + RARCH_WARN(" Current Value doesn't match Referenced Value - " + "Full Preset Will be Saved instead of Simple Preset\n"); + } + } + + if (continue_saving_reference) + { + const struct config_entry_list *entry = NULL; + + /* If the shader has parameters */ + if (shader->num_parameters) + { + size_t param_size = 4096 * sizeof(char); + char *override_parameters = (char*)malloc(param_size); + float parameter_value_reference; + + param_size = 4096 * sizeof(char); + override_parameters[0] = '\0'; + parameter_value_reference = 0.0f; + + if (override_parameters) + { + unsigned i; + + for (i = 0; i < shader->num_parameters; i++) + { + bool add_param_to_override = false; + + entry = config_get_entry(root_conf, shader->parameters[i].id); + + /* If the parameter is in the reference config */ + if (entry) + { + /* If the current param value is different than the referenced preset's value */ + config_get_float(root_conf, shader->parameters[i].id, ¶meter_value_reference); + if (shader->parameters[i].current != parameter_value_reference) + add_param_to_override = true; + } + /* If it's not in the reference config, but it's different than the + initial value of the shader */ + else if (shader->parameters[i].current != shader->parameters[i].initial) + add_param_to_override = true; + + /* Add the parameter name to the parameters list */ + if (add_param_to_override) + { + config_set_float(conf, shader->parameters[i].id, shader->parameters[i].current); + strlcat(override_parameters, ";", param_size); + strlcat(override_parameters, shader->parameters[i].id, param_size); + } + } + + /* Write the list of override parameter names if there are any*/ + if (!string_is_empty(override_parameters)) + config_set_string(conf, "parameters", override_parameters); + } + free(override_parameters); + } + + /* If the shader has textures */ + if (shader->luts) + { + char *shader_tex_path = (char*)malloc(3*PATH_MAX_LENGTH); + char *shader_tex_relative_path = shader_tex_path + PATH_MAX_LENGTH; + char *shader_tex_base_path = shader_tex_path + 2*PATH_MAX_LENGTH; + char *referenced_tex_absolute_path = (char*)malloc(PATH_MAX_LENGTH); + char *referenced_tex_path = (char*)malloc(PATH_MAX_LENGTH); + size_t tex_size = 4096 * sizeof(char); + char *textures = (char*)malloc(tex_size); + unsigned i; + + shader_tex_path[0] = '\0'; + shader_tex_relative_path[0] = '\0'; + shader_tex_base_path[0] = '\0'; + textures[0] = '\0'; + referenced_tex_absolute_path[0] = '\0'; + referenced_tex_path[0] = '\0'; + + for (i = 0; i < shader->luts; i++) + { + /* If the texture is defined in the reference config */ + entry = config_get_entry(root_conf, shader->lut[i].id); + if (entry) + { + /* Texture path from shader is already absolute */ + strlcpy(shader_tex_path, shader->lut[i].path, PATH_MAX_LENGTH); + strlcpy(referenced_tex_path, entry->value, PATH_MAX_LENGTH); + + /* Resolve the texture's path relative to the override config */ + if (!path_is_absolute(referenced_tex_path)) + fill_pathname_resolve_relative(referenced_tex_absolute_path, + root_conf->path, + entry->value, + PATH_MAX_LENGTH); + else + strlcpy(referenced_tex_absolute_path, referenced_tex_path, PATH_MAX_LENGTH); + + /* If the current shader texture path is different than the referenced paths then + * write the current path into the new preset */ + if (!string_is_equal(referenced_tex_absolute_path, shader->lut[i].path)) + { + /* Get the texture path relative to the new preset */ + path_relative_to(shader_tex_relative_path, shader_tex_path, path_basename(path), PATH_MAX_LENGTH); + + RARCH_LOG("[Shaders-Save Reference]: Texture override %s = %s.\n", + shader->lut[i].id, + shader->lut[i].path); + config_set_path(conf, shader->lut[i].id, shader->lut[i].path); + } + } + } + } + /* Write the file, return will be true if successful */ + ret = config_file_write(conf, path, false); + + if (!ret) + RARCH_WARN("[Shaders-Save Reference]: Failed writing Referenced Preset to %s - " + "Full Preset Will be Saved instead of Simple Preset\n", path); + } + config_file_free(root_conf); + } + config_file_free(conf); + } + free(relative_root_preset_path); + return ret; +} + /** * video_shader_write_preset: * @path : File to write to @@ -586,9 +959,9 @@ bool video_shader_write_preset(const char *path, { /* We need to clean up paths to be able to properly process them * path and shader->path can use '/' on Windows due to Qt being Qt */ - char clean_path[PATH_MAX_LENGTH]; - char clean_shader_path[PATH_MAX_LENGTH]; char preset_dir[PATH_MAX_LENGTH]; + config_file_t *conf; + bool ret; if (!shader || string_is_empty(path)) return false; @@ -599,178 +972,160 @@ bool video_shader_write_preset(const char *path, "presets", sizeof(preset_dir)); - strlcpy(clean_shader_path, shader->path, sizeof(clean_shader_path)); - path_resolve_realpath(clean_shader_path, sizeof(clean_shader_path), false); - - if (string_is_empty(shader->path)) - reference = false; - - /* Auto-shaders can be written as copies or references. - * If we write a reference to a copy, we could then overwrite the copy - * with any reference, thus creating a reference to a reference. - * To prevent this, we disallow saving references to auto-shaders. */ - if (reference && !strncmp(preset_dir, clean_shader_path, strlen(preset_dir))) - reference = false; - - /* Don't ever create a reference to the ever-changing retroarch preset - * TODO remove once we don't write this preset anymore */ - if (reference && !strncmp(path_basename(clean_shader_path), "retroarch", STRLEN_CONST("retroarch"))) - reference = false; - + /* If we should still save a referenced preset do it now */ if (reference) - { - /* write a reference preset */ - char buf[STRLEN_CONST("#reference \"") + PATH_MAX_LENGTH + 1] = "#reference \""; - size_t len = STRLEN_CONST("#reference \""); - char *preset_ref = buf + len; + if (video_shader_write_referenced_preset(path, shader_dir, shader)) + return true; - strlcpy(clean_path, path, sizeof(clean_path)); - path_resolve_realpath(clean_path, sizeof(clean_path), false); + /* If we aren't saving a referenced preset or weren't able to save one + * then save a full preset */ - path_relative_to(preset_ref, clean_shader_path, clean_path, PATH_MAX_LENGTH); - len += strlen(preset_ref); + /* Note: We always create a new/blank config + * file here. Loading and updating an existing + * file could leave us with unwanted/invalid + * parameters. */ + if (!(conf = config_file_new_alloc())) + return false; - buf[len++] = '\"'; + video_shader_write_conf_preset(conf, shader, path); - return filestream_write_file(clean_path, (void *)buf, len); - } - else - { - /* regular saving function */ - config_file_t *conf; - bool ret; + ret = config_file_write(conf, path, false); - /* Note: We always create a new/blank config - * file here. Loading and updating an existing - * file could leave us with unwanted/invalid - * parameters. */ - if (!(conf = config_file_new_alloc())) - return false; + config_file_free(conf); - video_shader_write_conf_preset(conf, shader, path); - - ret = config_file_write(conf, path, false); - - config_file_free(conf); - - return ret; - } + return ret; } /** - * video_shader_read_reference_path: - * @path : File to read + * override_config_values: + * @conf : Config file to be affected + * @override_conf : Config file who's values will be copied on top of conf * - * Returns: the reference path of a preset if it exists, - * otherwise returns NULL. + * Takes values from override_config and overrides values of conf + * The 'parameters' value will be the combined parameter list from both configs * - * The returned string needs to be freed. - */ -static char *video_shader_read_reference_path(const char *path) + * Returns 0 if nothing is overridden + * Returns 1 if something is overridden + **/ +bool override_config_values(config_file_t *conf, config_file_t *override_conf) { - /* We want shader presets that point to other presets. - * - * While config_file_new_from_path_to_string() does support the - * #include directive, it will not rebase relative paths on - * the include path. - * - * There's a plethora of reasons why a general solution is hard: - * - it's impossible to distinguish a generic string from a (relative) path - * - config_file_new_from_path_to_string() doesn't return the include path, - * so we cannot rebase afterwards - * - #include is recursive, so we'd actually need to track the include path - * for every setting - * - * So instead, we use a custom #reference directive, which is just a - * one-time/non-recursive redirection, e.g. - * - * #reference "" - * or - * #reference - * - * which we will load as config_file_new_from_path_to_string(). - */ - char *reference = NULL; - RFILE *file = NULL; - char *line = NULL; + int return_val = 0; + size_t param_size = 4096 * sizeof(char); + const char *id = NULL; + char *save = NULL; + size_t path_size = PATH_MAX_LENGTH; + char *override_texture_path = (char*)malloc(path_size); + char *resolved_path = (char*)malloc(path_size); + char *textures_in_conf = (char*)malloc(param_size); + size_t tmp_size = PATH_MAX_LENGTH; + char *tmp = (char*)malloc(3*tmp_size); + char *tmp_rel = tmp + tmp_size; + char *tmp_base = tmp + 2*tmp_size; + struct config_entry_list *override_entry = NULL; + char *override_parameters = (char*)malloc(param_size); - if (string_is_empty(path) || !path_is_valid(path)) - return NULL; - file = filestream_open(path, - RETRO_VFS_FILE_ACCESS_READ, - RETRO_VFS_FILE_ACCESS_HINT_NONE); - if (!file) - return NULL; + override_parameters[0] = '\0'; + textures_in_conf[0] = '\0'; + strlcpy(tmp_base, conf->path, tmp_size); - line = filestream_getline(file); - filestream_close(file); + if (conf == NULL || override_conf == NULL) return 0; - if (line && !strncmp("#reference", line, STRLEN_CONST("#reference"))) - { - char *ref_path = line + STRLEN_CONST("#reference"); + /* --------------------------------------------------------------------------------- + * ------------- Resolve Override texture paths to absolute paths------------------- + * --------------------------------------------------------------------------------- */ - /* have at least 1 whitespace */ - if (!isspace((unsigned char)*ref_path)) + /* ensure we use a clean base like the shader passes and texture paths do */ + path_resolve_realpath(tmp_base, tmp_size, false); + path_basedir(tmp_base); + + /* If there are textures in the referenced config */ + if (config_get_array(conf, "textures", textures_in_conf, param_size)) { - free(line); - return NULL; - } - ref_path++; - - while (isspace((unsigned char)*ref_path)) - ref_path++; - - if (*ref_path == '\"') - { - /* remove "" */ - char *p; - ref_path++; - - p = ref_path; - while (*p != '\0' && *p != '\"') - p++; - - if (*p == '\"') - *p = '\0'; - else + for ( id = strtok_r(textures_in_conf, ";", &save); + id; + id = strtok_r(NULL, ";", &save)) { - /* if there's no second ", remove whitespace at the end */ - p--; - while (isspace((unsigned char)*p)) - *p-- = '\0'; + /* Get the texture path from the override config */ + if (config_get_path(override_conf, id, override_texture_path, path_size)) + { + /* Resolve the texture's path relative to the override config */ + if (!path_is_absolute(override_texture_path)) + fill_pathname_resolve_relative(resolved_path, + override_conf->path, + override_texture_path, + PATH_MAX_LENGTH); + else + strlcpy(resolved_path, override_texture_path, path_size); + + path_relative_to(tmp_rel, resolved_path, tmp_base, tmp_size); + config_set_path(override_conf, id, tmp_rel); + + return_val = 1; + } } } - else + + /* --------------------------------------------------------------------------------- + * -------------Update Parameter List to include Override Parameters---------------- + * --------------------------------------------------------------------------------- */ + + /* If there is a 'parameters' entry in the override config we want to add these parameters + * to the referenced config if they are not already there */ + if (config_get_array(override_conf, "parameters", override_parameters, param_size)) { - /* remove whitespace at the end (e.g. carriage return) */ - char *end = ref_path + strlen(ref_path) - 1; - while (isspace((unsigned char)*end)) - *end-- = '\0'; + /* Get the string for the parameters from the root config */ + char *parameters = NULL; + parameters = (char*)malloc(param_size); + parameters[0] = '\0'; + + /* If there are is no parameters entry in the root config, add one */ + if (!config_get_array(conf, "parameters", parameters, param_size)) + { + config_set_string(conf, "parameters", ""); + config_get_array(conf, "parameters", parameters, param_size); + } + + /* Step through each parameter in override config */ + for ( id = strtok_r(override_parameters, ";", &save); + id; + id = strtok_r(NULL, ";", &save)) + { + /* If the parameter is not in the root config's parameter list add it */ + if (!strstr(parameters, id)) + { + strlcat(parameters, ";", param_size); + strlcat(parameters, id, param_size); + return_val = 1; + } + } + config_set_string(conf, "parameters", strdup(parameters)); + + free(parameters); } - if (string_is_empty(ref_path)) + /* --------------------------------------------------------------------------------- + * ------------- Update entries to match the override entries ---------------------- + * --------------------------------------------------------------------------------- */ + + for (override_entry = override_conf->entries; override_entry; override_entry = override_entry->next) { - free(line); - return NULL; + /* Only override an entry if the it's key is not "parameters", and not in list of textures */ + if (!string_is_empty(override_entry->key) && !string_is_equal(override_entry->key, "parameters") && !string_is_equal(override_entry->key, "textures")) + { + RARCH_LOG("[Shaders-Load Reference]: Entry overridden %s = %s.\n", + override_entry->key, override_entry->value); + config_set_string(conf, override_entry->key, strdup(override_entry->value)); + return_val = 1; + } } - reference = (char *)malloc(PATH_MAX_LENGTH); + free(tmp); + free(resolved_path); + free(override_texture_path); + free(override_parameters); + free(textures_in_conf); - if (!reference) - { - free(line); - return NULL; - } - - /* rebase relative reference path */ - fill_pathname_resolve_relative(reference, - path, ref_path, PATH_MAX_LENGTH); - } - - if (line) - free(line); - - return reference; + return return_val; } /** @@ -787,15 +1142,52 @@ static char *video_shader_read_reference_path(const char *path) **/ config_file_t *video_shader_read_preset(const char *path) { - char *reference = video_shader_read_reference_path(path); - if (reference) + config_file_t *conf; + conf = config_file_new_from_path_to_string(path); + + if (conf != NULL) { - config_file_t *conf = - config_file_new_from_path_to_string(reference); - free(reference); - return conf; + /* If the original config had a reference then it was really + * the override config. We now load a new config from the reference + * then override it's values with the override config */ + if (conf->reference) + { + char* root_preset_path = (char*)malloc(PATH_MAX_LENGTH); + /* Set override_conf to refer to the original config */ + config_file_t *override_conf = conf; + + /* Resolve the reference path relative to the config */ + if (path_is_absolute(conf->reference)) + strlcpy(root_preset_path, conf->reference, PATH_MAX_LENGTH); + else + fill_pathname_resolve_relative(root_preset_path, + conf->path, + conf->reference, + PATH_MAX_LENGTH); + + /* Create a new config from the root preset */ + conf = config_file_new_from_path_to_string(root_preset_path); + + /* Only try to override values if the config is not NULL + * If it is NULL there is no shader*/ + if (conf != NULL) + /* override_conf is from the initial file we loaded which + * has the #reference directive*/ + override_config_values(conf, override_conf); + else + RARCH_WARN("[ SHADER LOAD ] Could not read root shader preset in '#reference' line: %s\n", root_preset_path); + + free(root_preset_path); + config_file_free(override_conf); + } + + /* Set Path for originally loaded preset */ + config_set_path(conf, "loaded_preset_path", path); } - return config_file_new_from_path_to_string(path); + else + RARCH_WARN("[ SHADER LOAD ] Could not read preset: %s", path); + + return conf; } /** @@ -815,6 +1207,7 @@ bool video_shader_read_conf_preset(config_file_t *conf, unsigned shaders = 0; settings_t *settings = config_get_ptr(); bool watch_files = settings->bools.video_shader_watch_files; + char loaded_preset_path[PATH_MAX_LENGTH]; memset(shader, 0, sizeof(*shader)); @@ -826,7 +1219,7 @@ bool video_shader_read_conf_preset(config_file_t *conf, if (!shaders) { - RARCH_ERR("[Shaders]: Need to define at least 1 shader.\n"); + RARCH_ERR("[Shaders]: Need to define at least 1 shader pass.\n"); return false; } @@ -836,7 +1229,16 @@ bool video_shader_read_conf_preset(config_file_t *conf, shader->passes = MIN(shaders, GFX_MAX_SHADERS); + /* Set the path of the root preset for this shader */ strlcpy(shader->path, conf->path, sizeof(shader->path)); + + /* Set the path of the original preset which was loaded, this would be + * different than the root preset in the case preset of use of the #reference directive + * in the original preset loaded */ + config_get_path(conf, "loaded_preset_path", loaded_preset_path, PATH_MAX_LENGTH); + strlcpy( shader->loaded_preset_path, + loaded_preset_path, + sizeof(shader->loaded_preset_path)); if (watch_files) { @@ -858,6 +1260,9 @@ bool video_shader_read_conf_preset(config_file_t *conf, string_list_initialize(&file_list); string_list_append(&file_list, conf->path, attr); + /* TODO We aren't currently watching the originally loaded preset + * We should probably watch it for changes too */ + for (i = 0; i < shader->passes; i++) { if (!video_shader_parse_pass(conf, &shader->pass[i], i)) @@ -944,17 +1349,6 @@ static void shader_write_fbo(config_file_t *conf, fbo->scale_y, fbo->abs_y, i); } -#ifdef _WIN32 -static void make_relative_path_portable(char *path) -{ - /* use '/' instead of '\' for maximum portability */ - char *p; - for (p = path; *p; p++) - if (*p == '\\') - *p = '/'; -} -#endif - /** * video_shader_write_conf_preset: * @conf : Preset file to write to. diff --git a/gfx/video_shader_parse.h b/gfx/video_shader_parse.h index a7b6576660..cacf7987ee 100644 --- a/gfx/video_shader_parse.h +++ b/gfx/video_shader_parse.h @@ -153,8 +153,14 @@ struct video_shader unsigned variables; char prefix[64]; + + /* Path to the root preset */ char path[PATH_MAX_LENGTH]; + /* Path to the original preset loaded, if this is a preset with the #reference + * directive then this will be different than the path*/ + char loaded_preset_path[PATH_MAX_LENGTH]; + bool modern; /* Only used for XML shaders. */ /* indicative of whether shader was modified - * for instance from the menus */ diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 29876260ea..a4a975a302 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -3230,6 +3230,10 @@ MSG_HASH( MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE, "video_shader_preset_save" ) +MSG_HASH( + MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + "video_shader_preset_save_reference" + ) MSG_HASH( MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS, "video_shader_preset_save_as" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 03678e8a82..9cb5094365 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -6674,6 +6674,16 @@ MSG_HASH( /* Quick Menu > Shaders > Save */ +MSG_HASH( + MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + "Save Simple Preset" + ) + +MSG_HASH( + MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + "Saves a Shader Preset which has a link to the original preset loaded and includes only the parameter changes you made." + ) + MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS, "Save Shader Preset As" diff --git a/libretro-common/file/config_file.c b/libretro-common/file/config_file.c index 38dedaa06d..37438fc517 100644 --- a/libretro-common/file/config_file.c +++ b/libretro-common/file/config_file.c @@ -181,7 +181,7 @@ static char *config_file_strip_comment(char *str) *str = '\0'; return ++comment; } - + /* Comment character occurs at an offset: * Search for the start of a string literal value */ literal_start = strchr(str, '\"'); @@ -466,40 +466,48 @@ static bool config_file_parse_line(config_file_t *conf, char real_path[PATH_MAX_LENGTH]; char *path = NULL; char *include_line = NULL; + char *reference_line = NULL; /* Starting a line with an 'include' directive * appends a sub-config file * > All other comments are ignored */ if (!string_starts_with_size(comment, "include ", - STRLEN_CONST("include "))) + STRLEN_CONST("include ")) && + !string_starts_with_size(comment, "reference ", + STRLEN_CONST("reference "))) return false; - include_line = comment + STRLEN_CONST("include "); + /* Starting a line with an 'include' directive + * appends a sub-config file */ + if (string_starts_with_size(comment, "include ", + STRLEN_CONST("include "))) + { + include_line = comment + STRLEN_CONST("include "); - if (string_is_empty(include_line)) - return false; + if (string_is_empty(include_line)) + return false; - path = config_file_extract_value(include_line, false); + path = config_file_extract_value(include_line, false); - if (!path) - return false; + if (!path) + return false; if ( string_is_empty(path) || conf->include_depth >= MAX_INCLUDE_DEPTH) - { - free(path); - return false; - } + { + free(path); + return false; + } - real_path[0] = '\0'; - config_file_add_sub_conf(conf, path, + real_path[0] = '\0'; + config_file_add_sub_conf(conf, path, real_path, sizeof(real_path), cb); - config_file_initialize(&sub_conf); + config_file_initialize(&sub_conf); - switch (config_file_load_internal(&sub_conf, real_path, - conf->include_depth + 1, cb)) - { + switch (config_file_load_internal(&sub_conf, real_path, + conf->include_depth + 1, cb)) + { case 0: /* Pilfer internal list. */ config_file_add_child_list(conf, &sub_conf); @@ -510,6 +518,27 @@ static bool config_file_parse_line(config_file_t *conf, case 1: default: break; + } + } + + /* If it's a 'reference' directive */ + if (string_starts_with_size(comment, "reference ", + STRLEN_CONST("reference "))) + { + reference_line = comment + STRLEN_CONST("reference "); + + if (string_is_empty(reference_line)) + return false; + + path = config_file_extract_value(reference_line, false); + + if (!path) + return false; + + config_file_set_reference_path(conf, path); + + if (!path) + return false; } free(path); @@ -617,6 +646,16 @@ static int config_file_from_string_internal( return 0; } +void config_file_set_reference_path(config_file_t *conf, char *path) +{ + if (conf) + { + /* Assumes if you wanted a relative path the input path is + * already relative to the config + */ + conf->reference = strdup(path); + } +} bool config_file_deinitialize(config_file_t *conf) { @@ -774,6 +813,7 @@ void config_file_initialize(struct config_file *conf) conf->entries = NULL; conf->tail = NULL; conf->last = NULL; + conf->reference = NULL; conf->includes = NULL; conf->include_depth = 0; conf->guaranteed_no_duplicates = false; @@ -1245,6 +1285,10 @@ void config_file_dump_orbis(config_file_t *conf, int fd) { struct config_entry_list *list = NULL; struct config_include_list *includes = conf->includes; + + if (conf->reference) + fprintf(file, "#reference \"%s\"\n", conf->reference); + while (includes) { char cad[256]; @@ -1278,6 +1322,9 @@ void config_file_dump(config_file_t *conf, FILE *file, bool sort) struct config_entry_list *list = NULL; struct config_include_list *includes = conf->includes; + if (conf->reference) + fprintf(file, "#reference \"%s\"\n", conf->reference); + while (includes) { fprintf(file, "#include \"%s\"\n", includes->path); diff --git a/libretro-common/include/file/config_file.h b/libretro-common/include/file/config_file.h index 883cd76b2b..1913bf2b9b 100644 --- a/libretro-common/include/file/config_file.h +++ b/libretro-common/include/file/config_file.h @@ -54,6 +54,7 @@ RETRO_BEGIN_DECLS struct config_file { char *path; + char *reference; struct config_entry_list *entries; struct config_entry_list *tail; struct config_entry_list *last; @@ -107,6 +108,8 @@ config_file_t *config_file_new_from_path_to_string(const char *path); /* Frees config file. */ void config_file_free(config_file_t *conf); +void config_file_set_reference_path(config_file_t *conf, char *path); + bool config_file_deinitialize(config_file_t *conf); /* Loads a new config, and appends its data to conf. diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index c4d64c3718..70e2fd2fcd 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -2893,6 +2893,11 @@ static int generic_action_ok_shader_preset_save(const char *path, return 0; } + /* Save Auto Preset and have it immediately reapply the preset + * TODO: This seems necessary so that the loaded shader gains a link to the file saved + * But this is slow and seems like a redundant way to do this + * It seems like it would be better to just set the path and shader_preset_loaded + * on the current shader */ if (menu_shader_manager_save_auto_preset(menu_shader_get(), preset_type, dir_video_shader, dir_menu_config, true)) diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index d626842074..f69004c1d7 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5311,6 +5311,10 @@ unsigned menu_displaylist_build_list( case DISPLAYLIST_SHADER_PRESET_SAVE: { #if defined(HAVE_CG) || defined(HAVE_GLSL) || defined(HAVE_SLANG) || defined(HAVE_HLSL) + if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list, + MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + PARSE_ONLY_BOOL, false) == 0) + count++; if (menu_entries_append_enum(list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_AS), msg_hash_to_str(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_AS), diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 47318a57ce..abac18514a 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -9170,6 +9170,21 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); + CONFIG_BOOL( + list, list_info, + &settings->bools.video_shader_preset_save_reference_enable, + MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PRESET_SAVE_REFERENCE, + DEFAULT_VIDEO_SHADER_PRESET_SAVE_REFERENCE_ENABLE, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); } diff --git a/msg_hash.h b/msg_hash.h index a16105c685..1e1f29b79c 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -579,7 +579,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_NORD, MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_GRUVBOX_DARK, MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_BOYSENBERRY, - MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_HACKING_THE_KERNEL, + MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_HACKING_THE_KERNEL, MENU_ENUM_LABEL_VALUE_OZONE_COLOR_THEME_TWILIGHT_ZONE, MENU_ENUM_LABEL_VALUE_XMB_ICON_THEME_MONOCHROME, @@ -2325,6 +2325,7 @@ enum msg_hash_enums MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_PARENT), MENU_LABEL(VIDEO_SHADER_PRESET_REMOVE_GAME), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE), + MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_REFERENCE), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_AS), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_GLOBAL), MENU_LABEL(VIDEO_SHADER_PRESET_SAVE_CORE), diff --git a/retroarch.c b/retroarch.c index d047904254..5f441021ad 100644 --- a/retroarch.c +++ b/retroarch.c @@ -8063,7 +8063,9 @@ bool menu_shader_manager_init(void) video_shader_driver_get_current_shader(&shader_info); if (shader_info.data) - path_shader = shader_info.data->path; + /* Use the path of the originally loaded preset because it could + * have been a preset with a #reference in it to another preset */ + path_shader = shader_info.data->loaded_preset_path; else path_shader = retroarch_get_shader_preset(); @@ -8195,9 +8197,10 @@ clear: } static bool menu_shader_manager_save_preset_internal( - const struct video_shader *shader, const char *basename, + const struct video_shader *shader, + const char *basename, const char *dir_video_shader, - bool apply, bool save_reference, + bool apply, const char **target_dirs, size_t num_target_dirs) { @@ -8208,6 +8211,10 @@ static bool menu_shader_manager_save_preset_internal( char fullname[PATH_MAX_LENGTH]; char buffer[PATH_MAX_LENGTH]; + struct rarch_state *p_rarch = &rarch_st; + settings_t *settings = p_rarch->configuration_settings; + bool save_reference = settings->bools.video_shader_preset_save_reference_enable; + fullname[0] = buffer[0] = '\0'; if (!shader || !shader->passes) @@ -8218,9 +8225,6 @@ static bool menu_shader_manager_save_preset_internal( if (type == RARCH_SHADER_NONE) return false; - if (shader->modified) - save_reference = false; - if (!string_is_empty(basename)) { /* We are comparing against a fixed list of file @@ -8396,7 +8400,7 @@ static bool menu_shader_manager_operate_auto_preset( return menu_shader_manager_save_preset_internal( shader, file, dir_video_shader, - apply, true, + apply, auto_preset_dirs, ARRAY_SIZE(auto_preset_dirs)); case AUTO_SHADER_OP_REMOVE: @@ -8546,7 +8550,7 @@ bool menu_shader_manager_save_preset(const struct video_shader *shader, return menu_shader_manager_save_preset_internal( shader, basename, dir_video_shader, - apply, false, + apply, preset_dirs, ARRAY_SIZE(preset_dirs)); }