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 bke
19  *
20  * High level `.blend` file read/write,
21  * and functions for writing *partial* files (only selected data-blocks).
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "MEM_guardedalloc.h"
28 
29 #include "DNA_scene_types.h"
30 #include "DNA_screen_types.h"
31 #include "DNA_workspace_types.h"
32 
33 #include "BLI_listbase.h"
34 #include "BLI_path_util.h"
35 #include "BLI_string.h"
36 #include "BLI_system.h"
37 #include "BLI_utildefines.h"
38 
39 #include "IMB_colormanagement.h"
40 
41 #include "BKE_addon.h"
42 #include "BKE_appdir.h"
43 #include "BKE_blender.h"
44 #include "BKE_blender_version.h"
45 #include "BKE_blendfile.h"
46 #include "BKE_bpath.h"
47 #include "BKE_colorband.h"
48 #include "BKE_context.h"
49 #include "BKE_global.h"
50 #include "BKE_ipo.h"
51 #include "BKE_keyconfig.h"
52 #include "BKE_layer.h"
53 #include "BKE_lib_id.h"
54 #include "BKE_main.h"
55 #include "BKE_report.h"
56 #include "BKE_scene.h"
57 #include "BKE_screen.h"
58 #include "BKE_studiolight.h"
59 #include "BKE_workspace.h"
60 
61 #include "BLO_readfile.h"
62 #include "BLO_writefile.h"
63 
64 #include "RNA_access.h"
65 
66 #include "RE_pipeline.h"
67 
68 #ifdef WITH_PYTHON
69 #  include "BPY_extern.h"
70 #endif
71 
72 /* -------------------------------------------------------------------- */
73 /** \name High Level `.blend` file read/write.
74  * \{ */
75 
clean_paths_visit_cb(void * UNUSED (userdata),char * path_dst,const char * path_src)76 static bool clean_paths_visit_cb(void *UNUSED(userdata), char *path_dst, const char *path_src)
77 {
78   strcpy(path_dst, path_src);
79   BLI_path_slash_native(path_dst);
80   return !STREQ(path_dst, path_src);
81 }
82 
83 /* make sure path names are correct for OS */
clean_paths(Main * main)84 static void clean_paths(Main *main)
85 {
86   Scene *scene;
87 
88   BKE_bpath_traverse_main(main, clean_paths_visit_cb, BKE_BPATH_TRAVERSE_SKIP_MULTIFILE, NULL);
89 
90   for (scene = main->scenes.first; scene; scene = scene->id.next) {
91     BLI_path_slash_native(scene->r.pic);
92   }
93 }
94 
wm_scene_is_visible(wmWindowManager * wm,Scene * scene)95 static bool wm_scene_is_visible(wmWindowManager *wm, Scene *scene)
96 {
97   wmWindow *win;
98   for (win = wm->windows.first; win; win = win->next) {
99     if (win->scene == scene) {
100       return true;
101     }
102   }
103   return false;
104 }
105 
setup_app_userdef(BlendFileData * bfd)106 static void setup_app_userdef(BlendFileData *bfd)
107 {
108   if (bfd->user) {
109     /* only here free userdef themes... */
110     BKE_blender_userdef_data_set_and_free(bfd->user);
111     bfd->user = NULL;
112 
113     /* Security issue: any blend file could include a USER block.
114      *
115      * Currently we load prefs from BLENDER_STARTUP_FILE and later on load BLENDER_USERPREF_FILE,
116      * to load the preferences defined in the users home dir.
117      *
118      * This means we will never accidentally (or maliciously)
119      * enable scripts auto-execution by loading a '.blend' file.
120      */
121     U.flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
122   }
123 }
124 
125 /**
126  * Context matching, handle no-ui case
127  *
128  * \note this is called on Undo so any slow conversion functions here
129  * should be avoided or check (mode != LOAD_UNDO).
130  *
131  * \param bfd: Blend file data, freed by this function on exit.
132  * \param filepath: File path or identifier.
133  */
setup_app_data(bContext * C,BlendFileData * bfd,const char * filepath,const struct BlendFileReadParams * params,ReportList * reports)134 static void setup_app_data(bContext *C,
135                            BlendFileData *bfd,
136                            const char *filepath,
137                            const struct BlendFileReadParams *params,
138                            ReportList *reports)
139 {
140   Main *bmain = G_MAIN;
141   Scene *curscene = NULL;
142   const bool recover = (G.fileflags & G_FILE_RECOVER) != 0;
143   const bool is_startup = params->is_startup;
144   enum {
145     LOAD_UI = 1,
146     LOAD_UI_OFF,
147     LOAD_UNDO,
148   } mode;
149 
150   if (params->undo_direction != 0) {
151     BLI_assert(bfd->curscene != NULL);
152     mode = LOAD_UNDO;
153   }
154   /* may happen with library files - UNDO file should never have NULL curscene (but may have a
155    * NULL curscreen)... */
156   else if (ELEM(NULL, bfd->curscreen, bfd->curscene)) {
157     BKE_report(reports, RPT_WARNING, "Library file, loading empty scene");
158     mode = LOAD_UI_OFF;
159   }
160   else if (G.fileflags & G_FILE_NO_UI) {
161     mode = LOAD_UI_OFF;
162   }
163   else {
164     mode = LOAD_UI;
165   }
166 
167   /* Free all render results, without this stale data gets displayed after loading files */
168   if (mode != LOAD_UNDO) {
169     RE_FreeAllRenderResults();
170   }
171 
172   /* Only make filepaths compatible when loading for real (not undo) */
173   if (mode != LOAD_UNDO) {
174     clean_paths(bfd->main);
175   }
176 
177   /* XXX here the complex windowmanager matching */
178 
179   /* no load screens? */
180   if (mode != LOAD_UI) {
181     /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has,
182      * as long as the scene associated with the undo operation is visible
183      * in one of the open windows.
184      *
185      * - 'curscreen->scene' - scene the user is currently looking at.
186      * - 'bfd->curscene' - scene undo-step was created in.
187      *
188      * This means users can have 2+ windows open and undo in both without screens switching.
189      * But if they close one of the screens,
190      * undo will ensure that the scene being operated on will be activated
191      * (otherwise we'd be undoing on an off-screen scene which isn't acceptable).
192      * see: T43424
193      */
194     wmWindow *win;
195     bScreen *curscreen = NULL;
196     ViewLayer *cur_view_layer;
197     bool track_undo_scene;
198 
199     /* comes from readfile.c */
200     SWAP(ListBase, bmain->wm, bfd->main->wm);
201     SWAP(ListBase, bmain->workspaces, bfd->main->workspaces);
202     SWAP(ListBase, bmain->screens, bfd->main->screens);
203 
204     /* In case of actual new file reading without loading UI, we need to regenerate the session
205      * uuid of the UI-related datablocks we are keeping from previous session, otherwise their uuid
206      * will collide with some generated for newly read data. */
207     if (mode != LOAD_UNDO) {
208       ID *id;
209       FOREACH_MAIN_LISTBASE_ID_BEGIN (&bfd->main->wm, id) {
210         BKE_lib_libblock_session_uuid_renew(id);
211       }
212       FOREACH_MAIN_LISTBASE_ID_END;
213 
214       FOREACH_MAIN_LISTBASE_ID_BEGIN (&bfd->main->workspaces, id) {
215         BKE_lib_libblock_session_uuid_renew(id);
216       }
217       FOREACH_MAIN_LISTBASE_ID_END;
218 
219       FOREACH_MAIN_LISTBASE_ID_BEGIN (&bfd->main->screens, id) {
220         BKE_lib_libblock_session_uuid_renew(id);
221       }
222       FOREACH_MAIN_LISTBASE_ID_END;
223     }
224 
225     /* we re-use current window and screen */
226     win = CTX_wm_window(C);
227     curscreen = CTX_wm_screen(C);
228     /* but use Scene pointer from new file */
229     curscene = bfd->curscene;
230     cur_view_layer = bfd->cur_view_layer;
231 
232     track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first);
233 
234     if (curscene == NULL) {
235       curscene = bfd->main->scenes.first;
236     }
237     /* empty file, we add a scene to make Blender work */
238     if (curscene == NULL) {
239       curscene = BKE_scene_add(bfd->main, "Empty");
240     }
241     if (cur_view_layer == NULL) {
242       /* fallback to scene layer */
243       cur_view_layer = BKE_view_layer_default_view(curscene);
244     }
245 
246     if (track_undo_scene) {
247       /* keep the old (free'd) scene, let 'blo_lib_link_screen_restore'
248        * replace it with 'curscene' if its needed */
249     }
250     /* and we enforce curscene to be in current screen */
251     else if (win) { /* can run in bgmode */
252       win->scene = curscene;
253     }
254 
255     /* BKE_blender_globals_clear will free G_MAIN, here we can still restore pointers */
256     blo_lib_link_restore(bmain, bfd->main, CTX_wm_manager(C), curscene, cur_view_layer);
257     if (win) {
258       curscene = win->scene;
259     }
260 
261     if (track_undo_scene) {
262       wmWindowManager *wm = bfd->main->wm.first;
263       if (wm_scene_is_visible(wm, bfd->curscene) == false) {
264         curscene = bfd->curscene;
265         win->scene = curscene;
266         BKE_screen_view3d_scene_sync(curscreen, curscene);
267       }
268     }
269 
270     /* We need to tag this here because events may be handled immediately after.
271      * only the current screen is important because we wont have to handle
272      * events from multiple screens at once.*/
273     if (curscreen) {
274       BKE_screen_gizmo_tag_refresh(curscreen);
275     }
276   }
277 
278   /* free G_MAIN Main database */
279   //  CTX_wm_manager_set(C, NULL);
280   BKE_blender_globals_clear();
281 
282   bmain = G_MAIN = bfd->main;
283   bfd->main = NULL;
284 
285   CTX_data_main_set(C, bmain);
286 
287   /* case G_FILE_NO_UI or no screens in file */
288   if (mode != LOAD_UI) {
289     /* leave entire context further unaltered? */
290     CTX_data_scene_set(C, curscene);
291   }
292   else {
293     CTX_wm_manager_set(C, bmain->wm.first);
294     CTX_wm_screen_set(C, bfd->curscreen);
295     CTX_data_scene_set(C, bfd->curscene);
296     CTX_wm_area_set(C, NULL);
297     CTX_wm_region_set(C, NULL);
298     CTX_wm_menu_set(C, NULL);
299     curscene = bfd->curscene;
300   }
301 
302   /* Keep state from preferences. */
303   const int fileflags_keep = G_FILE_FLAG_ALL_RUNTIME;
304   G.fileflags = (G.fileflags & fileflags_keep) | (bfd->fileflags & ~fileflags_keep);
305 
306   /* this can happen when active scene was lib-linked, and doesn't exist anymore */
307   if (CTX_data_scene(C) == NULL) {
308     wmWindow *win = CTX_wm_window(C);
309 
310     /* in case we don't even have a local scene, add one */
311     if (!bmain->scenes.first) {
312       BKE_scene_add(bmain, "Empty");
313     }
314 
315     CTX_data_scene_set(C, bmain->scenes.first);
316     win->scene = CTX_data_scene(C);
317     curscene = CTX_data_scene(C);
318   }
319 
320   BLI_assert(curscene == CTX_data_scene(C));
321 
322   /* special cases, override loaded flags: */
323   if (G.f != bfd->globalf) {
324     const int flags_keep = G_FLAG_ALL_RUNTIME;
325     bfd->globalf &= G_FLAG_ALL_READFILE;
326     bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep);
327   }
328 
329   G.f = bfd->globalf;
330 
331 #ifdef WITH_PYTHON
332   /* let python know about new main */
333   if (CTX_py_init_get(C)) {
334     BPY_context_update(C);
335   }
336 #endif
337 
338   /* FIXME: this version patching should really be part of the file-reading code,
339    * but we still get too many unrelated data-corruption crashes otherwise... */
340   if (bmain->versionfile < 250) {
341     do_versions_ipos_to_animato(bmain);
342   }
343 
344   bmain->recovered = 0;
345 
346   /* startup.blend or recovered startup */
347   if (is_startup) {
348     bmain->name[0] = '\0';
349   }
350   else if (recover && G.relbase_valid) {
351     /* in case of autosave or quit.blend, use original filename instead
352      * use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */
353     filepath = bfd->filename;
354     bmain->recovered = 1;
355 
356     /* these are the same at times, should never copy to the same location */
357     if (bmain->name != filepath) {
358       BLI_strncpy(bmain->name, filepath, FILE_MAX);
359     }
360   }
361 
362   /* baseflags, groups, make depsgraph, etc */
363   /* first handle case if other windows have different scenes visible */
364   if (mode == LOAD_UI) {
365     wmWindowManager *wm = bmain->wm.first;
366 
367     if (wm) {
368       LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
369         if (win->scene && win->scene != curscene) {
370           BKE_scene_set_background(bmain, win->scene);
371         }
372       }
373     }
374   }
375 
376   /* Setting scene might require having a dependency graph, with copy on write
377    * we need to make sure we ensure scene has correct color management before
378    * constructing dependency graph.
379    */
380   if (mode != LOAD_UNDO) {
381     IMB_colormanagement_check_file_config(bmain);
382   }
383 
384   BKE_scene_set_background(bmain, curscene);
385 
386   if (mode != LOAD_UNDO) {
387     /* TODO(sergey): Can this be also move above? */
388     RE_FreeAllPersistentData();
389   }
390 
391   if (mode == LOAD_UNDO) {
392     /* In undo/redo case, we do a whole lot of magic tricks to avoid having to re-read linked
393      * data-blocks from libraries (since those are not supposed to change). Unfortunately, that
394      * means that we do not reset their user count, however we do increase that one when doing
395      * lib_link on local IDs using linked ones.
396      * There is no real way to predict amount of changes here, so we have to fully redo
397      * refcounting.
398      * Now that we re-use (and do not liblink in readfile.c) most local datablocks as well, we have
399      * to recompute refcount for all local IDs too. */
400     BKE_main_id_refcount_recompute(bmain, false);
401   }
402 }
403 
setup_app_blend_file_data(bContext * C,BlendFileData * bfd,const char * filepath,const struct BlendFileReadParams * params,ReportList * reports)404 static void setup_app_blend_file_data(bContext *C,
405                                       BlendFileData *bfd,
406                                       const char *filepath,
407                                       const struct BlendFileReadParams *params,
408                                       ReportList *reports)
409 {
410   if ((params->skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
411     setup_app_userdef(bfd);
412   }
413   if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
414     setup_app_data(C, bfd, filepath, params, reports);
415   }
416 }
417 
handle_subversion_warning(Main * main,ReportList * reports)418 static void handle_subversion_warning(Main *main, ReportList *reports)
419 {
420   if (main->minversionfile > BLENDER_FILE_VERSION ||
421       (main->minversionfile == BLENDER_FILE_VERSION &&
422        main->minsubversionfile > BLENDER_FILE_SUBVERSION)) {
423     BKE_reportf(reports,
424                 RPT_ERROR,
425                 "File written by newer Blender binary (%d.%d), expect loss of data!",
426                 main->minversionfile,
427                 main->minsubversionfile);
428   }
429 }
430 
BKE_blendfile_read_ex(bContext * C,const char * filepath,const struct BlendFileReadParams * params,ReportList * reports,const bool startup_update_defaults,const char * startup_app_template)431 bool BKE_blendfile_read_ex(bContext *C,
432                            const char *filepath,
433                            const struct BlendFileReadParams *params,
434                            ReportList *reports,
435                            /* Extra args. */
436                            const bool startup_update_defaults,
437                            const char *startup_app_template)
438 {
439 
440   /* Don't print startup file loading. */
441   if (params->is_startup == false) {
442     printf("Read blend: %s\n", filepath);
443   }
444 
445   BlendFileData *bfd = BLO_read_from_file(filepath, params->skip_flags, reports);
446   if (bfd) {
447     handle_subversion_warning(bfd->main, reports);
448     if (startup_update_defaults) {
449       if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
450         BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
451       }
452     }
453     setup_app_blend_file_data(C, bfd, filepath, params, reports);
454     BLO_blendfiledata_free(bfd);
455   }
456   else {
457     BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath);
458   }
459   return (bfd != NULL);
460 }
461 
BKE_blendfile_read(bContext * C,const char * filepath,const struct BlendFileReadParams * params,ReportList * reports)462 bool BKE_blendfile_read(bContext *C,
463                         const char *filepath,
464                         const struct BlendFileReadParams *params,
465                         ReportList *reports)
466 {
467   return BKE_blendfile_read_ex(C, filepath, params, reports, false, NULL);
468 }
469 
BKE_blendfile_read_from_memory_ex(bContext * C,const void * filebuf,int filelength,const struct BlendFileReadParams * params,ReportList * reports,const bool startup_update_defaults,const char * startup_app_template)470 bool BKE_blendfile_read_from_memory_ex(bContext *C,
471                                        const void *filebuf,
472                                        int filelength,
473                                        const struct BlendFileReadParams *params,
474                                        ReportList *reports,
475                                        /* Extra args. */
476                                        const bool startup_update_defaults,
477                                        const char *startup_app_template)
478 {
479   BlendFileData *bfd = BLO_read_from_memory(filebuf, filelength, params->skip_flags, reports);
480   if (bfd) {
481     if (startup_update_defaults) {
482       if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
483         BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
484       }
485     }
486     setup_app_blend_file_data(C, bfd, "<memory2>", params, reports);
487     BLO_blendfiledata_free(bfd);
488   }
489   else {
490     BKE_reports_prepend(reports, "Loading failed: ");
491   }
492   return (bfd != NULL);
493 }
494 
BKE_blendfile_read_from_memory(bContext * C,const void * filebuf,int filelength,const struct BlendFileReadParams * params,ReportList * reports)495 bool BKE_blendfile_read_from_memory(bContext *C,
496                                     const void *filebuf,
497                                     int filelength,
498                                     const struct BlendFileReadParams *params,
499                                     ReportList *reports)
500 {
501   return BKE_blendfile_read_from_memory_ex(C, filebuf, filelength, params, reports, false, NULL);
502 }
503 
504 /* memfile is the undo buffer */
BKE_blendfile_read_from_memfile(bContext * C,struct MemFile * memfile,const struct BlendFileReadParams * params,ReportList * reports)505 bool BKE_blendfile_read_from_memfile(bContext *C,
506                                      struct MemFile *memfile,
507                                      const struct BlendFileReadParams *params,
508                                      ReportList *reports)
509 {
510   Main *bmain = CTX_data_main(C);
511   BlendFileData *bfd = BLO_read_from_memfile(
512       bmain, BKE_main_blendfile_path(bmain), memfile, params, reports);
513   if (bfd) {
514     /* Removing the unused workspaces, screens and wm is useless here, setup_app_data will switch
515      * those lists with the ones from old bmain, which freeing is much more efficient than
516      * individual calls to `BKE_id_free()`.
517      * Further more, those are expected to be empty anyway with new memfile reading code. */
518     BLI_assert(BLI_listbase_is_empty(&bfd->main->wm));
519     BLI_assert(BLI_listbase_is_empty(&bfd->main->workspaces));
520     BLI_assert(BLI_listbase_is_empty(&bfd->main->screens));
521 
522     setup_app_blend_file_data(C, bfd, "<memory1>", params, reports);
523     BLO_blendfiledata_free(bfd);
524   }
525   else {
526     BKE_reports_prepend(reports, "Loading failed: ");
527   }
528   return (bfd != NULL);
529 }
530 
531 /**
532  * Utility to make a file 'empty' used for startup to optionally give an empty file.
533  * Handy for tests.
534  */
BKE_blendfile_read_make_empty(bContext * C)535 void BKE_blendfile_read_make_empty(bContext *C)
536 {
537   Main *bmain = CTX_data_main(C);
538   ListBase *lb;
539   ID *id;
540 
541   FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
542     FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
543       if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM, ID_WS)) {
544         break;
545       }
546       BKE_id_delete(bmain, id);
547     }
548     FOREACH_MAIN_LISTBASE_ID_END;
549   }
550   FOREACH_MAIN_LISTBASE_END;
551 }
552 
553 /* only read the userdef from a .blend */
BKE_blendfile_userdef_read(const char * filepath,ReportList * reports)554 UserDef *BKE_blendfile_userdef_read(const char *filepath, ReportList *reports)
555 {
556   BlendFileData *bfd;
557   UserDef *userdef = NULL;
558 
559   bfd = BLO_read_from_file(filepath, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, reports);
560   if (bfd) {
561     if (bfd->user) {
562       userdef = bfd->user;
563     }
564     BKE_main_free(bfd->main);
565     MEM_freeN(bfd);
566   }
567 
568   return userdef;
569 }
570 
BKE_blendfile_userdef_read_from_memory(const void * filebuf,int filelength,ReportList * reports)571 UserDef *BKE_blendfile_userdef_read_from_memory(const void *filebuf,
572                                                 int filelength,
573                                                 ReportList *reports)
574 {
575   BlendFileData *bfd;
576   UserDef *userdef = NULL;
577 
578   bfd = BLO_read_from_memory(
579       filebuf, filelength, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, reports);
580   if (bfd) {
581     if (bfd->user) {
582       userdef = bfd->user;
583     }
584     BKE_main_free(bfd->main);
585     MEM_freeN(bfd);
586   }
587   else {
588     BKE_reports_prepend(reports, "Loading failed: ");
589   }
590 
591   return userdef;
592 }
593 
BKE_blendfile_userdef_from_defaults(void)594 UserDef *BKE_blendfile_userdef_from_defaults(void)
595 {
596   UserDef *userdef = MEM_mallocN(sizeof(*userdef), __func__);
597   memcpy(userdef, &U_default, sizeof(*userdef));
598 
599   /* Add-ons. */
600   {
601     const char *addons[] = {
602         "io_anim_bvh",
603         "io_curve_svg",
604         "io_mesh_ply",
605         "io_mesh_stl",
606         "io_mesh_uv_layout",
607         "io_scene_fbx",
608         "io_scene_gltf2",
609         "io_scene_obj",
610         "io_scene_x3d",
611         "cycles",
612     };
613     for (int i = 0; i < ARRAY_SIZE(addons); i++) {
614       bAddon *addon = BKE_addon_new();
615       STRNCPY(addon->module, addons[i]);
616       BLI_addtail(&userdef->addons, addon);
617     }
618   }
619 
620   /* Theme. */
621   {
622     bTheme *btheme = MEM_mallocN(sizeof(*btheme), __func__);
623     memcpy(btheme, &U_theme_default, sizeof(*btheme));
624 
625     BLI_addtail(&userdef->themes, btheme);
626   }
627 
628 #ifdef WITH_PYTHON_SECURITY
629   /* use alternative setting for security nuts
630    * otherwise we'd need to patch the binary blob - startup.blend.c */
631   userdef->flag |= USER_SCRIPT_AUTOEXEC_DISABLE;
632 #else
633   userdef->flag &= ~USER_SCRIPT_AUTOEXEC_DISABLE;
634 #endif
635 
636   /* System-specific fonts directory. */
637   BKE_appdir_font_folder_default(userdef->fontdir);
638 
639   userdef->memcachelimit = min_ii(BLI_system_memory_max_in_megabytes_int() / 2,
640                                   userdef->memcachelimit);
641 
642   /* Init weight paint range. */
643   BKE_colorband_init(&userdef->coba_weight, true);
644 
645   /* Default studio light. */
646   BKE_studiolight_default(userdef->light_param, userdef->light_ambient);
647 
648   return userdef;
649 }
650 
651 /**
652  * Only write the userdef in a .blend
653  * \return success
654  */
BKE_blendfile_userdef_write(const char * filepath,ReportList * reports)655 bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports)
656 {
657   Main *mainb = MEM_callocN(sizeof(Main), "empty main");
658   bool ok = false;
659 
660   if (BLO_write_file(mainb,
661                      filepath,
662                      0,
663                      &(const struct BlendFileWriteParams){
664                          .use_userdef = true,
665                      },
666                      reports)) {
667     ok = true;
668   }
669 
670   MEM_freeN(mainb);
671 
672   return ok;
673 }
674 
675 /**
676  * Only write the userdef in a .blend, merging with the existing blend file.
677  * \return success
678  *
679  * \note In the future we should re-evaluate user preferences,
680  * possibly splitting out system/hardware specific prefs.
681  */
BKE_blendfile_userdef_write_app_template(const char * filepath,ReportList * reports)682 bool BKE_blendfile_userdef_write_app_template(const char *filepath, ReportList *reports)
683 {
684   /* if it fails, overwrite is OK. */
685   UserDef *userdef_default = BKE_blendfile_userdef_read(filepath, NULL);
686   if (userdef_default == NULL) {
687     return BKE_blendfile_userdef_write(filepath, reports);
688   }
689 
690   BKE_blender_userdef_app_template_data_swap(&U, userdef_default);
691   bool ok = BKE_blendfile_userdef_write(filepath, reports);
692   BKE_blender_userdef_app_template_data_swap(&U, userdef_default);
693   BKE_blender_userdef_data_free(userdef_default, false);
694   MEM_freeN(userdef_default);
695   return ok;
696 }
697 
BKE_blendfile_userdef_write_all(ReportList * reports)698 bool BKE_blendfile_userdef_write_all(ReportList *reports)
699 {
700   char filepath[FILE_MAX];
701   const char *cfgdir;
702   bool ok = true;
703   const bool use_template_userpref = BKE_appdir_app_template_has_userpref(U.app_template);
704 
705   if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL))) {
706     bool ok_write;
707     BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
708 
709     printf("Writing userprefs: '%s' ", filepath);
710     if (use_template_userpref) {
711       ok_write = BKE_blendfile_userdef_write_app_template(filepath, reports);
712     }
713     else {
714       ok_write = BKE_blendfile_userdef_write(filepath, reports);
715     }
716 
717     if (ok_write) {
718       printf("ok\n");
719     }
720     else {
721       printf("fail\n");
722       ok = false;
723     }
724   }
725   else {
726     BKE_report(reports, RPT_ERROR, "Unable to create userpref path");
727   }
728 
729   if (use_template_userpref) {
730     if ((cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template))) {
731       /* Also save app-template prefs */
732       BLI_path_join(filepath, sizeof(filepath), cfgdir, BLENDER_USERPREF_FILE, NULL);
733 
734       printf("Writing userprefs app-template: '%s' ", filepath);
735       if (BKE_blendfile_userdef_write(filepath, reports) != 0) {
736         printf("ok\n");
737       }
738       else {
739         printf("fail\n");
740         ok = false;
741       }
742     }
743     else {
744       BKE_report(reports, RPT_ERROR, "Unable to create app-template userpref path");
745       ok = false;
746     }
747   }
748 
749   if (ok) {
750     U.runtime.is_dirty = false;
751   }
752   return ok;
753 }
754 
BKE_blendfile_workspace_config_read(const char * filepath,const void * filebuf,int filelength,ReportList * reports)755 WorkspaceConfigFileData *BKE_blendfile_workspace_config_read(const char *filepath,
756                                                              const void *filebuf,
757                                                              int filelength,
758                                                              ReportList *reports)
759 {
760   BlendFileData *bfd;
761   WorkspaceConfigFileData *workspace_config = NULL;
762 
763   if (filepath) {
764     bfd = BLO_read_from_file(filepath, BLO_READ_SKIP_USERDEF, reports);
765   }
766   else {
767     bfd = BLO_read_from_memory(filebuf, filelength, BLO_READ_SKIP_USERDEF, reports);
768   }
769 
770   if (bfd) {
771     workspace_config = MEM_callocN(sizeof(*workspace_config), __func__);
772     workspace_config->main = bfd->main;
773 
774     /* Only 2.80+ files have actual workspaces, don't try to use screens
775      * from older versions. */
776     if (bfd->main->versionfile >= 280) {
777       workspace_config->workspaces = bfd->main->workspaces;
778     }
779 
780     MEM_freeN(bfd);
781   }
782 
783   return workspace_config;
784 }
785 
BKE_blendfile_workspace_config_write(Main * bmain,const char * filepath,ReportList * reports)786 bool BKE_blendfile_workspace_config_write(Main *bmain, const char *filepath, ReportList *reports)
787 {
788   const int fileflags = G.fileflags & ~G_FILE_NO_UI;
789   bool retval = false;
790 
791   BKE_blendfile_write_partial_begin(bmain);
792 
793   for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
794     BKE_blendfile_write_partial_tag_ID(&workspace->id, true);
795   }
796 
797   if (BKE_blendfile_write_partial(
798           bmain, filepath, fileflags, BLO_WRITE_PATH_REMAP_NONE, reports)) {
799     retval = true;
800   }
801 
802   BKE_blendfile_write_partial_end(bmain);
803 
804   return retval;
805 }
806 
BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData * workspace_config)807 void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace_config)
808 {
809   BKE_main_free(workspace_config->main);
810   MEM_freeN(workspace_config);
811 }
812 
813 /** \} */
814 
815 /* -------------------------------------------------------------------- */
816 /** \name Partial `.blend` file save.
817  * \{ */
818 
BKE_blendfile_write_partial_begin(Main * bmain_src)819 void BKE_blendfile_write_partial_begin(Main *bmain_src)
820 {
821   BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
822 }
823 
BKE_blendfile_write_partial_tag_ID(ID * id,bool set)824 void BKE_blendfile_write_partial_tag_ID(ID *id, bool set)
825 {
826   if (set) {
827     id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
828   }
829   else {
830     id->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
831   }
832 }
833 
blendfile_write_partial_cb(void * UNUSED (handle),Main * UNUSED (bmain),void * vid)834 static void blendfile_write_partial_cb(void *UNUSED(handle), Main *UNUSED(bmain), void *vid)
835 {
836   if (vid) {
837     ID *id = vid;
838     /* only tag for need-expand if not done, prevents eternal loops */
839     if ((id->tag & LIB_TAG_DOIT) == 0) {
840       id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
841     }
842 
843     if (id->lib && (id->lib->id.tag & LIB_TAG_DOIT) == 0) {
844       id->lib->id.tag |= LIB_TAG_DOIT;
845     }
846   }
847 }
848 
849 /**
850  * \param remap_mode: Choose the kind of path remapping or none #eBLO_WritePathRemap.
851  * \return Success.
852  */
BKE_blendfile_write_partial(Main * bmain_src,const char * filepath,const int write_flags,const int remap_mode,ReportList * reports)853 bool BKE_blendfile_write_partial(Main *bmain_src,
854                                  const char *filepath,
855                                  const int write_flags,
856                                  const int remap_mode,
857                                  ReportList *reports)
858 {
859   Main *bmain_dst = MEM_callocN(sizeof(Main), "copybuffer");
860   ListBase *lbarray_dst[MAX_LIBARRAY], *lbarray_src[MAX_LIBARRAY];
861   int a, retval;
862 
863   void *path_list_backup = NULL;
864   const int path_list_flag = (BKE_BPATH_TRAVERSE_SKIP_LIBRARY | BKE_BPATH_TRAVERSE_SKIP_MULTIFILE);
865 
866   /* This is needed to be able to load that file as a real one later
867    * (otherwise main->name will not be set at read time). */
868   BLI_strncpy(bmain_dst->name, bmain_src->name, sizeof(bmain_dst->name));
869 
870   BLO_main_expander(blendfile_write_partial_cb);
871   BLO_expand_main(NULL, bmain_src);
872 
873   /* move over all tagged blocks */
874   set_listbasepointers(bmain_src, lbarray_src);
875   a = set_listbasepointers(bmain_dst, lbarray_dst);
876   while (a--) {
877     ID *id, *nextid;
878     ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a];
879 
880     for (id = lb_src->first; id; id = nextid) {
881       nextid = id->next;
882       if (id->tag & LIB_TAG_DOIT) {
883         BLI_remlink(lb_src, id);
884         BLI_addtail(lb_dst, id);
885       }
886     }
887   }
888 
889   /* Backup paths because remap relative will overwrite them.
890    *
891    * NOTE: we do this only on the list of data-blocks that we are writing
892    * because the restored full list is not guaranteed to be in the same
893    * order as before, as expected by BKE_bpath_list_restore.
894    *
895    * This happens because id_sort_by_name does not take into account
896    * string case or the library name, so the order is not strictly
897    * defined for two linked data-blocks with the same name! */
898   if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
899     path_list_backup = BKE_bpath_list_backup(bmain_dst, path_list_flag);
900   }
901 
902   /* save the buffer */
903   retval = BLO_write_file(bmain_dst,
904                           filepath,
905                           write_flags,
906                           &(const struct BlendFileWriteParams){
907                               .remap_mode = remap_mode,
908                           },
909                           reports);
910 
911   if (path_list_backup) {
912     BKE_bpath_list_restore(bmain_dst, path_list_flag, path_list_backup);
913     BKE_bpath_list_free(path_list_backup);
914   }
915 
916   /* move back the main, now sorted again */
917   set_listbasepointers(bmain_src, lbarray_dst);
918   a = set_listbasepointers(bmain_dst, lbarray_src);
919   while (a--) {
920     ID *id;
921     ListBase *lb_dst = lbarray_dst[a], *lb_src = lbarray_src[a];
922 
923     while ((id = BLI_pophead(lb_src))) {
924       BLI_addtail(lb_dst, id);
925       id_sort_by_name(lb_dst, id, NULL);
926     }
927   }
928 
929   MEM_freeN(bmain_dst);
930 
931   return retval;
932 }
933 
BKE_blendfile_write_partial_end(Main * bmain_src)934 void BKE_blendfile_write_partial_end(Main *bmain_src)
935 {
936   BKE_main_id_tag_all(bmain_src, LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT, false);
937 }
938 
939 /** \} */
940