diff --git a/gfx/common/metal_common.m b/gfx/common/metal_common.m index 0d392e170c..cdc51d5451 100644 --- a/gfx/common/metal_common.m +++ b/gfx/common/metal_common.m @@ -1138,7 +1138,7 @@ typedef struct MTLALIGN(16) [self _freeVideoShader:_shader]; _shader = nil; - config_file_t *conf = config_file_new_from_path_to_string(path.UTF8String); + config_file_t *conf = video_shader_read_preset(path.UTF8String); struct video_shader *shader = (struct video_shader *)calloc(1, sizeof(*shader)); @try diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 5968169bdb..397d0f62ae 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -360,7 +360,7 @@ static bool d3d10_gfx_set_shader(void* data, enum rarch_shader_type type, const return false; } - if (!(conf = config_file_new_from_path_to_string(path))) + if (!(conf = video_shader_read_preset(path))) return false; d3d10->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d10->shader_preset)); diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 0c7789003f..3ce1e34003 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -378,7 +378,7 @@ static bool d3d11_gfx_set_shader(void* data, enum rarch_shader_type type, const return false; } - if (!(conf = config_file_new_from_path_to_string(path))) + if (!(conf = video_shader_read_preset(path))) return false; d3d11->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d11->shader_preset)); diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 4353b81c61..203a8aa67b 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -359,7 +359,7 @@ static bool d3d12_gfx_set_shader(void* data, enum rarch_shader_type type, const return false; } - if (!(conf = config_file_new_from_path_to_string(path))) + if (!(conf = video_shader_read_preset(path))) return false; d3d12->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d12->shader_preset)); diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index b5b2cb4199..de147d4736 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -329,7 +329,7 @@ static bool d3d9_init_multipass(d3d9_video_t *d3d, const char *shader_path) unsigned i; bool use_extra_pass = false; struct video_shader_pass *pass = NULL; - config_file_t *conf = config_file_new_from_path_to_string(shader_path); + config_file_t *conf = video_shader_read_preset(shader_path); if (!conf) { diff --git a/gfx/drivers/gx2_gfx.c b/gfx/drivers/gx2_gfx.c index 0eaeb6c729..0e860e7adf 100644 --- a/gfx/drivers/gx2_gfx.c +++ b/gfx/drivers/gx2_gfx.c @@ -1425,7 +1425,7 @@ static bool wiiu_gfx_set_shader(void *data, return false; } - if (!(conf = config_file_new_from_path_to_string(path))) + if (!(conf = video_shader_read_preset(path))) return false; wiiu->shader_preset = calloc(1, sizeof(*wiiu->shader_preset)); diff --git a/gfx/drivers_shader/shader_gl_cg.c b/gfx/drivers_shader/shader_gl_cg.c index 59029a535b..1b11922366 100644 --- a/gfx/drivers_shader/shader_gl_cg.c +++ b/gfx/drivers_shader/shader_gl_cg.c @@ -678,7 +678,7 @@ static bool gl_cg_load_preset(void *data, const char *path) return false; RARCH_LOG("[CG]: Loading Cg meta-shader: %s\n", path); - conf = config_file_new_from_path_to_string(path); + conf = video_shader_read_preset(path); if (!conf) { RARCH_ERR("Failed to load preset.\n"); diff --git a/gfx/drivers_shader/shader_gl_core.cpp b/gfx/drivers_shader/shader_gl_core.cpp index b1b12868f0..a8fdbc8457 100644 --- a/gfx/drivers_shader/shader_gl_core.cpp +++ b/gfx/drivers_shader/shader_gl_core.cpp @@ -2406,7 +2406,7 @@ gl_core_filter_chain_t *gl_core_filter_chain_create_from_preset( if (!shader) return nullptr; - unique_ptr conf{ config_file_new_from_path_to_string(path) }; + unique_ptr conf{ video_shader_read_preset(path) }; if (!conf) return nullptr; diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index a844628ed0..7a210e81f5 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -900,7 +900,7 @@ static void *gl_glsl_init(void *data, const char *path) if (is_preset) { - conf = config_file_new_from_path_to_string(path); + conf = video_shader_read_preset(path); if (conf) { ret = video_shader_read_conf_preset(conf, glsl->shader); diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index f891dd2a57..6d6ce40ae5 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -2887,7 +2887,7 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( if (!shader) return nullptr; - unique_ptr conf{ config_file_new_from_path_to_string(path) }; + unique_ptr conf{ video_shader_read_preset(path) }; if (!conf) return nullptr; diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index fc47456b3a..6bb994cc52 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -571,6 +571,234 @@ bool video_shader_resolve_parameters(config_file_t *conf, return video_shader_resolve_current_parameters(conf, shader); } +/** + * video_shader_write_preset: + * @path : File to write to + * @shader : Shader preset to write + * @reference : Whether a reference preset should be written + * + * Writes a preset to disk. Can be written as a reference preset. + * See: video_shader_read_preset + **/ +bool video_shader_write_preset(const char *path, + const struct video_shader *shader, bool reference) +{ + /* 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]; + + if (!shader || string_is_empty(path)) + return false; + + fill_pathname_join( + preset_dir, + config_get_ptr()->paths.directory_video_shader, + "presets", + sizeof(preset_dir)); + + strlcpy(clean_shader_path, shader->path, PATH_MAX_LENGTH); + path_resolve_realpath(clean_shader_path, PATH_MAX_LENGTH, 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 (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; + + strlcpy(clean_path, path, PATH_MAX_LENGTH); + path_resolve_realpath(clean_path, PATH_MAX_LENGTH, false); + + path_relative_to(preset_ref, clean_shader_path, clean_path, PATH_MAX_LENGTH); + len += strlen(preset_ref); + + buf[len++] = '\"'; + + return filestream_write_file(clean_path, (void *)buf, len); + } + else + { + /* regular saving function */ + config_file_t *conf; + bool ret; + + if (!(conf = config_file_new_alloc())) + return false; + + video_shader_write_conf_preset(conf, shader, path); + + ret = config_file_write(conf, path, false); + + config_file_free(conf); + + return ret; + } +} + +/** + * video_shader_read_reference_path: + * @path : File to read + * + * Returns: the reference path of a preset if it exists, + * otherwise returns NULL. + * + * The returned string needs to be freed. + */ +char *video_shader_read_reference_path(const char *path) +{ + /* 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; + config_file_t *conf = NULL; + RFILE *file = NULL; + char *line = NULL; + + if (string_is_empty(path)) + goto end; + + if (!path_is_valid(path)) + goto end; + + file = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) + goto end; + + line = filestream_getline(file); + filestream_close(file); + + if (line && !strncmp("#reference", line, STRLEN_CONST("#reference"))) + { + char *ref_path = line + STRLEN_CONST("#reference"); + + /* have at least 1 whitespace */ + if (!isspace(*ref_path)) + goto end; + ref_path++; + + while (isspace(*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 + { + /* if there's no second ", remove whitespace at the end */ + p--; + while (isspace(*p)) + *p-- = '\0'; + } + } + else + { + /* remove whitespace at the end (e.g. carriage return) */ + char *end = ref_path + strlen(ref_path) - 1; + while (isspace(*end)) + *end-- = '\0'; + } + + if (string_is_empty(ref_path)) + goto end; + + reference = (char *)malloc(PATH_MAX_LENGTH); + + if (!reference) + goto end; + + /* rebase relative reference path */ + if (!path_is_absolute(ref_path)) + { + fill_pathname_resolve_relative(reference, + path, ref_path, PATH_MAX_LENGTH); + } + else + strlcpy(reference, ref_path, PATH_MAX_LENGTH); + } + +end: + if (line) + free(line); + + return reference; +} + +/** + * video_shader_read_preset: + * @path : File to read + * + * Reads a preset from disk. + * If the preset is a reference preset, the referenced preset + * is loaded instead. + * + * Returns the read preset as a config object. + * + * The returned config object needs to be freed. + **/ +config_file_t *video_shader_read_preset(const char *path) +{ + config_file_t *conf; + char *reference = video_shader_read_reference_path(path); + if (reference) + { + conf = config_file_new_from_path_to_string(reference); + free(reference); + } + else + conf = config_file_new_from_path_to_string(path); + + return conf; +} + /** * video_shader_read_conf_preset: * @conf : Preset file to read from. @@ -745,7 +973,7 @@ static void make_relative_path_portable(char *path) * relative to it. **/ void video_shader_write_conf_preset(config_file_t *conf, - struct video_shader *shader, const char *preset_path) + const struct video_shader *shader, const char *preset_path) { unsigned i; char key[64]; diff --git a/gfx/video_shader_parse.h b/gfx/video_shader_parse.h index bd9ef2d01d..7ff5bdf391 100644 --- a/gfx/video_shader_parse.h +++ b/gfx/video_shader_parse.h @@ -159,6 +159,43 @@ struct video_shader struct video_shader_parameter parameters[GFX_MAX_PARAMETERS]; }; +/** + * video_shader_write_preset: + * @path : File to write to + * @shader : Shader preset to write + * @reference : Whether a reference preset should be written + * + * Writes a preset to disk. Can be written as a reference preset. + * See: video_shader_read_preset + **/ +bool video_shader_write_preset(const char *path, + const struct video_shader *shader, bool reference); + +/** + * video_shader_read_reference_path: + * @path : File to read + * + * Returns: the reference path of a preset if it exists, + * otherwise returns NULL. + * + * The returned string needs to be freed. + */ +char *video_shader_read_reference_path(const char *path); + +/** + * video_shader_read_preset: + * @path : File to read + * + * Reads a preset from disk. + * If the preset is a reference preset, the referenced preset + * is loaded instead. + * + * Returns: the read preset as a config object. + * + * The returned config object needs to be freed. + **/ +config_file_t *video_shader_read_preset(const char *path); + /** * video_shader_read_conf_preset: * @conf : Preset file to read from. @@ -183,7 +220,7 @@ bool video_shader_read_conf_preset(config_file_t *conf, * relative to it. **/ void video_shader_write_conf_preset(config_file_t *conf, - struct video_shader *shader, const char *preset_path); + const struct video_shader *shader, const char *preset_path); /** * video_shader_resolve_parameters: diff --git a/menu/cbs/menu_cbs_left.c b/menu/cbs/menu_cbs_left.c index f63d0644cc..804bd67dd3 100644 --- a/menu/cbs/menu_cbs_left.c +++ b/menu/cbs/menu_cbs_left.c @@ -84,6 +84,8 @@ static int shader_action_parameter_left(unsigned type, const char *label, bool w param_menu->current = param_prev->current; + menu_shader_set_modified(true); + return ret; } #endif @@ -292,6 +294,9 @@ static int action_left_shader_scale_pass(unsigned type, const char *label, shader_pass->fbo.valid = current_scale; shader_pass->fbo.scale_x = current_scale; shader_pass->fbo.scale_y = current_scale; + + menu_shader_set_modified(true); + return 0; } @@ -307,6 +312,9 @@ static int action_left_shader_filter_pass(unsigned type, const char *label, return menu_cbs_exit(); shader_pass->filter = ((shader_pass->filter + delta) % 3); + + menu_shader_set_modified(true); + return 0; } @@ -355,6 +363,8 @@ static int action_left_shader_num_passes(unsigned type, const char *label, menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); video_shader_resolve_parameters(NULL, shader); + menu_shader_set_modified(true); + return 0; } diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 7c4ba125ab..abc50466bc 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -1610,6 +1610,8 @@ static int generic_action_ok(const char *path, action_path, sizeof(shader_pass->source.path)); video_shader_resolve_parameters(NULL, shader); + + menu_shader_set_modified(true); } } #endif @@ -2591,7 +2593,7 @@ static void menu_input_st_string_cb_enable_settings(void *userdata, static void menu_input_st_string_cb_save_preset(void *userdata, const char *str) { - if (str && *str) + if (!string_is_empty(str)) { rarch_setting_t *setting = NULL; bool ret = false; @@ -2607,7 +2609,7 @@ static void menu_input_st_string_cb_save_preset(void *userdata, } else if (!string_is_empty(label)) ret = menu_shader_manager_save_preset(menu_shader_get(), - str, false, false); + str, true); if (ret) runloop_msg_queue_push( @@ -2703,73 +2705,26 @@ static int generic_action_ok_shader_preset_save(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx, unsigned action_type) { - char directory[PATH_MAX_LENGTH]; - char file[PATH_MAX_LENGTH]; - char tmp[PATH_MAX_LENGTH]; - settings_t *settings = config_get_ptr(); - struct retro_system_info *system = runloop_get_libretro_system_info(); - const char *core_name = system ? system->library_name : NULL; - - directory[0] = file[0] = tmp[0] = '\0'; - - if (action_type != ACTION_OK_SHADER_PRESET_SAVE_GLOBAL) - { - if (!string_is_empty(core_name)) - { - fill_pathname_join( - tmp, - settings->paths.directory_video_shader, - "presets", - sizeof(tmp)); - fill_pathname_join( - directory, - tmp, - core_name, - sizeof(directory)); - } - - if (!path_is_directory(directory)) - path_mkdir(directory); - } - else - { - fill_pathname_join( - directory, - settings->paths.directory_video_shader, - "presets", - sizeof(directory)); - - if (!path_is_directory(directory)) - path_mkdir(directory); - - fill_pathname_join( - file, - directory, - "global", - sizeof(file)); - } - + enum auto_shader_type preset_type; switch (action_type) { case ACTION_OK_SHADER_PRESET_SAVE_GLOBAL: + preset_type = SHADER_PRESET_GLOBAL; break; case ACTION_OK_SHADER_PRESET_SAVE_CORE: - if (!string_is_empty(core_name)) - fill_pathname_join(file, directory, core_name, sizeof(file)); - break; - case ACTION_OK_SHADER_PRESET_SAVE_GAME: - { - const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); - fill_pathname_join(file, directory, game_name, sizeof(file)); - } + preset_type = SHADER_PRESET_CORE; break; case ACTION_OK_SHADER_PRESET_SAVE_PARENT: - fill_pathname_parent_dir_name(tmp, path_get(RARCH_PATH_BASENAME), sizeof(tmp)); - fill_pathname_join(file, directory, tmp, sizeof(file)); + preset_type = SHADER_PRESET_PARENT; break; + case ACTION_OK_SHADER_PRESET_SAVE_GAME: + preset_type = SHADER_PRESET_GAME; + break; + default: + return 0; } - if (menu_shader_manager_save_preset(menu_shader_get(), file, false, true)) + if (menu_shader_manager_save_auto_preset(menu_shader_get(), preset_type, true)) runloop_msg_queue_push( msg_hash_to_str(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY), 1, 100, true, diff --git a/menu/cbs/menu_cbs_right.c b/menu/cbs/menu_cbs_right.c index 0951078eb4..76831a3cc8 100644 --- a/menu/cbs/menu_cbs_right.c +++ b/menu/cbs/menu_cbs_right.c @@ -84,6 +84,8 @@ int shader_action_parameter_right(unsigned type, const char *label, bool wraparo param_menu->current = param_prev->current; + menu_shader_set_modified(true); + return ret; } #endif @@ -315,6 +317,9 @@ static int action_right_shader_scale_pass(unsigned type, const char *label, shader_pass->fbo.valid = current_scale; shader_pass->fbo.scale_x = shader_pass->fbo.scale_y = current_scale; + + menu_shader_set_modified(true); + return 0; } @@ -330,6 +335,9 @@ static int action_right_shader_filter_pass(unsigned type, const char *label, return menu_cbs_exit(); shader_pass->filter = ((shader_pass->filter + delta) % 3); + + menu_shader_set_modified(true); + return 0; } @@ -377,6 +385,8 @@ static int action_right_shader_num_passes(unsigned type, const char *label, menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); video_shader_resolve_parameters(NULL, shader); + menu_shader_set_modified(true); + return 0; } #endif diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6ab88d6863..7444b7c49a 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5909,6 +5909,7 @@ void general_write_handler(rarch_setting_t *setting) struct video_shader *shader = menu_shader_get(); shader->passes = 0; + menu_shader_set_modified(true); menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); diff --git a/menu/menu_shader.c b/menu/menu_shader.c index 9c4c244a2f..4ebfd1eba4 100644 --- a/menu/menu_shader.c +++ b/menu/menu_shader.c @@ -37,6 +37,13 @@ /* Menu shader */ static struct video_shader *menu_driver_shader = NULL; +/* indicative of whether shader was modified from the menus: */ +static bool menu_driver_shader_modified = true; + +void menu_shader_set_modified(bool modified) +{ + menu_driver_shader_modified = modified; +} struct video_shader *menu_shader_get(void) { @@ -62,7 +69,6 @@ bool menu_shader_manager_init(void) enum rarch_shader_type type = RARCH_SHADER_NONE; bool ret = true; bool is_preset = false; - config_file_t *conf = NULL; const char *path_shader = NULL; struct video_shader *menu_shader = NULL; @@ -104,8 +110,22 @@ bool menu_shader_manager_init(void) if (is_preset) { - if (path_is_valid(path_shader)) - conf = config_file_new_from_path_to_string(path_shader); + config_file_t *conf = NULL; + + conf = video_shader_read_preset(path_shader); + + if (!conf) + { + ret = false; + goto end; + } + + if (video_shader_read_conf_preset(conf, menu_shader)) + video_shader_resolve_parameters(conf, menu_shader); + + menu_driver_shader_modified = false; + + config_file_free(conf); } else { @@ -114,12 +134,6 @@ bool menu_shader_manager_init(void) menu_shader->passes = 1; } - if (conf && video_shader_read_conf_preset(conf, menu_shader)) - video_shader_resolve_parameters(conf, menu_shader); - - if (conf) - config_file_free(conf); - end: menu_driver_shader = menu_shader; command_event(CMD_EVENT_SHADER_PRESET_LOADED, NULL); @@ -173,7 +187,7 @@ bool menu_shader_manager_set_preset(struct video_shader *shader, * Used when a preset is directly loaded. * No point in updating when the Preset was * created from the menu itself. */ - if (!(conf = config_file_new_from_path_to_string(preset_path))) + if (!(conf = video_shader_read_preset(preset_path))) { ret = false; goto end; @@ -197,31 +211,20 @@ end: return ret; } -/** - * menu_shader_manager_save_preset: - * @basename : basename of preset - * @apply : immediately set preset after saving - * - * Save a shader preset to disk. - **/ -bool menu_shader_manager_save_preset( - struct video_shader *shader, - const char *basename, bool apply, bool fullpath) +static bool menu_shader_manager_save_preset_internal( + const struct video_shader *shader, const char *basename, + bool apply, bool save_reference) { + bool ret = false; + enum rarch_shader_type type = RARCH_SHADER_NONE; + char *preset_path = NULL; + size_t i = 0; + char fullname[PATH_MAX_LENGTH]; char buffer[PATH_MAX_LENGTH]; - char preset_path[PATH_MAX_LENGTH]; - char config_directory[PATH_MAX_LENGTH]; - bool ret = false; - unsigned d; - enum rarch_shader_type type = RARCH_SHADER_NONE; - const char *dirs[3] = {0}; - config_file_t *conf = NULL; - config_directory[0] = '\0'; - buffer[0] = '\0'; - preset_path[0] = '\0'; + fullname[0] = buffer[0] = '\0'; - if (!shader) + if (!shader || !shader->passes) return false; type = menu_shader_manager_get_type(shader); @@ -229,33 +232,47 @@ bool menu_shader_manager_save_preset( if (type == RARCH_SHADER_NONE) return false; - if (basename) + if (menu_driver_shader_modified) + save_reference = false; + + if (!string_is_empty(basename)) { - strlcpy(buffer, basename, sizeof(buffer)); + strlcpy(fullname, basename, sizeof(fullname)); /* Append extension automatically as appropriate. */ - if ( !strstr(basename, - file_path_str(FILE_PATH_CGP_EXTENSION)) - && !strstr(basename, - file_path_str(FILE_PATH_GLSLP_EXTENSION)) - && !strstr(basename, - file_path_str(FILE_PATH_SLANGP_EXTENSION))) + if ( !strstr(basename, file_path_str(FILE_PATH_CGP_EXTENSION)) + && !strstr(basename, file_path_str(FILE_PATH_GLSLP_EXTENSION)) + && !strstr(basename, file_path_str(FILE_PATH_SLANGP_EXTENSION))) { const char *preset_ext = video_shader_get_preset_extension(type); - if (!string_is_empty(preset_ext)) - strlcat(buffer, preset_ext, sizeof(buffer)); + strlcat(fullname, preset_ext, sizeof(fullname)); } } else { const char *preset_ext = video_shader_get_preset_extension(type); - strlcpy(buffer, "retroarch", sizeof(buffer)); - strlcat(buffer, preset_ext, sizeof(buffer)); + strlcpy(fullname, "retroarch", sizeof(fullname)); + strlcat(fullname, preset_ext, sizeof(fullname)); } - if (!fullpath) + if (path_is_absolute(fullname)) { + preset_path = fullname; + + ret = video_shader_write_preset(preset_path, shader, save_reference); + + if (ret) + RARCH_LOG("Saved shader preset to %s.\n", preset_path); + else + RARCH_ERR("Failed writing shader preset to %s.\n", preset_path); + } + else + { + const char *dirs[3] = {0}; settings_t *settings = config_get_ptr(); + char config_directory[PATH_MAX_LENGTH]; + + config_directory[0] = '\0'; if (!path_is_empty(RARCH_PATH_CONFIG)) fill_pathname_basedir( @@ -263,63 +280,137 @@ bool menu_shader_manager_save_preset( path_get(RARCH_PATH_CONFIG), sizeof(config_directory)); - dirs[0] = settings->paths.directory_video_shader; - dirs[1] = settings->paths.directory_menu_config; - dirs[2] = config_directory; - } + dirs[0] = settings->paths.directory_video_shader; + dirs[1] = settings->paths.directory_menu_config; + dirs[2] = config_directory; - if (!(conf = config_file_new_alloc())) - return false; - - if (fullpath) - { - if (!string_is_empty(basename)) - strlcpy(preset_path, buffer, sizeof(preset_path)); - - video_shader_write_conf_preset(conf, shader, preset_path); - - if (config_file_write(conf, preset_path, false)) + for (i = 0; i < ARRAY_SIZE(dirs); i++) { - RARCH_LOG("Saved shader preset to %s.\n", preset_path); - if (apply) - menu_shader_manager_set_preset(NULL, type, preset_path, true); - ret = true; - } - else - RARCH_LOG("Failed writing shader preset to %s.\n", preset_path); - } - else - { - for (d = 0; d < ARRAY_SIZE(dirs); d++) - { - if (!*dirs[d]) + if (string_is_empty(dirs[i])) continue; - fill_pathname_join(preset_path, dirs[d], - buffer, sizeof(preset_path)); + fill_pathname_join(buffer, dirs[i], + fullname, sizeof(buffer)); - video_shader_write_conf_preset(conf, shader, preset_path); + preset_path = buffer; - if (config_file_write(conf, preset_path, false)) + ret = video_shader_write_preset(preset_path, shader, save_reference); + + if (ret) { RARCH_LOG("Saved shader preset to %s.\n", preset_path); - if (apply) - menu_shader_manager_set_preset(NULL, type, preset_path, true); - ret = true; break; } else - RARCH_LOG("Failed writing shader preset to %s.\n", preset_path); + RARCH_WARN("Failed writing shader preset to %s.\n", preset_path); } + + if (!ret) + RARCH_ERR("Failed to write shader preset. Make sure shader directory" + " and/or config directory are writable.\n"); } - config_file_free(conf); - if (ret) - return true; + if (ret && apply) + menu_shader_manager_set_preset(NULL, type, preset_path, true); - RARCH_ERR("Failed to save shader preset. Make sure config directory" - " and/or shader dir are writable.\n"); - return false; + return ret; +} + +/** + * menu_shader_manager_save_auto_preset: + * @shader : shader to save + * @type : type of shader preset which determines save path + * @apply : immediately set preset after saving + * + * Save a shader as an auto-shader to it's appropriate path: + * SHADER_PRESET_GLOBAL: /presets/global + * SHADER_PRESET_CORE: /presets// + * SHADER_PRESET_PARENT: /presets// + * SHADER_PRESET_GAME: /presets// + * Needs to be consistent with retroarch_load_shader_preset() + * Auto-shaders will be saved as a reference if possible + **/ +bool menu_shader_manager_save_auto_preset(const struct video_shader *shader, + enum auto_shader_type type, bool apply) +{ + char tmp[PATH_MAX_LENGTH]; + char directory[PATH_MAX_LENGTH]; + char file[PATH_MAX_LENGTH]; + settings_t *settings = config_get_ptr(); + struct retro_system_info *system = runloop_get_libretro_system_info(); + const char *core_name = system ? system->library_name : NULL; + + tmp[0] = directory[0] = file[0] = '\0'; + + if (type == SHADER_PRESET_GLOBAL) + { + fill_pathname_join( + directory, + settings->paths.directory_video_shader, + "presets", + sizeof(directory)); + } + else if (string_is_empty(core_name)) + { + return false; + } + else + { + fill_pathname_join( + tmp, + settings->paths.directory_video_shader, + "presets", + sizeof(tmp)); + fill_pathname_join( + directory, + tmp, + core_name, + sizeof(directory)); + } + + switch (type) + { + case SHADER_PRESET_GLOBAL: + fill_pathname_join(file, directory, "global", sizeof(file)); + break; + case SHADER_PRESET_CORE: + fill_pathname_join(file, directory, core_name, sizeof(file)); + break; + case SHADER_PRESET_PARENT: + { + fill_pathname_parent_dir_name(tmp, path_get(RARCH_PATH_BASENAME), sizeof(tmp)); + fill_pathname_join(file, directory, tmp, sizeof(file)); + break; + } + case SHADER_PRESET_GAME: + { + const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); + fill_pathname_join(file, directory, game_name, sizeof(file)); + break; + } + default: + return false; + } + + if (!path_is_directory(directory)) + path_mkdir(directory); + + return menu_shader_manager_save_preset_internal(shader, file, apply, true); +} + +/** + * menu_shader_manager_save_preset: + * @shader : shader to save + * @type : type of shader preset which determines save path + * @basename : basename of preset + * @apply : immediately set preset after saving + * + * Save a shader preset to disk. + **/ +bool menu_shader_manager_save_preset(const struct video_shader *shader, + const char *basename, bool apply) +{ + return menu_shader_manager_save_preset_internal(shader, basename, apply, false); } int menu_shader_manager_clear_num_passes(struct video_shader *shader) @@ -337,6 +428,8 @@ int menu_shader_manager_clear_num_passes(struct video_shader *shader) video_shader_resolve_parameters(NULL, shader); + menu_driver_shader_modified = true; + return 0; } @@ -353,6 +446,8 @@ int menu_shader_manager_clear_parameter(struct video_shader *shader, param->current = MIN(MAX(param->minimum, param->current), param->maximum); + menu_driver_shader_modified = true; + return 0; } @@ -367,6 +462,8 @@ int menu_shader_manager_clear_pass_filter(struct video_shader *shader, shader_pass->filter = RARCH_FILTER_UNSPEC; + menu_driver_shader_modified = true; + return 0; } @@ -382,6 +479,8 @@ void menu_shader_manager_clear_pass_scale(struct video_shader *shader, shader_pass->fbo.scale_x = 0; shader_pass->fbo.scale_y = 0; shader_pass->fbo.valid = false; + + menu_driver_shader_modified = true; } void menu_shader_manager_clear_pass_path(struct video_shader *shader, @@ -392,6 +491,8 @@ void menu_shader_manager_clear_pass_path(struct video_shader *shader, if (shader_pass) *shader_pass->source.path = '\0'; + + menu_driver_shader_modified = true; } /** @@ -460,7 +561,7 @@ void menu_shader_manager_apply_changes(struct video_shader *shader) if (shader->passes && type != RARCH_SHADER_NONE) { - menu_shader_manager_save_preset(shader, NULL, true, false); + menu_shader_manager_save_preset(shader, NULL, true); return; } diff --git a/menu/menu_shader.h b/menu/menu_shader.h index 6465d42920..b42c0591e6 100644 --- a/menu/menu_shader.h +++ b/menu/menu_shader.h @@ -24,6 +24,14 @@ RETRO_BEGIN_DECLS +enum auto_shader_type +{ + SHADER_PRESET_GLOBAL, + SHADER_PRESET_CORE, + SHADER_PRESET_PARENT, + SHADER_PRESET_GAME +}; + struct video_shader *menu_shader_get(void); void menu_shader_manager_free(void); @@ -48,16 +56,33 @@ bool menu_shader_manager_set_preset( struct video_shader *shader, enum rarch_shader_type type, const char *preset_path, bool apply); +/** + * menu_shader_manager_save_auto_preset: + * @shader : shader to save + * @type : type of shader preset which determines save path + * @apply : immediately set preset after saving + * + * Save a shader as an auto-shader to it's appropriate path: + * SHADER_PRESET_GLOBAL: /presets/global + * SHADER_PRESET_CORE: /presets// + * SHADER_PRESET_PARENT: /presets// + * SHADER_PRESET_GAME: /presets// + * Needs to be consistent with retroarch_load_shader_preset() + * Auto-shaders will be saved as a reference if possible + **/ +bool menu_shader_manager_save_auto_preset(const struct video_shader *shader, + enum auto_shader_type type, bool apply); + /** * menu_shader_manager_save_preset: + * @shader : shader to save * @basename : basename of preset * @apply : immediately set preset after saving * * Save a shader preset to disk. **/ -bool menu_shader_manager_save_preset( - struct video_shader *shader, - const char *basename, bool apply, bool fullpath); +bool menu_shader_manager_save_preset(const struct video_shader *shader, + const char *basename, bool apply); /** * menu_shader_manager_get_type: @@ -91,6 +116,8 @@ void menu_shader_manager_clear_pass_scale(struct video_shader *shader, void menu_shader_manager_clear_pass_path(struct video_shader *shader, unsigned i); +void menu_shader_set_modified(bool modified); + RETRO_END_DECLS #endif diff --git a/retroarch.c b/retroarch.c index e2e1bb7004..b4e75832a5 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2222,11 +2222,14 @@ bool retroarch_apply_shader(enum rarch_shader_type type, const char *preset_path retroarch_set_runtime_shader_preset(preset_path); /* reflect in shader manager */ - menu_shader_manager_set_preset(menu_shader_get(), type, preset_path, false); + if (menu_shader_manager_set_preset(menu_shader_get(), type, preset_path, false)) + if (!string_is_empty(preset_path)) + menu_shader_set_modified(false); /* Display message */ snprintf(msg, sizeof(msg), - "Shader: \"%s\"", preset_file ? preset_file : "(null)"); + preset_file ? "Shader: \"%s\"" : "Shader: %s", + preset_file ? preset_file : "None"); #ifdef HAVE_MENU_WIDGETS if (menu_widgets_inited) menu_widgets_set_message(msg); @@ -2236,7 +2239,7 @@ bool retroarch_apply_shader(enum rarch_shader_type type, const char *preset_path MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); RARCH_LOG("%s \"%s\".\n", msg_hash_to_str(MSG_APPLYING_SHADER), - preset_path); + preset_path ? preset_path : "null"); } else { @@ -2248,7 +2251,7 @@ bool retroarch_apply_shader(enum rarch_shader_type type, const char *preset_path /* Display error message */ snprintf(msg, sizeof(msg), "%s %s", msg_hash_to_str(MSG_FAILED_TO_APPLY_SHADER_PRESET), - preset_file ? preset_file : "(null)"); + preset_file ? preset_file : "null"); runloop_msg_queue_push( msg, 1, 180, true, NULL, diff --git a/ui/drivers/qt/shaderparamsdialog.cpp b/ui/drivers/qt/shaderparamsdialog.cpp index a858500220..a014b83f60 100644 --- a/ui/drivers/qt/shaderparamsdialog.cpp +++ b/ui/drivers/qt/shaderparamsdialog.cpp @@ -51,10 +51,10 @@ extern "C" { enum { - SHADER_PRESET_SAVE_CORE = 0, - SHADER_PRESET_SAVE_GAME, + SHADER_PRESET_SAVE_GLOBAL = 0, + SHADER_PRESET_SAVE_CORE, SHADER_PRESET_SAVE_PARENT, - SHADER_PRESET_SAVE_GLOBAL, + SHADER_PRESET_SAVE_GAME, SHADER_PRESET_SAVE_NORMAL }; @@ -263,6 +263,8 @@ void ShaderParamsDialog::onFilterComboBoxIndexChanged(int) if (video_shader) video_shader->pass[pass].filter = filter; + menu_shader_set_modified(true); + command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL); } } @@ -317,6 +319,8 @@ void ShaderParamsDialog::onScaleComboBoxIndexChanged(int) video_shader->pass[pass].fbo.valid = scale; } + menu_shader_set_modified(true); + command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL); } } @@ -393,6 +397,8 @@ void ShaderParamsDialog::onShaderPassMoveDownClicked() memcpy(&menu_shader->pass[pass + 1], tempPass.pass, sizeof(struct video_shader_pass)); } + menu_shader_set_modified(true); + reload(); } @@ -466,6 +472,8 @@ void ShaderParamsDialog::onShaderPassMoveUpClicked() memcpy(&menu_shader->pass[pass], tempPass.pass, sizeof(struct video_shader_pass)); } + menu_shader_set_modified(true); + reload(); } @@ -550,6 +558,8 @@ void ShaderParamsDialog::onShaderResetPass(int pass) } } + menu_shader_set_modified(true); + reload(); } @@ -594,6 +604,8 @@ void ShaderParamsDialog::onShaderResetParameter(QString parameter) param->current = param->initial; } + menu_shader_set_modified(true); + reload(); } @@ -647,6 +659,8 @@ void ShaderParamsDialog::onShaderAddPassClicked() else return; + menu_shader_set_modified(true); + shader_pass = &menu_shader->pass[menu_shader->passes - 1]; if (!shader_pass) @@ -679,76 +693,35 @@ void ShaderParamsDialog::onShaderSavePresetAsClicked() void ShaderParamsDialog::saveShaderPreset(const char *path, unsigned action_type) { - char directory[PATH_MAX_LENGTH]; - char file[PATH_MAX_LENGTH]; - char tmp[PATH_MAX_LENGTH]; - settings_t *settings = config_get_ptr(); - struct retro_system_info *system = runloop_get_libretro_system_info(); - const char *core_name = system ? system->library_name : NULL; - - directory[0] = file[0] = tmp[0] = '\0'; - - if (action_type != SHADER_PRESET_SAVE_GLOBAL) - { - if (!string_is_empty(core_name)) - { - fill_pathname_join( - tmp, - settings->paths.directory_video_shader, - "presets", - sizeof(tmp)); - fill_pathname_join( - directory, - tmp, - core_name, - sizeof(directory)); - } - - if (!path_is_directory(directory)) - path_mkdir(directory); - } - else - { - fill_pathname_join( - directory, - settings->paths.directory_video_shader, - "presets", - sizeof(directory)); - - if (!path_is_directory(directory)) - path_mkdir(directory); - - fill_pathname_join( - file, - directory, - "global", - sizeof(file)); - } + bool ret; + enum auto_shader_type preset_type; switch (action_type) { + case SHADER_PRESET_SAVE_GLOBAL: + preset_type = SHADER_PRESET_GLOBAL; + break; case SHADER_PRESET_SAVE_CORE: - if (!string_is_empty(core_name)) - fill_pathname_join(file, directory, core_name, sizeof(file)); + preset_type = SHADER_PRESET_PARENT; + break; + case SHADER_PRESET_SAVE_PARENT: + preset_type = SHADER_PRESET_PARENT; break; case SHADER_PRESET_SAVE_GAME: - { - const char *game_name = path_basename(path_get(RARCH_PATH_BASENAME)); - fill_pathname_join(file, directory, game_name, sizeof(file)); - break; - } - case SHADER_PRESET_SAVE_PARENT: - fill_pathname_parent_dir_name(tmp, path_get(RARCH_PATH_BASENAME), sizeof(tmp)); - fill_pathname_join(file, directory, tmp, sizeof(file)); + preset_type = SHADER_PRESET_GAME; break; case SHADER_PRESET_SAVE_NORMAL: - default: - if (!string_is_empty(path)) - strlcpy(file, path, sizeof(file)); break; + default: + return; } - if (menu_shader_manager_save_preset(menu_shader_get(), file, false, true)) + if (action_type == SHADER_PRESET_SAVE_NORMAL) + ret = menu_shader_manager_save_preset(menu_shader_get(), path, true); + else + ret = menu_shader_manager_save_auto_preset(menu_shader_get(), preset_type, true); + + if (ret) runloop_msg_queue_push( msg_hash_to_str(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY), 1, 100, true, NULL, @@ -766,22 +739,22 @@ void ShaderParamsDialog::saveShaderPreset(const char *path, unsigned action_type void ShaderParamsDialog::onShaderSaveGlobalPresetClicked() { - saveShaderPreset(NULL, SHADER_PRESET_SAVE_GLOBAL); + saveShaderPreset(NULL, SHADER_PRESET_GLOBAL); } void ShaderParamsDialog::onShaderSaveCorePresetClicked() { - saveShaderPreset(NULL, SHADER_PRESET_SAVE_CORE); + saveShaderPreset(NULL, SHADER_PRESET_CORE); } void ShaderParamsDialog::onShaderSaveParentPresetClicked() { - saveShaderPreset(NULL, SHADER_PRESET_SAVE_PARENT); + saveShaderPreset(NULL, SHADER_PRESET_PARENT); } void ShaderParamsDialog::onShaderSaveGamePresetClicked() { - saveShaderPreset(NULL, SHADER_PRESET_SAVE_GAME); + saveShaderPreset(NULL, SHADER_PRESET_GAME); } void ShaderParamsDialog::onShaderClearAllPassesClicked() @@ -794,8 +767,9 @@ void ShaderParamsDialog::onShaderClearAllPassesClicked() if (!menu_shader) return; - while (menu_shader->passes > 0) - menu_shader->passes--; + menu_shader->passes = 0; + + menu_shader_set_modified(true); onShaderApplyClicked(); } @@ -834,6 +808,8 @@ void ShaderParamsDialog::onShaderRemovePassClicked() menu_shader->passes--; + menu_shader_set_modified(true); + onShaderApplyClicked(); } @@ -1230,10 +1206,6 @@ void ShaderParamsDialog::addShaderParam(struct video_shader_parameter *param, QF slider->setValue(value); slider->setProperty("param", parameter); - struct video_shader *video_shader = NULL; - - getShaders(NULL, &video_shader); - connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSliderValueChanged(int))); box->addWidget(slider); @@ -1323,6 +1295,8 @@ void ShaderParamsDialog::onShaderParamCheckBoxClicked() if (param) param->current = (checkBox->isChecked() ? param->maximum : param->minimum); } + + menu_shader_set_modified(true); } } @@ -1386,6 +1360,8 @@ void ShaderParamsDialog::onShaderParamSliderValueChanged(int) param->current = newValue; } } + + menu_shader_set_modified(true); } if (spinBoxVariant.isValid()) @@ -1490,6 +1466,8 @@ void ShaderParamsDialog::onShaderParamSpinBoxValueChanged(int value) slider->blockSignals(false); } } + + menu_shader_set_modified(true); } } @@ -1570,5 +1548,7 @@ void ShaderParamsDialog::onShaderParamDoubleSpinBoxValueChanged(double value) slider->blockSignals(false); } } + + menu_shader_set_modified(true); } }