diff options
author | Bastien Montagne <mont29> | 2021-11-29 16:20:58 +0300 |
---|---|---|
committer | Bastien Montagne <bastien@blender.org> | 2021-11-29 16:22:38 +0300 |
commit | e5e8db73df86ee04260c5f2bd2c61dfa8eb7943f (patch) | |
tree | 46a5840efb51a363f17e64b93d5132679a5dbec4 /source/blender/blenkernel/intern/bpath.c | |
parent | 6ae34bb0714859d9ef0b7fa2aceb16b4531df48f (diff) |
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
Diffstat (limited to 'source/blender/blenkernel/intern/bpath.c')
-rw-r--r-- | source/blender/blenkernel/intern/bpath.c | 913 |
1 files changed, 332 insertions, 581 deletions
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" @@ -87,213 +88,155 @@ static CLG_LogRef LOG = {"bke.bpath"}; /* -------------------------------------------------------------------- */ -/** \name Check Missing Files +/** \name Generic File Path Traversal API * \{ */ -static bool checkMissingFiles_visit_cb(void *userdata, - char *UNUSED(path_dst), - const char *path_src) +void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id) { - ReportList *reports = (ReportList *)userdata; + 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 (!BLI_exists(path_src)) { - BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src); + if ((flag & BKE_BPATH_FOREACH_PATH_SKIP_LINKED) && ID_IS_LINKED(id)) { + return; } - 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); -} + 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); + } -/* -------------------------------------------------------------------- */ -/** \name Rebase Relative Paths - * \{ */ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); -typedef struct BPathRebase_Data { - const char *basedir_src; - const char *basedir_dst; - ReportList *reports; + BLI_assert(id_type != NULL); + if (id_type == NULL || id_type->foreach_path == NULL) { + return; + } - int count_tot; - int count_changed; - int count_failed; -} BPathRebase_Data; + id_type->foreach_path(id, bpath_data); +} -static bool bpath_relative_rebase_visit_cb(void *userdata, char *path_dst, const char *path_src) +void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data) { - BPathRebase_Data *data = (BPathRebase_Data *)userdata; + ID *id; + FOREACH_MAIN_ID_BEGIN (bpath_data->bmain, id) { + BKE_bpath_foreach_path_id(bpath_data, id); + } + FOREACH_MAIN_ID_END; +} - data->count_tot++; +bool BKE_bpath_foreach_path_fixed_process(BPathForeachPathData *bpath_data, char *path) +{ + const char *absolute_base_path = bpath_data->absolute_base_path; - 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); + char path_src_buf[FILE_MAX]; + const char *path_src; + char path_dst[FILE_MAX]; - /* This may fail, if so it's fine to leave absolute since the path is still valid. */ - BLI_path_rel(filepath, data->basedir_dst); + 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; + } - BLI_strncpy(path_dst, filepath, FILE_MAX); - data->count_changed++; - return true; - } + /* so functions can check old value */ + BLI_strncpy(path_dst, path, FILE_MAX); - /* Failed to make relative path absolute. */ - BLI_assert(0); - BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src); - data->count_failed++; - return false; + if (bpath_data->callback_function(bpath_data, path_dst, path_src)) { + BLI_strncpy(path, path_dst, FILE_MAX); + return true; } - /* Absolute, leave this as-is. */ return false; } -void BKE_bpath_relative_rebase(Main *bmain, - const char *basedir_src, - const char *basedir_dst, - ReportList *reports) +bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_data, + char *path_dir, + char *path_file) { - BPathRebase_Data data = {NULL}; - const int flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE); + const char *absolute_base_path = bpath_data->absolute_base_path; - BLI_assert(basedir_src[0] != '\0'); - BLI_assert(basedir_dst[0] != '\0'); - - data.basedir_src = basedir_src; - data.basedir_dst = basedir_dst; - data.reports = reports; - - BKE_bpath_traverse_main(bmain, bpath_relative_rebase_visit_cb, flag, (void *)&data); + char path_src[FILE_MAX]; + char path_dst[FILE_MAX]; - 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); -} + 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); -/* -------------------------------------------------------------------- */ -/** \name Make Paths Relative - * \{ */ + if (absolute_base_path) { + BLI_path_abs(path_src, absolute_base_path); + } -typedef struct BPathRemap_Data { - const char *basedir; - ReportList *reports; + 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; + } - int count_tot; - int count_changed; - int count_failed; -} BPathRemap_Data; + return false; +} -static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, const char *path_src) +bool BKE_bpath_foreach_path_allocated_process(BPathForeachPathData *bpath_data, char **path) { - BPathRemap_Data *data = (BPathRemap_Data *)userdata; - - data->count_tot++; + const char *absolute_base_path = bpath_data->absolute_base_path; - if (BLI_path_is_rel(path_src)) { - return false; /* already relative */ - } + char path_src_buf[FILE_MAX]; + const char *path_src; + char path_dst[FILE_MAX]; - strcpy(path_dst, path_src); - BLI_path_rel(path_dst, data->basedir); - if (BLI_path_is_rel(path_dst)) { - data->count_changed++; + 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 { - BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src); - data->count_failed++; + path_src = *path; } - return true; -} - -void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports) -{ - 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; + if (bpath_data->callback_function(bpath_data, path_dst, path_src)) { + MEM_freeN(*path); + (*path) = BLI_strdup(path_dst); + return true; } - 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); + return false; } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Make Paths Absolute +/** \name Check Missing Files * \{ */ -static bool bpath_absolute_convert_visit_cb(void *userdata, char *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) { - BPathRemap_Data *data = (BPathRemap_Data *)userdata; - - data->count_tot++; + ReportList *reports = (ReportList *)bpath_data->user_data; - if (BLI_path_is_rel(path_src) == false) { - return false; /* already absolute */ + if (!BLI_exists(path_src)) { + BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src); } - strcpy(path_dst, path_src); - BLI_path_abs(path_dst, data->basedir); - if (BLI_path_is_rel(path_dst) == false) { - data->count_changed++; - } - else { - BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src); - data->count_failed++; - } - return true; + return false; } -/* similar to BKE_bpath_relative_convert - keep in sync! */ -void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports) +void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports) { - 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_absolute_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); + 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}); } /** \} */ @@ -302,72 +245,79 @@ void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *re /** \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 +#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. * - * \returns found: 1/0. + * \return true if found, false otherwise. */ -#define MAX_RECUR 16 -static bool missing_files_find__recursive(char *filename_new, - const char *dirname, - const char *filename, +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_recur_depth) + int *r_recurse_depth) { - /* file searching stuff */ + /* TODO: Move this function to BLI_path_utils? The 'biggest size' behavior is quite specific + * though... */ DIR *dir; - struct dirent *de; BLI_stat_t status; char path[FILE_MAX]; int64_t size; bool found = false; - dir = opendir(dirname); + dir = opendir(search_directory); if (dir == NULL) { return found; } - if (*r_filesize == -1) { - *r_filesize = 0; /* dir opened fine */ + if (*r_filesize == FILESIZE_INVALID_DIRECTORY) { + *r_filesize = 0; /* The directory opened fine. */ } - while ((de = readdir(dir)) != NULL) { - + 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), dirname, de->d_name); + BLI_join_dirfile(path, sizeof(path), search_directory, de->d_name); if (BLI_stat(path, &status) == -1) { - continue; /* can't stat, don't bother with this file, could print debug info here */ + CLOG_WARN(&LOG, "Cannot get file status (`stat()`) of '%s'", path); + continue; } - 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 */ + 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 file */ + if ((size > 0) && (size > *r_filesize)) { /* Find the biggest matching file. */ *r_filesize = size; - BLI_strncpy(filename_new, path, FILE_MAX); + BLI_strncpy(r_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)++; + 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( - filename_new, path, filename, r_filesize, r_recur_depth); - (*r_recur_depth)--; + path, filename_src, r_filename_new, r_filesize, r_recurse_depth); + (*r_recurse_depth)--; } } } + closedir(dir); return found; } @@ -376,37 +326,37 @@ typedef struct BPathFind_Data { const char *basedir; const char *searchdir; ReportList *reports; - bool find_all; + bool find_all; /* Also search for files which current path is still valid. */ } BPathFind_Data; -static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const char *path_src) +static bool missing_files_find_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - BPathFind_Data *data = (BPathFind_Data *)userdata; + BPathFind_Data *data = (BPathFind_Data *)bpath_data->user_data; char filename_new[FILE_MAX]; - int64_t filesize = -1; - int recur_depth = 0; - bool found; + int64_t filesize = FILESIZE_INVALID_DIRECTORY; + int recurse_depth = 0; + bool is_found; - if (data->find_all == false) { - if (BLI_exists(path_src)) { - return false; - } + if (!data->find_all && 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); + is_found = missing_files_find__recursive( + data->searchdir, BLI_path_basename(path_src), filename_new, &filesize, &recurse_depth); - if (filesize == -1) { /* could not open dir */ + if (filesize == FILESIZE_INVALID_DIRECTORY) { BKE_reportf(data->reports, RPT_WARNING, - "Could not open directory '%s'", + "Could not open the directory '%s'", BLI_path_basename(data->searchdir)); return false; } - if (found == false) { + if (is_found == false) { BKE_reportf(data->reports, RPT_WARNING, "Could not find '%s' in '%s'", @@ -419,7 +369,7 @@ static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const c BLI_strncpy(path_dst, filename_new, FILE_MAX); - /* keep path relative if the previous one was relative */ + /* Keep the path relative if the previous one was relative. */ if (was_relative) { BLI_path_rel(path_dst, data->basedir); } @@ -433,480 +383,281 @@ void BKE_bpath_missing_files_find(Main *bmain, const bool find_all) { struct BPathFind_Data data = {NULL}; - const int flag = BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_RELOAD_EDITED; + 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_traverse_main(bmain, missing_files_find__visit_cb, flag, (void *)&data); + 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 Generic File Path Traversal API +/** \name Rebase Relative Paths * \{ */ -/** - * 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) +typedef struct BPathRebase_Data { + const char *basedir_src; + const char *basedir_dst; + ReportList *reports; + + int count_tot; + int count_changed; + int count_failed; +} BPathRebase_Data; + +static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - char path_src_buf[FILE_MAX]; - const char *path_src; - char path_dst[FILE_MAX]; + BPathRebase_Data *data = (BPathRebase_Data *)bpath_data->user_data; - if (absbase) { - BLI_strncpy(path_src_buf, path, sizeof(path_src_buf)); - BLI_path_abs(path_src_buf, absbase); - path_src = path_src_buf; + data->count_tot++; + + if (!BLI_path_is_rel(path_src)) { + /* Absolute, leave this as-is. */ + return false; } - else { - path_src = path; + + 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; } - /* so functions can check old value */ - BLI_strncpy(path_dst, path, FILE_MAX); + BLI_path_normalize(NULL, filepath); - if (visit_cb(userdata, path_dst, path_src)) { - BLI_strncpy(path, path_dst, FILE_MAX); - return true; - } + /* This may fail, if so it's fine to leave absolute since the path is still valid. */ + BLI_path_rel(filepath, data->basedir_dst); - return false; + BLI_strncpy(path_dst, filepath, FILE_MAX); + data->count_changed++; + return true; } -static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR], - char path_file[FILE_MAXFILE], - BPathVisitor visit_cb, - const char *absbase, - void *userdata) +void BKE_bpath_relative_rebase(Main *bmain, + const char *basedir_src, + const char *basedir_dst, + ReportList *reports) { - char path_src[FILE_MAX]; - char path_dst[FILE_MAX]; - - BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file); + BPathRebase_Data data = {NULL}; + const int flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE); - /* so functions can check old value */ - BLI_strncpy(path_dst, path_src, FILE_MAX); + BLI_assert(basedir_src[0] != '\0'); + BLI_assert(basedir_dst[0] != '\0'); - if (absbase) { - BLI_path_abs(path_src, absbase); - } + data.basedir_src = basedir_src; + data.basedir_dst = basedir_dst; + data.reports = reports; - 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; - } + BKE_bpath_foreach_path_main( + &(BPathForeachPathData){.bmain = bmain, + .callback_function = relative_rebase_foreach_path_cb, + .flag = flag, + .user_data = &data}); - return false; + 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); } -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; - } +/* -------------------------------------------------------------------- */ +/** \name Make Paths Relative Or Absolute + * \{ */ - return false; -} +typedef struct BPathRemap_Data { + const char *basedir; + ReportList *reports; -typedef struct Seq_callback_data { - const char *absbase; - void *bpath_user_data; - BPathVisitor visit_cb; - const int flag; -} Seq_callback_data; + int count_tot; + int count_changed; + int count_failed; +} BPathRemap_Data; -static bool seq_rewrite_path_callback(Sequence *seq, void *user_data) +static bool relative_convert_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - if (SEQ_HAS_PATH(seq)) { - StripElem *se = seq->strip->stripdata; - Seq_callback_data *cd = (Seq_callback_data *)user_data; + BPathRemap_Data *data = (BPathRemap_Data *)bpath_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); - } + data->count_tot++; - 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); - } + if (BLI_path_is_rel(path_src)) { + return false; /* Already relative. */ + } + + 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++; + } + else { + BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src); + data->count_failed++; } 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) +static bool absolute_convert_foreach_path_cb(BPathForeachPathData *bpath_data, + char *path_dst, + const char *path_src) { - const char *absbase = (flag & BKE_BPATH_TRAVERSE_ABS) ? ID_BLEND_PATH(bmain, id) : NULL; + BPathRemap_Data *data = (BPathRemap_Data *)bpath_data->user_data; - if ((flag & BKE_BPATH_TRAVERSE_SKIP_LIBRARY) && ID_IS_LINKED(id)) { - return; - } + data->count_tot++; - if (id->library_weak_reference != NULL) { - rewrite_path_fixed( - id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data); + if (!BLI_path_is_rel(path_src)) { + return false; /* Already absolute. */ } - 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; + 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++; + } + else { + BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src); + data->count_failed++; } + return true; } -void BKE_bpath_traverse_id_list( - Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data) +static void bpath_absolute_relative_convert(Main *bmain, + const char *basedir, + ReportList *reports, + BPathForeachPathFunctionCallback callback_function) { - ID *id; - for (id = lb->first; id; id = id->next) { - BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data); + BPathRemap_Data data = {NULL}; + 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; } + + data.basedir = basedir; + data.reports = reports; + + 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, + "Total files %d | Changed %d | Failed %d", + data.count_tot, + data.count_changed, + data.count_failed); } -void BKE_bpath_traverse_main(Main *bmain, - BPathVisitor visit_cb, - const int flag, - void *bpath_user_data) +void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports) { - 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); - } + bpath_absolute_relative_convert(bmain, basedir, reports, relative_convert_foreach_path_cb); } -/** - * 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) +void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports) { - /* 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; + bpath_absolute_relative_convert(bmain, basedir, reports, absolute_convert_foreach_path_cb); } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Backup/Restore/Free functions, +/** \name Backup/Restore/Free paths list 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); } /** \} */ |