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