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