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