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