Blender V4.5
blendfile.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include <cstdlib>
13#include <cstring>
14#include <optional>
15
16#include "CLG_log.h"
17
18#include "MEM_guardedalloc.h"
19
20#include "DNA_brush_types.h"
21#include "DNA_scene_types.h"
22#include "DNA_screen_types.h"
23#include "DNA_space_types.h"
24
25#include "BLI_fileops.h"
26#include "BLI_function_ref.hh"
27#include "BLI_listbase.h"
28#include "BLI_path_utils.hh"
29#include "BLI_string.h"
30#include "BLI_system.h"
31#include "BLI_time.h"
32#include "BLI_utildefines.h"
33#include "BLI_vector.hh"
34#include "BLI_vector_set.hh"
35
36#include "BLT_translation.hh"
37
39
40#include "BKE_addon.h"
41#include "BKE_appdir.hh"
42#include "BKE_blender.hh"
43#include "BKE_blender_version.h"
44#include "BKE_blendfile.hh"
45#include "BKE_bpath.hh"
46#include "BKE_colorband.hh"
47#include "BKE_context.hh"
48#include "BKE_global.hh"
49#include "BKE_idtype.hh"
50#include "BKE_layer.hh"
51#include "BKE_lib_id.hh"
52#include "BKE_lib_override.hh"
53#include "BKE_lib_query.hh"
54#include "BKE_lib_remap.hh"
55#include "BKE_library.hh"
56#include "BKE_main.hh"
57#include "BKE_main_idmap.hh"
58#include "BKE_main_namemap.hh"
59#include "BKE_preferences.h"
60#include "BKE_report.hh"
61#include "BKE_scene.hh"
62#include "BKE_screen.hh"
63#include "BKE_studiolight.h"
64#include "BKE_undo_system.hh"
65#include "BKE_workspace.hh"
66
67#include "BLO_read_write.hh"
68#include "BLO_readfile.hh"
69#include "BLO_userdef_default.h"
70#include "BLO_writefile.hh"
71
72#include "RE_pipeline.h"
73
74#ifdef WITH_PYTHON
75# include "BPY_extern.hh"
76#endif
77
78using namespace blender::bke;
79
80/* -------------------------------------------------------------------- */
83
85{
86 const char *ext_test[4] = {".blend", ".ble", ".blend.gz", nullptr};
87 return BLI_path_extension_check_array(str, ext_test);
88}
89
91 char *r_dir,
92 char **r_group,
93 char **r_name)
94{
95 /* We might get some data names with slashes,
96 * so we have to go up in path until we find blend file itself,
97 * then we know next path item is group, and everything else is data name. */
98 char *slash = nullptr, *prev_slash = nullptr, c = '\0';
99
100 r_dir[0] = '\0';
101 if (r_group) {
102 *r_group = nullptr;
103 }
104 if (r_name) {
105 *r_name = nullptr;
106 }
107
108 /* if path leads to an existing directory, we can be sure we're not (in) a library */
109 if (BLI_is_dir(path)) {
110 return false;
111 }
112
113 BLI_strncpy(r_dir, path, FILE_MAX_LIBEXTRA);
114
115 while ((slash = (char *)BLI_path_slash_rfind(r_dir))) {
116 char tc = *slash;
117 *slash = '\0';
118 if (BKE_blendfile_extension_check(r_dir) && BLI_is_file(r_dir)) {
119 break;
120 }
121 if (STREQ(r_dir, BLO_EMBEDDED_STARTUP_BLEND)) {
122 break;
123 }
124
125 if (prev_slash) {
126 *prev_slash = c;
127 }
128 prev_slash = slash;
129 c = tc;
130 }
131
132 if (!slash) {
133 return false;
134 }
135
136 if (slash[1] != '\0') {
137 BLI_assert(strlen(slash + 1) < BLO_GROUP_MAX);
138 if (r_group) {
139 *r_group = slash + 1;
140 }
141 }
142
143 if (prev_slash && (prev_slash[1] != '\0')) {
144 BLI_assert(strlen(prev_slash + 1) < MAX_ID_NAME - 2);
145 if (r_name) {
146 *r_name = prev_slash + 1;
147 }
148 }
149
150 return true;
151}
152
154{
155 BlendFileReadReport readfile_reports;
156 readfile_reports.reports = reports;
157 BlendHandle *bh = BLO_blendhandle_from_file(path, &readfile_reports);
158 if (bh != nullptr) {
160 return true;
161 }
162 return false;
163}
164
166
167/* -------------------------------------------------------------------- */
170
171static bool foreach_path_clean_cb(BPathForeachPathData * /*bpath_data*/,
172 char *path_dst,
173 size_t path_dst_maxncpy,
174 const char *path_src)
175{
176 BLI_strncpy(path_dst, path_src, path_dst_maxncpy);
177 BLI_path_slash_native(path_dst);
178 return !STREQ(path_dst, path_src);
179}
180
181/* make sure path names are correct for OS */
182static void clean_paths(Main *bmain)
183{
184 BPathForeachPathData foreach_path_data{};
185 foreach_path_data.bmain = bmain;
186 foreach_path_data.callback_function = foreach_path_clean_cb;
188 foreach_path_data.user_data = nullptr;
189
190 BKE_bpath_foreach_path_main(&foreach_path_data);
191
192 LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
193 BLI_path_slash_native(scene->r.pic);
194 }
195}
196
198{
199 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
200 if (win->scene == scene) {
201 return true;
202 }
203 }
204 return false;
205}
206
208{
209 if (bfd->user) {
210 /* only here free userdef themes... */
212 bfd->user = nullptr;
213
214 /* Security issue: any blend file could include a #BLO_CODE_USER block.
215 *
216 * Preferences are loaded from #BLENDER_STARTUP_FILE and later on load #BLENDER_USERPREF_FILE,
217 * to load the preferences defined in the users home directory.
218 *
219 * This means we will never accidentally (or maliciously)
220 * enable scripts auto-execution by loading a `.blend` file. */
222 }
223}
224
257
269{
270 if (reuse_data->is_libraries_remapped) {
271 return *reuse_data->remapper;
272 }
273
274 if (reuse_data->remapper == nullptr) {
275 reuse_data->remapper = MEM_new<id::IDRemapper>(__func__);
276 }
277
278 Main *new_bmain = reuse_data->new_bmain;
279 Main *old_bmain = reuse_data->old_bmain;
280 id::IDRemapper &remapper = *reuse_data->remapper;
281
282 LISTBASE_FOREACH (Library *, old_lib_iter, &old_bmain->libraries) {
283 /* In case newly opened `new_bmain` is a library of the `old_bmain`, remap it to null, since a
284 * file should never ever have linked data from itself. */
285 if (STREQ(old_lib_iter->runtime->filepath_abs, new_bmain->filepath)) {
286 remapper.add(&old_lib_iter->id, nullptr);
287 continue;
288 }
289
290 /* NOTE: Although this is quadratic complexity, it is not expected to be an issue in practice:
291 * - Files using more than a few tens of libraries are extremely rare.
292 * - This code is only executed once for every file reading (not on undos).
293 */
294 LISTBASE_FOREACH (Library *, new_lib_iter, &new_bmain->libraries) {
295 if (!STREQ(old_lib_iter->runtime->filepath_abs, new_lib_iter->runtime->filepath_abs)) {
296 continue;
297 }
298
299 remapper.add(&old_lib_iter->id, &new_lib_iter->id);
300 break;
301 }
302 }
303
304 reuse_data->is_libraries_remapped = true;
305 return *reuse_data->remapper;
306}
307
309{
312 /* ID is already remapped to its matching ID in the new main, or explicitly remapped to null,
313 * nothing else to do here. */
314 return true;
315 }
317 "There should never be a non-mappable (i.e. null) input here.");
319 return false;
320}
321
323 ID *id,
324 Library *lib,
325 const bool reuse_existing)
326{
327 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
328 /* Nothing to move for embedded ID. */
329 if (id->flag & ID_FLAG_EMBEDDED_DATA) {
330 remapper.add(id, id);
331 return true;
332 }
333
334 Main *new_bmain = reuse_data->new_bmain;
335 Main *old_bmain = reuse_data->old_bmain;
337 ListBase *old_lb = which_libbase(old_bmain, GS(id->name));
338
339 if (reuse_existing) {
340 /* A 'new' version of the same data may already exist in new_bmain, in the rare case
341 * that the same asset blend file was linked explicitly into the blend file we are loading.
342 * Don't move the old linked ID, but remap its usages to the new one instead. */
343 LISTBASE_FOREACH_BACKWARD (ID *, id_iter, new_lb) {
344 if (!ELEM(id_iter->lib, id->lib, lib)) {
345 continue;
346 }
347 if (!STREQ(id_iter->name + 2, id->name + 2)) {
348 continue;
349 }
350
351 remapper.add(id, id_iter);
352 return false;
353 }
354 }
355
356 /* If ID is already in the new_bmain, this should not have been called. */
358 BLI_assert(BLI_findindex(old_lb, id) >= 0);
359
360 /* Move from one list to another, and ensure name is valid. */
361 BLI_remlink_safe(old_lb, id);
362
363 /* In case the ID is linked and its library ID is re-used from the old Main, it is not possible
364 * to handle name_map (and ensure name uniqueness).
365 * This is because IDs are moved one by one from old Main's lists to new ones, while the re-used
366 * library's name_map would be built only from IDs in the new list, leading to incomplete/invalid
367 * states.
368 * Currently, such name uniqueness checks should not be needed, as no new name would be expected
369 * in the re-used library. Should this prove to be wrong at some point, the name check will have
370 * to happen at the end of #reuse_editable_asset_bmain_data_for_blendfile, in a separate loop
371 * over Main IDs.
372 */
373 const bool handle_name_map_updates = !ID_IS_LINKED(id) || id->lib != lib;
374 if (handle_name_map_updates) {
376 }
377
378 id->lib = lib;
379 BLI_addtail(new_lb, id);
380 if (handle_name_map_updates) {
382 *new_bmain, *new_lb, *id, nullptr, IDNewNameMode::RenameExistingNever, true);
383 }
384 else {
385 id_sort_by_name(new_lb, id, nullptr);
386 }
388
389 /* Remap to itself, to avoid re-processing this ID again. */
390 remapper.add(id, id);
391 return true;
392}
393
395 Library *old_lib)
396{
397 const id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
398 Library *new_lib = old_lib;
399 IDRemapperApplyResult result = remapper.apply(reinterpret_cast<ID **>(&new_lib),
401
402 switch (result) {
404 /* Move library to new bmain.
405 * There should be no filepath conflicts, as #reuse_bmain_data_remapper_ensure has
406 * already remapped existing libraries with matching filepath. */
407 reuse_bmain_move_id(reuse_data, &old_lib->id, nullptr, false);
408 /* Clear the name_map of the library, as not all of its IDs are guaranteed reused. The name
409 * map cannot be used/kept in valid state while some IDs are moved from old to new main. See
410 * also #reuse_bmain_move_id code. */
411 BKE_main_namemap_destroy(&old_lib->runtime->name_map);
412 return old_lib;
413 }
416 return nullptr;
417 }
419 /* Already in new bmain, only transfer flags. */
420 new_lib->runtime->tag |= old_lib->runtime->tag &
422 return new_lib;
423 }
425 /* Happens when the library is the newly opened blend file. */
426 return nullptr;
427 }
428 }
429
431 return nullptr;
432}
433
436{
437 ID *id = *cb_data->id_pointer;
438
439 if (id == nullptr) {
440 return IDWALK_RET_NOP;
441 }
442
443 if (GS(id->name) == ID_LI) {
444 /* Libraries are handled separately. */
446 }
447
448 ReuseOldBMainData *reuse_data = static_cast<ReuseOldBMainData *>(cb_data->user_data);
449
450 /* First check if it has already been remapped. */
451 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
454 }
455
456 if (id->lib == nullptr) {
457 /* There should be no links to local datablocks from linked editable data. */
458 remapper.add(id, nullptr);
461 }
462
463 /* Only preserve specific datablock types. */
465 remapper.add(id, nullptr);
467 }
468
469 /* There may be a new library pointer in new_bmain, matching a library in old_bmain, even
470 * though pointer values are not the same. So we need to check new linked IDs in new_bmain
471 * against both potential library pointers. */
472 Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, id->lib);
473
474 /* Happens when the library is the newly opened blend file. */
475 if (old_id_new_lib == nullptr) {
476 remapper.add(id, nullptr);
478 }
479
480 /* Move to new main database. */
481 return reuse_bmain_move_id(reuse_data, id, old_id_new_lib, true) ? IDWALK_RET_STOP_RECURSION :
483}
484
486{
487 Main *old_bmain = reuse_data->old_bmain;
488 LISTBASE_FOREACH (Library *, lib, &old_bmain->libraries) {
489 if (lib->runtime->tag & LIBRARY_ASSET_EDITABLE) {
490 return true;
491 }
492 }
493 return false;
494}
495
510 const short idcode)
511{
512 Main *new_bmain = reuse_data->new_bmain;
513 Main *old_bmain = reuse_data->old_bmain;
514
515 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
516
517 ListBase *old_lb = which_libbase(old_bmain, idcode);
518 ID *old_id_iter;
519
520 FOREACH_MAIN_LISTBASE_ID_BEGIN (old_lb, old_id_iter) {
521 /* Keep any datablocks from libraries marked as LIBRARY_ASSET_EDITABLE. */
522 if (!(ID_IS_LINKED(old_id_iter) && old_id_iter->lib->runtime->tag & LIBRARY_ASSET_EDITABLE)) {
523 continue;
524 }
525
526 Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data,
527 old_id_iter->lib);
528
529 /* Happens when the library is the newly opened blend file. */
530 if (old_id_new_lib == nullptr) {
531 remapper.add(old_id_iter, nullptr);
532 continue;
533 }
534
535 if (reuse_bmain_move_id(reuse_data, old_id_iter, old_id_new_lib, true)) {
536 /* Port over dependencies of re-used ID, unless matching already existing ones in
537 * new_bmain can be found.
538 *
539 * NOTE : No pointers are remapped here, this code only moves dependencies from old_bmain
540 * to new_bmain if needed, and add necessary remapping rules to the reuse_data.remapper. */
542 old_id_iter,
544 reuse_data,
546 }
547 }
549}
550
556{
557 ID *old_id_iter;
558 FOREACH_MAIN_LISTBASE_ID_BEGIN (&reuse_data->old_bmain->brushes, old_id_iter) {
559 const Brush *brush = reinterpret_cast<Brush *>(old_id_iter);
560 if (brush->gpencil_settings && brush->gpencil_settings->material &&
561 /* Don't unpin if this material is linked, then it can be preserved for the new file. */
562 !ID_IS_LINKED(brush->gpencil_settings->material))
563 {
564 /* Unpin material and clear pointer. */
565 brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
566 brush->gpencil_settings->material = nullptr;
567 }
568 }
570}
571
583static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code)
584{
585 Main *new_bmain = reuse_data->new_bmain;
586 Main *old_bmain = reuse_data->old_bmain;
587
589 ListBase *old_lb = which_libbase(old_bmain, id_code);
590
591 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
592
593 /* NOTE: Full swapping is only supported for ID types that are assumed to be only local
594 * data-blocks (like UI-like ones). Otherwise, the swapping could fail in many funny ways. */
597
598 std::swap(*new_lb, *old_lb);
599
600 /* TODO: Could add per-IDType control over name-maps clearing, if this becomes a performances
601 * concern. */
604
605 /* Original 'new' IDs have been moved into the old listbase and will be discarded (deleted).
606 * Original 'old' IDs have been moved into the new listbase and are being reused (kept).
607 * The discarded ones need to be remapped to a matching reused one, based on their names, if
608 * possible.
609 *
610 * Since both lists are ordered, and they are all local, we can do a smart parallel processing of
611 * both lists here instead of doing complete full list searches. */
612 ID *discarded_id_iter = static_cast<ID *>(old_lb->first);
613 ID *reused_id_iter = static_cast<ID *>(new_lb->first);
615 const int strcmp_result = strcmp(discarded_id_iter->name + 2, reused_id_iter->name + 2);
616 if (strcmp_result == 0) {
617 /* Matching IDs, we can remap the discarded 'new' one to the re-used 'old' one. */
619
620 discarded_id_iter = static_cast<ID *>(discarded_id_iter->next);
621 reused_id_iter = static_cast<ID *>(reused_id_iter->next);
622 }
623 else if (strcmp_result < 0) {
624 /* No matching reused 'old' ID for this discarded 'new' one. */
625 remapper.add(discarded_id_iter, nullptr);
626
627 discarded_id_iter = static_cast<ID *>(discarded_id_iter->next);
628 }
629 else {
630 reused_id_iter = static_cast<ID *>(reused_id_iter->next);
631 }
632 }
633 /* Also remap all remaining non-compared discarded 'new' IDs to null. */
634 for (; discarded_id_iter != nullptr;
635 discarded_id_iter = static_cast<ID *>(discarded_id_iter->next))
636 {
637 remapper.add(discarded_id_iter, nullptr);
638 }
639
641 /* Necessary as all `session_uid` are renewed on blendfile loading. */
643
644 /* Ensure that the reused ID is remapped to itself, since it is known to be in the `new_bmain`.
645 */
647 }
649}
650
656static void swap_wm_data_for_blendfile(ReuseOldBMainData *reuse_data, const bool load_ui)
657{
658 Main *old_bmain = reuse_data->old_bmain;
659 Main *new_bmain = reuse_data->new_bmain;
660 ListBase *old_wm_list = &old_bmain->wm;
661 ListBase *new_wm_list = &new_bmain->wm;
662
663 /* Currently there should never be more than one WM in a main. */
664 BLI_assert(BLI_listbase_count_at_most(new_wm_list, 2) <= 1);
665 BLI_assert(BLI_listbase_count_at_most(old_wm_list, 2) <= 1);
666
667 wmWindowManager *old_wm = static_cast<wmWindowManager *>(old_wm_list->first);
668 wmWindowManager *new_wm = static_cast<wmWindowManager *>(new_wm_list->first);
669
670 if (old_wm == nullptr) {
671 /* No current (old) WM. Either (new) WM from file is used, or if none, WM code is responsible
672 * to add a new default WM. Nothing to do here. */
673 return;
674 }
675
676 /* Current (old) WM, and (new) WM in file, and loading UI: use WM from file, keep old WM around
677 * for further processing in WM code. */
678 if (load_ui && new_wm != nullptr) {
679 /* Support window-manager ID references being held between file load operations by keeping
680 * #Main.wm.first memory address in-place, while swapping all of its contents.
681 *
682 * This is needed so items such as key-maps can be held by an add-on,
683 * without it pointing to invalid memory, see: #86431. */
684 BLI_remlink(old_wm_list, old_wm);
685 BLI_remlink(new_wm_list, new_wm);
686 BKE_lib_id_swap_full(nullptr,
687 &old_wm->id,
688 &new_wm->id,
689 true,
692 /* Not strictly necessary, but helps for readability. */
693 std::swap<wmWindowManager *>(old_wm, new_wm);
694 BLI_addhead(new_wm_list, new_wm);
695 /* Do not add old WM back to `old_bmain`, so that it does not get freed when `old_bmain` is
696 * freed. Calling WM code will need this old WM to restore some windows etc. data into the
697 * new WM, and is responsible to free it properly. */
698 reuse_data->wm_setup_data->old_wm = old_wm;
699
700 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
701 remapper.add(&old_wm->id, &new_wm->id);
702 }
703 /* Current (old) WM, but no (new) one in file (should only happen when reading pre 2.5 files, no
704 * WM back then), or not loading UI: Keep current WM. */
705 else {
706 swap_old_bmain_data_for_blendfile(reuse_data, ID_WM);
708 reuse_data->wm_setup_data->old_wm = old_wm;
709 }
710}
711
714{
715 ID *id = *cb_data->id_pointer;
716
717 if (id == nullptr) {
718 return IDWALK_RET_NOP;
719 }
720
721 ReuseOldBMainData *reuse_data = static_cast<ReuseOldBMainData *>(cb_data->user_data);
722
723 /* First check if it has already been remapped. */
724 id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
726 return IDWALK_RET_NOP;
727 }
728
729 IDNameLib_Map *id_map = reuse_data->id_map;
730 BLI_assert(id_map != nullptr);
731
732 ID *id_new = BKE_main_idmap_lookup_id(id_map, id);
733 remapper.add(id, id_new);
734
735 return IDWALK_RET_NOP;
736}
737
739 const short id_code)
740{
741 Main *new_bmain = reuse_data->new_bmain;
743
744 BLI_assert(reuse_data->id_map != nullptr);
745
746 ID *new_id_iter;
748 /* Check all ID usages and find a matching new ID to remap them to in `new_bmain` if possible
749 * (matching by names and libraries).
750 *
751 * Note that this call does not do any effective remapping, it only adds required remapping
752 * operations to the remapper. */
754 new_id_iter,
756 reuse_data,
758 }
760}
761
763{
764 ID *id = *cb_data->id_pointer;
765
766 if (id == nullptr) {
767 return IDWALK_RET_NOP;
768 }
769
770 /* Embedded data cannot (yet) be fully trusted to have the same lib pointer as their owner ID, so
771 * for now ignore them. This code should never have anything to fix for them anyway, otherwise
772 * there is something extremely wrong going on. */
773 if ((cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) != 0) {
774 return IDWALK_RET_NOP;
775 }
776
777 if (!ID_IS_LINKED(id)) {
778 ID *owner_id = cb_data->owner_id;
779
780 /* Do not allow linked data to use local data. */
781 if (ID_IS_LINKED(owner_id)) {
782 if (cb_data->cb_flag & IDWALK_CB_USER) {
783 id_us_min(id);
784 }
785 *cb_data->id_pointer = nullptr;
786 }
787 /* Do not allow local liboverride data to use local data as reference. */
788 else if (ID_IS_OVERRIDE_LIBRARY_REAL(owner_id) &&
789 &owner_id->override_library->reference == cb_data->id_pointer)
790 {
791 if (cb_data->cb_flag & IDWALK_CB_USER) {
792 id_us_min(id);
793 }
794 *cb_data->id_pointer = nullptr;
795 }
796 }
797
798 return IDWALK_RET_NOP;
799}
800
805{
806 Main *new_bmain = reuse_data->new_bmain;
807 ID *id_iter;
809 if (!ID_IS_LINKED(id_iter) && !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
810 continue;
811 }
812
813 ID *liboverride_reference = ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ?
814 id_iter->override_library->reference :
815 nullptr;
816
819
820 /* Liboverrides who lost their reference should not be liboverrides anymore, but regular IDs.
821 */
822 if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) &&
823 id_iter->override_library->reference != liboverride_reference)
824 {
826 }
827 }
829}
830
831/* Post-remapping helpers to ensure validity of the UI data. */
832
833static void view3d_data_consistency_ensure(wmWindow *win, Scene *scene, ViewLayer *view_layer)
834{
836
837 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
838 LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
839 if (sl->spacetype != SPACE_VIEW3D) {
840 continue;
841 }
842
843 View3D *v3d = reinterpret_cast<View3D *>(sl);
844 if (v3d->camera == nullptr || v3d->scenelock) {
845 v3d->camera = scene->camera;
846 }
847 if (v3d->localvd == nullptr) {
848 continue;
849 }
850
851 if (v3d->localvd->camera == nullptr || v3d->scenelock) {
852 v3d->localvd->camera = v3d->camera;
853 }
854 /* Local-view can become invalid during undo/redo steps, exit it when no valid object could
855 * be found. */
856 Base *base;
857 for (base = static_cast<Base *>(view_layer->object_bases.first); base; base = base->next) {
858 if (base->local_view_bits & v3d->local_view_uid) {
859 break;
860 }
861 }
862 if (base != nullptr) {
863 /* The local view3D still has a valid object, nothing else to do. */
864 continue;
865 }
866
867 /* No valid object found for the local view3D, it has to be cleared off. */
868 MEM_freeN(v3d->localvd);
869 v3d->localvd = nullptr;
870 v3d->local_view_uid = 0;
871
872 /* Region-base storage is different depending on whether the space is active or not. */
873 ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
874 LISTBASE_FOREACH (ARegion *, region, regionbase) {
875 if (region->regiontype != RGN_TYPE_WINDOW) {
876 continue;
877 }
878
879 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
880 MEM_SAFE_FREE(rv3d->localvd);
881 }
882 }
883 }
884}
885
887 Scene *cur_scene,
888 ViewLayer *cur_view_layer)
889{
890 /* There may not be any available WM (e.g. when reading `userpref.blend`). */
891 if (curwm == nullptr) {
892 return;
893 }
894
895 LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) {
896 if (win->scene == nullptr) {
897 win->scene = cur_scene;
898 }
899 if (BKE_view_layer_find(win->scene, win->view_layer_name) == nullptr) {
900 STRNCPY(win->view_layer_name, cur_view_layer->name);
901 }
902
903 view3d_data_consistency_ensure(win, win->scene, cur_view_layer);
904 }
905}
906
920 BlendFileData *bfd,
922 BlendFileReadWMSetupData *wm_setup_data,
924{
925 Main *bmain = G_MAIN;
926 const bool recover = (G.fileflags & G_FILE_RECOVER_READ) != 0;
927 enum {
928 LOAD_UI = 1,
929 LOAD_UI_OFF,
930 LOAD_UNDO,
931 } mode;
932
933 if (params->undo_direction != STEP_INVALID) {
934 BLI_assert(bfd->curscene != nullptr);
935 mode = LOAD_UNDO;
936 }
937 else if (bfd->fileflags & G_FILE_ASSET_EDIT_FILE) {
938 BKE_report(reports->reports,
940 "This file is managed by the asset system, you cannot overwrite it (using \"Save "
941 "As\" is possible)");
942 /* From now on the file in memory is a normal file, further saving it will contain a
943 * window-manager, scene, ... and potentially user created data. Use #Main.is_asset_edit_file
944 * to detect if saving this file needs extra protections. */
947 mode = LOAD_UI_OFF;
948 }
949 /* May happen with library files, loading undo-data should never have a null `curscene`
950 * (but may have a null `curscreen`). */
951 else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
952 BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
953 mode = LOAD_UI_OFF;
954 }
955 else if (G.fileflags & G_FILE_NO_UI) {
956 mode = LOAD_UI_OFF;
957 }
958 else {
959 mode = LOAD_UI;
960 }
961
962 /* Free all render results and interactive compositor renders, without this stale data gets
963 * displayed after loading files */
964 if (mode != LOAD_UNDO) {
967 }
968
969 /* Only make file-paths compatible when loading for real (not undo). */
970 if (mode != LOAD_UNDO) {
971 clean_paths(bfd->main);
972 }
973
975
976 /* Temporary data to handle swapping around IDs between old and new mains,
977 * and accumulate the required remapping accordingly. */
978 ReuseOldBMainData reuse_data = {nullptr};
979 reuse_data.new_bmain = bfd->main;
980 reuse_data.old_bmain = bmain;
981 reuse_data.wm_setup_data = wm_setup_data;
982
983 if (mode != LOAD_UNDO) {
984 const short ui_id_codes[]{ID_WS, ID_SCR};
985
986 /* WM needs special complex handling, regardless of whether UI is kept or loaded from file. */
987 swap_wm_data_for_blendfile(&reuse_data, mode == LOAD_UI);
988 if (mode != LOAD_UI) {
989 /* Re-use UI data from `old_bmain` if keeping existing UI. */
990 for (auto id_code : ui_id_codes) {
991 swap_old_bmain_data_for_blendfile(&reuse_data, id_code);
992 }
993 }
994
995 /* Needs to happen after all data from `old_bmain` has been moved into new one. */
996 BLI_assert(reuse_data.id_map == nullptr);
997 reuse_data.id_map = BKE_main_idmap_create(
998 reuse_data.new_bmain, true, reuse_data.old_bmain, MAIN_IDMAP_TYPE_NAME);
999
1001 if (mode != LOAD_UI) {
1002 for (auto id_code : ui_id_codes) {
1003 swap_old_bmain_data_dependencies_process(&reuse_data, id_code);
1004 }
1005 }
1006
1007 BKE_main_idmap_destroy(reuse_data.id_map);
1008
1009 if (!params->is_factory_settings && reuse_editable_asset_needed(&reuse_data)) {
1011 /* Keep linked brush asset data, similar to UI data. Only does a known
1012 * subset know. Could do everything, but that risks dragging along more
1013 * scene data than we want. */
1014 for (short idtype_index = 0; idtype_index < INDEX_ID_MAX; idtype_index++) {
1015 const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_idtype_index(idtype_index);
1016 if (ID_TYPE_SUPPORTS_ASSET_EDITABLE(idtype_info->id_code)) {
1017 reuse_editable_asset_bmain_data_for_blendfile(&reuse_data, idtype_info->id_code);
1018 }
1019 }
1020 }
1021
1022 if (mode != LOAD_UI) {
1023 LISTBASE_FOREACH (bScreen *, screen, &bfd->main->screens) {
1025 }
1026 }
1027 }
1028
1029 /* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, as long
1030 * as the scene associated with the undo operation is visible in one of the open windows.
1031 *
1032 * - 'curscreen->scene': Scene the user is currently looking at.
1033 * - 'bfd->curscene': Scene undo-step was created in.
1034 *
1035 * This means that users can have 2 or more windows open and undo in both without screens
1036 * switching. But if they close one of the screens, undo will ensure that the scene being
1037 * operated on will be activated (otherwise we'd be undoing on an off-screen scene which isn't
1038 * acceptable). See: #43424. */
1039 bool track_undo_scene = false;
1040
1041 /* Always use the Scene and ViewLayer pointers from new file, if possible. */
1042 ViewLayer *cur_view_layer = bfd->cur_view_layer;
1043 Scene *curscene = bfd->curscene;
1044
1045 wmWindow *win = nullptr;
1046 bScreen *curscreen = nullptr;
1047
1048 /* Ensure that there is a valid scene and view-layer. */
1049 if (curscene == nullptr) {
1050 curscene = static_cast<Scene *>(bfd->main->scenes.first);
1051 }
1052 /* Empty file, add a scene to make Blender work. */
1053 if (curscene == nullptr) {
1054 curscene = BKE_scene_add(bfd->main, "Empty");
1055 }
1056 if (cur_view_layer == nullptr) {
1057 /* Fallback to the active scene view layer. */
1058 cur_view_layer = BKE_view_layer_default_view(curscene);
1059 }
1060
1061 /* If UI is not loaded when opening actual `.blend` file,
1062 * and always in case of undo MEMFILE reading. */
1063 if (mode != LOAD_UI) {
1064 /* Re-use current window and screen. */
1065 win = CTX_wm_window(C);
1066 curscreen = CTX_wm_screen(C);
1067
1068 track_undo_scene = (mode == LOAD_UNDO && curscreen && curscene && bfd->main->wm.first);
1069
1070 if (track_undo_scene) {
1071 /* Keep the old (to-be-freed) scene, remapping below will ensure it's remapped to the
1072 * matching new scene if available, or null otherwise, in which case
1073 * #wm_data_consistency_ensure will define `curscene` as the active one. */
1074 }
1075 /* Enforce `curscene` to be in current screen. */
1076 else if (win) { /* The window may be null in background-mode. */
1077 win->scene = curscene;
1078 }
1079 }
1080
1082
1083 /* Apply remapping of ID pointers caused by re-using part of the data from the 'old' main into
1084 * the new one. */
1085 if (reuse_data.remapper != nullptr) {
1086 /* In undo case all "keeping old data" and remapping logic is now handled
1087 * in file reading code itself, so there should never be any remapping to do here. */
1088 BLI_assert(mode != LOAD_UNDO);
1089
1090 /* Handle all pending remapping from swapping old and new IDs around. */
1092 *reuse_data.remapper,
1095
1096 /* Fix potential invalid usages of now-locale-data created by remapping above. Should never
1097 * be needed in undo case, this is to address cases like:
1098 * "opening a blend-file that was a library of the previous opened blend-file". */
1100
1101 MEM_delete(reuse_data.remapper);
1102 reuse_data.remapper = nullptr;
1103
1104 wm_data_consistency_ensure(CTX_wm_manager(C), curscene, cur_view_layer);
1105 }
1106
1107 if (mode == LOAD_UNDO) {
1108 /* It's possible to undo into a time before the scene existed, in this case the window's scene
1109 * will be null. Since it doesn't make sense to remove the window, set it to the current scene.
1110 *
1111 * NOTE: Redo will restore the active scene to the window so a reasonably consistent state
1112 * is maintained. We could do better by keeping a window/scene map for each undo step.
1113 *
1114 * Another source of potential inconsistency is undoing into a step where the active camera
1115 * object does not exist (see e.g. #125636).
1116 */
1117 wm_data_consistency_ensure(CTX_wm_manager(C), curscene, cur_view_layer);
1118 }
1119
1121
1122 if (mode != LOAD_UI) {
1123 if (win) {
1124 curscene = win->scene;
1125 }
1126
1127 if (track_undo_scene) {
1128 wmWindowManager *wm = static_cast<wmWindowManager *>(bfd->main->wm.first);
1129 if (!wm_scene_is_visible(wm, bfd->curscene)) {
1130 curscene = bfd->curscene;
1131 if (win) {
1132 win->scene = curscene;
1133 }
1134 BKE_screen_view3d_scene_sync(curscreen, curscene);
1135 }
1136 }
1137
1138 /* We need to tag this here because events may be handled immediately after.
1139 * only the current screen is important because we won't have to handle
1140 * events from multiple screens at once. */
1141 if (curscreen) {
1143 }
1144 }
1145 CTX_data_scene_set(C, curscene);
1146
1148
1149 /* This frees the `old_bmain`. */
1151 bmain = G_MAIN;
1152 bfd->main = nullptr;
1153 CTX_data_main_set(C, bmain);
1154
1156
1157 /* These context data should remain valid if old UI is being re-used. */
1158 if (mode == LOAD_UI) {
1159 /* Setting a window-manger clears all other windowing members (window, screen, area, etc).
1160 * So only do it when effectively loading a new #wmWindowManager
1161 * otherwise just assert that the WM from context is still the same as in `new_bmain`. */
1162 CTX_wm_manager_set(C, static_cast<wmWindowManager *>(bmain->wm.first));
1164 CTX_wm_area_set(C, nullptr);
1165 CTX_wm_region_set(C, nullptr);
1166 CTX_wm_region_popup_set(C, nullptr);
1167 }
1168 BLI_assert(CTX_wm_manager(C) == static_cast<wmWindowManager *>(bmain->wm.first));
1169
1170 /* Keep state from preferences. */
1171 const int fileflags_keep = G_FILE_FLAG_ALL_RUNTIME;
1172 G.fileflags = (G.fileflags & fileflags_keep) | (bfd->fileflags & ~fileflags_keep);
1173
1174 /* Special cases, override any #G_FLAG_ALL_READFILE flags from the blend-file. */
1175 if (G.f != bfd->globalf) {
1176 const int flags_keep = G_FLAG_ALL_RUNTIME;
1178 bfd->globalf = (bfd->globalf & ~flags_keep) | (G.f & flags_keep);
1179 }
1180
1181 G.f = bfd->globalf;
1182
1183#ifdef WITH_PYTHON
1184 /* let python know about new main */
1185 if (CTX_py_init_get(C)) {
1187 }
1188#endif
1189
1190 if (mode != LOAD_UNDO) {
1191 /* Perform complex versioning that involves adding or removing IDs,
1192 * and/or needs to operate over the whole Main data-base
1193 * (versioning done in file reading code only operates on a per-library basis). */
1196 }
1197
1198 bmain->recovered = false;
1199
1200 /* `startup.blend` or recovered startup. */
1201 if (params->is_startup) {
1202 bmain->filepath[0] = '\0';
1203 }
1204 else if (recover) {
1205 /* In case of auto-save or `quit.blend`, use original file-path instead
1206 * (see also #read_global in `readfile.cc`). */
1207 bmain->recovered = true;
1208 STRNCPY(bmain->filepath, bfd->filepath);
1209 }
1210
1211 /* Set the loaded .blend file path for crash recovery. */
1212 STRNCPY(G.filepath_last_blend, bmain->filepath);
1213
1214 /* Base-flags, groups, make depsgraph, etc. */
1215 /* first handle case if other windows have different scenes visible. */
1216 if (mode == LOAD_UI) {
1217 wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
1218 if (wm) {
1219 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
1220 if (win->scene && win->scene != curscene) {
1221 BKE_scene_set_background(bmain, win->scene);
1222 }
1223 }
1224 }
1225 }
1226
1227 /* Setting scene might require having a dependency graph, with copy-on-eval
1228 * we need to make sure we ensure scene has correct color management before
1229 * constructing dependency graph. */
1230 if (mode != LOAD_UNDO) {
1232 }
1233
1234 BKE_scene_set_background(bmain, curscene);
1235
1236 if (mode != LOAD_UNDO) {
1237 /* TODO(@sergey): Can this be also move above? */
1239 }
1240
1241 /* Both undo and regular file loading can perform some fairly complex ID manipulation, simpler
1242 * and safer to fully redo reference-counting. This is a relatively cheap process anyway. */
1243 BKE_main_id_refcount_recompute(bmain, false);
1244
1246
1247 if (mode != LOAD_UNDO && liboverride::is_auto_resync_enabled()) {
1248 reports->duration.lib_overrides_resync = BLI_time_now_seconds();
1249
1251 bmain,
1252 nullptr,
1253 curscene,
1255 reports);
1256
1257 reports->duration.lib_overrides_resync = BLI_time_now_seconds() -
1258 reports->duration.lib_overrides_resync;
1259
1260 /* We need to rebuild some of the deleted override rules (for UI feedback purpose). */
1262 }
1263
1264 /* Now that liboverrides have been resynced and 'irrelevant' missing linked IDs has been removed,
1265 * report actual missing linked data. */
1266 if (mode != LOAD_UNDO) {
1267 ID *id_iter;
1268 int missing_linked_ids_num = 0;
1269 FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
1270 if (ID_IS_LINKED(id_iter) && (id_iter->tag & ID_TAG_MISSING)) {
1271 missing_linked_ids_num++;
1273 RPT_INFO,
1274 RPT_("LIB: %s: '%s' missing from '%s', parent '%s'"),
1276 id_iter->name + 2,
1277 id_iter->lib->runtime->filepath_abs,
1278 id_iter->lib->runtime->parent ?
1279 id_iter->lib->runtime->parent->runtime->filepath_abs :
1280 "<direct>");
1281 }
1282 }
1284 reports->count.missing_linked_id = missing_linked_ids_num;
1285 }
1286}
1287
1289 BlendFileData *bfd,
1291 BlendFileReadWMSetupData *wm_setup_data,
1293{
1294 if ((params->skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
1295 setup_app_userdef(bfd);
1296 }
1297 if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
1298 setup_app_data(C, bfd, params, wm_setup_data, reports);
1299 }
1300}
1301
1303{
1304 if (main->versionfile > BLENDER_FILE_VERSION || (main->versionfile == BLENDER_FILE_VERSION &&
1305 main->subversionfile > BLENDER_FILE_SUBVERSION))
1306 {
1307 BKE_reportf(reports->reports,
1309 "File written by newer Blender binary (%d.%d), expect loss of data!",
1310 main->versionfile,
1311 main->subversionfile);
1312 }
1313}
1314
1316 BlendFileData *bfd,
1318 BlendFileReadWMSetupData *wm_setup_data,
1320 /* Extra args. */
1321 const bool startup_update_defaults,
1322 const char *startup_app_template)
1323{
1324 if (bfd->main->is_read_invalid) {
1326 "File could not be read, critical data corruption detected");
1328 return;
1329 }
1330
1331 if (startup_update_defaults) {
1332 if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
1333 BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
1334 }
1335 }
1336 setup_app_blend_file_data(C, bfd, params, wm_setup_data, reports);
1338}
1339
1347
1348BlendFileData *BKE_blendfile_read(const char *filepath,
1351{
1352 /* Don't print startup file loading. */
1353 if (params->is_startup == false) {
1354 if (!G.quiet) {
1355 printf("Read blend: \"%s\"\n", filepath);
1356 }
1357 }
1358
1359 BlendFileData *bfd = BLO_read_from_file(filepath, eBLOReadSkip(params->skip_flags), reports);
1360 if (bfd && bfd->main->is_read_invalid) {
1362 bfd = nullptr;
1363 }
1364 if (bfd) {
1366 }
1367 else {
1368 BKE_reports_prependf(reports->reports, "Loading \"%s\" failed: ", filepath);
1369 }
1370 return bfd;
1371}
1372
1374 int file_buf_size,
1377{
1379 file_buf, file_buf_size, eBLOReadSkip(params->skip_flags), reports);
1380 if (bfd && bfd->main->is_read_invalid) {
1382 bfd = nullptr;
1383 }
1384 if (bfd) {
1385 /* Pass. */
1386 }
1387 else {
1388 BKE_reports_prepend(reports, "Loading failed: ");
1389 }
1390 return bfd;
1391}
1392
1394 MemFile *memfile,
1397{
1399 bmain, BKE_main_blendfile_path(bmain), memfile, params, reports);
1400 if (bfd && bfd->main->is_read_invalid) {
1402 bfd = nullptr;
1403 }
1404 if (bfd == nullptr) {
1405 BKE_reports_prepend(reports, "Loading failed: ");
1406 }
1407 return bfd;
1408}
1409
1411{
1412 Main *bmain = CTX_data_main(C);
1413 ListBase *lb;
1414 ID *id;
1415
1416 FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
1418 if (ELEM(GS(id->name), ID_SCE, ID_SCR, ID_WM, ID_WS)) {
1419 break;
1420 }
1421 BKE_id_delete(bmain, id);
1422 }
1424 }
1426}
1427
1429
1430/* -------------------------------------------------------------------- */
1455
1457{
1458 BlendFileData *bfd;
1459 UserDef *userdef = nullptr;
1460
1461 BlendFileReadReport blend_file_read_reports{};
1462 blend_file_read_reports.reports = reports;
1463
1464 bfd = BLO_read_from_file(
1465 filepath, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, &blend_file_read_reports);
1466 if (bfd) {
1467 if (bfd->user) {
1468 userdef = bfd->user;
1469 }
1470 BKE_main_free(bfd->main);
1471 MEM_delete(bfd);
1472 }
1473
1474 return userdef;
1475}
1476
1478 int file_buf_size,
1480{
1481 BlendFileData *bfd;
1482 UserDef *userdef = nullptr;
1483
1485 file_buf, file_buf_size, BLO_READ_SKIP_ALL & ~BLO_READ_SKIP_USERDEF, reports);
1486 if (bfd) {
1487 if (bfd->user) {
1488 userdef = bfd->user;
1489 }
1490 BKE_main_free(bfd->main);
1491 MEM_delete(bfd);
1492 }
1493 else {
1494 BKE_reports_prepend(reports, "Loading failed: ");
1495 }
1496
1497 return userdef;
1498}
1499
1501{
1502 UserDef *userdef = MEM_callocN<UserDef>(__func__);
1503 *userdef = blender::dna::shallow_copy(U_default);
1504
1505 /* Add-ons. */
1506 {
1507 const char *addons[] = {
1508 "io_anim_bvh",
1509 "io_curve_svg",
1510 "io_mesh_uv_layout",
1511 "io_scene_fbx",
1512 "io_scene_gltf2",
1513 "cycles",
1514 "pose_library",
1515 "bl_pkg",
1516 };
1517 for (int i = 0; i < ARRAY_SIZE(addons); i++) {
1518 bAddon *addon = BKE_addon_new();
1519 STRNCPY(addon->module, addons[i]);
1520 BLI_addtail(&userdef->addons, addon);
1521 }
1522 }
1523
1524 /* Theme. */
1525 {
1526 bTheme *btheme = MEM_mallocN<bTheme>(__func__);
1527 memcpy(btheme, &U_theme_default, sizeof(*btheme));
1528
1529 BLI_addtail(&userdef->themes, btheme);
1530 }
1531
1532#ifdef WITH_PYTHON_SECURITY
1533 /* use alternative setting for security nuts
1534 * otherwise we'd need to patch the binary blob - startup.blend.c */
1536#else
1538#endif
1539
1540 /* System-specific fonts directory.
1541 * NOTE: when not found, leaves as-is (`//` for the blend-file directory). */
1542 if (BKE_appdir_font_folder_default(userdef->fontdir, sizeof(userdef->fontdir))) {
1543 /* Not actually needed, just a convention that directory selection
1544 * adds a trailing slash. */
1545 BLI_path_slash_ensure(userdef->fontdir, sizeof(userdef->fontdir));
1546 }
1547
1549 userdef->memcachelimit);
1550
1551 /* Init weight paint range. */
1552 BKE_colorband_init(&userdef->coba_weight, true);
1553
1554 /* Default studio light. */
1556
1557 /*
1558 * Enable translation by default. ALT Linux specific patch.
1559 * See ALT#31561
1560 */
1561
1562 userdef->language = ULANGUAGE_AUTO;
1563 userdef->transopts |= USER_TR_IFACE;
1564 userdef->transopts |= USER_TR_TOOLTIPS;
1565 userdef->transopts |= USER_TR_NEWDATANAME;
1566
1568
1570
1571 {
1573 userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/General");
1575 userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Paint");
1577 userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Simulation");
1578
1580 userdef, "VIEW3D_AST_brush_gpencil_paint", "Brushes/Grease Pencil Draw/Draw");
1582 userdef, "VIEW3D_AST_brush_gpencil_paint", "Brushes/Grease Pencil Draw/Erase");
1584 userdef, "VIEW3D_AST_brush_gpencil_paint", "Brushes/Grease Pencil Draw/Utilities");
1585
1587 userdef, "VIEW3D_AST_brush_gpencil_sculpt", "Brushes/Grease Pencil Sculpt/Contrast");
1589 userdef, "VIEW3D_AST_brush_gpencil_sculpt", "Brushes/Grease Pencil Sculpt/Transform");
1591 userdef, "VIEW3D_AST_brush_gpencil_sculpt", "Brushes/Grease Pencil Sculpt/Utilities");
1592 }
1593
1594 return userdef;
1595}
1596
1598{
1599 Main *mainb = MEM_callocN<Main>("empty main");
1600 bool ok = false;
1601
1603 params.use_userdef = true;
1604
1605 if (BLO_write_file(mainb, filepath, 0, &params, reports)) {
1606 ok = true;
1607 }
1608
1609 MEM_freeN(mainb);
1610
1611 return ok;
1612}
1613
1615{
1616 /* Checking that `filepath` exists is not essential, it just avoids printing a warning that
1617 * the file can't be found. In this case it's not an error - as the file is used if it exists,
1618 * falling back to the defaults.
1619 * If the preferences exists but file reading fails - the file can be assumed corrupt
1620 * so overwriting the file is OK. */
1621 UserDef *userdef_default = BLI_exists(filepath) ? BKE_blendfile_userdef_read(filepath, nullptr) :
1622 nullptr;
1623 if (userdef_default == nullptr) {
1624 userdef_default = BKE_blendfile_userdef_from_defaults();
1625 }
1626
1628 bool ok = BKE_blendfile_userdef_write(filepath, reports);
1630 BKE_blender_userdef_data_free(userdef_default, false);
1631 MEM_freeN(userdef_default);
1632 return ok;
1633}
1634
1636{
1637 char filepath[FILE_MAX];
1638 bool ok = true;
1639 const bool use_template_userpref = BKE_appdir_app_template_has_userpref(U.app_template);
1640 std::optional<std::string> cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, nullptr);
1641
1642 if (cfgdir) {
1643 bool ok_write;
1644 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_USERPREF_FILE);
1645 if (!G.quiet) {
1646 printf("Writing userprefs: \"%s\" ", filepath);
1647 }
1648 if (use_template_userpref) {
1650 }
1651 else {
1652 ok_write = BKE_blendfile_userdef_write(filepath, reports);
1653 }
1654
1655 if (ok_write) {
1656 if (!G.quiet) {
1657 printf("ok\n");
1658 }
1659 BKE_report(reports, RPT_INFO, "Preferences saved");
1660 }
1661 else {
1662 if (!G.quiet) {
1663 printf("fail\n");
1664 }
1665 ok = false;
1666 BKE_report(reports, RPT_ERROR, "Saving preferences failed");
1667 }
1668 }
1669 else {
1670 BKE_report(reports, RPT_ERROR, "Unable to create userpref path");
1671 }
1672
1673 if (use_template_userpref) {
1674 cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, U.app_template);
1675 if (cfgdir) {
1676 /* Also save app-template preferences. */
1677 BLI_path_join(filepath, sizeof(filepath), cfgdir->c_str(), BLENDER_USERPREF_FILE);
1678
1679 if (!G.quiet) {
1680 printf("Writing userprefs app-template: \"%s\" ", filepath);
1681 }
1682 if (BKE_blendfile_userdef_write(filepath, reports) != 0) {
1683 if (!G.quiet) {
1684 printf("ok\n");
1685 }
1686 }
1687 else {
1688 if (!G.quiet) {
1689 printf("fail\n");
1690 }
1691 ok = false;
1692 }
1693 }
1694 else {
1695 BKE_report(reports, RPT_ERROR, "Unable to create app-template userpref path");
1696 ok = false;
1697 }
1698 }
1699
1700 if (ok) {
1701 U.runtime.is_dirty = false;
1702 }
1703 return ok;
1704}
1705
1707
1708/* -------------------------------------------------------------------- */
1711
1713 const void *file_buf,
1714 int file_buf_size,
1716{
1717 BlendFileData *bfd;
1718 WorkspaceConfigFileData *workspace_config = nullptr;
1719
1720 if (filepath) {
1721 BlendFileReadReport blend_file_read_reports{};
1722 blend_file_read_reports.reports = reports;
1723 bfd = BLO_read_from_file(filepath, BLO_READ_SKIP_USERDEF, &blend_file_read_reports);
1724 }
1725 else {
1726 bfd = BLO_read_from_memory(file_buf, file_buf_size, BLO_READ_SKIP_USERDEF, reports);
1727 }
1728
1729 if (bfd) {
1730 workspace_config = MEM_callocN<WorkspaceConfigFileData>(__func__);
1731 workspace_config->main = bfd->main;
1732
1733 /* Only 2.80+ files have actual workspaces, don't try to use screens
1734 * from older versions. */
1735 if (bfd->main->versionfile >= 280) {
1736 workspace_config->workspaces = bfd->main->workspaces;
1737 }
1738
1739 MEM_delete(bfd);
1740 }
1741
1742 return workspace_config;
1743}
1744
1746{
1747 BKE_main_free(workspace_config->main);
1748 MEM_freeN(workspace_config);
1749}
1750
1752
1753/* -------------------------------------------------------------------- */
1756
1757static CLG_LogRef LOG_PARTIALWRITE = {"bke.blendfile.partial_write"};
1758
1759namespace blender::bke::blendfile {
1760
1762 : reference_root_filepath_(reference_root_filepath)
1763{
1764 BKE_main_init(this->bmain);
1765 if (!reference_root_filepath_.empty()) {
1766 STRNCPY(this->bmain.filepath, reference_root_filepath_.c_str());
1767 }
1768 /* Only for IDs matching existing data in current G_MAIN. */
1769 matching_uid_map_ = BKE_main_idmap_create(&this->bmain, false, nullptr, MAIN_IDMAP_TYPE_UID);
1770 /* For all IDs existing in the context. */
1771 this->bmain.id_map = BKE_main_idmap_create(
1772 &this->bmain, false, nullptr, MAIN_IDMAP_TYPE_UID | MAIN_IDMAP_TYPE_NAME);
1773};
1774
1776{
1777 BKE_main_idmap_destroy(matching_uid_map_);
1778
1779 BLI_assert(this->bmain.next == nullptr);
1780 BKE_main_destroy(this->bmain);
1781};
1782
1783void PartialWriteContext::preempt_session_uid(ID *ctx_id, uint session_uid)
1784{
1785 /* If there is already an existing ID in the 'matching' set with that UID, it should be the same
1786 * as the given ctx_id. */
1787 ID *matching_ctx_id = BKE_main_idmap_lookup_uid(matching_uid_map_, session_uid);
1788 if (matching_ctx_id == ctx_id) {
1789 /* That ID has already been added to the context, nothing to do. */
1790 BLI_assert(matching_ctx_id->session_uid == session_uid);
1791 return;
1792 }
1793 if (matching_ctx_id != nullptr) {
1794 /* Another ID in the context, who has a matching ID in current G_MAIN, is sharing the same
1795 * session UID. This marks a critical corruption somewhere! */
1796 CLOG_FATAL(
1798 "Different matching IDs sharing the same session UID in the partial write context.");
1799 return;
1800 }
1801 /* No ID with this session UID in the context, who's matching a current ID in G_MAIN. Check if a
1802 * non-matching context ID is already using that UID, if yes, regenerate a new one for it, such
1803 * that given `ctx_id` can use the desired UID. */
1804 /* NOTE: In theory, there should never be any session uid collision currently, since these are
1805 * generated session-wide, regardless of the type/source of the IDs. */
1806 matching_ctx_id = BKE_main_idmap_lookup_uid(this->bmain.id_map, session_uid);
1807 BLI_assert(matching_ctx_id != ctx_id);
1808 if (matching_ctx_id) {
1810 3,
1811 "Non-matching IDs sharing the same session UID in the partial write context.");
1812 BKE_main_idmap_remove_id(this->bmain.id_map, matching_ctx_id);
1813 /* FIXME: Allow #BKE_lib_libblock_session_uid_renew to work with temp IDs? */
1814 matching_ctx_id->tag &= ~ID_TAG_TEMP_MAIN;
1815 BKE_lib_libblock_session_uid_renew(matching_ctx_id);
1816 matching_ctx_id->tag |= ID_TAG_TEMP_MAIN;
1817 BKE_main_idmap_insert_id(this->bmain.id_map, matching_ctx_id);
1818 BLI_assert(BKE_main_idmap_lookup_uid(this->bmain.id_map, session_uid) == nullptr);
1819 }
1820 ctx_id->session_uid = session_uid;
1821}
1822
1823void PartialWriteContext::process_added_id(ID *ctx_id,
1824 const PartialWriteContext::IDAddOperations operations)
1825{
1826 const bool set_fake_user = (operations & SET_FAKE_USER) != 0;
1827 const bool set_clipboard_mark = (operations & SET_CLIPBOARD_MARK) != 0;
1828
1829 if (set_fake_user) {
1830 id_fake_user_set(ctx_id);
1831 }
1832 else {
1833 /* NOTE: Using this tag will ensure that this ID is written on disk in current state (current
1834 * context session). However, reloading the blendfile will clear this tag. */
1835 id_us_ensure_real(ctx_id);
1836 }
1837
1838 if (set_clipboard_mark) {
1839 ctx_id->flag |= ID_FLAG_CLIPBOARD_MARK;
1840 }
1841}
1842
1843ID *PartialWriteContext::id_add_copy(const ID *id, const bool regenerate_session_uid)
1844{
1845 ID *ctx_root_id = nullptr;
1846 BLI_assert(BKE_main_idmap_lookup_uid(matching_uid_map_, id->session_uid) == nullptr);
1847 const int copy_flags = (LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT |
1848 /* NOTE: Could make this an option if needed in the future */
1850 ctx_root_id = BKE_id_copy_in_lib(nullptr, id->lib, id, std::nullopt, nullptr, copy_flags);
1851 ctx_root_id->tag |= ID_TAG_TEMP_MAIN;
1852 /* Ensure that the newly copied ID has a library in temp local bmain if it was linked.
1853 * While this could be optimized out in case the ID is made local in the context, this adds
1854 * complexity as default ID management code like 'make local' code will create invalid bmain
1855 * namemap data. */
1856 this->ensure_library(ctx_root_id);
1857 if (regenerate_session_uid) {
1858 /* Calling #BKE_lib_libblock_session_uid_renew is not needed here, copying already generated a
1859 * new one. */
1860 BLI_assert(BKE_main_idmap_lookup_uid(matching_uid_map_, id->session_uid) == nullptr);
1861 }
1862 else {
1863 this->preempt_session_uid(ctx_root_id, id->session_uid);
1864 BKE_main_idmap_insert_id(matching_uid_map_, ctx_root_id);
1865 }
1866 BKE_main_idmap_insert_id(this->bmain.id_map, ctx_root_id);
1867 BKE_libblock_management_main_add(&this->bmain, ctx_root_id);
1868 /* Note: remapping of external file relative paths is done as part of the 'write' process. */
1869 return ctx_root_id;
1870}
1871
1872void PartialWriteContext::make_local(ID *ctx_id, const int make_local_flags)
1873{
1874 /* Making an ID local typically resets its session UID, here we want to keep the same value. */
1875 const uint ctx_id_session_uid = ctx_id->session_uid;
1876 BKE_main_idmap_remove_id(this->bmain.id_map, ctx_id);
1877 BKE_main_idmap_remove_id(matching_uid_map_, ctx_id);
1878
1879 if (ID_IS_LINKED(ctx_id)) {
1880 BKE_lib_id_make_local(&this->bmain, ctx_id, make_local_flags);
1881 }
1882 /* NOTE: Cannot rely only on `ID_IS_OVERRIDE_LIBRARY` here, as the reference pointer to the
1883 * linked data may have already been cleared out by dependency management in code above that
1884 * call. */
1885 else if ((ctx_id->override_library || ID_IS_OVERRIDE_LIBRARY(ctx_id)) &&
1886 (make_local_flags & LIB_ID_MAKELOCAL_LIBOVERRIDE_CLEAR) != 0)
1887
1888 {
1890 }
1891
1892 this->preempt_session_uid(ctx_id, ctx_id_session_uid);
1893 BKE_main_idmap_insert_id(this->bmain.id_map, ctx_id);
1894 BKE_main_idmap_insert_id(matching_uid_map_, ctx_id);
1895}
1896
1897Library *PartialWriteContext::ensure_library(ID *ctx_id)
1898{
1899 if (!ID_IS_LINKED(ctx_id)) {
1900 return nullptr;
1901 }
1902 blender::StringRefNull lib_path = ctx_id->lib->runtime->filepath_abs;
1903 Library *ctx_lib = this->libraries_map_.lookup_default(lib_path, nullptr);
1904 if (!ctx_lib) {
1905 ctx_lib = reinterpret_cast<Library *>(id_add_copy(&ctx_id->lib->id, true));
1906 this->libraries_map_.add(lib_path, ctx_lib);
1907 }
1908 ctx_id->lib = ctx_lib;
1909 return ctx_lib;
1910}
1911Library *PartialWriteContext::ensure_library(blender::StringRefNull library_absolute_path)
1912{
1913 Library *ctx_lib = this->libraries_map_.lookup_default(library_absolute_path, nullptr);
1914 if (!ctx_lib) {
1915 const char *library_name = BLI_path_basename(library_absolute_path.c_str());
1916 ctx_lib = static_cast<Library *>(
1917 BKE_id_new_in_lib(&this->bmain, nullptr, ID_LI, library_name));
1918 ctx_lib->id.tag |= ID_TAG_TEMP_MAIN;
1919 id_us_min(&ctx_lib->id);
1920 this->libraries_map_.add(library_absolute_path, ctx_lib);
1921 }
1922 return ctx_lib;
1923}
1924
1926 const ID *id,
1930 dependencies_filter_cb)
1931{
1932 constexpr int make_local_flags = (LIB_ID_MAKELOCAL_INDIRECT | LIB_ID_MAKELOCAL_FORCE_LOCAL |
1934
1935 const bool add_dependencies = (options.operations & ADD_DEPENDENCIES) != 0;
1936 const bool clear_dependencies = (options.operations & CLEAR_DEPENDENCIES) != 0;
1937 const bool duplicate_dependencies = (options.operations & DUPLICATE_DEPENDENCIES) != 0;
1938 BLI_assert(clear_dependencies || add_dependencies || dependencies_filter_cb);
1939 BLI_assert(!clear_dependencies || !(add_dependencies || duplicate_dependencies));
1940 UNUSED_VARS_NDEBUG(add_dependencies, clear_dependencies, duplicate_dependencies);
1941
1942 /* Do not directly add an embedded ID. Add its owner instead. */
1943 if (id->flag & ID_FLAG_EMBEDDED_DATA) {
1944 id = BKE_id_owner_get(const_cast<ID *>(id), true);
1945 }
1946
1947 /* The given ID may have already been added (either explicitly or as a dependency) before. */
1948 ID *ctx_root_id = BKE_main_idmap_lookup_uid(matching_uid_map_, id->session_uid);
1949 if (ctx_root_id) {
1950 /* If the root orig ID is already in the context, assume all of its dependencies are as well.
1951 */
1952 BLI_assert(ctx_root_id->session_uid == id->session_uid);
1953 this->process_added_id(ctx_root_id, options.operations);
1954 return ctx_root_id;
1955 }
1956
1957 /* Local mapping, such that even in case dependencies are duplicated for this specific added ID,
1958 * once a dependency has been duplicated, it can be re-used for other ID usages within the
1959 * dependencies of the added ID. */
1960 blender::Map<const ID *, ID *> local_ctx_id_map;
1961 /* A list of IDs to post-process. Only contains IDs that were actually added to the context (not
1962 * the ones that were already there and were re-used). The #IDAddOperations item of the pair
1963 * stores the returned value from the given #dependencies_filter_cb (or given global #options
1964 * parameter otherwise). */
1966
1967 ctx_root_id = id_add_copy(id, false);
1968 BLI_assert(ctx_root_id->session_uid == id->session_uid);
1969 local_ctx_id_map.add(id, ctx_root_id);
1970 post_process_ids_todo.append({ctx_root_id, options.operations});
1971 this->process_added_id(ctx_root_id, options.operations);
1972
1973 blender::VectorSet<ID *> ids_to_process{ctx_root_id};
1974 auto dependencies_cb = [this,
1975 options,
1976 &local_ctx_id_map,
1977 &ids_to_process,
1978 &post_process_ids_todo,
1979 dependencies_filter_cb](LibraryIDLinkCallbackData *cb_data) -> int {
1980 ID **id_ptr = cb_data->id_pointer;
1981 const ID *orig_deps_id = *id_ptr;
1982
1984 return IDWALK_RET_NOP;
1985 }
1986 if (!orig_deps_id) {
1987 return IDWALK_RET_NOP;
1988 }
1989
1990 if (cb_data->cb_flag & IDWALK_CB_INTERNAL) {
1991 /* Cleanup internal ID pointers. */
1992 *id_ptr = nullptr;
1993 return IDWALK_RET_NOP;
1994 }
1995
1996 PartialWriteContext::IDAddOperations operations_final = (options.operations & MASK_INHERITED);
1997 if (dependencies_filter_cb) {
1998 const PartialWriteContext::IDAddOperations operations_per_id = dependencies_filter_cb(
1999 cb_data, options);
2000 operations_final = ((operations_per_id & MASK_PER_ID_USAGE) |
2001 (operations_final & ~MASK_PER_ID_USAGE));
2002 }
2003
2004 const bool add_dependencies = (operations_final & ADD_DEPENDENCIES) != 0;
2005 const bool clear_dependencies = (operations_final & CLEAR_DEPENDENCIES) != 0;
2006 const bool duplicate_dependencies = (operations_final & DUPLICATE_DEPENDENCIES) != 0;
2007 BLI_assert(clear_dependencies || add_dependencies);
2008 BLI_assert(!clear_dependencies || !(add_dependencies || duplicate_dependencies));
2009 UNUSED_VARS_NDEBUG(add_dependencies);
2010
2011 if (clear_dependencies) {
2012 if (cb_data->cb_flag & IDWALK_CB_NEVER_NULL) {
2014 "Clearing a 'never null' ID usage of '%s' by '%s', this is likely not a "
2015 "desired action",
2016 (*id_ptr)->name,
2017 cb_data->owner_id->name);
2018 }
2019 /* Owner ID should be a 'context-main' duplicate of a real Main ID, as such there should be
2020 * no need to decrease ID usages refcount here. */
2021 *id_ptr = nullptr;
2022 return IDWALK_RET_NOP;
2023 }
2024 /* else if (add_dependencies) */
2025 /* The given ID may have already been added (either explicitly or as a dependency) before. */
2026 ID *ctx_deps_id = nullptr;
2027 if (duplicate_dependencies) {
2028 ctx_deps_id = local_ctx_id_map.lookup(orig_deps_id);
2029 }
2030 else {
2031 ctx_deps_id = BKE_main_idmap_lookup_uid(matching_uid_map_, orig_deps_id->session_uid);
2032 }
2033 if (!ctx_deps_id) {
2034 if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
2035 /* Do not follow 'loop back' pointers. */
2036 /* NOTE: Not sure whether this should be considered an error or not. Typically hitting such
2037 * a case is bad practice. On the other hand, some of these pointers are present in
2038 * 'normal' IDs, like e.g. the parent collections ones. This implies that currently, all
2039 * attempt to adding a collection to a partial write context should make usage of a custom
2040 * `dependencies_filter_cb` function to explicitly clear these pointers. */
2042 "First dependency to ID '%s' found through a 'loopback' usage from ID '%s', "
2043 "this should never happen",
2044 (*id_ptr)->name,
2045 cb_data->owner_id->name);
2046 *id_ptr = nullptr;
2047 return IDWALK_RET_NOP;
2048 }
2049 ctx_deps_id = this->id_add_copy(orig_deps_id, duplicate_dependencies);
2050 local_ctx_id_map.add(orig_deps_id, ctx_deps_id);
2051 ids_to_process.add(ctx_deps_id);
2052 post_process_ids_todo.append({ctx_deps_id, operations_final});
2053 }
2054 if (duplicate_dependencies) {
2055 BLI_assert(ctx_deps_id->session_uid != orig_deps_id->session_uid);
2056 }
2057 else {
2058 BLI_assert(ctx_deps_id->session_uid == orig_deps_id->session_uid);
2059 }
2060 this->process_added_id(ctx_deps_id, operations_final);
2061 /* In-place remapping. */
2062 *id_ptr = ctx_deps_id;
2063 return IDWALK_RET_NOP;
2064 };
2065 while (!ids_to_process.is_empty()) {
2066 ID *ctx_id = ids_to_process.pop();
2068 &this->bmain, ctx_id, dependencies_cb, &options, IDWALK_DO_INTERNAL_RUNTIME_POINTERS);
2069 }
2070
2071 /* Post process all newly added IDs in the context:
2072 * - Make them local or ensure that their library reference is also in the context.
2073 */
2074 for (auto [ctx_id, options_final] : post_process_ids_todo) {
2075 const bool do_make_local = (options_final & MAKE_LOCAL) != 0;
2076 if (do_make_local) {
2077 this->make_local(ctx_id, make_local_flags);
2078 }
2079 }
2080
2081 return ctx_root_id;
2082}
2083
2088{
2089 Library *ctx_library = nullptr;
2090 if (library) {
2091 ctx_library = this->ensure_library(library->runtime->filepath_abs);
2092 }
2093 ID *ctx_id = static_cast<ID *>(
2094 BKE_id_new_in_lib(&this->bmain, ctx_library, id_type, id_name.c_str()));
2095 ctx_id->tag |= ID_TAG_TEMP_MAIN;
2096 id_us_min(ctx_id);
2097 this->process_added_id(ctx_id, options.operations);
2098 /* See function doc about why handling of #matching_uid_map_ can be skipped here. */
2099 BKE_main_idmap_insert_id(this->bmain.id_map, ctx_id);
2100 return ctx_id;
2101}
2102
2104{
2105 if (ID *ctx_id = BKE_main_idmap_lookup_uid(matching_uid_map_, id->session_uid)) {
2106 BKE_main_idmap_remove_id(matching_uid_map_, ctx_id);
2107 BKE_id_delete(&this->bmain, ctx_id);
2108 }
2109}
2110
2111void PartialWriteContext::remove_unused(const bool clear_extra_user)
2112{
2113 LibQueryUnusedIDsData parameters;
2114 parameters.do_local_ids = true;
2115 parameters.do_linked_ids = true;
2116 parameters.do_recursive = true;
2117
2118 if (clear_extra_user) {
2119 ID *id_iter;
2120 FOREACH_MAIN_ID_BEGIN (&this->bmain, id_iter) {
2121 id_us_clear_real(id_iter);
2122 }
2124 }
2125 BKE_lib_query_unused_ids_tag(&this->bmain, ID_TAG_DOIT, parameters);
2126
2128 3,
2129 "Removing %d unused IDs from current partial write context",
2130 parameters.num_total[INDEX_ID_NULL]);
2131 ID *id_iter;
2132 FOREACH_MAIN_ID_BEGIN (&this->bmain, id_iter) {
2133 if ((id_iter->tag & ID_TAG_DOIT) != 0) {
2134 BKE_main_idmap_remove_id(matching_uid_map_, id_iter);
2135 }
2136 }
2139}
2140
2142{
2143 BKE_main_idmap_clear(*matching_uid_map_);
2144 BKE_main_clear(this->bmain);
2145}
2146
2148{
2149 blender::Set<ID *> ids_in_context;
2150 blender::Set<uint> session_uids_in_context;
2151 bool is_valid = true;
2152
2153 ID *id_iter;
2154
2155 /* Fill `ids_in_context`, check uniqueness of session_uid's. */
2156 FOREACH_MAIN_ID_BEGIN (&this->bmain, id_iter) {
2157 ids_in_context.add(id_iter);
2158 if (session_uids_in_context.contains(id_iter->session_uid)) {
2159 CLOG_ERROR(&LOG_PARTIALWRITE, "ID %s does not have a unique session_uid", id_iter->name);
2160 is_valid = false;
2161 }
2162 else {
2163 session_uids_in_context.add(id_iter->session_uid);
2164 }
2165 }
2167
2168 /* Check that no ID uses IDs from outside this context. */
2169 auto id_validate_dependencies_cb = [&ids_in_context,
2170 &is_valid](LibraryIDLinkCallbackData *cb_data) -> int {
2171 ID **id_p = cb_data->id_pointer;
2172 ID *owner_id = cb_data->owner_id;
2173 ID *self_id = cb_data->self_id;
2174
2175 /* By definition, embedded IDs are not in Main, so they are not listed in this context either.
2176 */
2177 if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
2178 return IDWALK_RET_NOP;
2179 }
2180
2181 if (*id_p && !ids_in_context.contains(*id_p)) {
2182 if (owner_id != self_id) {
2183 CLOG_ERROR(
2185 "ID %s (used by ID '%s', embedded ID '%s') is not in current partial write context",
2186 (*id_p)->name,
2187 owner_id->name,
2188 self_id->name);
2189 }
2190 else {
2192 "ID %s (used by ID '%s') is not in current partial write context",
2193 (*id_p)->name,
2194 owner_id->name);
2195 }
2196 is_valid = false;
2197 }
2198 return IDWALK_RET_NOP;
2199 };
2200 FOREACH_MAIN_ID_BEGIN (&this->bmain, id_iter) {
2202 &this->bmain, id_iter, id_validate_dependencies_cb, nullptr, IDWALK_READONLY);
2203 }
2205
2206 return is_valid;
2207}
2208
2209bool PartialWriteContext::write(const char *write_filepath,
2210 const int write_flags,
2211 const int remap_mode,
2213{
2214 BLI_assert_msg(write_filepath != reference_root_filepath_,
2215 "A library blendfile should not overwrite currently edited blendfile");
2216
2217 /* In case the write path is the same as one of the libraries used by this context, make this
2218 * library local, and delete it (and all of its potentially remaining linked data). */
2219 blender::Vector<Library *> make_local_libs;
2220 LISTBASE_FOREACH (Library *, library, &this->bmain.libraries) {
2221 if (STREQ(write_filepath, library->runtime->filepath_abs)) {
2222 make_local_libs.append(library);
2223 }
2224 }
2225 /* Will likely change in the near future (embedded linked IDs, virtual libraries...), but
2226 * currently this should never happen. */
2227 if (make_local_libs.size() > 1) {
2229 "%d libraries found using the same filepath as destination one ('%s'), should "
2230 "never happen.",
2231 int32_t(make_local_libs.size()),
2232 write_filepath);
2233 }
2234 for (Library *lib : make_local_libs) {
2235 BKE_library_make_local(&this->bmain, lib, nullptr, false, false, false);
2236 BKE_id_delete(&this->bmain, lib);
2237 }
2238 make_local_libs.clear();
2239
2240 BLI_assert(this->is_valid());
2241
2242 BlendFileWriteParams blend_file_write_params{};
2243 blend_file_write_params.remap_mode = eBLO_WritePathRemap(remap_mode);
2244 return BLO_write_file(
2245 &this->bmain, write_filepath, write_flags, &blend_file_write_params, &reports);
2246}
2247
2248bool PartialWriteContext::write(const char *write_filepath, ReportList &reports)
2249{
2250 return this->write(write_filepath, 0, BLO_WRITE_PATH_REMAP_RELATIVE, reports);
2251}
2252
2253} // namespace blender::bke::blendfile
2254
struct bAddon * BKE_addon_new(void)
Definition addon.cc:34
#define BLENDER_USERPREF_FILE
@ BLENDER_USER_CONFIG
bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1)
Definition appdir.cc:1092
bool BKE_appdir_font_folder_default(char *dir, size_t dir_maxncpy)
Definition appdir.cc:226
std::optional< std::string > BKE_appdir_folder_id_create(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT
Definition appdir.cc:777
Blender util stuff.
void BKE_blender_globals_main_replace(Main *bmain)
Definition blender.cc:225
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
Definition blender.cc:285
void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *userdef_b)
Definition blender.cc:405
void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
Definition blender.cc:356
#define BLENDER_FILE_SUBVERSION
#define BLENDER_FILE_VERSION
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
Definition BKE_bpath.hh:61
void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data)
Definition bpath.cc:116
void BKE_colorband_init(ColorBand *coba, bool rangetype)
Definition colorband.cc:22
void CTX_data_main_set(bContext *C, Main *bmain)
bScreen * CTX_wm_screen(const bContext *C)
bool CTX_py_init_get(bContext *C)
void CTX_wm_manager_set(bContext *C, wmWindowManager *wm)
wmWindow * CTX_wm_window(const bContext *C)
void CTX_wm_screen_set(bContext *C, bScreen *screen)
void CTX_data_scene_set(bContext *C, Scene *scene)
Main * CTX_data_main(const bContext *C)
void CTX_wm_area_set(bContext *C, ScrArea *area)
void CTX_wm_region_set(bContext *C, ARegion *region)
wmWindowManager * CTX_wm_manager(const bContext *C)
void CTX_wm_region_popup_set(bContext *C, ARegion *region_popup)
#define G_FLAG_ALL_READFILE
#define G_MAIN
#define G_FILE_FLAG_ALL_RUNTIME
#define G_FLAG_ALL_RUNTIME
@ G_FILE_ASSET_EDIT_FILE
@ G_FILE_RECOVER_READ
@ G_FILE_NO_UI
const IDTypeInfo * BKE_idtype_get_info_from_idtype_index(const int idtype_index)
Definition idtype.cc:128
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:165
ViewLayer * BKE_view_layer_default_view(const Scene *scene)
ViewLayer * BKE_view_layer_find(const Scene *scene, const char *layer_name)
void BKE_id_delete(Main *bmain, void *idv) ATTR_NONNULL()
IDNewNameResult BKE_id_new_name_validate(Main &bmain, ListBase &lb, ID &id, const char *newname, IDNewNameMode mode, bool do_linked_data)
Definition lib_id.cc:1883
struct ID * BKE_id_copy_in_lib(Main *bmain, std::optional< Library * > owner_library, const ID *id, std::optional< const ID * > new_owner_id, ID **new_id_p, int flag)
Definition lib_id.cc:663
void size_t BKE_id_multi_tagged_delete(Main *bmain) ATTR_NONNULL()
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
Definition lib_id.cc:1770
@ LIB_ID_COPY_ASSET_METADATA
@ LIB_ID_CREATE_NO_USER_REFCOUNT
@ LIB_ID_CREATE_NO_MAIN
void * BKE_id_new_in_lib(Main *bmain, std::optional< Library * > owner_library, short type, const char *name)
Definition lib_id.cc:1477
void BKE_library_make_local(Main *bmain, const Library *lib, GHash *old_to_new_ids, bool untagged_only, bool set_fake, bool clear_asset_data)
Definition lib_id.cc:2109
void id_fake_user_set(ID *id)
Definition lib_id.cc:391
@ LIB_ID_MAKELOCAL_INDIRECT
@ LIB_ID_MAKELOCAL_FORCE_LOCAL
@ LIB_ID_MAKELOCAL_LIBOVERRIDE_CLEAR
ID * BKE_id_owner_get(ID *id, const bool debug_relationship_assert=true)
Definition lib_id.cc:2491
bool BKE_lib_id_make_local(Main *bmain, ID *id, int flags)
Definition lib_id.cc:586
void id_us_ensure_real(ID *id)
Definition lib_id.cc:308
void id_us_clear_real(ID *id)
Definition lib_id.cc:326
void BKE_lib_id_swap_full(Main *bmain, ID *id_a, ID *id_b, const bool do_self_remap, const int self_remap_flags)
Definition lib_id.cc:1052
void id_us_min(ID *id)
Definition lib_id.cc:361
void BKE_libblock_management_main_add(Main *bmain, void *idv)
Definition lib_id.cc:1113
void BKE_main_id_refcount_recompute(Main *bmain, bool do_linked_only)
Definition lib_id.cc:2015
void BKE_lib_libblock_session_uid_renew(ID *id)
Definition lib_id.cc:1471
void BKE_lib_override_library_free(IDOverrideLibrary **liboverride, bool do_id_user)
void BKE_lib_override_library_main_resync(Main *bmain, const blender::Map< Library *, Library * > *new_to_old_libraries_map, Scene *scene, ViewLayer *view_layer, BlendFileReadReport *reports)
void BKE_lib_override_library_make_local(Main *bmain, ID *id)
void BKE_lib_override_library_main_operations_create(Main *bmain, bool force_auto, int *r_report_flags)
@ IDWALK_CB_LOOPBACK
@ IDWALK_CB_USER
@ IDWALK_CB_INTERNAL
@ IDWALK_CB_EMBEDDED_NOT_OWNING
@ IDWALK_CB_EMBEDDED
@ IDWALK_CB_NEVER_NULL
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, LibraryForeachIDFlag flag)
Definition lib_query.cc:431
@ IDWALK_RET_STOP_RECURSION
@ IDWALK_RET_NOP
@ IDWALK_RECURSE
@ IDWALK_INCLUDE_UI
@ IDWALK_DO_LIBRARY_POINTER
@ IDWALK_NOP
@ IDWALK_READONLY
@ IDWALK_DO_INTERNAL_RUNTIME_POINTERS
void BKE_libblock_remap_multiple_raw(Main *bmain, blender::bke::id::IDRemapper &mappings, const int remap_flags)
Definition lib_remap.cc:667
IDRemapperApplyResult
@ ID_REMAP_RESULT_SOURCE_REMAPPED
@ ID_REMAP_RESULT_SOURCE_UNASSIGNED
@ ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE
@ ID_REMAP_RESULT_SOURCE_UNAVAILABLE
@ ID_REMAP_APPLY_DEFAULT
@ ID_REMAP_SKIP_USER_CLEAR
@ ID_REMAP_SKIP_USER_REFCOUNT
@ ID_REMAP_SKIP_NEVER_NULL_USAGE
@ ID_REMAP_FORCE_UI_POINTERS
@ ID_REMAP_SKIP_UPDATE_TAGGING
@ LIBRARY_ASSET_EDITABLE
@ LIBRARY_ASSET_FILE_WRITABLE
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:563
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:882
void BKE_main_clear(Main &bmain)
Definition main.cc:68
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:532
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:526
#define FOREACH_MAIN_LISTBASE_END
Definition BKE_main.hh:544
void BKE_main_destroy(Main &bmain)
Definition main.cc:165
void BKE_main_init(Main &bmain)
Definition main.cc:55
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
Definition BKE_main.hh:537
void BKE_main_free(Main *bmain)
Definition main.cc:175
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:557
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:872
ID ID ID * BKE_main_idmap_lookup_uid(IDNameLib_Map *id_map, uint session_uid) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
IDNameLib_Map * BKE_main_idmap_create(Main *bmain, bool create_valid_ids_set, Main *old_bmain, int idmap_types) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition main_idmap.cc:77
void BKE_main_idmap_remove_id(IDNameLib_Map *id_map, const ID *id) ATTR_NONNULL()
void BKE_main_idmap_destroy(IDNameLib_Map *id_map) ATTR_NONNULL()
void BKE_main_idmap_insert_id(IDNameLib_Map *id_map, ID *id) ATTR_NONNULL()
void BKE_main_idmap_clear(IDNameLib_Map &id_map)
ID ID * BKE_main_idmap_lookup_id(IDNameLib_Map *id_map, const ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
@ MAIN_IDMAP_TYPE_UID
@ MAIN_IDMAP_TYPE_NAME
void BKE_main_namemap_remove_id(Main &bmain, ID &id)
void BKE_main_namemap_destroy(UniqueName_Map **r_name_map) ATTR_NONNULL()
void BKE_main_namemap_clear(Main &bmain)
bool BKE_main_namemap_validate(Main &bmain)
void BKE_preferences_asset_library_default_add(struct UserDef *userdef) ATTR_NONNULL()
void BKE_preferences_extension_repo_add_defaults_all(UserDef *userdef)
bool BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(UserDef *userdef, const char *shelf_idname, const char *catalog_path)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_reports_prependf(ReportList *reports, const char *prepend_format,...) ATTR_PRINTF_FORMAT(2
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
void void BKE_reports_prepend(ReportList *reports, const char *prepend)
Definition report.cc:206
Scene * BKE_scene_add(Main *bmain, const char *name)
Definition scene.cc:2010
void BKE_scene_set_background(Main *bmain, Scene *sce)
Definition scene.cc:2043
ScrArea ScrArea void BKE_screen_gizmo_tag_refresh(bScreen *screen)
Definition screen.cc:481
void BKE_screen_view3d_scene_sync(bScreen *screen, Scene *scene)
Definition screen.cc:998
void BKE_screen_runtime_refresh_for_blendfile(bScreen *screen)
Definition screen.cc:496
void BKE_studiolight_default(SolidLight lights[4], float light_ambient[3])
@ STEP_INVALID
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:612
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:373
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:461
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:456
int BLI_findindex(const ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:586
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define LISTBASE_FOREACH_BACKWARD(type, var, list)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
int BLI_listbase_count_at_most(const ListBase *listbase, int count_max) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:511
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
void BLI_addhead(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:91
bool BLI_remlink_safe(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:154
MINLINE int min_ii(int a, int b)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_extension_check_array(const char *path, const char **ext_array) ATTR_NONNULL(1
#define FILE_MAX
void BLI_path_slash_native(char *path) ATTR_NONNULL(1)
#define BLI_path_join(...)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
int BLI_system_memory_max_in_megabytes_int(void)
Definition system.cc:206
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:65
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define STREQ(a, b)
void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
external readfile function prototypes.
#define BLO_GROUP_MAX
eBLOReadSkip
@ BLO_READ_SKIP_DATA
@ BLO_READ_SKIP_USERDEF
#define BLO_EMBEDDED_STARTUP_BLEND
BlendHandle * BLO_blendhandle_from_file(const char *filepath, BlendFileReadReport *reports)
void BLO_blendhandle_close(BlendHandle *bh) ATTR_NONNULL(1)
BlendFileData * BLO_read_from_memfile(Main *oldmain, const char *filepath, MemFile *memfile, const BlendFileReadParams *params, ReportList *reports)
BlendFileData * BLO_read_from_memory(const void *mem, int memsize, eBLOReadSkip skip_flags, ReportList *reports)
#define BLO_READ_SKIP_ALL
void BLO_readfile_id_runtime_data_free_all(Main &bmain)
Definition readfile.cc:2250
void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
BlendFileData * BLO_read_from_file(const char *filepath, eBLOReadSkip skip_flags, BlendFileReadReport *reports)
void BLO_read_do_version_after_setup(Main *new_bmain, BlendfileLinkAppendContext *lapp_context, BlendFileReadReport *reports)
void BLO_blendfiledata_free(BlendFileData *bfd)
const UserDef U_default
const bTheme U_theme_default
external writefile.cc function prototypes.
bool BLO_write_file(Main *mainvar, const char *filepath, int write_flags, const BlendFileWriteParams *params, ReportList *reports)
eBLO_WritePathRemap
@ BLO_WRITE_PATH_REMAP_RELATIVE
#define RPT_(msgid)
void BPY_context_update(bContext *C)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
#define CLOG_FATAL(clg_ref,...)
Definition CLG_log.h:183
@ ID_FLAG_CLIPBOARD_MARK
Definition DNA_ID.h:712
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:687
struct Library Library
struct ID ID
@ INDEX_ID_NULL
Definition DNA_ID.h:1266
@ ID_TAG_TEMP_MAIN
Definition DNA_ID.h:879
@ ID_TAG_MISSING
Definition DNA_ID.h:775
@ ID_TAG_DOIT
Definition DNA_ID.h:944
@ ID_WM
@ ID_LI
@ ID_WS
@ ID_SCE
@ ID_SCR
@ GP_BRUSH_MATERIAL_PINNED
@ RGN_TYPE_WINDOW
@ SPACE_VIEW3D
#define FILE_MAX_LIBEXTRA
@ ULANGUAGE_AUTO
@ USER_TR_NEWDATANAME
@ USER_TR_TOOLTIPS
@ USER_TR_IFACE
@ USER_SCRIPT_AUTOEXEC_DISABLE
@ WM_INIT_FLAG_WINDOW
void IMB_colormanagement_check_file_config(Main *bmain)
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
ReportList * reports
Definition WM_types.hh:1025
#define U
void BKE_blendfile_read_setup_undo(bContext *C, BlendFileData *bfd, const BlendFileReadParams *params, BlendFileReadReport *reports)
static void view3d_data_consistency_ensure(wmWindow *win, Scene *scene, ViewLayer *view_layer)
Definition blendfile.cc:833
void BKE_blendfile_read_setup_readfile(bContext *C, BlendFileData *bfd, const BlendFileReadParams *params, BlendFileReadWMSetupData *wm_setup_data, BlendFileReadReport *reports, const bool startup_update_defaults, const char *startup_app_template)
bool BKE_blendfile_userdef_write_all(ReportList *reports)
static void wm_data_consistency_ensure(wmWindowManager *curwm, Scene *cur_scene, ViewLayer *cur_view_layer)
Definition blendfile.cc:886
BKE_main_namemap_clear * new_bmain
Definition blendfile.cc:603
static int swap_old_bmain_data_for_blendfile_dependencies_process_cb(LibraryIDLinkCallbackData *cb_data)
Definition blendfile.cc:712
static void handle_subversion_warning(Main *main, BlendFileReadReport *reports)
WorkspaceConfigFileData * BKE_blendfile_workspace_config_read(const char *filepath, const void *file_buf, int file_buf_size, ReportList *reports)
bool BKE_blendfile_userdef_write(const char *filepath, ReportList *reports)
static void setup_app_data(bContext *C, BlendFileData *bfd, const BlendFileReadParams *params, BlendFileReadWMSetupData *wm_setup_data, BlendFileReadReport *reports)
Definition blendfile.cc:919
static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, ID *id)
Definition blendfile.cc:308
static void reuse_bmain_data_invalid_local_usages_fix(ReuseOldBMainData *reuse_data)
Definition blendfile.cc:804
void BKE_blendfile_read_make_empty(bContext *C)
static void swap_wm_data_for_blendfile(ReuseOldBMainData *reuse_data, const bool load_ui)
Definition blendfile.cc:656
ID * discarded_id_iter
Definition blendfile.cc:612
BKE_main_namemap_clear * old_bmain
Definition blendfile.cc:602
bool BKE_blendfile_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name)
Definition blendfile.cc:90
bool BKE_blendfile_extension_check(const char *str)
Definition blendfile.cc:84
static bool reuse_editable_asset_needed(ReuseOldBMainData *reuse_data)
Definition blendfile.cc:485
static void swap_old_bmain_data_dependencies_process(ReuseOldBMainData *reuse_data, const short id_code)
Definition blendfile.cc:738
void BKE_blendfile_workspace_config_data_free(WorkspaceConfigFileData *workspace_config)
UserDef * BKE_blendfile_userdef_from_defaults()
static Library * reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data, Library *old_lib)
Definition blendfile.cc:394
static id::IDRemapper & reuse_bmain_data_remapper_ensure(ReuseOldBMainData *reuse_data)
Definition blendfile.cc:268
UserDef * BKE_blendfile_userdef_read(const char *filepath, ReportList *reports)
static void clean_paths(Main *bmain)
Definition blendfile.cc:182
BlendFileData * BKE_blendfile_read_from_memfile(Main *bmain, MemFile *memfile, const BlendFileReadParams *params, ReportList *reports)
static int reuse_bmain_data_invalid_local_usages_fix_cb(LibraryIDLinkCallbackData *cb_data)
Definition blendfile.cc:762
static void reuse_editable_asset_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short idcode)
Definition blendfile.cc:509
UserDef * BKE_blendfile_userdef_read_from_memory(const void *file_buf, int file_buf_size, ReportList *reports)
BlendFileData * BKE_blendfile_read(const char *filepath, const BlendFileReadParams *params, BlendFileReadReport *reports)
BlendFileData * BKE_blendfile_read_from_memory(const void *file_buf, int file_buf_size, const BlendFileReadParams *params, ReportList *reports)
static void setup_app_blend_file_data(bContext *C, BlendFileData *bfd, const BlendFileReadParams *params, BlendFileReadWMSetupData *wm_setup_data, BlendFileReadReport *reports)
ID * reused_id_iter
Definition blendfile.cc:613
static bool wm_scene_is_visible(wmWindowManager *wm, Scene *scene)
Definition blendfile.cc:197
static bool foreach_path_clean_cb(BPathForeachPathData *, char *path_dst, size_t path_dst_maxncpy, const char *path_src)
Definition blendfile.cc:171
static bool reuse_bmain_move_id(ReuseOldBMainData *reuse_data, ID *id, Library *lib, const bool reuse_existing)
Definition blendfile.cc:322
bool BKE_blendfile_userdef_write_app_template(const char *filepath, ReportList *reports)
static CLG_LogRef LOG_PARTIALWRITE
std::swap * new_lb
Definition blendfile.cc:598
bool BKE_blendfile_is_readable(const char *path, ReportList *reports)
Definition blendfile.cc:153
static void setup_app_userdef(BlendFileData *bfd)
Definition blendfile.cc:207
static void unpin_file_local_grease_pencil_brush_materials(const ReuseOldBMainData *reuse_data)
Definition blendfile.cc:555
static int reuse_editable_asset_bmain_data_dependencies_process_cb(LibraryIDLinkCallbackData *cb_data)
Definition blendfile.cc:434
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
const Value & lookup(const Key &key) const
Definition BLI_map.hh:545
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr const char * c_str() const
bool add(const Key &key)
int64_t size() const
void append(const T &value)
void remove_unused(bool clear_extra_user=false)
ID * id_add(const ID *id, IDAddOptions options, blender::FunctionRef< IDAddOperations(LibraryIDLinkCallbackData *cb_data, IDAddOptions options)> dependencies_filter_cb=nullptr)
bool write(const char *write_filepath, int write_flags, int remap_mode, ReportList &reports)
ID * id_create(short id_type, StringRefNull id_name, Library *library, IDAddOptions options)
IDRemapperApplyResult get_mapping_result(ID *id, IDRemapperApplyOptions options, const ID *id_self) const
IDRemapperApplyResult apply(ID **r_id_ptr, IDRemapperApplyOptions options, ID *id_self=nullptr) const
void add(ID *old_id, ID *new_id)
void add_overwrite(ID *old_id, ID *new_id)
std::string id_name(void *id)
CCL_NAMESPACE_BEGIN struct Options options
#define str(s)
#define main()
#define printf(...)
#define ID_TYPE_SUPPORTS_ASSET_EDITABLE(id_type)
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
#define MEM_SAFE_FREE(v)
#define ID_IS_LINKED(_id)
#define MAX_ID_NAME
#define INDEX_ID_MAX
#define ID_IS_OVERRIDE_LIBRARY(_id)
#define GS(a)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, LibQueryUnusedIDsData &parameters)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
void RE_FreeInteractiveCompositorRenders()
eBPathForeachFlag flag
Definition BKE_bpath.hh:94
BPathForeachPathFunctionCallback callback_function
Definition BKE_bpath.hh:93
struct Base * next
unsigned short local_view_bits
UserDef * user
ViewLayer * cur_view_layer
char filepath[1024]
bScreen * curscreen
wmWindowManager * old_wm
eBLO_WritePathRemap remap_mode
struct ID * reference
Definition DNA_ID.h:324
short id_code
Definition DNA_ID.h:404
int tag
Definition DNA_ID.h:424
struct Library * lib
Definition DNA_ID.h:410
IDOverrideLibrary * override_library
Definition DNA_ID.h:459
short flag
Definition DNA_ID.h:420
char name[66]
Definition DNA_ID.h:415
unsigned int session_uid
Definition DNA_ID.h:444
std::array< int, INDEX_ID_MAX > num_total
LibraryForeachIDCallbackFlag cb_flag
ID id
Definition DNA_ID.h:505
LibraryRuntimeHandle * runtime
Definition DNA_ID.h:516
void * last
void * first
ListBase brushes
Definition BKE_main.hh:271
ListBase scenes
Definition BKE_main.hh:245
ListBase wm
Definition BKE_main.hh:276
bool is_asset_edit_file
Definition BKE_main.hh:171
char filepath[1024]
Definition BKE_main.hh:155
bool recovered
Definition BKE_main.hh:178
IDNameLib_Map * id_map
Definition BKE_main.hh:301
ListBase screens
Definition BKE_main.hh:261
short versionfile
Definition BKE_main.hh:156
ListBase workspaces
Definition BKE_main.hh:284
bool is_read_invalid
Definition BKE_main.hh:203
struct RegionView3D * localvd
IDNameLib_Map * id_map
Definition blendfile.cc:255
BlendFileReadWMSetupData * wm_setup_data
Definition blendfile.cc:242
id::IDRemapper * remapper
Definition blendfile.cc:248
struct Object * camera
struct ListBase addons
float light_ambient[3]
struct ListBase themes
char fontdir[768]
struct SolidLight light_param[4]
struct ColorBand coba_weight
struct Object * camera
struct View3D * localvd
short scenelock
unsigned short local_view_uid
ListBase object_bases
char name[64]
char module[128]
ListBase areabase
struct Scene * scene
struct WorkSpaceInstanceHook * workspace_hook
i
Definition text_draw.cc:230
static DynamicLibrary lib