1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 */
16
17 /** \file
18 * \ingroup bli
19 */
20
21 /* TODO,
22 * currently there are some cases we don't support.
23 * - passing output paths to the visitor?, like render out.
24 * - passing sequence strips with many images.
25 * - passing directory paths - visitors don't know which path is a dir or a file.
26 * */
27
28 #include <sys/stat.h>
29
30 #include <string.h>
31
32 /* path/file handling stuff */
33 #ifndef WIN32
34 # include <dirent.h>
35 # include <unistd.h>
36 #else
37 # include "BLI_winstuff.h"
38 # include <io.h>
39 #endif
40
41 #include "MEM_guardedalloc.h"
42
43 #include "DNA_brush_types.h"
44 #include "DNA_cachefile_types.h"
45 #include "DNA_fluid_types.h"
46 #include "DNA_freestyle_types.h"
47 #include "DNA_image_types.h"
48 #include "DNA_material_types.h"
49 #include "DNA_mesh_types.h"
50 #include "DNA_modifier_types.h"
51 #include "DNA_movieclip_types.h"
52 #include "DNA_node_types.h"
53 #include "DNA_object_fluidsim_types.h"
54 #include "DNA_object_force_types.h"
55 #include "DNA_object_types.h"
56 #include "DNA_particle_types.h"
57 #include "DNA_pointcache_types.h"
58 #include "DNA_scene_types.h"
59 #include "DNA_sequence_types.h"
60 #include "DNA_sound_types.h"
61 #include "DNA_text_types.h"
62 #include "DNA_texture_types.h"
63 #include "DNA_vfont_types.h"
64 #include "DNA_volume_types.h"
65
66 #include "BLI_blenlib.h"
67 #include "BLI_utildefines.h"
68
69 #include "BKE_font.h"
70 #include "BKE_image.h"
71 #include "BKE_lib_id.h"
72 #include "BKE_library.h"
73 #include "BKE_main.h"
74 #include "BKE_node.h"
75 #include "BKE_report.h"
76 #include "BKE_sequencer.h"
77
78 #include "BKE_bpath.h" /* own include */
79
80 #include "CLG_log.h"
81
82 #ifndef _MSC_VER
83 # include "BLI_strict_flags.h"
84 #endif
85
86 static CLG_LogRef LOG = {"bke.bpath"};
87
88 /* -------------------------------------------------------------------- */
89 /** \name Check Missing Files
90 * \{ */
91
checkMissingFiles_visit_cb(void * userdata,char * UNUSED (path_dst),const char * path_src)92 static bool checkMissingFiles_visit_cb(void *userdata,
93 char *UNUSED(path_dst),
94 const char *path_src)
95 {
96 ReportList *reports = (ReportList *)userdata;
97
98 if (!BLI_exists(path_src)) {
99 BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src);
100 }
101
102 return false;
103 }
104
105 /* high level function */
BKE_bpath_missing_files_check(Main * bmain,ReportList * reports)106 void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports)
107 {
108 BKE_bpath_traverse_main(bmain,
109 checkMissingFiles_visit_cb,
110 BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_SKIP_PACKED,
111 reports);
112 }
113
114 /** \} */
115
116 /* -------------------------------------------------------------------- */
117 /** \name Rebase Relative Paths
118 * \{ */
119
120 typedef struct BPathRebase_Data {
121 const char *basedir_src;
122 const char *basedir_dst;
123 ReportList *reports;
124
125 int count_tot;
126 int count_changed;
127 int count_failed;
128 } BPathRebase_Data;
129
bpath_relative_rebase_visit_cb(void * userdata,char * path_dst,const char * path_src)130 static bool bpath_relative_rebase_visit_cb(void *userdata, char *path_dst, const char *path_src)
131 {
132 BPathRebase_Data *data = (BPathRebase_Data *)userdata;
133
134 data->count_tot++;
135
136 if (BLI_path_is_rel(path_src)) {
137 char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
138 BLI_strncpy(filepath, path_src, FILE_MAX);
139 if (BLI_path_abs(filepath, data->basedir_src)) {
140 BLI_path_normalize(NULL, filepath);
141
142 /* This may fail, if so it's fine to leave absolute since the path is still valid. */
143 BLI_path_rel(filepath, data->basedir_dst);
144
145 BLI_strncpy(path_dst, filepath, FILE_MAX);
146 data->count_changed++;
147 return true;
148 }
149
150 /* Failed to make relative path absolute. */
151 BLI_assert(0);
152 BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
153 data->count_failed++;
154 return false;
155 }
156
157 /* Absolute, leave this as-is. */
158 return false;
159 }
160
BKE_bpath_relative_rebase(Main * bmain,const char * basedir_src,const char * basedir_dst,ReportList * reports)161 void BKE_bpath_relative_rebase(Main *bmain,
162 const char *basedir_src,
163 const char *basedir_dst,
164 ReportList *reports)
165 {
166 BPathRebase_Data data = {NULL};
167 const int flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE);
168
169 BLI_assert(basedir_src[0] != '\0');
170 BLI_assert(basedir_dst[0] != '\0');
171
172 data.basedir_src = basedir_src;
173 data.basedir_dst = basedir_dst;
174 data.reports = reports;
175
176 BKE_bpath_traverse_main(bmain, bpath_relative_rebase_visit_cb, flag, (void *)&data);
177
178 BKE_reportf(reports,
179 data.count_failed ? RPT_WARNING : RPT_INFO,
180 "Total files %d | Changed %d | Failed %d",
181 data.count_tot,
182 data.count_changed,
183 data.count_failed);
184 }
185
186 /** \} */
187
188 /* -------------------------------------------------------------------- */
189 /** \name Make Paths Relative
190 * \{ */
191
192 typedef struct BPathRemap_Data {
193 const char *basedir;
194 ReportList *reports;
195
196 int count_tot;
197 int count_changed;
198 int count_failed;
199 } BPathRemap_Data;
200
bpath_relative_convert_visit_cb(void * userdata,char * path_dst,const char * path_src)201 static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, const char *path_src)
202 {
203 BPathRemap_Data *data = (BPathRemap_Data *)userdata;
204
205 data->count_tot++;
206
207 if (BLI_path_is_rel(path_src)) {
208 return false; /* already relative */
209 }
210
211 strcpy(path_dst, path_src);
212 BLI_path_rel(path_dst, data->basedir);
213 if (BLI_path_is_rel(path_dst)) {
214 data->count_changed++;
215 }
216 else {
217 BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src);
218 data->count_failed++;
219 }
220 return true;
221 }
222
BKE_bpath_relative_convert(Main * bmain,const char * basedir,ReportList * reports)223 void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports)
224 {
225 BPathRemap_Data data = {NULL};
226 const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY;
227
228 if (basedir[0] == '\0') {
229 CLOG_ERROR(&LOG, "basedir='', this is a bug");
230 return;
231 }
232
233 data.basedir = basedir;
234 data.reports = reports;
235
236 BKE_bpath_traverse_main(bmain, bpath_relative_convert_visit_cb, flag, (void *)&data);
237
238 BKE_reportf(reports,
239 data.count_failed ? RPT_WARNING : RPT_INFO,
240 "Total files %d | Changed %d | Failed %d",
241 data.count_tot,
242 data.count_changed,
243 data.count_failed);
244 }
245
246 /** \} */
247
248 /* -------------------------------------------------------------------- */
249 /** \name Make Paths Absolute
250 * \{ */
251
bpath_absolute_convert_visit_cb(void * userdata,char * path_dst,const char * path_src)252 static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, const char *path_src)
253 {
254 BPathRemap_Data *data = (BPathRemap_Data *)userdata;
255
256 data->count_tot++;
257
258 if (BLI_path_is_rel(path_src) == false) {
259 return false; /* already absolute */
260 }
261
262 strcpy(path_dst, path_src);
263 BLI_path_abs(path_dst, data->basedir);
264 if (BLI_path_is_rel(path_dst) == false) {
265 data->count_changed++;
266 }
267 else {
268 BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
269 data->count_failed++;
270 }
271 return true;
272 }
273
274 /* similar to BKE_bpath_relative_convert - keep in sync! */
BKE_bpath_absolute_convert(Main * bmain,const char * basedir,ReportList * reports)275 void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports)
276 {
277 BPathRemap_Data data = {NULL};
278 const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY;
279
280 if (basedir[0] == '\0') {
281 CLOG_ERROR(&LOG, "basedir='', this is a bug");
282 return;
283 }
284
285 data.basedir = basedir;
286 data.reports = reports;
287
288 BKE_bpath_traverse_main(bmain, bpath_absolute_convert_visit_cb, flag, (void *)&data);
289
290 BKE_reportf(reports,
291 data.count_failed ? RPT_WARNING : RPT_INFO,
292 "Total files %d | Changed %d | Failed %d",
293 data.count_tot,
294 data.count_changed,
295 data.count_failed);
296 }
297
298 /** \} */
299
300 /* -------------------------------------------------------------------- */
301 /** \name Find Missing Files
302 * \{ */
303
304 /**
305 * find this file recursively, use the biggest file so thumbnails don't get used by mistake
306 * \param filename_new: the path will be copied here, caller must initialize as empty string.
307 * \param dirname: subdir to search
308 * \param filename: set this filename
309 * \param filesize: filesize for the file
310 *
311 * \returns found: 1/0.
312 */
313 #define MAX_RECUR 16
missing_files_find__recursive(char * filename_new,const char * dirname,const char * filename,int64_t * r_filesize,int * r_recur_depth)314 static bool missing_files_find__recursive(char *filename_new,
315 const char *dirname,
316 const char *filename,
317 int64_t *r_filesize,
318 int *r_recur_depth)
319 {
320 /* file searching stuff */
321 DIR *dir;
322 struct dirent *de;
323 BLI_stat_t status;
324 char path[FILE_MAX];
325 int64_t size;
326 bool found = false;
327
328 dir = opendir(dirname);
329
330 if (dir == NULL) {
331 return found;
332 }
333
334 if (*r_filesize == -1) {
335 *r_filesize = 0; /* dir opened fine */
336 }
337
338 while ((de = readdir(dir)) != NULL) {
339
340 if (FILENAME_IS_CURRPAR(de->d_name)) {
341 continue;
342 }
343
344 BLI_join_dirfile(path, sizeof(path), dirname, de->d_name);
345
346 if (BLI_stat(path, &status) == -1) {
347 continue; /* cant stat, don't bother with this file, could print debug info here */
348 }
349
350 if (S_ISREG(status.st_mode)) { /* is file */
351 if (BLI_path_ncmp(filename, de->d_name, FILE_MAX) == 0) { /* name matches */
352 /* open the file to read its size */
353 size = status.st_size;
354 if ((size > 0) && (size > *r_filesize)) { /* find the biggest file */
355 *r_filesize = size;
356 BLI_strncpy(filename_new, path, FILE_MAX);
357 found = true;
358 }
359 }
360 }
361 else if (S_ISDIR(status.st_mode)) { /* is subdir */
362 if (*r_recur_depth <= MAX_RECUR) {
363 (*r_recur_depth)++;
364 found |= missing_files_find__recursive(
365 filename_new, path, filename, r_filesize, r_recur_depth);
366 (*r_recur_depth)--;
367 }
368 }
369 }
370 closedir(dir);
371 return found;
372 }
373
374 typedef struct BPathFind_Data {
375 const char *basedir;
376 const char *searchdir;
377 ReportList *reports;
378 bool find_all;
379 } BPathFind_Data;
380
missing_files_find__visit_cb(void * userdata,char * path_dst,const char * path_src)381 static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const char *path_src)
382 {
383 BPathFind_Data *data = (BPathFind_Data *)userdata;
384 char filename_new[FILE_MAX];
385
386 int64_t filesize = -1;
387 int recur_depth = 0;
388 bool found;
389
390 if (data->find_all == false) {
391 if (BLI_exists(path_src)) {
392 return false;
393 }
394 }
395
396 filename_new[0] = '\0';
397
398 found = missing_files_find__recursive(
399 filename_new, data->searchdir, BLI_path_basename(path_src), &filesize, &recur_depth);
400
401 if (filesize == -1) { /* could not open dir */
402 BKE_reportf(data->reports,
403 RPT_WARNING,
404 "Could not open directory '%s'",
405 BLI_path_basename(data->searchdir));
406 return false;
407 }
408 if (found == false) {
409 BKE_reportf(data->reports,
410 RPT_WARNING,
411 "Could not find '%s' in '%s'",
412 BLI_path_basename(path_src),
413 data->searchdir);
414 return false;
415 }
416
417 bool was_relative = BLI_path_is_rel(path_dst);
418
419 BLI_strncpy(path_dst, filename_new, FILE_MAX);
420
421 /* keep path relative if the previous one was relative */
422 if (was_relative) {
423 BLI_path_rel(path_dst, data->basedir);
424 }
425
426 return true;
427 }
428
BKE_bpath_missing_files_find(Main * bmain,const char * searchpath,ReportList * reports,const bool find_all)429 void BKE_bpath_missing_files_find(Main *bmain,
430 const char *searchpath,
431 ReportList *reports,
432 const bool find_all)
433 {
434 struct BPathFind_Data data = {NULL};
435 const int flag = BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_RELOAD_EDITED;
436
437 data.basedir = BKE_main_blendfile_path(bmain);
438 data.reports = reports;
439 data.searchdir = searchpath;
440 data.find_all = find_all;
441
442 BKE_bpath_traverse_main(bmain, missing_files_find__visit_cb, flag, (void *)&data);
443 }
444
445 /** \} */
446
447 /* -------------------------------------------------------------------- */
448 /** \name Generic File Path Traversal API
449 * \{ */
450
451 /**
452 * Run a visitor on a string, replacing the contents of the string as needed.
453 */
rewrite_path_fixed(char * path,BPathVisitor visit_cb,const char * absbase,void * userdata)454 static bool rewrite_path_fixed(char *path,
455 BPathVisitor visit_cb,
456 const char *absbase,
457 void *userdata)
458 {
459 char path_src_buf[FILE_MAX];
460 const char *path_src;
461 char path_dst[FILE_MAX];
462
463 if (absbase) {
464 BLI_strncpy(path_src_buf, path, sizeof(path_src_buf));
465 BLI_path_abs(path_src_buf, absbase);
466 path_src = path_src_buf;
467 }
468 else {
469 path_src = path;
470 }
471
472 /* so functions can check old value */
473 BLI_strncpy(path_dst, path, FILE_MAX);
474
475 if (visit_cb(userdata, path_dst, path_src)) {
476 BLI_strncpy(path, path_dst, FILE_MAX);
477 return true;
478 }
479
480 return false;
481 }
482
rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],char path_file[FILE_MAXFILE],BPathVisitor visit_cb,const char * absbase,void * userdata)483 static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],
484 char path_file[FILE_MAXFILE],
485 BPathVisitor visit_cb,
486 const char *absbase,
487 void *userdata)
488 {
489 char path_src[FILE_MAX];
490 char path_dst[FILE_MAX];
491
492 BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file);
493
494 /* so functions can check old value */
495 BLI_strncpy(path_dst, path_src, FILE_MAX);
496
497 if (absbase) {
498 BLI_path_abs(path_src, absbase);
499 }
500
501 if (visit_cb(userdata, path_dst, (const char *)path_src)) {
502 BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE);
503 return true;
504 }
505
506 return false;
507 }
508
rewrite_path_alloc(char ** path,BPathVisitor visit_cb,const char * absbase,void * userdata)509 static bool rewrite_path_alloc(char **path,
510 BPathVisitor visit_cb,
511 const char *absbase,
512 void *userdata)
513 {
514 char path_src_buf[FILE_MAX];
515 const char *path_src;
516 char path_dst[FILE_MAX];
517
518 if (absbase) {
519 BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf));
520 BLI_path_abs(path_src_buf, absbase);
521 path_src = path_src_buf;
522 }
523 else {
524 path_src = *path;
525 }
526
527 if (visit_cb(userdata, path_dst, path_src)) {
528 MEM_freeN(*path);
529 (*path) = BLI_strdup(path_dst);
530 return true;
531 }
532
533 return false;
534 }
535
536 /**
537 * Run visitor function 'visit' on all paths contained in 'id'.
538 */
BKE_bpath_traverse_id(Main * bmain,ID * id,BPathVisitor visit_cb,const int flag,void * bpath_user_data)539 void BKE_bpath_traverse_id(
540 Main *bmain, ID *id, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
541 {
542 const char *absbase = (flag & BKE_BPATH_TRAVERSE_ABS) ? ID_BLEND_PATH(bmain, id) : NULL;
543
544 if ((flag & BKE_BPATH_TRAVERSE_SKIP_LIBRARY) && ID_IS_LINKED(id)) {
545 return;
546 }
547
548 switch (GS(id->name)) {
549 case ID_IM: {
550 Image *ima;
551 ima = (Image *)id;
552 if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
553 /* Skip empty file paths, these are typically from generated images and
554 * don't make sense to add directories to until the image has been saved
555 * once to give it a meaningful value. */
556 if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) &&
557 ima->filepath[0]) {
558 if (rewrite_path_fixed(ima->filepath, visit_cb, absbase, bpath_user_data)) {
559 if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) {
560 if (!BKE_image_has_packedfile(ima) &&
561 /* image may have been painted onto (and not saved, T44543) */
562 !BKE_image_is_dirty(ima)) {
563 BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
564 }
565 }
566 }
567 }
568 }
569 break;
570 }
571 case ID_BR: {
572 Brush *brush = (Brush *)id;
573 if (brush->icon_filepath[0]) {
574 rewrite_path_fixed(brush->icon_filepath, visit_cb, absbase, bpath_user_data);
575 }
576 break;
577 }
578 case ID_OB: {
579 Object *ob = (Object *)id;
580 ModifierData *md;
581 ParticleSystem *psys;
582
583 #define BPATH_TRAVERSE_POINTCACHE(ptcaches) \
584 { \
585 PointCache *cache; \
586 for (cache = (ptcaches).first; cache; cache = cache->next) { \
587 if (cache->flag & PTCACHE_DISK_CACHE) { \
588 rewrite_path_fixed(cache->path, visit_cb, absbase, bpath_user_data); \
589 } \
590 } \
591 } \
592 (void)0
593
594 for (md = ob->modifiers.first; md; md = md->next) {
595 if (md->type == eModifierType_Fluidsim) {
596 FluidsimModifierData *fluidmd = (FluidsimModifierData *)md;
597 if (fluidmd->fss) {
598 rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
599 }
600 }
601 else if (md->type == eModifierType_Fluid) {
602 FluidModifierData *fmd = (FluidModifierData *)md;
603 if (fmd->type & MOD_FLUID_TYPE_DOMAIN && fmd->domain) {
604 rewrite_path_fixed(fmd->domain->cache_directory, visit_cb, absbase, bpath_user_data);
605 }
606 }
607 else if (md->type == eModifierType_Cloth) {
608 ClothModifierData *clmd = (ClothModifierData *)md;
609 BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
610 }
611 else if (md->type == eModifierType_Ocean) {
612 OceanModifierData *omd = (OceanModifierData *)md;
613 rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
614 }
615 else if (md->type == eModifierType_MeshCache) {
616 MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md;
617 rewrite_path_fixed(mcmd->filepath, visit_cb, absbase, bpath_user_data);
618 }
619 }
620
621 if (ob->soft) {
622 BPATH_TRAVERSE_POINTCACHE(ob->soft->shared->ptcaches);
623 }
624
625 for (psys = ob->particlesystem.first; psys; psys = psys->next) {
626 BPATH_TRAVERSE_POINTCACHE(psys->ptcaches);
627 }
628
629 #undef BPATH_TRAVERSE_POINTCACHE
630
631 break;
632 }
633 case ID_SO: {
634 bSound *sound = (bSound *)id;
635 if (sound->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
636 rewrite_path_fixed(sound->filepath, visit_cb, absbase, bpath_user_data);
637 }
638 break;
639 }
640 case ID_VO: {
641 Volume *volume = (Volume *)id;
642 if (volume->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
643 rewrite_path_fixed(volume->filepath, visit_cb, absbase, bpath_user_data);
644 }
645 break;
646 }
647 case ID_TXT:
648 if (((Text *)id)->filepath) {
649 rewrite_path_alloc(&((Text *)id)->filepath, visit_cb, absbase, bpath_user_data);
650 }
651 break;
652 case ID_VF: {
653 VFont *vfont = (VFont *)id;
654 if (vfont->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
655 if (BKE_vfont_is_builtin(vfont) == false) {
656 rewrite_path_fixed(((VFont *)id)->filepath, visit_cb, absbase, bpath_user_data);
657 }
658 }
659 break;
660 }
661 case ID_MA: {
662 Material *ma = (Material *)id;
663 bNodeTree *ntree = ma->nodetree;
664
665 if (ntree) {
666 bNode *node;
667
668 for (node = ntree->nodes.first; node; node = node->next) {
669 if (node->type == SH_NODE_SCRIPT) {
670 NodeShaderScript *nss = (NodeShaderScript *)node->storage;
671 rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
672 }
673 else if (node->type == SH_NODE_TEX_IES) {
674 NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
675 rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
676 }
677 }
678 }
679 break;
680 }
681 case ID_NT: {
682 bNodeTree *ntree = (bNodeTree *)id;
683 bNode *node;
684
685 if (ntree->type == NTREE_SHADER) {
686 /* same as lines above */
687 for (node = ntree->nodes.first; node; node = node->next) {
688 if (node->type == SH_NODE_SCRIPT) {
689 NodeShaderScript *nss = (NodeShaderScript *)node->storage;
690 rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
691 }
692 else if (node->type == SH_NODE_TEX_IES) {
693 NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
694 rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
695 }
696 }
697 }
698 break;
699 }
700 case ID_SCE: {
701 Scene *scene = (Scene *)id;
702 if (scene->ed) {
703 Sequence *seq;
704
705 SEQ_ALL_BEGIN (scene->ed, seq) {
706 if (SEQ_HAS_PATH(seq)) {
707 StripElem *se = seq->strip->stripdata;
708
709 if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) {
710 rewrite_path_fixed_dirfile(
711 seq->strip->dir, se->name, visit_cb, absbase, bpath_user_data);
712 }
713 else if ((seq->type == SEQ_TYPE_IMAGE) && se) {
714 /* might want an option not to loop over all strips */
715 unsigned int len = (unsigned int)MEM_allocN_len(se) / (unsigned int)sizeof(*se);
716 unsigned int i;
717
718 if (flag & BKE_BPATH_TRAVERSE_SKIP_MULTIFILE) {
719 /* only operate on one path */
720 len = MIN2(1u, len);
721 }
722
723 for (i = 0; i < len; i++, se++) {
724 rewrite_path_fixed_dirfile(
725 seq->strip->dir, se->name, visit_cb, absbase, bpath_user_data);
726 }
727 }
728 else {
729 /* simple case */
730 rewrite_path_fixed(seq->strip->dir, visit_cb, absbase, bpath_user_data);
731 }
732 }
733 }
734 SEQ_ALL_END;
735 }
736 break;
737 }
738 case ID_ME: {
739 Mesh *me = (Mesh *)id;
740 if (me->ldata.external) {
741 rewrite_path_fixed(me->ldata.external->filename, visit_cb, absbase, bpath_user_data);
742 }
743 break;
744 }
745 case ID_LI: {
746 Library *lib = (Library *)id;
747 /* keep packedfile paths always relative to the blend */
748 if (lib->packedfile == NULL) {
749 if (rewrite_path_fixed(lib->filepath, visit_cb, absbase, bpath_user_data)) {
750 BKE_library_filepath_set(bmain, lib, lib->filepath);
751 }
752 }
753 break;
754 }
755 case ID_MC: {
756 MovieClip *clip = (MovieClip *)id;
757 rewrite_path_fixed(clip->filepath, visit_cb, absbase, bpath_user_data);
758 break;
759 }
760 case ID_CF: {
761 CacheFile *cache_file = (CacheFile *)id;
762 rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data);
763 break;
764 }
765 default:
766 /* Nothing to do for other IDs that don't contain file paths. */
767 break;
768 }
769 }
770
BKE_bpath_traverse_id_list(Main * bmain,ListBase * lb,BPathVisitor visit_cb,const int flag,void * bpath_user_data)771 void BKE_bpath_traverse_id_list(
772 Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
773 {
774 ID *id;
775 for (id = lb->first; id; id = id->next) {
776 BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data);
777 }
778 }
779
BKE_bpath_traverse_main(Main * bmain,BPathVisitor visit_cb,const int flag,void * bpath_user_data)780 void BKE_bpath_traverse_main(Main *bmain,
781 BPathVisitor visit_cb,
782 const int flag,
783 void *bpath_user_data)
784 {
785 ListBase *lbarray[MAX_LIBARRAY];
786 int a = set_listbasepointers(bmain, lbarray);
787 while (a--) {
788 BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
789 }
790 }
791
792 /**
793 * Rewrites a relative path to be relative to the main file - unless the path is
794 * absolute, in which case it is not altered.
795 */
BKE_bpath_relocate_visitor(void * pathbase_v,char * path_dst,const char * path_src)796 bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
797 {
798 /* be sure there is low chance of the path being too short */
799 char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
800 const char *base_new = ((char **)pathbase_v)[0];
801 const char *base_old = ((char **)pathbase_v)[1];
802
803 if (BLI_path_is_rel(base_old)) {
804 CLOG_ERROR(&LOG, "old base path '%s' is not absolute.", base_old);
805 return false;
806 }
807
808 /* Make referenced file absolute. This would be a side-effect of
809 * BLI_path_normalize, but we do it explicitly so we know if it changed. */
810 BLI_strncpy(filepath, path_src, FILE_MAX);
811 if (BLI_path_abs(filepath, base_old)) {
812 /* Path was relative and is now absolute. Remap.
813 * Important BLI_path_normalize runs before the path is made relative
814 * because it wont work for paths that start with "//../" */
815 BLI_path_normalize(base_new, filepath);
816 BLI_path_rel(filepath, base_new);
817 BLI_strncpy(path_dst, filepath, FILE_MAX);
818 return true;
819 }
820
821 /* Path was not relative to begin with. */
822 return false;
823 }
824
825 /** \} */
826
827 /* -------------------------------------------------------------------- */
828 /** \name Backup/Restore/Free functions,
829 *
830 * \note These functions assume the data won't change order.
831 * \{ */
832
833 struct PathStore {
834 struct PathStore *next, *prev;
835 };
836
bpath_list_append(void * userdata,char * UNUSED (path_dst),const char * path_src)837 static bool bpath_list_append(void *userdata, char *UNUSED(path_dst), const char *path_src)
838 {
839 /* store the path and string in a single alloc */
840 ListBase *ls = userdata;
841 size_t path_size = strlen(path_src) + 1;
842 struct PathStore *path_store = MEM_mallocN(sizeof(struct PathStore) + path_size, __func__);
843 char *filepath = (char *)(path_store + 1);
844
845 memcpy(filepath, path_src, path_size);
846 BLI_addtail(ls, path_store);
847 return false;
848 }
849
bpath_list_restore(void * userdata,char * path_dst,const char * path_src)850 static bool bpath_list_restore(void *userdata, char *path_dst, const char *path_src)
851 {
852 /* assume ls->first wont be NULL because the number of paths can't change!
853 * (if they do caller is wrong) */
854 ListBase *ls = userdata;
855 struct PathStore *path_store = ls->first;
856 const char *filepath = (char *)(path_store + 1);
857 bool ret;
858
859 if (STREQ(path_src, filepath)) {
860 ret = false;
861 }
862 else {
863 BLI_strncpy(path_dst, filepath, FILE_MAX);
864 ret = true;
865 }
866
867 BLI_freelinkN(ls, path_store);
868 return ret;
869 }
870
871 /* return ls_handle */
BKE_bpath_list_backup(Main * bmain,const int flag)872 void *BKE_bpath_list_backup(Main *bmain, const int flag)
873 {
874 ListBase *ls = MEM_callocN(sizeof(ListBase), __func__);
875
876 BKE_bpath_traverse_main(bmain, bpath_list_append, flag, ls);
877
878 return ls;
879 }
880
BKE_bpath_list_restore(Main * bmain,const int flag,void * ls_handle)881 void BKE_bpath_list_restore(Main *bmain, const int flag, void *ls_handle)
882 {
883 ListBase *ls = ls_handle;
884
885 BKE_bpath_traverse_main(bmain, bpath_list_restore, flag, ls);
886 }
887
BKE_bpath_list_free(void * ls_handle)888 void BKE_bpath_list_free(void *ls_handle)
889 {
890 ListBase *ls = ls_handle;
891 BLI_assert(BLI_listbase_is_empty(ls)); /* assumes we were used */
892 BLI_freelistN(ls);
893 MEM_freeN(ls);
894 }
895
896 /** \} */
897