716 lines
21 KiB
C++
716 lines
21 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup bke
|
|
*/
|
|
|
|
/* TODO:
|
|
* currently there are some cases we don't support.
|
|
* - passing output paths to the visitor?, like render out.
|
|
* - passing sequence strips with many images.
|
|
* - passing directory paths - visitors don't know which path is a dir or a file.
|
|
*/
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <cstring>
|
|
|
|
/* path/file handling stuff */
|
|
#ifndef WIN32
|
|
# include <dirent.h>
|
|
# include <unistd.h>
|
|
#else
|
|
# include "BLI_winstuff.h"
|
|
# include <io.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_brush_types.h"
|
|
#include "DNA_cachefile_types.h"
|
|
#include "DNA_fluid_types.h"
|
|
#include "DNA_freestyle_types.h"
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
#include "DNA_movieclip_types.h"
|
|
#include "DNA_node_types.h"
|
|
#include "DNA_object_fluidsim_types.h"
|
|
#include "DNA_object_force_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_particle_types.h"
|
|
#include "DNA_pointcache_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_sequence_types.h"
|
|
#include "DNA_sound_types.h"
|
|
#include "DNA_text_types.h"
|
|
#include "DNA_texture_types.h"
|
|
#include "DNA_vfont_types.h"
|
|
#include "DNA_volume_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DEG_depsgraph.hh"
|
|
|
|
#include "BKE_idtype.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_lib_id.h"
|
|
#include "BKE_library.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_node.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_vfont.h"
|
|
|
|
#include "BKE_bpath.h" /* own include */
|
|
|
|
#include "CLG_log.h"
|
|
|
|
#include "SEQ_iterator.hh"
|
|
|
|
#ifndef _MSC_VER
|
|
# include "BLI_strict_flags.h"
|
|
#endif
|
|
|
|
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) :
|
|
nullptr;
|
|
bpath_data->absolute_base_path = absbase;
|
|
bpath_data->owner_id = id;
|
|
bpath_data->is_path_modified = false;
|
|
|
|
if ((flag & BKE_BPATH_FOREACH_PATH_SKIP_LINKED) && ID_IS_LINKED(id)) {
|
|
return;
|
|
}
|
|
|
|
if (id->library_weak_reference != nullptr &&
|
|
(flag & BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES) == 0) {
|
|
BKE_bpath_foreach_path_fixed_process(bpath_data,
|
|
id->library_weak_reference->library_filepath,
|
|
sizeof(id->library_weak_reference->library_filepath));
|
|
}
|
|
|
|
bNodeTree *embedded_node_tree = ntreeFromID(id);
|
|
if (embedded_node_tree != nullptr) {
|
|
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 != nullptr);
|
|
if (id_type == nullptr || id_type->foreach_path == nullptr) {
|
|
return;
|
|
}
|
|
|
|
id_type->foreach_path(id, bpath_data);
|
|
|
|
if (bpath_data->is_path_modified) {
|
|
DEG_id_tag_update(id, ID_RECALC_SOURCE | ID_RECALC_COPY_ON_WRITE);
|
|
}
|
|
}
|
|
|
|
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,
|
|
size_t path_maxncpy)
|
|
{
|
|
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) {
|
|
STRNCPY(path_src_buf, path);
|
|
BLI_path_abs(path_src_buf, absolute_base_path);
|
|
path_src = path_src_buf;
|
|
}
|
|
else {
|
|
path_src = path;
|
|
}
|
|
|
|
/* so functions can check old value */
|
|
STRNCPY(path_dst, path);
|
|
|
|
if (bpath_data->callback_function(bpath_data, path_dst, sizeof(path_dst), path_src)) {
|
|
BLI_strncpy(path, path_dst, path_maxncpy);
|
|
bpath_data->is_path_modified = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_data,
|
|
char *path_dir,
|
|
size_t path_dir_maxncpy,
|
|
char *path_file,
|
|
size_t path_file_maxncpy)
|
|
{
|
|
const char *absolute_base_path = bpath_data->absolute_base_path;
|
|
|
|
char path_src[FILE_MAX];
|
|
char path_dst[FILE_MAX];
|
|
|
|
BLI_path_join(path_src, sizeof(path_src), path_dir, path_file);
|
|
|
|
/* So that functions can access the old value. */
|
|
STRNCPY(path_dst, path_src);
|
|
|
|
if (absolute_base_path) {
|
|
BLI_path_abs(path_src, absolute_base_path);
|
|
}
|
|
|
|
if (bpath_data->callback_function(
|
|
bpath_data, path_dst, sizeof(path_dst), (const char *)path_src)) {
|
|
BLI_path_split_dir_file(path_dst, path_dir, path_dir_maxncpy, path_file, path_file_maxncpy);
|
|
bpath_data->is_path_modified = true;
|
|
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) {
|
|
STRNCPY(path_src_buf, *path);
|
|
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, sizeof(path_dst), path_src)) {
|
|
MEM_freeN(*path);
|
|
(*path) = BLI_strdup(path_dst);
|
|
bpath_data->is_path_modified = true;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Check Missing Files
|
|
* \{ */
|
|
|
|
static bool check_missing_files_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|
char * /*path_dst*/,
|
|
size_t /*path_dst_maxncpy*/,
|
|
|
|
const char *path_src)
|
|
{
|
|
ReportList *reports = (ReportList *)bpath_data->user_data;
|
|
|
|
if (!BLI_exists(path_src)) {
|
|
BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports)
|
|
{
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = check_missing_files_foreach_path_cb;
|
|
path_data.flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_SKIP_PACKED |
|
|
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES;
|
|
path_data.user_data = reports;
|
|
BKE_bpath_foreach_path_main(&path_data);
|
|
|
|
if (BLI_listbase_is_empty(&reports->list)) {
|
|
BKE_reportf(reports, RPT_INFO, "No missing files");
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \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_filepath_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_filepath_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 == nullptr) {
|
|
return found;
|
|
}
|
|
|
|
if (*r_filesize == FILESIZE_INVALID_DIRECTORY) {
|
|
*r_filesize = 0; /* The directory opened fine. */
|
|
}
|
|
|
|
for (dirent *de = readdir(dir); de != nullptr; de = readdir(dir)) {
|
|
if (FILENAME_IS_CURRPAR(de->d_name)) {
|
|
continue;
|
|
}
|
|
|
|
BLI_path_join(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_filepath_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_filepath_new, r_filesize, r_recurse_depth);
|
|
(*r_recurse_depth)--;
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
return found;
|
|
}
|
|
|
|
struct BPathFind_Data {
|
|
const char *basedir;
|
|
const char *searchdir;
|
|
ReportList *reports;
|
|
bool find_all; /* Also search for files which current path is still valid. */
|
|
};
|
|
|
|
static bool missing_files_find_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|
char *path_dst,
|
|
size_t path_dst_maxncpy,
|
|
const char *path_src)
|
|
{
|
|
BPathFind_Data *data = (BPathFind_Data *)bpath_data->user_data;
|
|
char filepath_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;
|
|
}
|
|
|
|
filepath_new[0] = '\0';
|
|
|
|
is_found = missing_files_find__recursive(
|
|
data->searchdir, BLI_path_basename(path_src), filepath_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;
|
|
}
|
|
|
|
/* Keep the path relative if the previous one was relative. */
|
|
if (BLI_path_is_rel(path_dst)) {
|
|
BLI_path_rel(filepath_new, data->basedir);
|
|
}
|
|
BLI_strncpy(path_dst, filepath_new, path_dst_maxncpy);
|
|
return true;
|
|
}
|
|
|
|
void BKE_bpath_missing_files_find(Main *bmain,
|
|
const char *searchpath,
|
|
ReportList *reports,
|
|
const bool find_all)
|
|
{
|
|
BPathFind_Data data = {nullptr};
|
|
const int flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_RELOAD_EDITED |
|
|
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES;
|
|
|
|
data.basedir = BKE_main_blendfile_path(bmain);
|
|
data.reports = reports;
|
|
data.searchdir = searchpath;
|
|
data.find_all = find_all;
|
|
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = missing_files_find_foreach_path_cb;
|
|
path_data.flag = eBPathForeachFlag(flag);
|
|
path_data.user_data = &data;
|
|
BKE_bpath_foreach_path_main(&path_data);
|
|
}
|
|
|
|
#undef MAX_DIR_RECURSE
|
|
#undef FILESIZE_INVALID_DIRECTORY
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Rebase Relative Paths
|
|
* \{ */
|
|
|
|
struct BPathRebase_Data {
|
|
const char *basedir_src;
|
|
const char *basedir_dst;
|
|
ReportList *reports;
|
|
|
|
int count_tot;
|
|
int count_changed;
|
|
int count_failed;
|
|
};
|
|
|
|
static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|
char *path_dst,
|
|
size_t path_dst_maxncpy,
|
|
const char *path_src)
|
|
{
|
|
BPathRebase_Data *data = (BPathRebase_Data *)bpath_data->user_data;
|
|
|
|
data->count_tot++;
|
|
|
|
if (!BLI_path_is_rel(path_src)) {
|
|
/* Absolute, leave this as-is. */
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
BLI_path_normalize(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, path_dst_maxncpy);
|
|
data->count_changed++;
|
|
return true;
|
|
}
|
|
|
|
void BKE_bpath_relative_rebase(Main *bmain,
|
|
const char *basedir_src,
|
|
const char *basedir_dst,
|
|
ReportList *reports)
|
|
{
|
|
BPathRebase_Data data = {nullptr};
|
|
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');
|
|
|
|
data.basedir_src = basedir_src;
|
|
data.basedir_dst = basedir_dst;
|
|
data.reports = reports;
|
|
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = relative_rebase_foreach_path_cb;
|
|
path_data.flag = eBPathForeachFlag(flag);
|
|
path_data.user_data = &data;
|
|
BKE_bpath_foreach_path_main(&path_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 Relative Or Absolute
|
|
* \{ */
|
|
|
|
struct BPathRemap_Data {
|
|
const char *basedir;
|
|
ReportList *reports;
|
|
|
|
int count_tot;
|
|
int count_changed;
|
|
int count_failed;
|
|
};
|
|
|
|
static bool relative_convert_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|
char *path_dst,
|
|
size_t path_dst_maxncpy,
|
|
const char *path_src)
|
|
{
|
|
BPathRemap_Data *data = (BPathRemap_Data *)bpath_data->user_data;
|
|
|
|
data->count_tot++;
|
|
|
|
if (BLI_path_is_rel(path_src)) {
|
|
return false; /* Already relative. */
|
|
}
|
|
|
|
char path_test[FILE_MAX];
|
|
STRNCPY(path_test, path_src);
|
|
|
|
BLI_path_rel(path_test, data->basedir);
|
|
if (!BLI_path_is_rel(path_test)) {
|
|
const char *type_name = BKE_idtype_get_info_from_id(bpath_data->owner_id)->name;
|
|
const char *id_name = bpath_data->owner_id->name + 2;
|
|
BKE_reportf(data->reports,
|
|
RPT_WARNING,
|
|
"Path '%s' cannot be made relative for %s '%s'",
|
|
path_src,
|
|
type_name,
|
|
id_name);
|
|
data->count_failed++;
|
|
return false;
|
|
}
|
|
|
|
BLI_strncpy(path_dst, path_test, path_dst_maxncpy);
|
|
data->count_changed++;
|
|
return true;
|
|
}
|
|
|
|
static bool absolute_convert_foreach_path_cb(BPathForeachPathData *bpath_data,
|
|
char *path_dst,
|
|
size_t path_dst_maxncpy,
|
|
const char *path_src)
|
|
{
|
|
BPathRemap_Data *data = (BPathRemap_Data *)bpath_data->user_data;
|
|
|
|
data->count_tot++;
|
|
|
|
if (!BLI_path_is_rel(path_src)) {
|
|
return false; /* Already absolute. */
|
|
}
|
|
|
|
char path_test[FILE_MAX];
|
|
STRNCPY(path_test, path_src);
|
|
BLI_path_abs(path_test, data->basedir);
|
|
if (BLI_path_is_rel(path_test)) {
|
|
const char *type_name = BKE_idtype_get_info_from_id(bpath_data->owner_id)->name;
|
|
const char *id_name = bpath_data->owner_id->name + 2;
|
|
BKE_reportf(data->reports,
|
|
RPT_WARNING,
|
|
"Path '%s' cannot be made absolute for %s '%s'",
|
|
path_src,
|
|
type_name,
|
|
id_name);
|
|
data->count_failed++;
|
|
return false;
|
|
}
|
|
|
|
BLI_strncpy(path_dst, path_test, path_dst_maxncpy);
|
|
data->count_changed++;
|
|
return true;
|
|
}
|
|
|
|
static void bpath_absolute_relative_convert(Main *bmain,
|
|
const char *basedir,
|
|
ReportList *reports,
|
|
BPathForeachPathFunctionCallback callback_function)
|
|
{
|
|
BPathRemap_Data data = {nullptr};
|
|
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;
|
|
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = callback_function;
|
|
path_data.flag = eBPathForeachFlag(flag);
|
|
path_data.user_data = &data;
|
|
BKE_bpath_foreach_path_main(&path_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_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 Backup/Restore/Free paths list functions.
|
|
*
|
|
* \{ */
|
|
|
|
struct PathStore {
|
|
PathStore *next, *prev;
|
|
/** Over allocate. */
|
|
char filepath[0];
|
|
};
|
|
|
|
static bool bpath_list_append(BPathForeachPathData *bpath_data,
|
|
char * /*path_dst*/,
|
|
size_t /*path_dst_maxncpy*/,
|
|
const char *path_src)
|
|
{
|
|
ListBase *path_list = static_cast<ListBase *>(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. */
|
|
PathStore *path_store = static_cast<PathStore *>(
|
|
MEM_mallocN(sizeof(PathStore) + path_size, __func__));
|
|
|
|
char *filepath = path_store->filepath;
|
|
|
|
memcpy(filepath, path_src, path_size);
|
|
BLI_addtail(path_list, path_store);
|
|
return false;
|
|
}
|
|
|
|
static bool bpath_list_restore(BPathForeachPathData *bpath_data,
|
|
char *path_dst,
|
|
size_t path_dst_maxncpy,
|
|
const char *path_src)
|
|
{
|
|
ListBase *path_list = static_cast<ListBase *>(bpath_data->user_data);
|
|
|
|
/* `ls->first` should never be nullptr, 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));
|
|
|
|
PathStore *path_store = static_cast<PathStore *>(path_list->first);
|
|
const char *filepath = path_store->filepath;
|
|
bool is_path_changed = false;
|
|
|
|
if (!STREQ(path_src, filepath)) {
|
|
BLI_strncpy(path_dst, filepath, path_dst_maxncpy);
|
|
is_path_changed = true;
|
|
}
|
|
|
|
BLI_freelinkN(path_list, path_store);
|
|
return is_path_changed;
|
|
}
|
|
|
|
void *BKE_bpath_list_backup(Main *bmain, const eBPathForeachFlag flag)
|
|
{
|
|
ListBase *path_list = static_cast<ListBase *>(MEM_callocN(sizeof(ListBase), __func__));
|
|
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = bpath_list_append;
|
|
path_data.flag = flag;
|
|
path_data.user_data = path_list;
|
|
BKE_bpath_foreach_path_main(&path_data);
|
|
|
|
return path_list;
|
|
}
|
|
|
|
void BKE_bpath_list_restore(Main *bmain, const eBPathForeachFlag flag, void *path_list_handle)
|
|
{
|
|
ListBase *path_list = static_cast<ListBase *>(path_list_handle);
|
|
|
|
BPathForeachPathData path_data{};
|
|
path_data.bmain = bmain;
|
|
path_data.callback_function = bpath_list_restore;
|
|
path_data.flag = flag;
|
|
path_data.user_data = path_list;
|
|
BKE_bpath_foreach_path_main(&path_data);
|
|
}
|
|
|
|
void BKE_bpath_list_free(void *path_list_handle)
|
|
{
|
|
ListBase *path_list = static_cast<ListBase *>(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);
|
|
}
|
|
|
|
/** \} */
|