From e5e8db73df86ee04260c5f2bd2c61dfa8eb7943f Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 29 Nov 2021 14:20:58 +0100 Subject: [PATCH] Refactor BKE_bpath module. The main goal of this refactor is to make BPath module use `IDTypeInfo`, and move each ID-specific part of the `foreach_path` looper into their own IDTypeInfo struct, using a new `foreach_path` callback. Additionally, following improvements/cleanups are included: * Attempt to get better, more consistent namings. ** In particular, move from `path_visitor` to more standard `foreach_path`. * Update and extend documentation. ** API doc was moved to header, according to recent discussions on this topic. * Remove `BKE_bpath_relocate_visitor` from API, this is specific callback that belongs in `lib_id.c` user code. NOTE: This commit is expected to be 100% non-behavioral-change. This implies that several potential further changes were only noted as comments (like using a more generic solution for `lib_id_library_local_paths`, addressing inconsistencies like path of packed libraries always being skipped, regardless of the `BKE_BPATH_FOREACH_PATH_SKIP_PACKED` `eBPathForeachFlag` flag value, etc.). NOTE: basic unittests were added to master already in rBdcc500e5a265093bc9cc. Reviewed By: brecht Differential Revision: https://developer.blender.org/D13381 --- source/blender/blenkernel/BKE_bpath.h | 197 +++- source/blender/blenkernel/BKE_idtype.h | 8 + source/blender/blenkernel/intern/action.c | 1 + source/blender/blenkernel/intern/armature.c | 1 + source/blender/blenkernel/intern/blendfile.c | 18 +- source/blender/blenkernel/intern/bpath.c | 1047 +++++++---------- .../blender/blenkernel/intern/bpath_test.cc | 4 +- source/blender/blenkernel/intern/brush.c | 10 + source/blender/blenkernel/intern/cachefile.c | 8 + source/blender/blenkernel/intern/camera.c | 1 + source/blender/blenkernel/intern/collection.c | 1 + source/blender/blenkernel/intern/curve.c | 1 + source/blender/blenkernel/intern/gpencil.c | 1 + source/blender/blenkernel/intern/hair.c | 1 + source/blender/blenkernel/intern/image.c | 30 + source/blender/blenkernel/intern/ipo.c | 1 + source/blender/blenkernel/intern/key.c | 1 + source/blender/blenkernel/intern/lattice.c | 1 + source/blender/blenkernel/intern/lib_id.c | 50 +- source/blender/blenkernel/intern/library.c | 18 + source/blender/blenkernel/intern/light.c | 1 + source/blender/blenkernel/intern/lightprobe.c | 1 + source/blender/blenkernel/intern/linestyle.c | 1 + source/blender/blenkernel/intern/mask.c | 1 + source/blender/blenkernel/intern/material.c | 1 + source/blender/blenkernel/intern/mball.c | 1 + source/blender/blenkernel/intern/mesh.cc | 10 + source/blender/blenkernel/intern/movieclip.c | 8 + source/blender/blenkernel/intern/node.cc | 25 + source/blender/blenkernel/intern/object.cc | 62 + source/blender/blenkernel/intern/paint.c | 2 + source/blender/blenkernel/intern/particle.c | 1 + .../blender/blenkernel/intern/pointcloud.cc | 1 + source/blender/blenkernel/intern/scene.c | 41 + source/blender/blenkernel/intern/screen.c | 1 + .../blender/blenkernel/intern/simulation.cc | 1 + source/blender/blenkernel/intern/sound.c | 13 + source/blender/blenkernel/intern/speaker.c | 1 + source/blender/blenkernel/intern/text.c | 11 + source/blender/blenkernel/intern/texture.c | 1 + source/blender/blenkernel/intern/vfont.c | 17 + source/blender/blenkernel/intern/volume.cc | 13 + source/blender/blenkernel/intern/workspace.c | 1 + source/blender/blenkernel/intern/world.c | 1 + source/blender/blenloader/intern/writefile.c | 3 +- .../blender/editors/asset/intern/asset_ops.cc | 24 +- source/blender/makesdna/DNA_scene_types.h | 1 + source/blender/python/intern/bpy.c | 22 +- source/blender/windowmanager/intern/wm.c | 1 + 49 files changed, 946 insertions(+), 721 deletions(-) diff --git a/source/blender/blenkernel/BKE_bpath.h b/source/blender/blenkernel/BKE_bpath.h index 3ec5409ca7f..984c7f609f6 100644 --- a/source/blender/blenkernel/BKE_bpath.h +++ b/source/blender/blenkernel/BKE_bpath.h @@ -16,10 +16,14 @@ /** \file * \ingroup bke - * \attention Based on ghash, difference is ghash is not a fixed size, - * so for BPath we don't need to malloc + * + * \warning All paths manipulated by this API are assumed to be either constant char buffers of + * `FILE_MAX` size, or allocated char buffers not bigger than `FILE_MAX`. */ +/* TODO: Make this module handle a bit more safely string length, instead of assuming buffers are + * FILE_MAX len etc. */ + #pragma once #ifdef __cplusplus @@ -31,66 +35,175 @@ struct ListBase; struct Main; struct ReportList; -/* Function that does something with an ID's file path. Should return 1 if the - * path has changed, and in that case, should write the result to pathOut. */ -typedef bool (*BPathVisitor)(void *userdata, char *path_dst, const char *path_src); -/* Executes 'visit' for each path associated with 'id'. */ -void BKE_bpath_traverse_id(struct Main *bmain, - struct ID *id, - BPathVisitor visit_cb, - const int flag, - void *bpath_user_data); -void BKE_bpath_traverse_id_list(struct Main *bmain, - struct ListBase *lb, - BPathVisitor visit_cb, - const int flag, - void *bpath_user_data); -void BKE_bpath_traverse_main(struct Main *bmain, - BPathVisitor visit_cb, - const int flag, - void *bpath_user_data); -bool BKE_bpath_relocate_visitor(void *oldbasepath, char *path_dst, const char *path_src); +/** \name Core `foreach_path` API. + * \{ */ -/* Functions for temp backup/restore of paths, path count must NOT change */ -void *BKE_bpath_list_backup(struct Main *bmain, const int flag); -void BKE_bpath_list_restore(struct Main *bmain, const int flag, void *ls_handle); -void BKE_bpath_list_free(void *ls_handle); +typedef enum eBPathForeachFlag { + /** Flags controlling the behavior of the generic BPath API. */ -enum { - /* convert paths to absolute */ - BKE_BPATH_TRAVERSE_ABS = (1 << 0), - /* skip library paths */ - BKE_BPATH_TRAVERSE_SKIP_LIBRARY = (1 << 1), - /* skip packed data */ - BKE_BPATH_TRAVERSE_SKIP_PACKED = (1 << 2), - /* skip paths where a single dir is used with an array of files, eg. - * sequence strip images and pointcache. in this case only use the first - * file, this is needed for directory manipulation functions which might - * otherwise modify the same directory multiple times */ - BKE_BPATH_TRAVERSE_SKIP_MULTIFILE = (1 << 3), - /* reload data (when the path is edited) */ - BKE_BPATH_TRAVERSE_RELOAD_EDITED = (1 << 4), -}; + /** Ensures the `absolute_base_path` member of #BPathForeachPathData is initialized properly with + * the path of the current .blend file. This can be used by the callbacks to convert relative + * paths to absolute ones. */ + BKE_BPATH_FOREACH_PATH_ABSOLUTE = (1 << 0), + /** Skip paths of linked IDs. */ + BKE_BPATH_FOREACH_PATH_SKIP_LINKED = (1 << 1), + /** Skip paths when their matching data is packed. */ + BKE_BPATH_FOREACH_PATH_SKIP_PACKED = (1 << 2), -/* high level funcs */ + /** Flags not affecting the generic BPath API. Those may be used by specific IDTypeInfo + * `foreach_path` implementations and/or callbacks to implement specific behaviors. */ -/* creates a text file with missing files if there are any */ + /** Skip paths where a single dir is used with an array of files, eg. sequence strip images or + * pointcaches. In this case only use the first file path is processed. + * + * This is needed for directory manipulation callbacks which might otherwise modify the same + * directory multiple times. */ + BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE = (1 << 8), + /** Reload data (when the path is edited). + * \note Ony used by Image IDType currently. */ + BKE_BPATH_FOREACH_PATH_RELOAD_EDITED = (1 << 9), +} eBPathForeachFlag; + +struct BPathForeachPathData; + +/** Callback used to iterate over an ID's file paths. + * + * \note `path`s parameters should be considered as having a maximal `FILE_MAX` string length. + * + * \return `true` if the path has been changed, and in that case, result should be written into + * `r_path_dst`. */ +typedef bool (*BPathForeachPathFunctionCallback)(struct BPathForeachPathData *bpath_data, + char *r_path_dst, + const char *path_src); + +/** Storage for common data needed accross the BPath 'foreach_path' code. */ +typedef struct BPathForeachPathData { + struct Main *bmain; + + BPathForeachPathFunctionCallback callback_function; + eBPathForeachFlag flag; + + void *user_data; + + /* 'Private' data, caller don't need to set those. */ + + /** The root to use as base for relative paths. Only set if `BKE_BPATH_FOREACH_PATH_ABSOLUTE` + * flag is set, NULL otherwise. */ + const char *absolute_base_path; +} BPathForeachPathData; + +/** Run `bpath_data.callback_function` on all paths contained in `id`. */ +void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, struct ID *id); + +/** Run `bpath_data.callback_function` on all paths of all IDs in `bmain`. */ +void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data); + +/** \} */ + +/** \name Helpers to handle common cases from `IDTypeInfo`'s `foreach_path` functions. + * \{ */ + +/* TODO: Investigate using macros around those calls to check a bit better about actual + * strings/buffers length (e,g, with static asserts). */ + +/** + * Run the callback on a path, replacing the content of the string as needed. + * + * \param path: A fixed, FILE_MAX-sized char buffer. + * + * \return true is \a path was modified, false otherwise. + */ +bool BKE_bpath_foreach_path_fixed_process(struct BPathForeachPathData *bpath_data, char *path); + +/** + * Run the callback on a (directory + file) path, replacing the content of the two strings as + * needed. + * + * \param path_dir: A fixed, FILE_MAXDIR-sized char buffer. + * \param path_file: A fixed, FILE_MAXFILE-sized char buffer. + * + * \return true is \a path_dir and/or \a path_file were modified, false otherwise. + */ +bool BKE_bpath_foreach_path_dirfile_fixed_process(struct BPathForeachPathData *bpath_data, + char *path_dir, + char *path_file); + +/** + * Run the callback on a path, replacing the content of the string as needed. + * + * \param path: A pointer to a MEM-allocated string. If modified, it will be freed and replaced by + * a new allocated string. + * \note path is expected to be FILE_MAX size or smaller. + * + * \return true is \a path was modified and re-allocated, false otherwise. + */ +bool BKE_bpath_foreach_path_allocated_process(struct BPathForeachPathData *bpath_data, + char **path); + +/** \} */ + +/** \name High level features. + * \{ */ + +/** Check for missing files. */ void BKE_bpath_missing_files_check(struct Main *bmain, struct ReportList *reports); + +/** Recursively search into given search directory, for all file paths of all IDs in given \a + * bmain, and replace existing paths as needed. + * + * \note The search will happen into the whole search directory tree recursively (with a limit of + * MAX_DIR_RECURSE), if several files are found matching a searched filename, the biggest one will + * be used. This is so that things like thumbnails don't get selected instead of the actual image + * e.g. + * + * \param searchpath: The root directory in which the new filepaths should be searched for. + * \param find_all: If `true`, also search for files which current path is still valid, if `false` + * skip those still valid paths. + * */ void BKE_bpath_missing_files_find(struct Main *bmain, const char *searchpath, struct ReportList *reports, const bool find_all); + +/** Rebase all relative file paths in given \a bmain from \a basedir_src to \a basedir_dst. */ void BKE_bpath_relative_rebase(struct Main *bmain, const char *basedir_src, const char *basedir_dst, struct ReportList *reports); + +/** Make all absolute file paths in given \a bmain relative to given \a basedir. */ void BKE_bpath_relative_convert(struct Main *bmain, const char *basedir, struct ReportList *reports); + +/** Make all relative file paths in given \a bmain absolute, using given \a basedir as root. */ void BKE_bpath_absolute_convert(struct Main *bmain, const char *basedir, struct ReportList *reports); +/** Temp backup of paths from all IDs in given \a bmain. + * + * \return An opaque handle to pass to #BKE_bpath_list_restore and #BKE_bpath_list_free. + */ +void *BKE_bpath_list_backup(struct Main *bmain, const eBPathForeachFlag flag); + +/** Restore the temp backup of paths from \a path_list_handle into all IDs in given \a bmain. + * + * \note This function assumes that the data in given Main did not change (no + * addition/deletion/re-ordering of IDs, or their file paths) since the call to + * #BKE_bpath_list_backup that generated the given \a path_list_handle. */ +void BKE_bpath_list_restore(struct Main *bmain, + const eBPathForeachFlag flag, + void *path_list_handle); + +/** Free the temp backup of paths in \a path_list_handle. + * + * \note This function assumes that the path list has already been restored with a call to + * #BKE_bpath_list_restore, and is therefore empty. */ +void BKE_bpath_list_free(void *path_list_handle); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 05441f06eb8..7a7f32fe838 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -35,6 +35,7 @@ struct BlendDataReader; struct BlendExpander; struct BlendLibReader; struct BlendWriter; +struct BPathForeachPathData; struct ID; struct LibraryForeachIDData; struct Main; @@ -100,6 +101,8 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, IDTypeForeachCacheFunctionCallback function_callback, void *user_data); +typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data); + typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, struct ID *id); typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, @@ -189,6 +192,11 @@ typedef struct IDTypeInfo { */ IDTypeForeachCacheFunction foreach_cache; + /** + * Iterator over all file paths of given ID. + */ + IDTypeForeachPathFunction foreach_path; + /** * For embedded IDs, return their owner ID. */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 408a0b3065e..f19727a8cdc 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -328,6 +328,7 @@ IDTypeInfo IDType_ID_AC = { .make_local = NULL, .foreach_id = action_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = action_blend_write, diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 7bb6d9c1452..96524ce78d7 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -330,6 +330,7 @@ IDTypeInfo IDType_ID_AR = { .make_local = NULL, .foreach_id = armature_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = armature_blend_write, diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index fc535fc2ad1..11b54a27185 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -78,7 +78,9 @@ /** \name High Level `.blend` file read/write. * \{ */ -static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const char *path_src) +static bool foreach_path_clean_cb(BPathForeachPathData *UNUSED(bpath_data), + char *path_dst, + const char *path_src) { strcpy(path_dst, path_src); BLI_path_slash_native(path_dst); @@ -86,13 +88,14 @@ static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const c } /* make sure path names are correct for OS */ -static void clean_paths(Main *main) +static void clean_paths(Main *bmain) { - Scene *scene; + BKE_bpath_foreach_path_main(&(BPathForeachPathData){.bmain = bmain, + .callback_function = foreach_path_clean_cb, + .flag = BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE, + .user_data = NULL}); - BKE_bpath_traverse_main(main, clean_paths_visit_cb, BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, NULL); - - for (scene = main->scenes.first; scene; scene = scene->id.next) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { BLI_path_slash_native(scene->r.pic); } } @@ -887,7 +890,8 @@ bool BKE_blendfile_write_partial(Main *bmain_src, int a, retval; void *path_list_backup = NULL; - const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); + const eBPathForeachFlag path_list_flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | + BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE); /* This is needed to be able to load that file as a real one later * (otherwise main->name will not be set at read time). */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 9ce58d8129b..cea21d2a946 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -66,6 +66,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BKE_idtype.h" #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_library.h" @@ -86,15 +87,141 @@ static CLG_LogRef LOG = {"bke.bpath"}; +/* -------------------------------------------------------------------- */ +/** \name Generic File Path Traversal API + * \{ */ + +void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id) +{ + const eBPathForeachFlag flag = bpath_data->flag; + const char *absbase = (flag & BKE_BPATH_FOREACH_PATH_ABSOLUTE) ? + ID_BLEND_PATH(bpath_data->bmain, id) : + NULL; + bpath_data->absolute_base_path = absbase; + + if ((flag & BKE_BPATH_FOREACH_PATH_SKIP_LINKED) && ID_IS_LINKED(id)) { + return; + } + + if (id->library_weak_reference != NULL) { + BKE_bpath_foreach_path_fixed_process(bpath_data, id->library_weak_reference->library_filepath); + } + + bNodeTree *embedded_node_tree = ntreeFromID(id); + if (embedded_node_tree != NULL) { + BKE_bpath_foreach_path_id(bpath_data, &embedded_node_tree->id); + } + + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + + BLI_assert(id_type != NULL); + if (id_type == NULL || id_type->foreach_path == NULL) { + return; + } + + id_type->foreach_path(id, bpath_data); +} + +void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data) +{ + ID *id; + FOREACH_MAIN_ID_BEGIN (bpath_data->bmain, id) { + BKE_bpath_foreach_path_id(bpath_data, id); + } + FOREACH_MAIN_ID_END; +} + +bool BKE_bpath_foreach_path_fixed_process(BPathForeachPathData *bpath_data, char *path) +{ + const char *absolute_base_path = bpath_data->absolute_base_path; + + char path_src_buf[FILE_MAX]; + const char *path_src; + char path_dst[FILE_MAX]; + + if (absolute_base_path) { + BLI_strncpy(path_src_buf, path, sizeof(path_src_buf)); + BLI_path_abs(path_src_buf, absolute_base_path); + path_src = path_src_buf; + } + else { + path_src = path; + } + + /* so functions can check old value */ + BLI_strncpy(path_dst, path, FILE_MAX); + + if (bpath_data->callback_function(bpath_data, path_dst, path_src)) { + BLI_strncpy(path, path_dst, FILE_MAX); + return true; + } + + return false; +} + +bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_data, + char *path_dir, + char *path_file) +{ + const char *absolute_base_path = bpath_data->absolute_base_path; + + char path_src[FILE_MAX]; + char path_dst[FILE_MAX]; + + BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file); + + /* So that functions can access the old value. */ + BLI_strncpy(path_dst, path_src, FILE_MAX); + + if (absolute_base_path) { + BLI_path_abs(path_src, absolute_base_path); + } + + if (bpath_data->callback_function(bpath_data, path_dst, (const char *)path_src)) { + BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE); + return true; + } + + return false; +} + +bool BKE_bpath_foreach_path_allocated_process(BPathForeachPathData *bpath_data, char **path) +{ + const char *absolute_base_path = bpath_data->absolute_base_path; + + char path_src_buf[FILE_MAX]; + const char *path_src; + char path_dst[FILE_MAX]; + + if (absolute_base_path) { + BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf)); + BLI_path_abs(path_src_buf, absolute_base_path); + path_src = path_src_buf; + } + else { + path_src = *path; + } + + if (bpath_data->callback_function(bpath_data, path_dst, path_src)) { + MEM_freeN(*path); + (*path) = BLI_strdup(path_dst); + return true; + } + + return false; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Check Missing Files * \{ */ -static bool checkMissingFiles_visit_cb(void *userdata, - char *UNUSED(path_dst), - const char *path_src) +static bool check_missing_files_foreach_path_cb(BPathForeachPathData *bpath_data, + char *UNUSED(path_dst), + const char *path_src) { - ReportList *reports = (ReportList *)userdata; + ReportList *reports = (ReportList *)bpath_data->user_data; if (!BLI_exists(path_src)) { BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src); @@ -103,17 +230,178 @@ static bool checkMissingFiles_visit_cb(void *userdata, return false; } -/* high level function */ void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports) { - BKE_bpath_traverse_main(bmain, - checkMissingFiles_visit_cb, - BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_SKIP_PACKED, - reports); + BKE_bpath_foreach_path_main(&(BPathForeachPathData){ + .bmain = bmain, + .callback_function = check_missing_files_foreach_path_cb, + .flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_SKIP_PACKED, + .user_data = reports}); } /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Find Missing Files + * \{ */ + +#define MAX_DIR_RECURSE 16 +#define FILESIZE_INVALID_DIRECTORY -1 + +/** Find the given filename recursively in the given search directory and its sub-directories. + * + * \note Use the biggest matching file found, so that thumbnails don't get used by mistake. + * + * \param search_directory: Directory to search in. + * \param filename_src: Search for this filename. + * \param r_filename_new: The path of the new found file will be copied here, caller must + * initialize as empty string. + * \param r_filesize: Size of the file, `FILESIZE_INVALID_DIRECTORY` if search directory could not + * be opened. + * \param r_recurse_depth: Current recursion depth. + * + * \return true if found, false otherwise. + */ +static bool missing_files_find__recursive(const char *search_directory, + const char *filename_src, + char r_filename_new[FILE_MAX], + int64_t *r_filesize, + int *r_recurse_depth) +{ + /* TODO: Move this function to BLI_path_utils? The 'biggest size' behavior is quite specific + * though... */ + DIR *dir; + BLI_stat_t status; + char path[FILE_MAX]; + int64_t size; + bool found = false; + + dir = opendir(search_directory); + + if (dir == NULL) { + return found; + } + + if (*r_filesize == FILESIZE_INVALID_DIRECTORY) { + *r_filesize = 0; /* The directory opened fine. */ + } + + for (struct dirent *de = readdir(dir); de != NULL; de = readdir(dir)) { + if (FILENAME_IS_CURRPAR(de->d_name)) { + continue; + } + + BLI_join_dirfile(path, sizeof(path), search_directory, de->d_name); + + if (BLI_stat(path, &status) == -1) { + CLOG_WARN(&LOG, "Cannot get file status (`stat()`) of '%s'", path); + continue; + } + + if (S_ISREG(status.st_mode)) { /* It is a file. */ + if (BLI_path_ncmp(filename_src, de->d_name, FILE_MAX) == 0) { /* Names match. */ + size = status.st_size; + if ((size > 0) && (size > *r_filesize)) { /* Find the biggest matching file. */ + *r_filesize = size; + BLI_strncpy(r_filename_new, path, FILE_MAX); + found = true; + } + } + } + else if (S_ISDIR(status.st_mode)) { /* It is a sub-directory. */ + if (*r_recurse_depth <= MAX_DIR_RECURSE) { + (*r_recurse_depth)++; + found |= missing_files_find__recursive( + path, filename_src, r_filename_new, r_filesize, r_recurse_depth); + (*r_recurse_depth)--; + } + } + } + + closedir(dir); + return found; +} + +typedef struct BPathFind_Data { + const char *basedir; + const char *searchdir; + ReportList *reports; + bool find_all; /* Also search for files which current path is still valid. */ +} BPathFind_Data; + +static bool missing_files_find_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) +{ + BPathFind_Data *data = (BPathFind_Data *)bpath_data->user_data; + char filename_new[FILE_MAX]; + + int64_t filesize = FILESIZE_INVALID_DIRECTORY; + int recurse_depth = 0; + bool is_found; + + if (!data->find_all && BLI_exists(path_src)) { + return false; + } + + filename_new[0] = '\0'; + + is_found = missing_files_find__recursive( + data->searchdir, BLI_path_basename(path_src), filename_new, &filesize, &recurse_depth); + + if (filesize == FILESIZE_INVALID_DIRECTORY) { + BKE_reportf(data->reports, + RPT_WARNING, + "Could not open the directory '%s'", + BLI_path_basename(data->searchdir)); + return false; + } + if (is_found == false) { + BKE_reportf(data->reports, + RPT_WARNING, + "Could not find '%s' in '%s'", + BLI_path_basename(path_src), + data->searchdir); + return false; + } + + bool was_relative = BLI_path_is_rel(path_dst); + + BLI_strncpy(path_dst, filename_new, FILE_MAX); + + /* Keep the path relative if the previous one was relative. */ + if (was_relative) { + BLI_path_rel(path_dst, data->basedir); + } + + return true; +} + +void BKE_bpath_missing_files_find(Main *bmain, + const char *searchpath, + ReportList *reports, + const bool find_all) +{ + struct BPathFind_Data data = {NULL}; + const int flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_RELOAD_EDITED; + + data.basedir = BKE_main_blendfile_path(bmain); + data.reports = reports; + data.searchdir = searchpath; + data.find_all = find_all; + + BKE_bpath_foreach_path_main( + &(BPathForeachPathData){.bmain = bmain, + .callback_function = missing_files_find_foreach_path_cb, + .flag = flag, + .user_data = &data}); +} + +#undef MAX_DIR_RECURSE +#undef FILESIZE_INVALID_DIRECTORY + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Rebase Relative Paths * \{ */ @@ -128,35 +416,35 @@ typedef struct BPathRebase_Data { int count_failed; } BPathRebase_Data; -static bool bpath_relative_rebase_visit_cb(void *userdata, char *path_dst, const char *path_src) +static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - BPathRebase_Data *data = (BPathRebase_Data *)userdata; + BPathRebase_Data *data = (BPathRebase_Data *)bpath_data->user_data; data->count_tot++; - if (BLI_path_is_rel(path_src)) { - char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE]; - BLI_strncpy(filepath, path_src, FILE_MAX); - if (BLI_path_abs(filepath, data->basedir_src)) { - BLI_path_normalize(NULL, filepath); + if (!BLI_path_is_rel(path_src)) { + /* Absolute, leave this as-is. */ + return false; + } - /* This may fail, if so it's fine to leave absolute since the path is still valid. */ - BLI_path_rel(filepath, data->basedir_dst); - - BLI_strncpy(path_dst, filepath, FILE_MAX); - data->count_changed++; - return true; - } - - /* Failed to make relative path absolute. */ - BLI_assert(0); + char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE]; + BLI_strncpy(filepath, path_src, FILE_MAX); + if (!BLI_path_abs(filepath, data->basedir_src)) { BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src); data->count_failed++; return false; } - /* Absolute, leave this as-is. */ - return false; + BLI_path_normalize(NULL, filepath); + + /* This may fail, if so it's fine to leave absolute since the path is still valid. */ + BLI_path_rel(filepath, data->basedir_dst); + + BLI_strncpy(path_dst, filepath, FILE_MAX); + data->count_changed++; + return true; } void BKE_bpath_relative_rebase(Main *bmain, @@ -165,7 +453,7 @@ void BKE_bpath_relative_rebase(Main *bmain, ReportList *reports) { BPathRebase_Data data = {NULL}; - const int flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); + const int flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE); BLI_assert(basedir_src[0] != '\0'); BLI_assert(basedir_dst[0] != '\0'); @@ -174,7 +462,11 @@ void BKE_bpath_relative_rebase(Main *bmain, data.basedir_dst = basedir_dst; data.reports = reports; - BKE_bpath_traverse_main(bmain, bpath_relative_rebase_visit_cb, flag, (void *)&data); + BKE_bpath_foreach_path_main( + &(BPathForeachPathData){.bmain = bmain, + .callback_function = relative_rebase_foreach_path_cb, + .flag = flag, + .user_data = &data}); BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO, @@ -187,7 +479,7 @@ void BKE_bpath_relative_rebase(Main *bmain, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Make Paths Relative +/** \name Make Paths Relative Or Absolute * \{ */ typedef struct BPathRemap_Data { @@ -199,17 +491,19 @@ typedef struct BPathRemap_Data { int count_failed; } BPathRemap_Data; -static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, const char *path_src) +static bool relative_convert_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - BPathRemap_Data *data = (BPathRemap_Data *)userdata; + BPathRemap_Data *data = (BPathRemap_Data *)bpath_data->user_data; data->count_tot++; if (BLI_path_is_rel(path_src)) { - return false; /* already relative */ + return false; /* Already relative. */ } - strcpy(path_dst, path_src); + BLI_strncpy(path_dst, path_src, FILE_MAX); BLI_path_rel(path_dst, data->basedir); if (BLI_path_is_rel(path_dst)) { data->count_changed++; @@ -221,46 +515,19 @@ static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, cons return true; } -void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports) +static bool absolute_convert_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - BPathRemap_Data data = {NULL}; - const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY; - - if (basedir[0] == '\0') { - CLOG_ERROR(&LOG, "basedir='', this is a bug"); - return; - } - - data.basedir = basedir; - data.reports = reports; - - BKE_bpath_traverse_main(bmain, bpath_relative_convert_visit_cb, flag, (void *)&data); - - BKE_reportf(reports, - data.count_failed ? RPT_WARNING : RPT_INFO, - "Total files %d | Changed %d | Failed %d", - data.count_tot, - data.count_changed, - data.count_failed); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Make Paths Absolute - * \{ */ - -static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, const char *path_src) -{ - BPathRemap_Data *data = (BPathRemap_Data *)userdata; + BPathRemap_Data *data = (BPathRemap_Data *)bpath_data->user_data; data->count_tot++; - if (BLI_path_is_rel(path_src) == false) { - return false; /* already absolute */ + if (!BLI_path_is_rel(path_src)) { + return false; /* Already absolute. */ } - strcpy(path_dst, path_src); + BLI_strncpy(path_dst, path_src, FILENAME_MAX); BLI_path_abs(path_dst, data->basedir); if (BLI_path_is_rel(path_dst) == false) { data->count_changed++; @@ -272,12 +539,15 @@ static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, cons return true; } -/* similar to BKE_bpath_relative_convert - keep in sync! */ -void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports) +static void bpath_absolute_relative_convert(Main *bmain, + const char *basedir, + ReportList *reports, + BPathForeachPathFunctionCallback callback_function) { BPathRemap_Data data = {NULL}; - const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY; + const int flag = BKE_BPATH_FOREACH_PATH_SKIP_LINKED; + BLI_assert(basedir[0] != '\0'); if (basedir[0] == '\0') { CLOG_ERROR(&LOG, "basedir='', this is a bug"); return; @@ -286,7 +556,8 @@ void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *re data.basedir = basedir; data.reports = reports; - BKE_bpath_traverse_main(bmain, bpath_absolute_convert_visit_cb, flag, (void *)&data); + BKE_bpath_foreach_path_main(&(BPathForeachPathData){ + .bmain = bmain, .callback_function = callback_function, .flag = flag, .user_data = &data}); BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO, @@ -296,617 +567,97 @@ void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *re data.count_failed); } +void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports) +{ + bpath_absolute_relative_convert(bmain, basedir, reports, relative_convert_foreach_path_cb); +} + +void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports) +{ + bpath_absolute_relative_convert(bmain, basedir, reports, absolute_convert_foreach_path_cb); +} + /** \} */ /* -------------------------------------------------------------------- */ -/** \name Find Missing Files - * \{ */ - -/** - * find this file recursively, use the biggest file so thumbnails don't get used by mistake - * \param filename_new: the path will be copied here, caller must initialize as empty string. - * \param dirname: subdir to search - * \param filename: set this filename - * \param filesize: filesize for the file +/** \name Backup/Restore/Free paths list functions. * - * \returns found: 1/0. - */ -#define MAX_RECUR 16 -static bool missing_files_find__recursive(char *filename_new, - const char *dirname, - const char *filename, - int64_t *r_filesize, - int *r_recur_depth) -{ - /* file searching stuff */ - DIR *dir; - struct dirent *de; - BLI_stat_t status; - char path[FILE_MAX]; - int64_t size; - bool found = false; - - dir = opendir(dirname); - - if (dir == NULL) { - return found; - } - - if (*r_filesize == -1) { - *r_filesize = 0; /* dir opened fine */ - } - - while ((de = readdir(dir)) != NULL) { - - if (FILENAME_IS_CURRPAR(de->d_name)) { - continue; - } - - BLI_join_dirfile(path, sizeof(path), dirname, de->d_name); - - if (BLI_stat(path, &status) == -1) { - continue; /* can't stat, don't bother with this file, could print debug info here */ - } - - if (S_ISREG(status.st_mode)) { /* is file */ - if (BLI_path_ncmp(filename, de->d_name, FILE_MAX) == 0) { /* name matches */ - /* open the file to read its size */ - size = status.st_size; - if ((size > 0) && (size > *r_filesize)) { /* find the biggest file */ - *r_filesize = size; - BLI_strncpy(filename_new, path, FILE_MAX); - found = true; - } - } - } - else if (S_ISDIR(status.st_mode)) { /* is subdir */ - if (*r_recur_depth <= MAX_RECUR) { - (*r_recur_depth)++; - found |= missing_files_find__recursive( - filename_new, path, filename, r_filesize, r_recur_depth); - (*r_recur_depth)--; - } - } - } - closedir(dir); - return found; -} - -typedef struct BPathFind_Data { - const char *basedir; - const char *searchdir; - ReportList *reports; - bool find_all; -} BPathFind_Data; - -static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const char *path_src) -{ - BPathFind_Data *data = (BPathFind_Data *)userdata; - char filename_new[FILE_MAX]; - - int64_t filesize = -1; - int recur_depth = 0; - bool found; - - if (data->find_all == false) { - if (BLI_exists(path_src)) { - return false; - } - } - - filename_new[0] = '\0'; - - found = missing_files_find__recursive( - filename_new, data->searchdir, BLI_path_basename(path_src), &filesize, &recur_depth); - - if (filesize == -1) { /* could not open dir */ - BKE_reportf(data->reports, - RPT_WARNING, - "Could not open directory '%s'", - BLI_path_basename(data->searchdir)); - return false; - } - if (found == false) { - BKE_reportf(data->reports, - RPT_WARNING, - "Could not find '%s' in '%s'", - BLI_path_basename(path_src), - data->searchdir); - return false; - } - - bool was_relative = BLI_path_is_rel(path_dst); - - BLI_strncpy(path_dst, filename_new, FILE_MAX); - - /* keep path relative if the previous one was relative */ - if (was_relative) { - BLI_path_rel(path_dst, data->basedir); - } - - return true; -} - -void BKE_bpath_missing_files_find(Main *bmain, - const char *searchpath, - ReportList *reports, - const bool find_all) -{ - struct BPathFind_Data data = {NULL}; - const int flag = BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_RELOAD_EDITED; - - data.basedir = BKE_main_blendfile_path(bmain); - data.reports = reports; - data.searchdir = searchpath; - data.find_all = find_all; - - BKE_bpath_traverse_main(bmain, missing_files_find__visit_cb, flag, (void *)&data); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Generic File Path Traversal API - * \{ */ - -/** - * Run a visitor on a string, replacing the contents of the string as needed. - */ -static bool rewrite_path_fixed(char *path, - BPathVisitor visit_cb, - const char *absbase, - void *userdata) -{ - char path_src_buf[FILE_MAX]; - const char *path_src; - char path_dst[FILE_MAX]; - - if (absbase) { - BLI_strncpy(path_src_buf, path, sizeof(path_src_buf)); - BLI_path_abs(path_src_buf, absbase); - path_src = path_src_buf; - } - else { - path_src = path; - } - - /* so functions can check old value */ - BLI_strncpy(path_dst, path, FILE_MAX); - - if (visit_cb(userdata, path_dst, path_src)) { - BLI_strncpy(path, path_dst, FILE_MAX); - return true; - } - - return false; -} - -static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR], - char path_file[FILE_MAXFILE], - BPathVisitor visit_cb, - const char *absbase, - void *userdata) -{ - char path_src[FILE_MAX]; - char path_dst[FILE_MAX]; - - BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file); - - /* so functions can check old value */ - BLI_strncpy(path_dst, path_src, FILE_MAX); - - if (absbase) { - BLI_path_abs(path_src, absbase); - } - - if (visit_cb(userdata, path_dst, (const char *)path_src)) { - BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE); - return true; - } - - return false; -} - -static bool rewrite_path_alloc(char **path, - BPathVisitor visit_cb, - const char *absbase, - void *userdata) -{ - char path_src_buf[FILE_MAX]; - const char *path_src; - char path_dst[FILE_MAX]; - - if (absbase) { - BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf)); - BLI_path_abs(path_src_buf, absbase); - path_src = path_src_buf; - } - else { - path_src = *path; - } - - if (visit_cb(userdata, path_dst, path_src)) { - MEM_freeN(*path); - (*path) = BLI_strdup(path_dst); - return true; - } - - return false; -} - -typedef struct Seq_callback_data { - const char *absbase; - void *bpath_user_data; - BPathVisitor visit_cb; - const int flag; -} Seq_callback_data; - -static bool seq_rewrite_path_callback(Sequence *seq, void *user_data) -{ - if (SEQ_HAS_PATH(seq)) { - StripElem *se = seq->strip->stripdata; - Seq_callback_data *cd = (Seq_callback_data *)user_data; - - if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) { - rewrite_path_fixed_dirfile( - seq->strip->dir, se->name, cd->visit_cb, cd->absbase, cd->bpath_user_data); - } - else if ((seq->type == SEQ_TYPE_IMAGE) && se) { - /* might want an option not to loop over all strips */ - unsigned int len = (unsigned int)MEM_allocN_len(se) / (unsigned int)sizeof(*se); - unsigned int i; - - if (cd->flag & BKE_BPATH_TRAVERSE_SKIP_MULTIFILE) { - /* only operate on one path */ - len = MIN2(1u, len); - } - - for (i = 0; i < len; i++, se++) { - rewrite_path_fixed_dirfile( - seq->strip->dir, se->name, cd->visit_cb, cd->absbase, cd->bpath_user_data); - } - } - else { - /* simple case */ - rewrite_path_fixed(seq->strip->dir, cd->visit_cb, cd->absbase, cd->bpath_user_data); - } - } - return true; -} - -/** - * Run visitor function 'visit' on all paths contained in 'id'. - */ -void BKE_bpath_traverse_id( - Main *bmain, ID *id, BPathVisitor visit_cb, const int flag, void *bpath_user_data) -{ - const char *absbase = (flag & BKE_BPATH_TRAVERSE_ABS) ? ID_BLEND_PATH(bmain, id) : NULL; - - if ((flag & BKE_BPATH_TRAVERSE_SKIP_LIBRARY) && ID_IS_LINKED(id)) { - return; - } - - if (id->library_weak_reference != NULL) { - rewrite_path_fixed( - id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data); - } - - switch (GS(id->name)) { - case ID_IM: { - Image *ima; - ima = (Image *)id; - if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { - /* Skip empty file paths, these are typically from generated images and - * don't make sense to add directories to until the image has been saved - * once to give it a meaningful value. */ - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) && - ima->filepath[0]) { - if (rewrite_path_fixed(ima->filepath, visit_cb, absbase, bpath_user_data)) { - if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) { - if (!BKE_image_has_packedfile(ima) && - /* image may have been painted onto (and not saved, T44543) */ - !BKE_image_is_dirty(ima)) { - BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD); - } - } - } - } - } - break; - } - case ID_BR: { - Brush *brush = (Brush *)id; - if (brush->icon_filepath[0]) { - rewrite_path_fixed(brush->icon_filepath, visit_cb, absbase, bpath_user_data); - } - break; - } - case ID_OB: { - Object *ob = (Object *)id; - ModifierData *md; - ParticleSystem *psys; - -#define BPATH_TRAVERSE_POINTCACHE(ptcaches) \ - { \ - PointCache *cache; \ - for (cache = (ptcaches).first; cache; cache = cache->next) { \ - if (cache->flag & PTCACHE_DISK_CACHE) { \ - rewrite_path_fixed(cache->path, visit_cb, absbase, bpath_user_data); \ - } \ - } \ - } \ - (void)0 - - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Fluidsim) { - FluidsimModifierData *fluidmd = (FluidsimModifierData *)md; - if (fluidmd->fss) { - rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data); - } - } - else if (md->type == eModifierType_Fluid) { - FluidModifierData *fmd = (FluidModifierData *)md; - if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { - rewrite_path_fixed(fmd->domain->cache_directory, visit_cb, absbase, bpath_user_data); - } - } - else if (md->type == eModifierType_Cloth) { - ClothModifierData *clmd = (ClothModifierData *)md; - BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches); - } - else if (md->type == eModifierType_Ocean) { - OceanModifierData *omd = (OceanModifierData *)md; - rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data); - } - else if (md->type == eModifierType_MeshCache) { - MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md; - rewrite_path_fixed(mcmd->filepath, visit_cb, absbase, bpath_user_data); - } - } - - if (ob->soft) { - BPATH_TRAVERSE_POINTCACHE(ob->soft->shared->ptcaches); - } - - for (psys = ob->particlesystem.first; psys; psys = psys->next) { - BPATH_TRAVERSE_POINTCACHE(psys->ptcaches); - } - -#undef BPATH_TRAVERSE_POINTCACHE - - break; - } - case ID_SO: { - bSound *sound = (bSound *)id; - if (sound->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { - rewrite_path_fixed(sound->filepath, visit_cb, absbase, bpath_user_data); - } - break; - } - case ID_VO: { - Volume *volume = (Volume *)id; - if (volume->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { - rewrite_path_fixed(volume->filepath, visit_cb, absbase, bpath_user_data); - } - break; - } - case ID_TXT: - if (((Text *)id)->filepath) { - rewrite_path_alloc(&((Text *)id)->filepath, visit_cb, absbase, bpath_user_data); - } - break; - case ID_VF: { - VFont *vfont = (VFont *)id; - if (vfont->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { - if (BKE_vfont_is_builtin(vfont) == false) { - rewrite_path_fixed(((VFont *)id)->filepath, visit_cb, absbase, bpath_user_data); - } - } - break; - } - case ID_MA: { - Material *ma = (Material *)id; - bNodeTree *ntree = ma->nodetree; - - if (ntree) { - bNode *node; - - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_SCRIPT) { - NodeShaderScript *nss = (NodeShaderScript *)node->storage; - rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data); - } - else if (node->type == SH_NODE_TEX_IES) { - NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage; - rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data); - } - } - } - break; - } - case ID_NT: { - bNodeTree *ntree = (bNodeTree *)id; - bNode *node; - - if (ntree->type == NTREE_SHADER) { - /* same as lines above */ - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == SH_NODE_SCRIPT) { - NodeShaderScript *nss = (NodeShaderScript *)node->storage; - rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data); - } - else if (node->type == SH_NODE_TEX_IES) { - NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage; - rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data); - } - } - } - break; - } - case ID_SCE: { - Scene *scene = (Scene *)id; - if (scene->ed) { - Seq_callback_data user_data = {absbase, bpath_user_data, visit_cb, flag}; - SEQ_for_each_callback(&scene->ed->seqbase, seq_rewrite_path_callback, &user_data); - } - break; - } - case ID_ME: { - Mesh *me = (Mesh *)id; - if (me->ldata.external) { - rewrite_path_fixed(me->ldata.external->filename, visit_cb, absbase, bpath_user_data); - } - break; - } - case ID_LI: { - Library *lib = (Library *)id; - /* keep packedfile paths always relative to the blend */ - if (lib->packedfile == NULL) { - if (rewrite_path_fixed(lib->filepath, visit_cb, absbase, bpath_user_data)) { - BKE_library_filepath_set(bmain, lib, lib->filepath); - } - } - break; - } - case ID_MC: { - MovieClip *clip = (MovieClip *)id; - rewrite_path_fixed(clip->filepath, visit_cb, absbase, bpath_user_data); - break; - } - case ID_CF: { - CacheFile *cache_file = (CacheFile *)id; - rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data); - break; - } - default: - /* Nothing to do for other IDs that don't contain file paths. */ - break; - } -} - -void BKE_bpath_traverse_id_list( - Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data) -{ - ID *id; - for (id = lb->first; id; id = id->next) { - BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data); - } -} - -void BKE_bpath_traverse_main(Main *bmain, - BPathVisitor visit_cb, - const int flag, - void *bpath_user_data) -{ - ListBase *lbarray[INDEX_ID_MAX]; - int a = set_listbasepointers(bmain, lbarray); - while (a--) { - BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data); - } -} - -/** - * Rewrites a relative path to be relative to the main file - unless the path is - * absolute, in which case it is not altered. - */ -bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src) -{ - /* be sure there is low chance of the path being too short */ - char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE]; - const char *base_new = ((char **)pathbase_v)[0]; - const char *base_old = ((char **)pathbase_v)[1]; - - if (BLI_path_is_rel(base_old)) { - CLOG_ERROR(&LOG, "old base path '%s' is not absolute.", base_old); - return false; - } - - /* Make referenced file absolute. This would be a side-effect of - * BLI_path_normalize, but we do it explicitly so we know if it changed. */ - BLI_strncpy(filepath, path_src, FILE_MAX); - if (BLI_path_abs(filepath, base_old)) { - /* Path was relative and is now absolute. Remap. - * Important BLI_path_normalize runs before the path is made relative - * because it won't work for paths that start with "//../" */ - BLI_path_normalize(base_new, filepath); - BLI_path_rel(filepath, base_new); - BLI_strncpy(path_dst, filepath, FILE_MAX); - return true; - } - - /* Path was not relative to begin with. */ - return false; -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Backup/Restore/Free functions, - * - * \note These functions assume the data won't change order. * \{ */ struct PathStore { struct PathStore *next, *prev; }; -static bool bpath_list_append(void *userdata, char *UNUSED(path_dst), const char *path_src) +static bool bpath_list_append(BPathForeachPathData *bpath_data, + char *UNUSED(path_dst), + const char *path_src) { - /* store the path and string in a single alloc */ - ListBase *ls = userdata; + ListBase *path_list = bpath_data->user_data; size_t path_size = strlen(path_src) + 1; + + /* NOTE: the PathStore and its string are allocated together in a single alloc. */ struct PathStore *path_store = MEM_mallocN(sizeof(struct PathStore) + path_size, __func__); char *filepath = (char *)(path_store + 1); - memcpy(filepath, path_src, path_size); - BLI_addtail(ls, path_store); + BLI_strncpy(filepath, path_src, path_size); + BLI_addtail(path_list, path_store); return false; } -static bool bpath_list_restore(void *userdata, char *path_dst, const char *path_src) +static bool bpath_list_restore(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - /* assume ls->first won't be NULL because the number of paths can't change! - * (if they do caller is wrong) */ - ListBase *ls = userdata; - struct PathStore *path_store = ls->first; + ListBase *path_list = bpath_data->user_data; + + /* `ls->first` should never be NULL, because the number of paths should not change. + * If this happens, there is a bug in caller code. */ + BLI_assert(!BLI_listbase_is_empty(path_list)); + + struct PathStore *path_store = path_list->first; const char *filepath = (char *)(path_store + 1); - bool ret; + bool is_path_changed = false; - if (STREQ(path_src, filepath)) { - ret = false; - } - else { + if (!STREQ(path_src, filepath)) { BLI_strncpy(path_dst, filepath, FILE_MAX); - ret = true; + is_path_changed = true; } - BLI_freelinkN(ls, path_store); - return ret; + BLI_freelinkN(path_list, path_store); + return is_path_changed; } -/* return ls_handle */ -void *BKE_bpath_list_backup(Main *bmain, const int flag) +void *BKE_bpath_list_backup(Main *bmain, const eBPathForeachFlag flag) { - ListBase *ls = MEM_callocN(sizeof(ListBase), __func__); + ListBase *path_list = MEM_callocN(sizeof(ListBase), __func__); - BKE_bpath_traverse_main(bmain, bpath_list_append, flag, ls); + BKE_bpath_foreach_path_main(&(BPathForeachPathData){.bmain = bmain, + .callback_function = bpath_list_append, + .flag = flag, + .user_data = path_list}); - return ls; + return path_list; } -void BKE_bpath_list_restore(Main *bmain, const int flag, void *ls_handle) +void BKE_bpath_list_restore(Main *bmain, const eBPathForeachFlag flag, void *path_list_handle) { - ListBase *ls = ls_handle; + ListBase *path_list = path_list_handle; - BKE_bpath_traverse_main(bmain, bpath_list_restore, flag, ls); + BKE_bpath_foreach_path_main(&(BPathForeachPathData){.bmain = bmain, + .callback_function = bpath_list_restore, + .flag = flag, + .user_data = path_list}); } -void BKE_bpath_list_free(void *ls_handle) +void BKE_bpath_list_free(void *path_list_handle) { - ListBase *ls = ls_handle; - BLI_assert(BLI_listbase_is_empty(ls)); /* assumes we were used */ - BLI_freelistN(ls); - MEM_freeN(ls); + ListBase *path_list = path_list_handle; + /* The whole list should have been consumed by #BKE_bpath_list_restore, see also comment in + * #bpath_list_restore. */ + BLI_assert(BLI_listbase_is_empty(path_list)); + + BLI_freelistN(path_list); + MEM_freeN(path_list); } /** \} */ diff --git a/source/blender/blenkernel/intern/bpath_test.cc b/source/blender/blenkernel/intern/bpath_test.cc index 12dabb11e96..ee921cc2135 100644 --- a/source/blender/blenkernel/intern/bpath_test.cc +++ b/source/blender/blenkernel/intern/bpath_test.cc @@ -160,7 +160,7 @@ TEST_F(BPathTest, list_backup_restore) MovieClip *movie_clip = reinterpret_cast(bmain->movieclips.first); BLI_strncpy(movie_clip->filepath, MOVIECLIP_PATH_ABSOLUTE, sizeof(movie_clip->filepath)); - void *path_list_handle = BKE_bpath_list_backup(bmain, 0); + void *path_list_handle = BKE_bpath_list_backup(bmain, static_cast(0)); ListBase *path_list = reinterpret_cast(path_list_handle); EXPECT_EQ(BLI_listbase_count(path_list), 2); @@ -169,7 +169,7 @@ TEST_F(BPathTest, list_backup_restore) text->filepath = BLI_strdup(TEXT_PATH_ABSOLUTE); BLI_strncpy(movie_clip->filepath, MOVIECLIP_PATH_RELATIVE, sizeof(movie_clip->filepath)); - BKE_bpath_list_restore(bmain, 0, path_list_handle); + BKE_bpath_list_restore(bmain, static_cast(0), path_list_handle); EXPECT_STREQ(text->filepath, TEXT_PATH_RELATIVE); EXPECT_STREQ(movie_clip->filepath, MOVIECLIP_PATH_ABSOLUTE); diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index eeee2dc2615..e92dd8a5ace 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -33,6 +33,7 @@ #include "BLT_translation.h" +#include "BKE_bpath.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -218,6 +219,14 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) BKE_texture_mtex_foreach_id(data, &brush->mask_mtex)); } +static void brush_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Brush *brush = (Brush *)id; + if (brush->icon_filepath[0] != '\0') { + BKE_bpath_foreach_path_fixed_process(bpath_data, brush->icon_filepath); + } +} + static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Brush *brush = (Brush *)id; @@ -422,6 +431,7 @@ IDTypeInfo IDType_ID_BR = { .make_local = brush_make_local, .foreach_id = brush_foreach_id, .foreach_cache = NULL, + .foreach_path = brush_foreach_path, .owner_get = NULL, .blend_write = brush_blend_write, diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 3330b33cdd7..2e0e95b19c5 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -40,6 +40,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_bpath.h" #include "BKE_cachefile.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -94,6 +95,12 @@ static void cache_file_free_data(ID *id) BLI_freelistN(&cache_file->object_paths); } +static void cache_file_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + CacheFile *cache_file = (CacheFile *)id; + BKE_bpath_foreach_path_fixed_process(bpath_data, cache_file->filepath); +} + static void cache_file_blend_write(BlendWriter *writer, ID *id, const void *id_address) { CacheFile *cache_file = (CacheFile *)id; @@ -142,6 +149,7 @@ IDTypeInfo IDType_ID_CF = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = cache_file_foreach_path, .owner_get = NULL, .blend_write = cache_file_blend_write, diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index c0ab4a64d4a..0b9de2ddb38 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -190,6 +190,7 @@ IDTypeInfo IDType_ID_CA = { .make_local = NULL, .foreach_id = camera_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = camera_blend_write, diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index c025556430b..979e0a91c95 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -383,6 +383,7 @@ IDTypeInfo IDType_ID_GR = { .make_local = NULL, .foreach_id = collection_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = collection_owner_get, .blend_write = collection_blend_write, diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 8f5e0f8f3d0..e82773a0074 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -320,6 +320,7 @@ IDTypeInfo IDType_ID_CU = { .make_local = NULL, .foreach_id = curve_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = curve_blend_write, diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 04c5b09ed27..f9841aeb8c2 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -328,6 +328,7 @@ IDTypeInfo IDType_ID_GD = { .make_local = NULL, .foreach_id = greasepencil_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = greasepencil_blend_write, diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 9b4a1ba5b38..b8d88a039c0 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -190,6 +190,7 @@ IDTypeInfo IDType_ID_HA = { .make_local = NULL, .foreach_id = hair_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = hair_blend_write, diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 22529467a14..295157abe9a 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -75,6 +75,7 @@ #include "BLT_translation.h" +#include "BKE_bpath.h" #include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_icons.h" @@ -252,6 +253,34 @@ static void image_foreach_cache(ID *id, } } +static void image_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Image *ima = (Image *)id; + const eBPathForeachFlag flag = bpath_data->flag; + + if (BKE_image_has_packedfile(ima) && (flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { + return; + } + /* Skip empty file paths, these are typically from generated images and + * don't make sense to add directories to until the image has been saved + * once to give it a meaningful value. */ + /* TODO re-assess whether this behavior is disired in the new generic code context. */ + if (!ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) || + ima->filepath[0] == '\0') { + return; + } + + if (BKE_bpath_foreach_path_fixed_process(bpath_data, ima->filepath)) { + if (flag & BKE_BPATH_FOREACH_PATH_RELOAD_EDITED) { + if (!BKE_image_has_packedfile(ima) && + /* Image may have been painted onto (and not saved, T44543). */ + !BKE_image_is_dirty(ima)) { + BKE_image_signal(bpath_data->bmain, ima, NULL, IMA_SIGNAL_RELOAD); + } + } + } +} + static void image_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Image *ima = (Image *)id; @@ -377,6 +406,7 @@ IDTypeInfo IDType_ID_IM = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = image_foreach_cache, + .foreach_path = image_foreach_path, .owner_get = NULL, .blend_write = image_blend_write, diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 4532e2f9883..d2e053f58e8 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -193,6 +193,7 @@ IDTypeInfo IDType_ID_IP = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = NULL, diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index d601c9594d0..eedd02b2271 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -221,6 +221,7 @@ IDTypeInfo IDType_ID_KE = { .make_local = NULL, .foreach_id = shapekey_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also * share a lot with those (non linkable, only ever used by one owner ID, etc.). */ .owner_get = shapekey_owner_get, diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 98b801160fb..af0d91d29fc 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -205,6 +205,7 @@ IDTypeInfo IDType_ID_LT = { .make_local = NULL, .foreach_id = lattice_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = lattice_blend_write, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 0b0ed199981..d41e81a07b5 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -107,6 +107,8 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = NULL, + .owner_get = NULL, .blend_write = NULL, .blend_read_data = NULL, @@ -124,19 +126,57 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = { /* ************* general ************************ */ +/** + * Rewrites a relative path to be relative to the main file - unless the path is + * absolute, in which case it is not altered. + */ +static bool lib_id_library_local_paths_callback(BPathForeachPathData *bpath_data, + char *r_path_dst, + const char *path_src) +{ + const char **data = bpath_data->user_data; + /* be sure there is low chance of the path being too short */ + char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE]; + const char *base_new = data[0]; + const char *base_old = data[1]; + + if (BLI_path_is_rel(base_old)) { + CLOG_ERROR(&LOG, "old base path '%s' is not absolute.", base_old); + return false; + } + + /* Make referenced file absolute. This would be a side-effect of + * BLI_path_normalize, but we do it explicitly so we know if it changed. */ + BLI_strncpy(filepath, path_src, FILE_MAX); + if (BLI_path_abs(filepath, base_old)) { + /* Path was relative and is now absolute. Remap. + * Important BLI_path_normalize runs before the path is made relative + * because it won't work for paths that start with "//../" */ + BLI_path_normalize(base_new, filepath); + BLI_path_rel(filepath, base_new); + BLI_strncpy(r_path_dst, filepath, FILE_MAX); + return true; + } + + /* Path was not relative to begin with. */ + return false; +} + /** * This has to be called from each make_local_* func, we could call from BKE_lib_id_make_local() * but then the make local functions would not be self contained. * Also note that the id _must_ have a library - campbell */ +/* TODO: This can probably be replaced by an ID-level version of #BKE_bpath_relative_rebase. */ static void lib_id_library_local_paths(Main *bmain, Library *lib, ID *id) { const char *bpath_user_data[2] = {BKE_main_blendfile_path(bmain), lib->filepath_abs}; - BKE_bpath_traverse_id(bmain, - id, - BKE_bpath_relocate_visitor, - BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, - (void *)bpath_user_data); + BKE_bpath_foreach_path_id( + &(BPathForeachPathData){.bmain = bmain, + .callback_function = lib_id_library_local_paths_callback, + .flag = BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE, + .user_data = (void *)bpath_user_data}, + id); } static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData *cb_data) diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 74b1a612ccf..d7282b14734 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -36,6 +36,7 @@ #include "BLT_translation.h" +#include "BKE_bpath.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -60,6 +61,22 @@ static void library_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->parent, IDWALK_CB_NEVER_SELF); } +static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Library *lib = (Library *)id; + + /* FIXME: Find if we should respect #BKE_BPATH_FOREACH_PATH_SKIP_PACKED here, and if not, explain + * why. */ + if (lib->packedfile != + NULL /*&& (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0 */) { + return; + } + + if (BKE_bpath_foreach_path_fixed_process(bpath_data, lib->filepath)) { + BKE_library_filepath_set(bpath_data->bmain, lib, lib->filepath); + } +} + IDTypeInfo IDType_ID_LI = { .id_code = ID_LI, .id_filter = 0, @@ -77,6 +94,7 @@ IDTypeInfo IDType_ID_LI = { .make_local = NULL, .foreach_id = library_foreach_id, .foreach_cache = NULL, + .foreach_path = library_foreach_path, .owner_get = NULL, .blend_write = NULL, diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index 305df19b70d..e73cda7e24d 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -203,6 +203,7 @@ IDTypeInfo IDType_ID_LA = { .make_local = NULL, .foreach_id = light_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = light_blend_write, diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 58390d8e912..035e41815e5 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -100,6 +100,7 @@ IDTypeInfo IDType_ID_LP = { .make_local = NULL, .foreach_id = lightprobe_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = lightprobe_blend_write, diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 7300dff3a09..b198808b8f3 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -762,6 +762,7 @@ IDTypeInfo IDType_ID_LS = { .make_local = NULL, .foreach_id = linestyle_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = linestyle_blend_write, diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index e3d3b54f458..7b916cb1aae 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -263,6 +263,7 @@ IDTypeInfo IDType_ID_MSK = { .make_local = NULL, .foreach_id = mask_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = mask_blend_write, diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index cf3fd4a7c6e..42009267f49 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -269,6 +269,7 @@ IDTypeInfo IDType_ID_MA = { .make_local = NULL, .foreach_id = material_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = material_blend_write, diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 37fbd2b1e3c..e3eaac800fa 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -197,6 +197,7 @@ IDTypeInfo IDType_ID_MB = { .make_local = NULL, .foreach_id = metaball_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = metaball_blend_write, diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 3f115d98891..24739233ea5 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -48,6 +48,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_bpath.h" #include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_global.h" @@ -183,6 +184,14 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Mesh *me = (Mesh *)id; + if (me->ldata.external) { + BKE_bpath_foreach_path_fixed_process(bpath_data, me->ldata.external->filename); + } +} + static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Mesh *mesh = (Mesh *)id; @@ -371,6 +380,7 @@ IDTypeInfo IDType_ID_ME = { /* make_local */ nullptr, /* foreach_id */ mesh_foreach_id, /* foreach_cache */ nullptr, + /* foreach_path */ mesh_foreach_path, /* owner_get */ nullptr, /* blend_write */ mesh_blend_write, diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index ae7a56ff9a0..5a6e5620c5b 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -60,6 +60,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_bpath.h" #include "BKE_colortools.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -165,6 +166,12 @@ static void movie_clip_foreach_cache(ID *id, function_callback(id, &key, (void **)&movie_clip->tracking.camera.intrinsics, 0, user_data); } +static void movie_clip_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + MovieClip *movie_clip = (MovieClip *)id; + BKE_bpath_foreach_path_fixed_process(bpath_data, movie_clip->filepath); +} + static void write_movieTracks(BlendWriter *writer, ListBase *tracks) { MovieTrackingTrack *track; @@ -355,6 +362,7 @@ IDTypeInfo IDType_ID_MC = { .make_local = NULL, .foreach_id = movie_clip_foreach_id, .foreach_cache = movie_clip_foreach_cache, + .foreach_path = movie_clip_foreach_path, .owner_get = NULL, .blend_write = movieclip_blend_write, diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 288d46bf089..7d5ba4189a3 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -63,6 +63,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" +#include "BKE_bpath.h" #include "BKE_colortools.h" #include "BKE_cryptomatte.h" #include "BKE_global.h" @@ -416,6 +417,29 @@ static void node_foreach_cache(ID *id, } } +static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + bNodeTree *ntree = reinterpret_cast(id); + + switch (ntree->type) { + case NTREE_SHADER: { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == SH_NODE_SCRIPT) { + NodeShaderScript *nss = reinterpret_cast(node->storage); + BKE_bpath_foreach_path_fixed_process(bpath_data, nss->filepath); + } + else if (node->type == SH_NODE_TEX_IES) { + NodeShaderTexIES *ies = reinterpret_cast(node->storage); + BKE_bpath_foreach_path_fixed_process(bpath_data, ies->filepath); + } + } + break; + } + default: + break; + } +} + static ID *node_owner_get(Main *bmain, ID *id) { if ((id->flag & LIB_EMBEDDED_DATA) == 0) { @@ -1055,6 +1079,7 @@ IDTypeInfo IDType_ID_NT = { /* make_local */ nullptr, /* foreach_id */ node_foreach_id, /* foreach_cache */ node_foreach_cache, + /* foreach_path */ node_foreach_path, /* owner_get */ node_owner_get, /* blend_write */ ntree_blend_write, diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index e3b9e4109ed..efb8cdd9c9d 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -83,6 +83,7 @@ #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_asset.h" +#include "BKE_bpath.h" #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" @@ -548,6 +549,66 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void object_foreach_path_pointcache(ListBase *ptcache_list, BPathForeachPathData *bpath_data) +{ + for (PointCache *cache = (PointCache *)ptcache_list->first; cache != nullptr; + cache = cache->next) { + if (cache->flag & PTCACHE_DISK_CACHE) { + BKE_bpath_foreach_path_fixed_process(bpath_data, cache->path); + } + } +} + +static void object_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Object *ob = reinterpret_cast(id); + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + /* TODO: Move that to #ModifierTypeInfo. */ + switch (md->type) { + case eModifierType_Fluidsim: { + FluidsimModifierData *fluidmd = reinterpret_cast(md); + if (fluidmd->fss) { + BKE_bpath_foreach_path_fixed_process(bpath_data, fluidmd->fss->surfdataPath); + } + break; + } + case eModifierType_Fluid: { + FluidModifierData *fmd = reinterpret_cast(md); + if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) { + BKE_bpath_foreach_path_fixed_process(bpath_data, fmd->domain->cache_directory); + } + break; + } + case eModifierType_Cloth: { + ClothModifierData *clmd = reinterpret_cast(md); + object_foreach_path_pointcache(&clmd->ptcaches, bpath_data); + break; + } + case eModifierType_Ocean: { + OceanModifierData *omd = reinterpret_cast(md); + BKE_bpath_foreach_path_fixed_process(bpath_data, omd->cachepath); + break; + } + case eModifierType_MeshCache: { + MeshCacheModifierData *mcmd = reinterpret_cast(md); + BKE_bpath_foreach_path_fixed_process(bpath_data, mcmd->filepath); + break; + } + default: + break; + } + } + + if (ob->soft != nullptr) { + object_foreach_path_pointcache(&ob->soft->shared->ptcaches, bpath_data); + } + + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + object_foreach_path_pointcache(&psys->ptcaches, bpath_data); + } +} + static void write_fmaps(BlendWriter *writer, ListBase *fbase) { LISTBASE_FOREACH (bFaceMap *, fmap, fbase) { @@ -1266,6 +1327,7 @@ IDTypeInfo IDType_ID_OB = { /* make_local */ object_make_local, /* foreach_id */ object_foreach_id, /* foreach_cache */ nullptr, + /* foreach_path */ object_foreach_path, /* owner_get */ nullptr, /* blend_write */ object_blend_write, diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 9c4b7ebb37a..f419bef64a0 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -151,6 +151,7 @@ IDTypeInfo IDType_ID_PAL = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = palette_blend_write, @@ -217,6 +218,7 @@ IDTypeInfo IDType_ID_PC = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = paint_curve_blend_write, diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index bb3113c7e28..b868ee57289 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -508,6 +508,7 @@ IDTypeInfo IDType_ID_PA = { .make_local = NULL, .foreach_id = particle_settings_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = particle_settings_blend_write, diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 26d9c88566c..f8ef6eb4a54 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -183,6 +183,7 @@ IDTypeInfo IDType_ID_PT = { /* make_local */ nullptr, /* foreach_id */ pointcloud_foreach_id, /* foreach_cache */ nullptr, + /* foreach_path */ nullptr, /* owner_get */ nullptr, /* blend_write */ pointcloud_blend_write, diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index a5937326e3b..31e1ca464fc 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -71,6 +71,7 @@ #include "BKE_anim_data.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_bpath.h" #include "BKE_cachefile.h" #include "BKE_collection.h" #include "BKE_colortools.h" @@ -891,6 +892,45 @@ static void scene_foreach_cache(ID *id, user_data); } +static bool seq_foreach_path_callback(Sequence *seq, void *user_data) +{ + if (SEQ_HAS_PATH(seq)) { + StripElem *se = seq->strip->stripdata; + BPathForeachPathData *bpath_data = (BPathForeachPathData *)user_data; + + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) { + BKE_bpath_foreach_path_dirfile_fixed_process(bpath_data, seq->strip->dir, se->name); + } + else if ((seq->type == SEQ_TYPE_IMAGE) && se) { + /* NOTE: An option not to loop over all strips could be usefull? */ + unsigned int len = (unsigned int)MEM_allocN_len(se) / (unsigned int)sizeof(*se); + unsigned int i; + + if (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE) { + /* only operate on one path */ + len = MIN2(1u, len); + } + + for (i = 0; i < len; i++, se++) { + BKE_bpath_foreach_path_dirfile_fixed_process(bpath_data, seq->strip->dir, se->name); + } + } + else { + /* simple case */ + BKE_bpath_foreach_path_fixed_process(bpath_data, seq->strip->dir); + } + } + return true; +} + +static void scene_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Scene *scene = (Scene *)id; + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_path_callback, bpath_data); + } +} + static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Scene *sce = (Scene *)id; @@ -1610,6 +1650,7 @@ IDTypeInfo IDType_ID_SCE = { .make_local = NULL, .foreach_id = scene_foreach_id, .foreach_cache = scene_foreach_cache, + .foreach_path = scene_foreach_path, .owner_get = NULL, .blend_write = scene_blend_write, diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 81c85e93cc0..4b631e5b1cc 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -312,6 +312,7 @@ IDTypeInfo IDType_ID_SCR = { .make_local = NULL, .foreach_id = screen_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = screen_blend_write, diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 44d326bdb64..b0f9de5963a 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -162,6 +162,7 @@ IDTypeInfo IDType_ID_SIM = { /* make_local */ nullptr, /* foreach_id */ simulation_foreach_id, /* foreach_cache */ nullptr, + /* foreach_path */ nullptr, /* owner_get */ nullptr, /* blend_write */ simulation_blend_write, diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index ce348648532..3e99896054a 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -54,6 +54,7 @@ # include #endif +#include "BKE_bpath.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" @@ -133,6 +134,17 @@ static void sound_foreach_cache(ID *id, function_callback(id, &key, &sound->waveform, 0, user_data); } +static void sound_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + bSound *sound = (bSound *)id; + if (sound->packedfile != NULL && (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { + return; + } + + /* FIXME: This does not check for empty path... */ + BKE_bpath_foreach_path_fixed_process(bpath_data, sound->filepath); +} + static void sound_blend_write(BlendWriter *writer, ID *id, const void *id_address) { bSound *sound = (bSound *)id; @@ -214,6 +226,7 @@ IDTypeInfo IDType_ID_SO = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = sound_foreach_cache, + .foreach_path = sound_foreach_path, .owner_get = NULL, .blend_write = sound_blend_write, diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 79a5e4fd9f4..b7199dc1e20 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -107,6 +107,7 @@ IDTypeInfo IDType_ID_SPK = { .make_local = NULL, .foreach_id = speaker_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = speaker_blend_write, diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 9655d2fcbca..9a7e54600a5 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -49,6 +49,7 @@ #include "DNA_text_types.h" #include "DNA_userdef_types.h" +#include "BKE_bpath.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -169,6 +170,15 @@ static void text_free_data(ID *id) #endif } +static void text_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Text *text = (Text *)id; + + if (text->filepath != NULL) { + BKE_bpath_foreach_path_allocated_process(bpath_data, &text->filepath); + } +} + static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Text *text = (Text *)id; @@ -250,6 +260,7 @@ IDTypeInfo IDType_ID_TXT = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = text_foreach_path, .owner_get = NULL, .blend_write = text_blend_write, diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index f6e437088cc..5725e8a5dcc 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -219,6 +219,7 @@ IDTypeInfo IDType_ID_TE = { .make_local = NULL, .foreach_id = texture_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = texture_blend_write, diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c index 9ec81989d53..a17a57dc5ef 100644 --- a/source/blender/blenkernel/intern/vfont.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -49,6 +49,7 @@ #include "DNA_vfont_types.h" #include "BKE_anim_path.h" +#include "BKE_bpath.h" #include "BKE_curve.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -123,6 +124,21 @@ static void vfont_free_data(ID *id) } } +static void vfont_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + VFont *vfont = (VFont *)id; + + if (vfont->packedfile != NULL && (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { + return; + } + + if (BKE_vfont_is_builtin(vfont)) { + return; + } + + BKE_bpath_foreach_path_fixed_process(bpath_data, vfont->filepath); +} + static void vfont_blend_write(BlendWriter *writer, ID *id, const void *id_address) { VFont *vf = (VFont *)id; @@ -170,6 +186,7 @@ IDTypeInfo IDType_ID_VF = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = NULL, + .foreach_path = vfont_foreach_path, .owner_get = NULL, .blend_write = vfont_blend_write, diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 75f23ca0598..c79d4e8cd87 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -41,6 +41,7 @@ #include "BLI_utildefines.h" #include "BKE_anim_data.h" +#include "BKE_bpath.h" #include "BKE_geometry_set.hh" #include "BKE_global.h" #include "BKE_idtype.h" @@ -576,6 +577,17 @@ static void volume_foreach_cache(ID *id, function_callback(id, &key, (void **)&volume->runtime.grids, 0, user_data); } +static void volume_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Volume *volume = reinterpret_cast(id); + + if (volume->packedfile != nullptr && (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { + return; + } + + BKE_bpath_foreach_path_fixed_process(bpath_data, volume->filepath); +} + static void volume_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Volume *volume = (Volume *)id; @@ -653,6 +665,7 @@ IDTypeInfo IDType_ID_VO = { /* make_local */ nullptr, /* foreach_id */ volume_foreach_id, /* foreach_cache */ volume_foreach_cache, + /* foreach_path */ volume_foreach_path, /* owner_get */ nullptr, /* blend_write */ volume_blend_write, diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 35a262470e7..ac3608ffd33 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -195,6 +195,7 @@ IDTypeInfo IDType_ID_WS = { .make_local = NULL, .foreach_id = workspace_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = workspace_blend_write, diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 5b6b90712cd..b2e90b7ba12 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -200,6 +200,7 @@ IDTypeInfo IDType_ID_WO = { .make_local = NULL, .foreach_id = world_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = world_blend_write, diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 56ff7151cb1..84db3826e4c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1332,7 +1332,8 @@ bool BLO_write_file(Main *mainvar, /* path backup/restore */ void *path_list_backup = NULL; - const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); + const eBPathForeachFlag path_list_flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | + BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE); if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) { BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* save to disk"); diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 0177a06aa0a..f182dcc1ad0 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -880,11 +880,12 @@ struct FileCheckCallbackInfo { bool external_file_found; }; -static bool external_file_check_callback(void *callback_info_ptr, +static bool external_file_check_callback(BPathForeachPathData *bpath_data, char * /*path_dst*/, const char *path_src) { - FileCheckCallbackInfo *callback_info = static_cast(callback_info_ptr); + FileCheckCallbackInfo *callback_info = static_cast( + bpath_data->user_data); BKE_reportf(callback_info->reports, RPT_ERROR, "Unable to install asset bundle, has external dependency \"%s\"", @@ -904,12 +905,19 @@ static bool has_external_files(Main *bmain, struct ReportList *reports) { struct FileCheckCallbackInfo callback_info = {reports, false}; - BKE_bpath_traverse_main( - bmain, - &external_file_check_callback, - BKE_BPATH_TRAVERSE_SKIP_PACKED /* Packed files are fine. */ - | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE /* Only report multifiles once, it's enough. */, - &callback_info); + eBPathForeachFlag flag = static_cast( + BKE_BPATH_FOREACH_PATH_SKIP_PACKED /* Packed files are fine. */ + | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE); /* Only report multifiles once, it's enough. */ + + BPathForeachPathData bpath_data = { + /* bmain */ bmain, + /* callback_function */ &external_file_check_callback, + /* flag */ flag, + /* user_data */ &callback_info, + /* absolute_base_path */ nullptr, + }; + + BKE_bpath_foreach_path_main(&bpath_data); return callback_info.external_file_found; } diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 634e97f782f..c66ac3a6211 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -720,6 +720,7 @@ typedef struct RenderData { /* path to render output */ /** 1024 = FILE_MAX. */ + /* NOTE: Excluded from `BKE_bpath_foreach_path_` / `scene_foreach_path` code. */ char pic[1024]; /* stamps flags. */ diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 3377b2c283e..22d54ad72ba 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -94,10 +94,13 @@ static PyObject *bpy_script_paths(PyObject *UNUSED(self)) return ret; } -static bool bpy_blend_paths_visit_cb(void *userdata, char *UNUSED(path_dst), const char *path_src) +static bool bpy_blend_foreach_path_cb(BPathForeachPathData *bpath_data, + char *UNUSED(path_dst), + const char *path_src) { - PyList_APPEND((PyObject *)userdata, PyC_UnicodeFromByte(path_src)); - return false; /* never edits the path */ + PyObject *py_list = bpath_data->user_data; + PyList_APPEND(py_list, PyC_UnicodeFromByte(path_src)); + return false; /* Never edits the path. */ } PyDoc_STRVAR(bpy_blend_paths_doc, @@ -115,7 +118,7 @@ PyDoc_STRVAR(bpy_blend_paths_doc, " :rtype: list of strings\n"); static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { - int flag = 0; + eBPathForeachFlag flag = 0; PyObject *list; bool absolute = false; @@ -137,18 +140,21 @@ static PyObject *bpy_blend_paths(PyObject *UNUSED(self), PyObject *args, PyObjec } if (absolute) { - flag |= BKE_BPATH_TRAVERSE_ABS; + flag |= BKE_BPATH_FOREACH_PATH_ABSOLUTE; } if (!packed) { - flag |= BKE_BPATH_TRAVERSE_SKIP_PACKED; + flag |= BKE_BPATH_FOREACH_PATH_SKIP_PACKED; } if (local) { - flag |= BKE_BPATH_TRAVERSE_SKIP_LIBRARY; + flag |= BKE_BPATH_FOREACH_PATH_SKIP_LINKED; } list = PyList_New(0); - BKE_bpath_traverse_main(G_MAIN, bpy_blend_paths_visit_cb, flag, (void *)list); + BKE_bpath_foreach_path_main(&(BPathForeachPathData){.bmain = G_MAIN, + .callback_function = bpy_blend_foreach_path_cb, + .flag = flag, + .user_data = list}); return list; } diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 6e3e09ab650..13ee7d7f894 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -280,6 +280,7 @@ IDTypeInfo IDType_ID_WM = { .make_local = NULL, .foreach_id = window_manager_foreach_id, .foreach_cache = NULL, + .foreach_path = NULL, .owner_get = NULL, .blend_write = window_manager_blend_write,