From 2245af23e9d0f70a2c15366f1b3f07809f33f7d0 Mon Sep 17 00:00:00 2001 From: LazyBumHorse Date: Wed, 17 Jul 2019 15:12:42 +0200 Subject: [PATCH] much improved handling of relative shader paths - save texture paths in relative format as well - always write portable relative paths on Windows using '/' instead of '\' - remove an ancient piece of code that could sometimes fail loading relative paths - fix absolute path handling between different drives for Windows - integrate video_shader_resolve_relative() into video_shader_parse_* functions --- gfx/drivers/d3d10.c | 4 +- gfx/drivers/d3d11.c | 4 +- gfx/drivers/d3d12.c | 4 +- gfx/drivers/d3d9.c | 4 +- gfx/drivers/gx2_gfx.c | 4 +- gfx/drivers_shader/shader_gl_cg.c | 3 +- gfx/drivers_shader/shader_gl_core.cpp | 4 +- gfx/drivers_shader/shader_glsl.c | 4 +- gfx/drivers_shader/shader_vulkan.cpp | 4 +- gfx/video_shader_parse.c | 175 ++++++++++++-------------- gfx/video_shader_parse.h | 19 +-- libretro-common/file/file_path.c | 10 +- menu/menu_shader.c | 11 +- 13 files changed, 98 insertions(+), 152 deletions(-) diff --git a/gfx/drivers/d3d10.c b/gfx/drivers/d3d10.c index 597c40f24e..bc28d61c8a 100644 --- a/gfx/drivers/d3d10.c +++ b/gfx/drivers/d3d10.c @@ -365,11 +365,9 @@ static bool d3d10_gfx_set_shader(void* data, enum rarch_shader_type type, const d3d10->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d10->shader_preset)); - if (!video_shader_read_conf_preset(conf, d3d10->shader_preset)) + if (!video_shader_read_conf_preset(conf, d3d10->shader_preset, path)) goto error; - video_shader_resolve_relative(d3d10->shader_preset, path); - source = &d3d10->frame.texture[0]; for (i = 0; i < d3d10->shader_preset->passes; source = &d3d10->pass[i++].rt) { diff --git a/gfx/drivers/d3d11.c b/gfx/drivers/d3d11.c index 7728a4ff1d..3a51aca1c7 100644 --- a/gfx/drivers/d3d11.c +++ b/gfx/drivers/d3d11.c @@ -383,11 +383,9 @@ static bool d3d11_gfx_set_shader(void* data, enum rarch_shader_type type, const d3d11->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d11->shader_preset)); - if (!video_shader_read_conf_preset(conf, d3d11->shader_preset)) + if (!video_shader_read_conf_preset(conf, d3d11->shader_preset, path)) goto error; - video_shader_resolve_relative(d3d11->shader_preset, path); - source = &d3d11->frame.texture[0]; for (i = 0; i < d3d11->shader_preset->passes; source = &d3d11->pass[i++].rt) { diff --git a/gfx/drivers/d3d12.c b/gfx/drivers/d3d12.c index 78364de742..77196a233f 100644 --- a/gfx/drivers/d3d12.c +++ b/gfx/drivers/d3d12.c @@ -364,11 +364,9 @@ static bool d3d12_gfx_set_shader(void* data, enum rarch_shader_type type, const d3d12->shader_preset = (struct video_shader*)calloc(1, sizeof(*d3d12->shader_preset)); - if (!video_shader_read_conf_preset(conf, d3d12->shader_preset)) + if (!video_shader_read_conf_preset(conf, d3d12->shader_preset, path)) goto error; - video_shader_resolve_relative(d3d12->shader_preset, path); - source = &d3d12->frame.texture[0]; for (i = 0; i < d3d12->shader_preset->passes; source = &d3d12->pass[i++].rt) { diff --git a/gfx/drivers/d3d9.c b/gfx/drivers/d3d9.c index 22bccae001..2f079445c4 100644 --- a/gfx/drivers/d3d9.c +++ b/gfx/drivers/d3d9.c @@ -335,7 +335,7 @@ static bool d3d9_init_multipass(d3d9_video_t *d3d, const char *shader_path) memset(&d3d->shader, 0, sizeof(d3d->shader)); - if (!video_shader_read_conf_preset(conf, &d3d->shader)) + if (!video_shader_read_conf_preset(conf, &d3d->shader, shader_path)) { config_file_free(conf); RARCH_ERR("[D3D9]: Failed to parse shader preset.\n"); @@ -344,8 +344,6 @@ static bool d3d9_init_multipass(d3d9_video_t *d3d, const char *shader_path) config_file_free(conf); - if (!string_is_empty(shader_path)) - video_shader_resolve_relative(&d3d->shader, shader_path); RARCH_LOG("[D3D9]: Found %u shaders.\n", d3d->shader.passes); for (i = 0; i < d3d->shader.passes; i++) diff --git a/gfx/drivers/gx2_gfx.c b/gfx/drivers/gx2_gfx.c index 16bee11474..59fd77d2a3 100644 --- a/gfx/drivers/gx2_gfx.c +++ b/gfx/drivers/gx2_gfx.c @@ -1458,15 +1458,13 @@ static bool wiiu_gfx_set_shader(void *data, wiiu->shader_preset = calloc(1, sizeof(*wiiu->shader_preset)); - if (!video_shader_read_conf_preset(conf, wiiu->shader_preset)) + if (!video_shader_read_conf_preset(conf, wiiu->shader_preset, path)) { free(wiiu->shader_preset); wiiu->shader_preset = NULL; return false; } - video_shader_resolve_relative(wiiu->shader_preset, path); - #if 0 video_shader_resolve_parameters(conf, wiiu->shader_preset); #else diff --git a/gfx/drivers_shader/shader_gl_cg.c b/gfx/drivers_shader/shader_gl_cg.c index 0e62c8ed8f..def689692a 100644 --- a/gfx/drivers_shader/shader_gl_cg.c +++ b/gfx/drivers_shader/shader_gl_cg.c @@ -692,14 +692,13 @@ static bool gl_cg_load_preset(void *data, const char *path) return false; } - if (!video_shader_read_conf_preset(conf, cg->shader)) + if (!video_shader_read_conf_preset(conf, cg->shader, path)) { RARCH_ERR("Failed to parse CGP file.\n"); config_file_free(conf); return false; } - video_shader_resolve_relative(cg->shader, path); video_shader_resolve_parameters(conf, cg->shader); config_file_free(conf); diff --git a/gfx/drivers_shader/shader_gl_core.cpp b/gfx/drivers_shader/shader_gl_core.cpp index 71cc6ae3f8..e342441957 100644 --- a/gfx/drivers_shader/shader_gl_core.cpp +++ b/gfx/drivers_shader/shader_gl_core.cpp @@ -2413,11 +2413,9 @@ gl_core_filter_chain_t *gl_core_filter_chain_create_from_preset( if (!conf) return nullptr; - if (!video_shader_read_conf_preset(conf.get(), shader.get())) + if (!video_shader_read_conf_preset(conf.get(), shader.get(), path)) return nullptr; - video_shader_resolve_relative(shader.get(), path); - bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid; unique_ptr chain{ new gl_core_filter_chain(shader->passes + (last_pass_is_fbo ? 1 : 0)) }; diff --git a/gfx/drivers_shader/shader_glsl.c b/gfx/drivers_shader/shader_glsl.c index 94965b5c52..88f636b185 100644 --- a/gfx/drivers_shader/shader_glsl.c +++ b/gfx/drivers_shader/shader_glsl.c @@ -903,7 +903,7 @@ static void *gl_glsl_init(void *data, const char *path) conf = config_file_new_from_path_to_string(path); if (conf) { - ret = video_shader_read_conf_preset(conf, glsl->shader); + ret = video_shader_read_conf_preset(conf, glsl->shader, path); glsl->shader->modern = true; } } @@ -934,8 +934,6 @@ static void *gl_glsl_init(void *data, const char *path) } } - if (!string_is_empty(path)) - video_shader_resolve_relative(glsl->shader, path); video_shader_resolve_parameters(conf, glsl->shader); if (conf) diff --git a/gfx/drivers_shader/shader_vulkan.cpp b/gfx/drivers_shader/shader_vulkan.cpp index 12e042ad2c..553a4aba2b 100644 --- a/gfx/drivers_shader/shader_vulkan.cpp +++ b/gfx/drivers_shader/shader_vulkan.cpp @@ -2890,11 +2890,9 @@ vulkan_filter_chain_t *vulkan_filter_chain_create_from_preset( if (!conf) return nullptr; - if (!video_shader_read_conf_preset(conf.get(), shader.get())) + if (!video_shader_read_conf_preset(conf.get(), shader.get(), path)) return nullptr; - video_shader_resolve_relative(shader.get(), path); - bool last_pass_is_fbo = shader->pass[shader->passes - 1].fbo.valid; auto tmpinfo = *info; tmpinfo.num_passes = shader->passes + (last_pass_is_fbo ? 1 : 0); diff --git a/gfx/video_shader_parse.c b/gfx/video_shader_parse.c index 6b99d8d6b9..b0c032d125 100644 --- a/gfx/video_shader_parse.c +++ b/gfx/video_shader_parse.c @@ -102,13 +102,14 @@ static enum gfx_wrap_type wrap_str_to_mode(const char *wrap_mode) * @conf : Preset file to read from. * @pass : Shader passes handle. * @i : Index of shader pass. + * @ref_path : Base path used to resolve relative paths * * Parses shader pass from preset file. * * Returns: true (1) if successful, otherwise false (0). **/ static bool video_shader_parse_pass(config_file_t *conf, - struct video_shader_pass *pass, unsigned i) + struct video_shader_pass *pass, unsigned i, const char *ref_path) { char shader_name[64]; char filter_name_buf[64]; @@ -125,41 +126,33 @@ static bool video_shader_parse_pass(config_file_t *conf, char scale_type_x[64]; char scale_type_y[64]; char frame_count_mod[64]; - size_t path_size = PATH_MAX_LENGTH * sizeof(char); - char *tmp_str = (char*)malloc(path_size); - char *tmp_path = NULL; + size_t path_size = PATH_MAX_LENGTH; + char *tmp_path = (char*)malloc(path_size); struct gfx_fbo_scale *scale = NULL; bool tmp_bool = false; float fattr = 0.0f; int iattr = 0; - fp_fbo_buf[0] = mipmap_buf[0] = alias_buf[0] = - scale_name_buf[0] = attr_name_buf[0] = scale_type[0] = - scale_type_x[0] = scale_type_y[0] = frame_count_mod[0] = - tmp_str[0] = shader_name[0] = filter_name_buf[0] = - wrap_name_buf[0] = wrap_mode[0] = frame_count_mod_buf[0] = '\0'; - srgb_output_buf[0] = '\0'; + if (!tmp_path) + return false; + + fp_fbo_buf[0] = mipmap_buf[0] = alias_buf[0] = + scale_name_buf[0] = attr_name_buf[0] = scale_type[0] = + scale_type_x[0] = scale_type_y[0] = frame_count_mod[0] = + shader_name[0] = filter_name_buf[0] = wrap_name_buf[0] = + wrap_mode[0] = frame_count_mod_buf[0] = srgb_output_buf[0] = '\0'; /* Source */ snprintf(shader_name, sizeof(shader_name), "shader%u", i); - if (!config_get_path(conf, shader_name, tmp_str, path_size)) + if (!config_get_path(conf, shader_name, tmp_path, path_size)) { RARCH_ERR("Couldn't parse shader source (%s).\n", shader_name); - if (tmp_str) - free(tmp_str); + free(tmp_path); return false; } - tmp_path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); - strlcpy(tmp_path, tmp_str, path_size); - path_resolve_realpath(tmp_path, path_size); - - if (!path_is_valid(tmp_path)) - strlcpy(pass->source.path, tmp_str, sizeof(pass->source.path)); - else - strlcpy(pass->source.path, tmp_path, sizeof(pass->source.path)); - - free(tmp_str); + fill_pathname_resolve_relative(pass->source.path, + ref_path, tmp_path, sizeof(pass->source.path)); free(tmp_path); /* Smooth */ @@ -314,22 +307,27 @@ static bool video_shader_parse_pass(config_file_t *conf, * video_shader_parse_textures: * @conf : Preset file to read from. * @shader : Shader pass handle. + * @ref_path : Base path used to resolve relative paths * * Parses shader textures. * * Returns: true (1) if successful, otherwise false (0). **/ static bool video_shader_parse_textures(config_file_t *conf, - struct video_shader *shader) + struct video_shader *shader, const char *ref_path) { - size_t path_size = PATH_MAX_LENGTH * sizeof(char); + size_t path_size = PATH_MAX_LENGTH; const char *id = NULL; char *save = NULL; - char *textures = (char*)malloc(1024 * sizeof(char)); + char *textures = (char*)malloc(1024 + path_size); + char *tmp_path = textures + 1024; - textures[0] = '\0'; + if (!textures) + return false; - if (!config_get_array(conf, "textures", textures, 1024 * sizeof(char))) + textures[0] = '\0'; + + if (!config_get_array(conf, "textures", textures, 1024)) { free(textures); return true; @@ -345,28 +343,19 @@ static bool video_shader_parse_textures(config_file_t *conf, char id_mipmap[64]; bool mipmap = false; bool smooth = false; - char *tmp_path = NULL; id_filter[0] = id_wrap[0] = wrap_mode[0] = id_mipmap[0] = '\0'; - if (!config_get_array(conf, id, shader->lut[shader->luts].path, - sizeof(shader->lut[shader->luts].path))) + if (!config_get_array(conf, id, tmp_path, path_size)) { RARCH_ERR("Cannot find path to texture \"%s\" ...\n", id); free(textures); return false; } - tmp_path = (char*)malloc(PATH_MAX_LENGTH * sizeof(char)); - tmp_path[0] = '\0'; - strlcpy(tmp_path, shader->lut[shader->luts].path, - path_size); - path_resolve_realpath(tmp_path, path_size); + fill_pathname_resolve_relative(shader->lut[shader->luts].path, + ref_path, tmp_path, sizeof(shader->lut[shader->luts].path)); - if (path_is_valid(tmp_path)) - strlcpy(shader->lut[shader->luts].path, - tmp_path, sizeof(shader->lut[shader->luts].path)); - free(tmp_path); strlcpy(shader->lut[shader->luts].id, id, sizeof(shader->lut[shader->luts].id)); @@ -574,6 +563,7 @@ bool video_shader_resolve_parameters(config_file_t *conf, * video_shader_read_conf_preset: * @conf : Preset file to read from. * @shader : Shader passes handle. + * @ref_path : Base path used to resolve relative paths * * Loads preset file and all associated state (passes, * textures, imports, etc). @@ -581,7 +571,7 @@ bool video_shader_resolve_parameters(config_file_t *conf, * Returns: true (1) if successful, otherwise false (0). **/ bool video_shader_read_conf_preset(config_file_t *conf, - struct video_shader *shader) + struct video_shader *shader, const char* ref_path) { unsigned i; union string_list_elem_attr attr; @@ -628,7 +618,7 @@ bool video_shader_read_conf_preset(config_file_t *conf, for (i = 0; i < shader->passes; i++) { - if (!video_shader_parse_pass(conf, &shader->pass[i], i)) + if (!video_shader_parse_pass(conf, &shader->pass[i], i, ref_path)) { if (file_list) { @@ -658,7 +648,7 @@ bool video_shader_read_conf_preset(config_file_t *conf, command_event(CMD_EVENT_SHADER_PRESET_LOADED, NULL); - if (!video_shader_parse_textures(conf, shader)) + if (!video_shader_parse_textures(conf, shader, ref_path)) return false; return true; @@ -722,14 +712,28 @@ static void shader_write_fbo(config_file_t *conf, fbo->scale_y, fbo->abs_y, i); } +static void make_relative_path_portable(char *path) +{ +#ifdef _WIN32 + /* use '/' instead of '\' for maximum portability */ + if (!path_is_absolute(path)) + { + char *p; + for (p = path; *p; p++) + if (*p == '\\') + *p = '/'; + } +#endif +} + /** * video_shader_write_conf_preset: * @conf : Preset file to write to. * @shader : Shader passes handle. * @preset_path : Optional path to where the preset will be written. * - * Saves preset and all associated state (passes, - * textures, imports, etc) to disk. + * Writes preset and all associated state (passes, + * textures, imports, etc) into @conf. * If @preset_path is not NULL, shader paths are saved * relative to it. **/ @@ -739,35 +743,42 @@ void video_shader_write_conf_preset(config_file_t *conf, unsigned i; char key[64]; size_t tmp_size = PATH_MAX_LENGTH; - char *tmp = (char*)malloc(tmp_size); - char *tmp_rel = (char*)malloc(tmp_size); - char *tmp_base = (char*)malloc(tmp_size); + char *tmp = (char*)malloc(3*tmp_size); + char *tmp_rel = tmp + tmp_size; + char *tmp_base = tmp + 2*tmp_size; - if (!tmp || !tmp_rel || !tmp_base) + if (!tmp) return; config_set_int(conf, "shaders", shader->passes); if (shader->feedback_pass >= 0) config_set_int(conf, "feedback_pass", shader->feedback_pass); + if (preset_path) + { + strlcpy(tmp_base, preset_path, tmp_size); + + /* ensure we use a clean base like the shader passes and texture paths do */ + path_resolve_realpath(tmp_base, tmp_size); + path_basedir(tmp_base); + } + for (i = 0; i < shader->passes; i++) { const struct video_shader_pass *pass = &shader->pass[i]; snprintf(key, sizeof(key), "shader%u", i); - strlcpy(tmp, pass->source.path, tmp_size); if (preset_path) { - strlcpy(tmp_base, preset_path, tmp_size); - - path_basedir(tmp_base); + strlcpy(tmp, pass->source.path, tmp_size); path_relative_to(tmp_rel, tmp, tmp_base, tmp_size); + make_relative_path_portable(tmp_rel); config_set_path(conf, key, tmp_rel); } else - config_set_path(conf, key, tmp); + config_set_path(conf, key, pass->source.path); if (pass->filter != RARCH_FILTER_UNSPEC) @@ -794,9 +805,6 @@ void video_shader_write_conf_preset(config_file_t *conf, shader_write_fbo(conf, &pass->fbo, i); } - free(tmp); - free(tmp_rel); - free(tmp_base); if (shader->num_parameters) { @@ -853,7 +861,16 @@ void video_shader_write_conf_preset(config_file_t *conf, key[0] = '\0'; - config_set_string(conf, shader->lut[i].id, shader->lut[i].path); + if (preset_path) + { + strlcpy(tmp, shader->lut[i].path, tmp_size); + path_relative_to(tmp_rel, tmp, tmp_base, tmp_size); + make_relative_path_portable(tmp_rel); + + config_set_path(conf, shader->lut[i].id, tmp_rel); + } + else + config_set_path(conf, shader->lut[i].id, shader->lut[i].path); if (shader->lut[i].filter != RARCH_FILTER_UNSPEC) { @@ -874,6 +891,8 @@ void video_shader_write_conf_preset(config_file_t *conf, } } } + + free(tmp); } const char *video_shader_to_str(enum rarch_shader_type type) @@ -1010,46 +1029,6 @@ enum rarch_shader_type video_shader_parse_type(const char *path) return video_shader_get_type_from_ext(path_get_extension(path), &is_preset); } -/** - * video_shader_resolve_relative: - * @shader : Shader pass handle. - * @ref_path : Relative shader path. - * - * Resolves relative shader path (@ref_path) into absolute - * shader paths. - **/ -void video_shader_resolve_relative(struct video_shader *shader, - const char *ref_path) -{ - unsigned i; - size_t tmp_path_size = 4096 * sizeof(char); - char *tmp_path = (char*)malloc(tmp_path_size); - - if (!tmp_path) - return; - - tmp_path[0] = '\0'; - - for (i = 0; i < shader->passes; i++) - { - if (!*shader->pass[i].source.path) - continue; - - strlcpy(tmp_path, shader->pass[i].source.path, tmp_path_size); - fill_pathname_resolve_relative(shader->pass[i].source.path, - ref_path, tmp_path, sizeof(shader->pass[i].source.path)); - } - - for (i = 0; i < shader->luts; i++) - { - strlcpy(tmp_path, shader->lut[i].path, tmp_path_size); - fill_pathname_resolve_relative(shader->lut[i].path, - ref_path, tmp_path, sizeof(shader->lut[i].path)); - } - - free(tmp_path); -} - bool video_shader_check_for_changes(void) { if (!file_change_data) diff --git a/gfx/video_shader_parse.h b/gfx/video_shader_parse.h index ad6fa783f1..825d0ad758 100644 --- a/gfx/video_shader_parse.h +++ b/gfx/video_shader_parse.h @@ -163,14 +163,14 @@ struct video_shader * video_shader_read_conf_preset: * @conf : Preset file to read from. * @shader : Shader passes handle. - * + * @ref_path : Base path used to resolve relative paths * Loads preset file and all associated state (passes, * textures, imports, etc). * * Returns: true (1) if successful, otherwise false (0). **/ bool video_shader_read_conf_preset(config_file_t *conf, - struct video_shader *shader); + struct video_shader *shader, const char* ref_path); /** * video_shader_write_conf_preset: @@ -178,25 +178,14 @@ bool video_shader_read_conf_preset(config_file_t *conf, * @shader : Shader passes handle. * @preset_path : Optional path to where the preset will be written. * - * Saves preset and all associated state (passes, - * textures, imports, etc) to disk. + * Writes preset and all associated state (passes, + * textures, imports, etc) into @conf. * If @preset_path is not NULL, shader paths are saved * relative to it. **/ void video_shader_write_conf_preset(config_file_t *conf, struct video_shader *shader, const char *preset_path); -/** - * video_shader_resolve_relative: - * @shader : Shader pass handle. - * @ref_path : Relative shader path. - * - * Resolves relative shader path (@ref_path) into absolute - * shader paths. - **/ -void video_shader_resolve_relative(struct video_shader *shader, - const char *ref_path); - /** * video_shader_resolve_parameters: * @conf : Preset file to read from. diff --git a/libretro-common/file/file_path.c b/libretro-common/file/file_path.c index d478f2842e..09cea5708e 100644 --- a/libretro-common/file/file_path.c +++ b/libretro-common/file/file_path.c @@ -804,7 +804,7 @@ void path_resolve_realpath(char *buf, size_t size) void path_relative_to(char *out, const char *path, const char *base, size_t size) { - unsigned i; + size_t i; const char *trimmed_path, *trimmed_base; #ifdef _WIN32 @@ -813,8 +813,8 @@ void path_relative_to(char *out, && path[1] == ':' && base[1] == ':' && path[0] != base[0]) { - out[0] = '\0'; - strlcat(out, path, size); + strlcpy(out, path, size); + return; } #endif @@ -827,8 +827,8 @@ void path_relative_to(char *out, /* Each segment of base turns into ".." */ out[0] = '\0'; for (i = 0; trimmed_base[i]; i++) - if (trimmed_base[i] == '/' || trimmed_base[i] == '\\') - strlcat(out, "../", size); /* Use '/' as universal separator */ + if (trimmed_base[i] == path_default_slash_c()) + strlcat(out, ".." path_default_slash(), size); strlcat(out, trimmed_path, size); } diff --git a/menu/menu_shader.c b/menu/menu_shader.c index 60877c32eb..ceb93b23a7 100644 --- a/menu/menu_shader.c +++ b/menu/menu_shader.c @@ -138,12 +138,9 @@ bool menu_shader_manager_init(void) if ( !string_is_empty(new_path) && conf && - video_shader_read_conf_preset(conf, menu_driver_shader) + video_shader_read_conf_preset(conf, menu_driver_shader, new_path) ) - { - video_shader_resolve_relative(menu_driver_shader, new_path); video_shader_resolve_parameters(conf, menu_driver_shader); - } if (new_path) free(new_path); @@ -194,11 +191,9 @@ bool menu_shader_manager_set_preset(void *data, RARCH_LOG("Setting Menu shader: %s.\n", preset_path); - if (video_shader_read_conf_preset(conf, shader)) - { - video_shader_resolve_relative(shader, preset_path); + if (video_shader_read_conf_preset(conf, shader, preset_path)) video_shader_resolve_parameters(conf, shader); - } + config_file_free(conf); #ifdef HAVE_MENU