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
This commit is contained in:
Bastien Montagne 2021-11-29 14:20:58 +01:00 committed by Bastien Montagne
parent 6ae34bb071
commit e5e8db73df
49 changed files with 946 additions and 721 deletions

View File

@ -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

View File

@ -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.
*/

View File

@ -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,

View File

@ -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,

View File

@ -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). */

File diff suppressed because it is too large Load Diff

View File

@ -160,7 +160,7 @@ TEST_F(BPathTest, list_backup_restore)
MovieClip *movie_clip = reinterpret_cast<MovieClip *>(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<eBPathForeachFlag>(0));
ListBase *path_list = reinterpret_cast<ListBase *>(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<eBPathForeachFlag>(0), path_list_handle);
EXPECT_STREQ(text->filepath, TEXT_PATH_RELATIVE);
EXPECT_STREQ(movie_clip->filepath, MOVIECLIP_PATH_ABSOLUTE);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<bNodeTree *>(id);
switch (ntree->type) {
case NTREE_SHADER: {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == SH_NODE_SCRIPT) {
NodeShaderScript *nss = reinterpret_cast<NodeShaderScript *>(node->storage);
BKE_bpath_foreach_path_fixed_process(bpath_data, nss->filepath);
}
else if (node->type == SH_NODE_TEX_IES) {
NodeShaderTexIES *ies = reinterpret_cast<NodeShaderTexIES *>(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,

View File

@ -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<Object *>(id);
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
/* TODO: Move that to #ModifierTypeInfo. */
switch (md->type) {
case eModifierType_Fluidsim: {
FluidsimModifierData *fluidmd = reinterpret_cast<FluidsimModifierData *>(md);
if (fluidmd->fss) {
BKE_bpath_foreach_path_fixed_process(bpath_data, fluidmd->fss->surfdataPath);
}
break;
}
case eModifierType_Fluid: {
FluidModifierData *fmd = reinterpret_cast<FluidModifierData *>(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<ClothModifierData *>(md);
object_foreach_path_pointcache(&clmd->ptcaches, bpath_data);
break;
}
case eModifierType_Ocean: {
OceanModifierData *omd = reinterpret_cast<OceanModifierData *>(md);
BKE_bpath_foreach_path_fixed_process(bpath_data, omd->cachepath);
break;
}
case eModifierType_MeshCache: {
MeshCacheModifierData *mcmd = reinterpret_cast<MeshCacheModifierData *>(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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -54,6 +54,7 @@
# include <AUD_Special.h>
#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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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<Volume *>(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,

View File

@ -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,

View File

@ -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,

View File

@ -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");

View File

@ -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<FileCheckCallbackInfo *>(callback_info_ptr);
FileCheckCallbackInfo *callback_info = static_cast<FileCheckCallbackInfo *>(
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<eBPathForeachFlag>(
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;
}

View File

@ -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. */

View File

@ -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;
}

View File

@ -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,