Blender V4.3
writefile.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
61#include <cerrno>
62#include <climits>
63#include <cmath>
64#include <cstdio>
65#include <cstdlib>
66#include <cstring>
67#include <fcntl.h>
68
69#ifdef WIN32
70# include "BLI_winstuff.h"
71# include "winsock2.h"
72# include <io.h>
73#else
74# include <unistd.h> /* FreeBSD, for write() and close(). */
75#endif
76
77#include "BLI_utildefines.h"
78
79#include "CLG_log.h"
80
81/* Allow writefile to use deprecated functionality (for forward compatibility code). */
82#define DNA_DEPRECATED_ALLOW
83
86#include "DNA_genfile.h"
87#include "DNA_key_types.h"
88#include "DNA_sdna_types.h"
89
90#include "BLI_bitmap.h"
91#include "BLI_blenlib.h"
92#include "BLI_endian_defines.h"
93#include "BLI_endian_switch.h"
95#include "BLI_link_utils.h"
96#include "BLI_linklist.h"
97#include "BLI_math_base.h"
98#include "BLI_mempool.h"
99#include "BLI_set.hh"
100#include "BLI_threads.h"
101
102#include "MEM_guardedalloc.h" /* MEM_freeN */
103
104#include "BKE_asset.hh"
105#include "BKE_blender_version.h"
106#include "BKE_bpath.hh"
107#include "BKE_global.hh" /* For #Global `G`. */
108#include "BKE_idprop.hh"
109#include "BKE_idtype.hh"
110#include "BKE_layer.hh"
111#include "BKE_lib_id.hh"
112#include "BKE_lib_override.hh"
113#include "BKE_lib_query.hh"
114#include "BKE_main.hh"
115#include "BKE_main_namemap.hh"
116#include "BKE_node.hh"
117#include "BKE_packedFile.hh"
118#include "BKE_preferences.h"
119#include "BKE_report.hh"
120#include "BKE_workspace.hh"
121
122#include "DRW_engine.hh"
123
124#include "BLO_blend_defs.hh"
125#include "BLO_blend_validate.hh"
126#include "BLO_read_write.hh"
127#include "BLO_readfile.hh"
128#include "BLO_undofile.hh"
129#include "BLO_writefile.hh"
130
131#include "readfile.hh"
132
133#include <zstd.h>
134
135/* Make preferences read-only. */
136#define U (*((const UserDef *)&U))
137
138/* ********* my write, buffered writing with minimum size chunks ************ */
139
140/* Use optimal allocation since blocks of this size are kept in memory for undo. */
141#define MEM_BUFFER_SIZE MEM_SIZE_OPTIMAL(1 << 17) /* 128kb */
142#define MEM_CHUNK_SIZE MEM_SIZE_OPTIMAL(1 << 15) /* ~32kb */
143
144#define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */
145#define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */
146
147#define ZSTD_COMPRESSION_LEVEL 3
148
149static CLG_LogRef LOG = {"blo.writefile"};
150
152// #define USE_WRITE_DATA_LEN
153
154/* -------------------------------------------------------------------- */
164
166 public:
167 virtual bool open(const char *filepath) = 0;
168 virtual bool close() = 0;
169 virtual bool write(const void *buf, size_t buf_len) = 0;
170
172 bool use_buf = true;
173};
174
175class RawWriteWrap : public WriteWrap {
176 public:
177 bool open(const char *filepath) override;
178 bool close() override;
179 bool write(const void *buf, size_t buf_len) override;
180
181 private:
182 int file_handle = 0;
183};
184
185bool RawWriteWrap::open(const char *filepath)
186{
187 int file;
188
189 file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
190
191 if (file != -1) {
192 file_handle = file;
193 return true;
194 }
195
196 return false;
197}
199{
200 return (::close(file_handle) != -1);
201}
202bool RawWriteWrap::write(const void *buf, size_t buf_len)
203{
204 return ::write(file_handle, buf, buf_len) == buf_len;
205}
206
207class ZstdWriteWrap : public WriteWrap {
208 WriteWrap &base_wrap;
209
210 ListBase threadpool = {};
211 ListBase tasks = {};
212 ThreadMutex mutex = {};
213 ThreadCondition condition = {};
214 int next_frame = 0;
215 int num_frames = 0;
216
217 ListBase frames = {};
218
219 bool write_error = false;
220
221 public:
222 ZstdWriteWrap(WriteWrap &base_wrap) : base_wrap(base_wrap) {}
223
224 bool open(const char *filepath) override;
225 bool close() override;
226 bool write(const void *buf, size_t buf_len) override;
227
228 private:
229 struct ZstdWriteBlockTask;
230 void write_task(ZstdWriteBlockTask *task);
231 void write_u32_le(uint32_t val);
232 void write_seekable_frames();
233};
234
237 void *data;
238 size_t size;
241
242 static void *write_task(void *userdata)
243 {
244 auto *task = static_cast<ZstdWriteBlockTask *>(userdata);
245 task->ww->write_task(task);
246 return nullptr;
247 }
248};
249
250void ZstdWriteWrap::write_task(ZstdWriteBlockTask *task)
251{
252 size_t out_buf_len = ZSTD_compressBound(task->size);
253 void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer");
254 size_t out_size = ZSTD_compress(
255 out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL);
256
257 MEM_freeN(task->data);
258
259 BLI_mutex_lock(&mutex);
260
261 while (next_frame != task->frame_number) {
262 BLI_condition_wait(&condition, &mutex);
263 }
264
265 if (ZSTD_isError(out_size)) {
266 write_error = true;
267 }
268 else {
269 if (base_wrap.write(out_buf, out_size)) {
270 ZstdFrame *frameinfo = static_cast<ZstdFrame *>(
271 MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo"));
272 frameinfo->uncompressed_size = task->size;
273 frameinfo->compressed_size = out_size;
274 BLI_addtail(&frames, frameinfo);
275 }
276 else {
277 write_error = true;
278 }
279 }
280
281 next_frame++;
282
283 BLI_mutex_unlock(&mutex);
284 BLI_condition_notify_all(&condition);
285
286 MEM_freeN(out_buf);
287}
288
289bool ZstdWriteWrap::open(const char *filepath)
290{
291 if (!base_wrap.open(filepath)) {
292 return false;
293 }
294
295 /* Leave one thread open for the main writing logic, unless we only have one HW thread. */
296 int num_threads = max_ii(1, BLI_system_thread_count() - 1);
297 BLI_threadpool_init(&threadpool, ZstdWriteBlockTask::write_task, num_threads);
298 BLI_mutex_init(&mutex);
299 BLI_condition_init(&condition);
300
301 return true;
302}
303
304void ZstdWriteWrap::write_u32_le(uint32_t val)
305{
306#ifdef __BIG_ENDIAN__
308#endif
309 base_wrap.write(&val, sizeof(uint32_t));
310}
311
312/* In order to implement efficient seeking when reading the .blend, we append
313 * a skippable frame that encodes information about the other frames present
314 * in the file.
315 * The format here follows the upstream spec for seekable files:
316 * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md
317 * If this information is not present in a file (e.g. if it was compressed
318 * with external tools), it can still be opened in Blender, but seeking will
319 * not be supported, so more memory might be needed. */
320void ZstdWriteWrap::write_seekable_frames()
321{
322 /* Write seek table header (magic number and frame size). */
323 write_u32_le(0x184D2A5E);
324
325 /* The actual frame number might not match num_frames if there was a write error. */
326 const uint32_t num_frames = BLI_listbase_count(&frames);
327 /* Each frame consists of two u32, so 8 bytes each.
328 * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */
329 const uint32_t frame_size = num_frames * 8 + 9;
330 write_u32_le(frame_size);
331
332 /* Write seek table entries. */
333 LISTBASE_FOREACH (ZstdFrame *, frame, &frames) {
334 write_u32_le(frame->compressed_size);
335 write_u32_le(frame->uncompressed_size);
336 }
337
338 /* Write seek table footer (number of frames, option flags and second magic number). */
339 write_u32_le(num_frames);
340 const char flags = 0; /* We don't store checksums for each frame. */
341 base_wrap.write(&flags, 1);
342 write_u32_le(0x8F92EAB1);
343}
344
346{
347 BLI_threadpool_end(&threadpool);
348 BLI_freelistN(&tasks);
349
350 BLI_mutex_end(&mutex);
351 BLI_condition_end(&condition);
352
353 write_seekable_frames();
354 BLI_freelistN(&frames);
355
356 return base_wrap.close() && !write_error;
357}
358
359bool ZstdWriteWrap::write(const void *buf, size_t buf_len)
360{
361 if (write_error) {
362 return false;
363 }
364
365 ZstdWriteBlockTask *task = static_cast<ZstdWriteBlockTask *>(
366 MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__));
367 task->data = MEM_mallocN(buf_len, __func__);
368 memcpy(task->data, buf, buf_len);
369 task->size = buf_len;
370 task->frame_number = num_frames++;
371 task->ww = this;
372
373 BLI_mutex_lock(&mutex);
374 BLI_addtail(&tasks, task);
375
376 /* If there's a free worker thread, just push the block into that thread.
377 * Otherwise, we wait for the earliest thread to finish.
378 * We look up the earliest thread while holding the mutex, but release it
379 * before joining the thread to prevent a deadlock. */
380 ZstdWriteBlockTask *first_task = static_cast<ZstdWriteBlockTask *>(tasks.first);
381 BLI_mutex_unlock(&mutex);
382 if (!BLI_available_threads(&threadpool)) {
383 BLI_threadpool_remove(&threadpool, first_task);
384
385 /* If the task list was empty before we pushed our task, there should
386 * always be a free thread. */
387 BLI_assert(first_task != task);
388 BLI_remlink(&tasks, first_task);
389 MEM_freeN(first_task);
390 }
391 BLI_threadpool_insert(&threadpool, task);
392
393 return true;
394}
395
398/* -------------------------------------------------------------------- */
402struct WriteData {
403 const SDNA *sdna;
404
405 struct {
409 size_t used_len;
410
412 size_t max_size;
416
417#ifdef USE_WRITE_DATA_LEN
419 size_t write_len;
420#endif
421
424
426 struct {
438
444
449
456};
457
460};
461
463{
464 WriteData *wd = MEM_new<WriteData>(__func__);
465
467
468 wd->ww = ww;
469
470 if ((ww == nullptr) || (ww->use_buf)) {
471 if (ww == nullptr) {
474 }
475 else {
478 }
479 wd->buffer.buf = static_cast<uchar *>(MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf"));
480 }
481
482 return wd;
483}
484
485static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
486{
487 if ((wd == nullptr) || wd->validation_data.critical_error || (mem == nullptr) || memlen < 1) {
488 return;
489 }
490
491 if (memlen > INT_MAX) {
492 BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX.");
493 return;
494 }
495
497 return;
498 }
499
500 /* Memory based save. */
501 if (wd->use_memfile) {
502 BLO_memfile_chunk_add(&wd->mem, static_cast<const char *>(mem), memlen);
503 }
504 else {
505 if (!wd->ww->write(mem, memlen)) {
507 }
508 }
509}
510
511static void writedata_free(WriteData *wd)
512{
513 if (wd->buffer.buf) {
514 MEM_freeN(wd->buffer.buf);
515 }
516 MEM_delete(wd);
517}
518
521/* -------------------------------------------------------------------- */
529static void mywrite_flush(WriteData *wd)
530{
531 if (wd->buffer.used_len != 0) {
533 wd->buffer.used_len = 0;
534 }
535}
536
542static void mywrite(WriteData *wd, const void *adr, size_t len)
543{
545 return;
546 }
547
548 if (UNLIKELY(adr == nullptr)) {
549 BLI_assert(0);
550 return;
551 }
552
553#ifdef USE_WRITE_DATA_LEN
554 wd->write_len += len;
555#endif
556
557 if (wd->buffer.buf == nullptr) {
558 writedata_do_write(wd, adr, len);
559 }
560 else {
561 /* If we have a single big chunk, write existing data in
562 * buffer and write out big chunk in smaller pieces. */
563 if (len > wd->buffer.chunk_size) {
564 if (wd->buffer.used_len != 0) {
566 wd->buffer.used_len = 0;
567 }
568
569 do {
570 size_t writelen = std::min(len, wd->buffer.chunk_size);
571 writedata_do_write(wd, adr, writelen);
572 adr = (const char *)adr + writelen;
573 len -= writelen;
574 } while (len > 0);
575
576 return;
577 }
578
579 /* If data would overflow buffer, write out the buffer. */
580 if (len + wd->buffer.used_len > wd->buffer.max_size - 1) {
582 wd->buffer.used_len = 0;
583 }
584
585 /* Append data at end of buffer. */
586 memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len);
587 wd->buffer.used_len += len;
588 }
589}
590
598static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
599{
600 WriteData *wd = writedata_new(ww);
601
602 if (current != nullptr) {
603 BLO_memfile_write_init(&wd->mem, current, compare);
604 wd->use_memfile = true;
605 }
606
607 return wd;
608}
609
616static bool mywrite_end(WriteData *wd)
617{
618 if (wd->buffer.used_len != 0) {
620 wd->buffer.used_len = 0;
621 }
622
623 if (wd->use_memfile) {
625 }
626
627 const bool err = wd->validation_data.critical_error;
628 writedata_free(wd);
629
630 return err;
631}
632
638static void mywrite_id_begin(WriteData *wd, ID *id)
639{
640 BLI_assert(wd->is_writing_id == false);
641 wd->is_writing_id = true;
642
644
645 if (wd->use_memfile) {
646 wd->mem.current_id_session_uid = id->session_uid;
647
648 /* If current next memchunk does not match the ID we are about to write, or is not the _first_
649 * one for said ID, try to find the correct memchunk in the mapping using ID's session_uid. */
650 const MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk;
651 const MemFileChunk *prev_memchunk = curr_memchunk != nullptr ?
652 static_cast<MemFileChunk *>(curr_memchunk->prev) :
653 nullptr;
654 if (curr_memchunk == nullptr || curr_memchunk->id_session_uid != id->session_uid ||
655 (prev_memchunk != nullptr &&
656 (prev_memchunk->id_session_uid == curr_memchunk->id_session_uid)))
657 {
658 if (MemFileChunk *ref = wd->mem.id_session_uid_mapping.lookup_default(id->session_uid,
659 nullptr))
660 {
661 wd->mem.reference_current_chunk = static_cast<MemFileChunk *>(ref);
662 }
663 /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */
664 }
665 /* Otherwise, we try with the current memchunk in any case, whether it is matching current
666 * ID's session_uid or not. */
667 }
668}
669
675static void mywrite_id_end(WriteData *wd, ID * /*id*/)
676{
677 if (wd->use_memfile) {
678 /* Very important to do it after every ID write now, otherwise we cannot know whether a
679 * specific ID changed or not. */
680 mywrite_flush(wd);
682 }
683
686
687 BLI_assert(wd->is_writing_id == true);
688 wd->is_writing_id = false;
689}
690
693/* -------------------------------------------------------------------- */
704static bool write_at_address_validate(WriteData *wd, int filecode, const void *address)
705{
706 /* Skip in undo case. */
707 if (wd->use_memfile) {
708 return true;
709 }
710
711 if (wd->is_writing_id && filecode == BLO_CODE_DATA) {
712 if (!wd->validation_data.per_id_addresses_set.add(address)) {
714 "Same identifier (old address) used several times for a same ID, skipping this "
715 "block to avoid critical corruption of the Blender file.");
716 return false;
717 }
718 }
719 return true;
720}
721
723 WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr, const void *data)
724{
725 BHead bh;
726
727 BLI_assert(struct_nr > 0 && struct_nr < SDNA_TYPE_MAX);
728
729 if (adr == nullptr || data == nullptr || nr == 0) {
730 return;
731 }
732
733 if (!write_at_address_validate(wd, filecode, adr)) {
734 return;
735 }
736
737 /* Initialize #BHead. */
738 bh.code = filecode;
739 bh.old = adr;
740 bh.nr = nr;
741
742 bh.SDNAnr = struct_nr;
743 bh.len = nr * DNA_struct_size(wd->sdna, bh.SDNAnr);
744
745 if (bh.len == 0) {
746 return;
747 }
748
749 mywrite(wd, &bh, sizeof(BHead));
750 mywrite(wd, data, size_t(bh.len));
751}
752
753static void writestruct_nr(
754 WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
755{
756 writestruct_at_address_nr(wd, filecode, struct_nr, nr, adr, adr);
757}
758
762static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
763{
764 BHead bh;
765
766 if (adr == nullptr || len == 0) {
767 return;
768 }
769
770 if (!write_at_address_validate(wd, filecode, adr)) {
771 return;
772 }
773
774 /* Align to 4 (writes uninitialized bytes in some cases). */
775 len = (len + 3) & ~size_t(3);
776
777 if (len > INT_MAX) {
778 BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX.");
779 return;
780 }
781
782 /* Initialize #BHead. */
783 bh.code = filecode;
784 bh.old = adr;
785 bh.nr = 1;
786 BLI_STATIC_ASSERT(SDNA_RAW_DATA_STRUCT_INDEX == 0, "'raw data' SDNA struct index should be 0")
788 bh.len = int(len);
789
790 mywrite(wd, &bh, sizeof(BHead));
791 mywrite(wd, adr, len);
792}
793
797static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
798{
799 const Link *link = static_cast<Link *>(lb->first);
800
801 while (link) {
802 writestruct_nr(wd, filecode, struct_nr, 1, link);
803 link = link->next;
804 }
805}
806
807#if 0
808static void writelist_id(WriteData *wd, int filecode, const char *structname, const ListBase *lb)
809{
810 const Link *link = lb->first;
811 if (link) {
812
813 const int struct_nr = DNA_struct_find_with_alias(wd->sdna, structname);
814 if (struct_nr == -1) {
815 printf("error: can't find SDNA code <%s>\n", structname);
816 return;
817 }
818
819 while (link) {
820 writestruct_nr(wd, filecode, struct_nr, 1, link);
821 link = link->next;
822 }
823 }
824}
825#endif
826
827#define writestruct_at_address(wd, filecode, struct_id, nr, adr, data) \
828 writestruct_at_address_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr, data)
829
830#define writestruct(wd, filecode, struct_id, nr, adr) \
831 writestruct_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr)
832
835/* -------------------------------------------------------------------- */
845static void current_screen_compat(Main *mainvar,
846 bool use_active_win,
847 bScreen **r_screen,
848 Scene **r_scene,
849 ViewLayer **r_view_layer)
850{
851 wmWindowManager *wm;
852 wmWindow *window = nullptr;
853
854 /* Find a global current screen in the first open window, to have
855 * a reasonable default for reading in older versions. */
856 wm = static_cast<wmWindowManager *>(mainvar->wm.first);
857
858 if (wm) {
859 if (use_active_win) {
860 /* Write the active window into the file, needed for multi-window undo #43424. */
861 for (window = static_cast<wmWindow *>(wm->windows.first); window; window = window->next) {
862 if (window->active) {
863 break;
864 }
865 }
866
867 /* Fallback. */
868 if (window == nullptr) {
869 window = static_cast<wmWindow *>(wm->windows.first);
870 }
871 }
872 else {
873 window = static_cast<wmWindow *>(wm->windows.first);
874 }
875 }
876
877 *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : nullptr;
878 *r_scene = (window) ? window->scene : nullptr;
879 *r_view_layer = (window && *r_scene) ? BKE_view_layer_find(*r_scene, window->view_layer_name) :
880 nullptr;
881}
882
884 int sfra;
885 int efra;
887};
888
895static void write_renderinfo(WriteData *wd, Main *mainvar)
896{
897 bScreen *curscreen;
898 Scene *curscene = nullptr;
899 ViewLayer *view_layer;
900
901 /* XXX: in future, handle multiple windows with multiple screens? */
902 current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer);
903
904 LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) {
905 if (!ID_IS_LINKED(sce) && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) {
907 data.sfra = sce->r.sfra;
908 data.efra = sce->r.efra;
909 memset(data.scene_name, 0, sizeof(data.scene_name));
910
911 STRNCPY(data.scene_name, sce->id.name + 2);
912
913 writedata(wd, BLO_CODE_REND, sizeof(data), &data);
914 }
915 }
916}
917
918static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
919{
920 BLO_write_struct(writer, wmKeyMapItem, kmi);
921 if (kmi->properties) {
922 IDP_BlendWrite(writer, kmi->properties);
923 }
924}
925
926static void write_userdef(BlendWriter *writer, const UserDef *userdef)
927{
928 writestruct(writer->wd, BLO_CODE_USER, UserDef, 1, userdef);
929
930 LISTBASE_FOREACH (const bTheme *, btheme, &userdef->themes) {
931 BLO_write_struct(writer, bTheme, btheme);
932 }
933
934 LISTBASE_FOREACH (const wmKeyMap *, keymap, &userdef->user_keymaps) {
935 BLO_write_struct(writer, wmKeyMap, keymap);
936
937 LISTBASE_FOREACH (const wmKeyMapDiffItem *, kmdi, &keymap->diff_items) {
938 BLO_write_struct(writer, wmKeyMapDiffItem, kmdi);
939 if (kmdi->remove_item) {
940 write_keymapitem(writer, kmdi->remove_item);
941 }
942 if (kmdi->add_item) {
943 write_keymapitem(writer, kmdi->add_item);
944 }
945 }
946
947 LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) {
948 write_keymapitem(writer, kmi);
949 }
950 }
951
952 LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
953 BLO_write_struct(writer, wmKeyConfigPref, kpt);
954 if (kpt->prop) {
955 IDP_BlendWrite(writer, kpt->prop);
956 }
957 }
958
959 LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
960 BLO_write_struct(writer, bUserMenu, um);
961 LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
962 if (umi->type == USER_MENU_TYPE_OPERATOR) {
963 const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
964 BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
965 if (umi_op->prop) {
966 IDP_BlendWrite(writer, umi_op->prop);
967 }
968 }
969 else if (umi->type == USER_MENU_TYPE_MENU) {
970 const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
971 BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
972 }
973 else if (umi->type == USER_MENU_TYPE_PROP) {
974 const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
975 BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
976 }
977 else {
978 BLO_write_struct(writer, bUserMenuItem, umi);
979 }
980 }
981 }
982
983 LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
984 BLO_write_struct(writer, bAddon, bext);
985 if (bext->prop) {
986 IDP_BlendWrite(writer, bext->prop);
987 }
988 }
989
990 LISTBASE_FOREACH (const bPathCompare *, path_cmp, &userdef->autoexec_paths) {
991 BLO_write_struct(writer, bPathCompare, path_cmp);
992 }
993
994 LISTBASE_FOREACH (const bUserScriptDirectory *, script_dir, &userdef->script_directories) {
995 BLO_write_struct(writer, bUserScriptDirectory, script_dir);
996 }
997
998 LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library_ref, &userdef->asset_libraries) {
999 BLO_write_struct(writer, bUserAssetLibrary, asset_library_ref);
1000 }
1001
1002 LISTBASE_FOREACH (const bUserExtensionRepo *, repo_ref, &userdef->extension_repos) {
1003 BLO_write_struct(writer, bUserExtensionRepo, repo_ref);
1005 }
1007 const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings)
1008 {
1009 BLO_write_struct(writer, bUserAssetShelfSettings, shelf_settings);
1010 BKE_asset_catalog_path_list_blend_write(writer, shelf_settings->enabled_catalog_paths);
1011 }
1012
1013 LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
1014 BLO_write_struct(writer, uiStyle, style);
1015 }
1016}
1017
1020{
1021 ListBase *lbarray[INDEX_ID_MAX];
1022 ID *id;
1023 int a, tot;
1024 bool found_one;
1025
1026 for (; main; main = main->next) {
1027 a = tot = set_listbasepointers(main, lbarray);
1028
1029 /* Test: is lib being used. */
1030 if (main->curlib && main->curlib->packedfile) {
1031 found_one = true;
1032 }
1033 else if (wd->use_memfile) {
1034 /* When writing undo step we always write all existing libraries, makes reading undo step
1035 * much easier when dealing with purely indirectly used libraries. */
1036 found_one = true;
1037 }
1038 else {
1039 found_one = false;
1040 while (!found_one && tot--) {
1041 for (id = static_cast<ID *>(lbarray[tot]->first); id; id = static_cast<ID *>(id->next)) {
1042 if (id->us > 0 &&
1043 ((id->tag & ID_TAG_EXTERN) ||
1044 ((id->tag & ID_TAG_INDIRECT) && (id->flag & ID_FLAG_INDIRECT_WEAK_LINK))))
1045 {
1046 found_one = true;
1047 break;
1048 }
1049 }
1050 }
1051 }
1052
1053 /* To be able to restore `quit.blend` and temp saves,
1054 * the packed blend has to be in undo buffers... */
1055 /* XXX needs rethink, just like save UI in undo files now -
1056 * would be nice to append things only for the `quit.blend` and temp saves. */
1057 if (found_one) {
1058 /* Not overridable. */
1059
1060 void *runtime_name_data = main->curlib->runtime.name_map;
1061 main->curlib->runtime.name_map = nullptr;
1062
1063 BlendWriter writer = {wd};
1064 writestruct(wd, ID_LI, Library, 1, main->curlib);
1065 BKE_id_blend_write(&writer, &main->curlib->id);
1066
1067 main->curlib->runtime.name_map = static_cast<UniqueName_Map *>(runtime_name_data);
1068
1069 if (main->curlib->packedfile) {
1070 BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
1071 if (wd->use_memfile == false) {
1072 CLOG_INFO(&LOG, 2, "Write packed .blend: %s", main->curlib->filepath);
1073 }
1074 }
1075
1076 /* Write link placeholders for all direct linked IDs. */
1077 while (a--) {
1078 for (id = static_cast<ID *>(lbarray[a]->first); id; id = static_cast<ID *>(id->next)) {
1079 if (id->us > 0 &&
1080 ((id->tag & ID_TAG_EXTERN) ||
1081 ((id->tag & ID_TAG_INDIRECT) && (id->flag & ID_FLAG_INDIRECT_WEAK_LINK))))
1082 {
1083 if (!BKE_idtype_idcode_is_linkable(GS(id->name))) {
1084 CLOG_ERROR(&LOG,
1085 "Data-block '%s' from lib '%s' is not linkable, but is flagged as "
1086 "directly linked",
1087 id->name,
1088 main->curlib->runtime.filepath_abs);
1089 }
1090 writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id);
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 mywrite_flush(wd);
1098}
1099
1100#ifdef WITH_BUILDINFO
1101extern "C" ulong build_commit_timestamp;
1102extern "C" char build_hash[];
1103#endif
1104
1110static void write_global(WriteData *wd, int fileflags, Main *mainvar)
1111{
1112 const bool is_undo = wd->use_memfile;
1113 FileGlobal fg;
1114 bScreen *screen;
1115 Scene *scene;
1116 ViewLayer *view_layer;
1117 char subvstr[8];
1118
1119 /* Prevent memory checkers from complaining. */
1120 memset(fg._pad, 0, sizeof(fg._pad));
1121 memset(fg.filepath, 0, sizeof(fg.filepath));
1122 memset(fg.build_hash, 0, sizeof(fg.build_hash));
1123 fg._pad1 = nullptr;
1124
1125 current_screen_compat(mainvar, is_undo, &screen, &scene, &view_layer);
1126
1127 /* XXX: still remap `G`. */
1128 fg.curscreen = screen;
1129 fg.curscene = scene;
1130 fg.cur_view_layer = view_layer;
1131
1132 /* Prevent to save this, is not good convention, and feature with concerns. */
1133 fg.fileflags = (fileflags & ~G_FILE_FLAG_ALL_RUNTIME);
1134
1135 fg.globalf = G.f;
1136 /* Write information needed for recovery. */
1137 if (fileflags & G_FILE_RECOVER_WRITE) {
1138 STRNCPY(fg.filepath, mainvar->filepath);
1139 /* Compression is often turned of when writing recovery files. However, when opening the file,
1140 * it should be enabled again. */
1141 fg.fileflags = G.fileflags & G_FILE_COMPRESS;
1142 }
1143 SNPRINTF(subvstr, "%4d", BLENDER_FILE_SUBVERSION);
1144 memcpy(fg.subvstr, subvstr, 4);
1145
1149#ifdef WITH_BUILDINFO
1150 /* TODO(sergey): Add branch name to file as well? */
1153#else
1155 STRNCPY(fg.build_hash, "unknown");
1156#endif
1157 writestruct(wd, BLO_CODE_GLOB, FileGlobal, 1, &fg);
1158}
1159
1165static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
1166{
1167 if (thumb) {
1168 writedata(wd, BLO_CODE_TEST, BLEN_THUMB_MEMSIZE_FILE(thumb->width, thumb->height), thumb);
1169 }
1170}
1171
1174/* -------------------------------------------------------------------- */
1178#define ID_BUFFER_STATIC_SIZE 8192
1179
1185
1186static void id_buffer_init_for_id_type(BLO_Write_IDBuffer *id_buffer, const IDTypeInfo *id_type)
1187{
1188 if (id_type != id_buffer->id_type) {
1189 const size_t idtype_struct_size = id_type->struct_size;
1190 if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
1191 CLOG_ERROR(&LOG,
1192 "ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, "
1193 "which needs %lu bytes",
1195 id_type->name,
1196 idtype_struct_size);
1197 id_buffer->temp_id = static_cast<ID *>(MEM_mallocN(idtype_struct_size, __func__));
1198 }
1199 else {
1200 if (static_cast<void *>(id_buffer->temp_id) != id_buffer->id_buffer_static) {
1201 MEM_SAFE_FREE(id_buffer->temp_id);
1202 }
1203 id_buffer->temp_id = reinterpret_cast<ID *>(id_buffer->id_buffer_static);
1204 }
1205 id_buffer->id_type = id_type;
1206 }
1207}
1208
1209static void id_buffer_init_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
1210{
1212
1213 if (is_undo) {
1214 /* Record the changes that happened up to this undo push in
1215 * recalc_up_to_undo_push, and clear `recalc_after_undo_push` again
1216 * to start accumulating for the next undo push. */
1217 id->recalc_up_to_undo_push = id->recalc_after_undo_push;
1218 id->recalc_after_undo_push = 0;
1219 }
1220
1221 /* Copy ID data itself into buffer, to be able to freely modify it. */
1222 const size_t idtype_struct_size = id_buffer->id_type->struct_size;
1223 ID *temp_id = id_buffer->temp_id;
1224 memcpy(temp_id, id, idtype_struct_size);
1225
1226 /* Clear runtime data to reduce false detection of changed data in undo/redo context. */
1227 if (is_undo) {
1228 temp_id->tag &= ID_TAG_KEEP_ON_UNDO;
1229 }
1230 else {
1231 temp_id->tag = 0;
1232 }
1233 temp_id->us = 0;
1234 temp_id->icon_id = 0;
1235 /* Those listbase data change every time we add/remove an ID, and also often when
1236 * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
1237 * detections between undo steps. */
1238 temp_id->prev = nullptr;
1239 temp_id->next = nullptr;
1240 /* Those runtime pointers should never be set during writing stage, but just in case clear
1241 * them too. */
1242 temp_id->orig_id = nullptr;
1243 temp_id->newid = nullptr;
1244 /* Even though in theory we could be able to preserve this python instance across undo even
1245 * when we need to re-read the ID into its original address, this is currently cleared in
1246 * #direct_link_id_common in `readfile.cc` anyway. */
1247 temp_id->py_instance = nullptr;
1248
1249 DrawDataList *drawdata = DRW_drawdatalist_from_id(temp_id);
1250 if (drawdata) {
1251 BLI_listbase_clear(reinterpret_cast<ListBase *>(drawdata));
1252 }
1253}
1254
1255/* Helper callback for checking linked IDs used by given ID (assumed local), to ensure directly
1256 * linked data is tagged accordingly. */
1258{
1259 ID *self_id = cb_data->self_id;
1260 ID *id = *cb_data->id_pointer;
1261 const int cb_flag = cb_data->cb_flag;
1262
1263 if (id == nullptr || !ID_IS_LINKED(id)) {
1264 return IDWALK_RET_NOP;
1265 }
1266 BLI_assert(!ID_IS_LINKED(self_id));
1267 BLI_assert((cb_flag & IDWALK_CB_INDIRECT_USAGE) == 0);
1268
1269 if (self_id->tag & ID_TAG_RUNTIME) {
1270 return IDWALK_RET_NOP;
1271 }
1272
1273 if (!BKE_idtype_idcode_is_linkable(GS(id->name))) {
1274 /* Usages of unlinkable IDs (aka ShapeKeys and some UI IDs) should never cause them to be
1275 * considered as directly linked. This can often happen e.g. from UI data (the Outliner will
1276 * have links to most IDs).
1277 */
1278 return IDWALK_RET_NOP;
1279 }
1280
1281 if (cb_flag & IDWALK_CB_DIRECT_WEAK_LINK) {
1283 }
1284 else {
1285 id_lib_extern(id);
1286 }
1287
1288 return IDWALK_RET_NOP;
1289}
1290
1297static bool write_file_handle(Main *mainvar,
1298 WriteWrap *ww,
1299 MemFile *compare,
1300 MemFile *current,
1301 int write_flags,
1302 bool use_userdef,
1303 const BlendThumbnail *thumb)
1304{
1305 BHead bhead;
1306 ListBase mainlist;
1307 char buf[16];
1308 WriteData *wd;
1309
1310 wd = mywrite_begin(ww, compare, current);
1311 BlendWriter writer = {wd};
1312
1313 /* Clear 'directly linked' flag for all linked data, these are not necessarily valid/up-to-date
1314 * info, they will be re-generated while write code is processing local IDs below. */
1315 if (!wd->use_memfile) {
1316 ID *id_iter;
1317 FOREACH_MAIN_ID_BEGIN (mainvar, id_iter) {
1318 if (ID_IS_LINKED(id_iter) && BKE_idtype_idcode_is_linkable(GS(id_iter->name))) {
1319 if (USER_EXPERIMENTAL_TEST(&U, use_all_linked_data_direct)) {
1320 /* Forces all linked data to be considered as directly linked.
1321 * FIXME: Workaround some BAT tool limitations for Heist production, should be removed
1322 * asap afterward. */
1323 id_lib_extern(id_iter);
1324 }
1325 else if (GS(id_iter->name) == ID_SCE) {
1326 /* For scenes, do not force them into 'indirectly linked' status.
1327 * The main reason is that scenes typically have no users, so most linked scene would be
1328 * systematically 'lost' on file save.
1329 *
1330 * While this change re-introduces the 'no-more-used data laying around in files for
1331 * ever' issue when it comes to scenes, this solution seems to be the most sensible one
1332 * for the time being, considering that:
1333 * - Scene are a top-level container.
1334 * - Linked scenes are typically explicitly linked by the user.
1335 * - Cases where scenes would be indirectly linked by other data (e.g. when linking a
1336 * collection or material) can be considered at the very least as not following sane
1337 * practice in data dependencies.
1338 * - There are typically not hundreds of scenes in a file, and they are always very
1339 * easily discoverable and browsable from the main UI. */
1340 }
1341 else {
1342 id_iter->tag |= ID_TAG_INDIRECT;
1343 id_iter->tag &= ~ID_TAG_EXTERN;
1344 }
1345 }
1346 }
1348 }
1349
1350 /* Recompute all ID user-counts if requested. Allows to avoid skipping writing of IDs wrongly
1351 * detected as unused due to invalid user-count. */
1352 if (!wd->use_memfile) {
1353 if (USER_EXPERIMENTAL_TEST(&U, use_recompute_usercount_on_save_debug)) {
1354 BKE_main_id_refcount_recompute(mainvar, false);
1355 }
1356 }
1357
1358 blo_split_main(&mainlist, mainvar);
1359
1360 SNPRINTF(buf,
1361 "BLENDER%c%c%.3d",
1362 (sizeof(void *) == 8) ? '-' : '_',
1363 (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
1365
1366 mywrite(wd, buf, 12);
1367
1368 write_renderinfo(wd, mainvar);
1369 write_thumb(wd, thumb);
1370 write_global(wd, write_flags, mainvar);
1371
1372 /* The window-manager and screen often change,
1373 * avoid thumbnail detecting changes because of this. */
1374 mywrite_flush(wd);
1375
1376 OverrideLibraryStorage *override_storage = wd->use_memfile ?
1377 nullptr :
1379
1380 /* This outer loop allows to save first data-blocks from real mainvar,
1381 * then the temp ones from override process,
1382 * if needed, without duplicating whole code. */
1383 Main *bmain = mainvar;
1385 do {
1386 ListBase *lbarray[INDEX_ID_MAX];
1387 int a = set_listbasepointers(bmain, lbarray);
1388 while (a--) {
1389 ID *id = static_cast<ID *>(lbarray[a]->first);
1390
1391 if (id == nullptr || GS(id->name) == ID_LI) {
1392 continue; /* Libraries are handled separately below. */
1393 }
1394
1395 const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
1396 id_buffer_init_for_id_type(id_buffer, id_type);
1397
1398 for (; id; id = static_cast<ID *>(id->next)) {
1399 /* We should never attempt to write non-regular IDs
1400 * (i.e. all kind of temp/runtime ones). */
1402 0);
1403
1404 /* We only write unused IDs in undo case. */
1405 if (!wd->use_memfile) {
1406 /* NOTE: All 'never unused' local IDs (Scenes, WindowManagers, ...) should always be
1407 * written to disk, so their user-count should never be zero currently. Note that
1408 * libraries have already been skipped above, as they need a specific handling. */
1409 if (id->us == 0) {
1410 /* FIXME: #124857: Some old files seem to cause incorrect handling of their temp
1411 * screens.
1412 *
1413 * See e.g. file attached to #124777 (from 2.79.1).
1414 *
1415 * For now ignore, issue is not obvious to track down (`temp` bScreen ID from read data
1416 * _does_ have the proper `temp` tag), and seems anecdotal at worst. */
1417 BLI_assert((id_type->flags & IDTYPE_FLAGS_NEVER_UNUSED) == 0);
1418 continue;
1419 }
1420
1421 /* XXX Special handling for ShapeKeys, as having unused shapekeys is not a good thing
1422 * (and reported as error by e.g. `BLO_main_validate_shapekeys`), skip writing shapekeys
1423 * when their 'owner' is not written.
1424 *
1425 * NOTE: Since ShapeKeys are conceptually embedded IDs (like root node trees e.g.), this
1426 * behavior actually makes sense anyway. This remains more of a temp hack until topic of
1427 * how to handle unused data on save is properly tackled. */
1428 if (GS(id->name) == ID_KE) {
1429 Key *shape_key = reinterpret_cast<Key *>(id);
1430 /* NOTE: Here we are accessing the real owner ID data, not it's 'proxy' shallow copy
1431 * generated for its file-writing. This is not expected to be an issue, but is worth
1432 * noting. */
1433 if (shape_key->from == nullptr || shape_key->from->us == 0) {
1434 continue;
1435 }
1436 }
1437 }
1438
1439 if ((id->tag & ID_TAG_RUNTIME) != 0 && !wd->use_memfile) {
1440 /* Runtime IDs are never written to .blend files, and they should not influence
1441 * (in)direct status of linked IDs they may use. */
1442 continue;
1443 }
1444
1445 const bool do_override = !ELEM(override_storage, nullptr, bmain) &&
1447
1448 /* If not writing undo data, properly set directly linked IDs as `ID_TAG_EXTERN`. */
1449 if (!wd->use_memfile) {
1451 id,
1453 nullptr,
1455 }
1456
1457 if (do_override) {
1458 BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
1459 }
1460
1461 mywrite_id_begin(wd, id);
1462
1463 id_buffer_init_from_id(id_buffer, id, wd->use_memfile);
1464
1465 if (id_type->blend_write != nullptr) {
1466 id_type->blend_write(&writer, static_cast<ID *>(id_buffer->temp_id), id);
1467 }
1468
1469 if (do_override) {
1471 }
1472
1473 mywrite_id_end(wd, id);
1474 }
1475
1476 mywrite_flush(wd);
1477 }
1478 } while ((bmain != override_storage) && (bmain = override_storage));
1479
1480 BLO_write_destroy_id_buffer(&id_buffer);
1481
1482 if (override_storage) {
1484 override_storage = nullptr;
1485 }
1486
1487 /* Special handling, operating over split Mains... */
1488 write_libraries(wd, mainvar->next);
1489
1490 /* So changes above don't cause a 'DNA1' to be detected as changed on undo. */
1491 mywrite_flush(wd);
1492
1493 if (use_userdef) {
1494 write_userdef(&writer, &U);
1495 }
1496
1497 /* Write DNA last, because (to be implemented) test for which structs are written.
1498 *
1499 * Note that we *borrow* the pointer to 'DNAstr',
1500 * so writing each time uses the same address and doesn't cause unnecessary undo overhead. */
1501 writedata(wd, BLO_CODE_DNA1, size_t(wd->sdna->data_size), wd->sdna->data);
1502
1503 /* End of file. */
1504 memset(&bhead, 0, sizeof(BHead));
1505 bhead.code = BLO_CODE_ENDB;
1506 mywrite(wd, &bhead, sizeof(BHead));
1507
1508 blo_join_main(&mainlist);
1509
1510 return mywrite_end(wd);
1511}
1512
1517static bool do_history(const char *filepath, ReportList *reports)
1518{
1519 /* Add 2 because version number maximum is double-digits. */
1520 char filepath_tmp1[FILE_MAX + 2], filepath_tmp2[FILE_MAX + 2];
1521 int version_number = min_ii(99, U.versions);
1522
1523 if (version_number == 0) {
1524 return true;
1525 }
1526
1527 if (strlen(filepath) < 2) {
1528 BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short");
1529 return false;
1530 }
1531
1532 while (version_number > 1) {
1533 SNPRINTF(filepath_tmp1, "%s%d", filepath, version_number - 1);
1534 if (BLI_exists(filepath_tmp1)) {
1535 SNPRINTF(filepath_tmp2, "%s%d", filepath, version_number);
1536
1537 if (BLI_rename_overwrite(filepath_tmp1, filepath_tmp2)) {
1538 BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1539 return false;
1540 }
1541 }
1542 version_number--;
1543 }
1544
1545 /* Needed when `version_number == 1`. */
1546 if (BLI_exists(filepath)) {
1547 SNPRINTF(filepath_tmp1, "%s%d", filepath, version_number);
1548
1549 if (BLI_rename_overwrite(filepath, filepath_tmp1)) {
1550 BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1551 return false;
1552 }
1553 }
1554
1555 return true;
1556}
1557
1558static void write_file_main_validate_pre(Main *bmain, ReportList *reports)
1559{
1560 if (!bmain->lock) {
1561 return;
1562 }
1563
1564 if (G.debug & G_DEBUG_IO) {
1565 BKE_report(
1566 reports, RPT_DEBUG, "Checking validity of current .blend file *BEFORE* save to disk");
1567 }
1568
1569 BLO_main_validate_shapekeys(bmain, reports);
1571 BKE_report(reports,
1572 RPT_ERROR,
1573 "Critical data corruption: Conflicts and/or otherwise invalid data-blocks names "
1574 "(see console for details)");
1575 }
1576
1577 if (G.debug & G_DEBUG_IO) {
1578 BLO_main_validate_libraries(bmain, reports);
1579 }
1580}
1581
1582static void write_file_main_validate_post(Main *bmain, ReportList *reports)
1583{
1584 if (!bmain->lock) {
1585 return;
1586 }
1587
1588 if (G.debug & G_DEBUG_IO) {
1589 BKE_report(
1590 reports, RPT_DEBUG, "Checking validity of current .blend file *BEFORE* save to disk");
1591 BLO_main_validate_libraries(bmain, reports);
1592 }
1593}
1594
1595static bool BLO_write_file_impl(Main *mainvar,
1596 const char *filepath,
1597 const int write_flags,
1599 ReportList *reports,
1600 WriteWrap &ww)
1601{
1602 BLI_assert(!BLI_path_is_rel(filepath));
1604
1605 char tempname[FILE_MAX + 1];
1606
1607 eBLO_WritePathRemap remap_mode = params->remap_mode;
1608 const bool use_save_versions = params->use_save_versions;
1609 const bool use_save_as_copy = params->use_save_as_copy;
1610 const bool use_userdef = params->use_userdef;
1611 const BlendThumbnail *thumb = params->thumb;
1612 const bool relbase_valid = (mainvar->filepath[0] != '\0');
1613
1614 /* Extra protection: Never save a non asset file as asset file. Otherwise a normal file is turned
1615 * into an asset file, which can result in data loss because the asset system will allow editing
1616 * this file from the UI, regenerating its content with just the asset and it dependencies. */
1617 if ((write_flags & G_FILE_ASSET_EDIT_FILE) && !mainvar->is_asset_edit_file) {
1618 BKE_reportf(reports, RPT_ERROR, "Cannot save normal file (%s) as asset system file", tempname);
1619 return false;
1620 }
1621
1622 /* Path backup/restore. */
1623 void *path_list_backup = nullptr;
1626
1627 write_file_main_validate_pre(mainvar, reports);
1628
1629 /* Open temporary file, so we preserve the original in case we crash. */
1630 SNPRINTF(tempname, "%s@", filepath);
1631
1632 if (ww.open(tempname) == false) {
1634 reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno));
1635 return false;
1636 }
1637
1638 if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
1639 /* Paths will already be absolute, no remapping to do. */
1640 if (relbase_valid == false) {
1641 remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1642 }
1643 }
1644
1645 /* Remapping of relative paths to new file location. */
1646 if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1647 if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1648 /* Make all relative as none of the existing paths can be relative in an unsaved document. */
1649 if (relbase_valid == false) {
1651 }
1652 }
1653
1654 /* The source path only makes sense to set if the file was saved (`relbase_valid`). */
1655 char dir_src[FILE_MAX];
1656 char dir_dst[FILE_MAX];
1657
1658 /* Normalize the paths in case there is some subtle difference (so they can be compared). */
1659 if (relbase_valid) {
1660 BLI_path_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
1661 BLI_path_normalize(dir_src);
1662 }
1663 else {
1664 dir_src[0] = '\0';
1665 }
1666 BLI_path_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
1667 BLI_path_normalize(dir_dst);
1668
1669 /* Only for relative, not relative-all, as this means making existing paths relative. */
1670 if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1671 if (relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
1672 /* Saved to same path. Nothing to do. */
1673 remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1674 }
1675 }
1676 else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
1677 if (relbase_valid == false) {
1678 /* Unsaved, all paths are absolute.Even if the user manages to set a relative path,
1679 * there is no base-path that can be used to make it absolute. */
1680 remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1681 }
1682 }
1683
1684 if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1685 /* Some path processing (e.g. with libraries) may use the current `main->filepath`, if this
1686 * is not matching the path currently used for saving, unexpected paths corruptions can
1687 * happen. See #98201. */
1688 char mainvar_filepath_orig[FILE_MAX];
1689 STRNCPY(mainvar_filepath_orig, mainvar->filepath);
1690 STRNCPY(mainvar->filepath, filepath);
1691
1692 /* Check if we need to backup and restore paths. */
1693 if (UNLIKELY(use_save_as_copy)) {
1694 path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag);
1695 }
1696
1697 switch (remap_mode) {
1699 /* Saved, make relative paths relative to new location (if possible). */
1700 BLI_assert(relbase_valid);
1701 BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, nullptr);
1702 break;
1704 /* Make all relative (when requested or unsaved). */
1705 BKE_bpath_relative_convert(mainvar, dir_dst, nullptr);
1706 break;
1708 /* Make all absolute (when requested or unsaved). */
1709 BLI_assert(relbase_valid);
1710 BKE_bpath_absolute_convert(mainvar, dir_src, nullptr);
1711 break;
1713 BLI_assert_unreachable(); /* Unreachable. */
1714 break;
1715 }
1716
1717 STRNCPY(mainvar->filepath, mainvar_filepath_orig);
1718 }
1719 }
1720
1721 /* Actual file writing. */
1722 const bool err = write_file_handle(
1723 mainvar, &ww, nullptr, nullptr, write_flags, use_userdef, thumb);
1724
1725 ww.close();
1726
1727 if (UNLIKELY(path_list_backup)) {
1728 BKE_bpath_list_restore(mainvar, path_list_flag, path_list_backup);
1729 BKE_bpath_list_free(path_list_backup);
1730 }
1731
1732 if (err) {
1733 BKE_report(reports, RPT_ERROR, strerror(errno));
1734 remove(tempname);
1735
1736 return false;
1737 }
1738
1739 /* File save to temporary file was successful, now do reverse file history
1740 * (move `.blend1` -> `.blend2`, `.blend` -> `.blend1` .. etc). */
1741 if (use_save_versions) {
1742 if (!do_history(filepath, reports)) {
1743 BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)");
1744 return false;
1745 }
1746 }
1747
1748 if (BLI_rename_overwrite(tempname, filepath) != 0) {
1749 BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)");
1750 return false;
1751 }
1752
1753 write_file_main_validate_post(mainvar, reports);
1754
1755 return true;
1756}
1757
1760/* -------------------------------------------------------------------- */
1764bool BLO_write_file(Main *mainvar,
1765 const char *filepath,
1766 const int write_flags,
1768 ReportList *reports)
1769{
1770 RawWriteWrap raw_wrap;
1771
1772 if (write_flags & G_FILE_COMPRESS) {
1773 ZstdWriteWrap zstd_wrap(raw_wrap);
1774 return BLO_write_file_impl(mainvar, filepath, write_flags, params, reports, zstd_wrap);
1775 }
1776
1777 return BLO_write_file_impl(mainvar, filepath, write_flags, params, reports, raw_wrap);
1778}
1779
1780bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
1781{
1782 bool use_userdef = false;
1783
1784 const bool err = write_file_handle(
1785 mainvar, nullptr, compare, current, write_flags, use_userdef, nullptr);
1786
1787 return (err == 0);
1788}
1789
1790/*
1791 * API to handle writing IDs while clearing some of their runtime data.
1792 */
1793
1795{
1796 return MEM_cnew<BLO_Write_IDBuffer>(__func__);
1797}
1798
1799void BLO_write_init_id_buffer_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
1800{
1801 const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
1802 id_buffer_init_for_id_type(id_buffer, id_type);
1803 id_buffer_init_from_id(id_buffer, id, is_undo);
1804}
1805
1807{
1808 return id_buffer->temp_id;
1809}
1810
1812{
1813 if (static_cast<void *>((*id_buffer)->temp_id) != (*id_buffer)->id_buffer_static) {
1814 MEM_SAFE_FREE((*id_buffer)->temp_id);
1815 }
1816 MEM_SAFE_FREE(*id_buffer);
1817}
1818
1819/*
1820 * API to write chunks of data.
1821 */
1822
1823void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
1824{
1825 writedata(writer->wd, BLO_CODE_DATA, size_in_bytes, data_ptr);
1826}
1827
1828void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
1829{
1830 BLO_write_struct_array_by_name(writer, struct_name, 1, data_ptr);
1831}
1832
1834 const char *struct_name,
1835 int array_size,
1836 const void *data_ptr)
1837{
1838 int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1839 if (UNLIKELY(struct_id == -1)) {
1840 CLOG_ERROR(&LOG, "Can't find SDNA code <%s>", struct_name);
1841 return;
1842 }
1843 BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr);
1844}
1845
1846void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
1847{
1848 writestruct_nr(writer->wd, BLO_CODE_DATA, struct_id, 1, data_ptr);
1849}
1850
1852 int struct_id,
1853 const void *address,
1854 const void *data_ptr)
1855{
1857 writer, BLO_CODE_DATA, struct_id, address, data_ptr);
1858}
1859
1861 BlendWriter *writer, int filecode, int struct_id, const void *address, const void *data_ptr)
1862{
1863 writestruct_at_address_nr(writer->wd, filecode, struct_id, 1, address, data_ptr);
1864}
1865
1867 int struct_id,
1868 int array_size,
1869 const void *data_ptr)
1870{
1871 writestruct_nr(writer->wd, BLO_CODE_DATA, struct_id, array_size, data_ptr);
1872}
1873
1875 BlendWriter *writer, int struct_id, int array_size, const void *address, const void *data_ptr)
1876{
1877 writestruct_at_address_nr(writer->wd, BLO_CODE_DATA, struct_id, array_size, address, data_ptr);
1878}
1879
1880void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, const ListBase *list)
1881{
1882 writelist_nr(writer->wd, BLO_CODE_DATA, struct_id, list);
1883}
1884
1885void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
1886{
1887 int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1888 if (UNLIKELY(struct_id == -1)) {
1889 CLOG_ERROR(&LOG, "Can't find SDNA code <%s>", struct_name);
1890 return;
1891 }
1892 BLO_write_struct_list_by_id(writer, struct_id, list);
1893}
1894
1895void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
1896{
1897 writestruct_at_address_nr(writer->wd, GS(id->name), struct_id, 1, id_address, id);
1898}
1899
1900int BLO_get_struct_id_by_name(const BlendWriter *writer, const char *struct_name)
1901{
1902 int struct_id = DNA_struct_find_with_alias(writer->wd->sdna, struct_name);
1903 return struct_id;
1904}
1905
1906void BLO_write_char_array(BlendWriter *writer, uint num, const char *data_ptr)
1907{
1908 BLO_write_raw(writer, sizeof(char) * size_t(num), data_ptr);
1909}
1910
1911void BLO_write_int8_array(BlendWriter *writer, uint num, const int8_t *data_ptr)
1912{
1913 BLO_write_raw(writer, sizeof(int8_t) * size_t(num), data_ptr);
1914}
1915
1916void BLO_write_uint8_array(BlendWriter *writer, uint num, const uint8_t *data_ptr)
1917{
1918 BLO_write_raw(writer, sizeof(uint8_t) * size_t(num), data_ptr);
1919}
1920
1921void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
1922{
1923 BLO_write_raw(writer, sizeof(int32_t) * size_t(num), data_ptr);
1924}
1925
1926void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
1927{
1928 BLO_write_raw(writer, sizeof(uint32_t) * size_t(num), data_ptr);
1929}
1930
1931void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
1932{
1933 BLO_write_raw(writer, sizeof(float) * size_t(num), data_ptr);
1934}
1935
1936void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
1937{
1938 BLO_write_raw(writer, sizeof(double) * size_t(num), data_ptr);
1939}
1940
1941void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
1942{
1943 BLO_write_raw(writer, sizeof(void *) * size_t(num), data_ptr);
1944}
1945
1946void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
1947{
1948 BLO_write_raw(writer, sizeof(float[3]) * size_t(num), data_ptr);
1949}
1950
1951void BLO_write_string(BlendWriter *writer, const char *data_ptr)
1952{
1953 if (data_ptr != nullptr) {
1954 BLO_write_raw(writer, strlen(data_ptr) + 1, data_ptr);
1955 }
1956}
1957
1959 const void *data,
1960 const size_t approximate_size_in_bytes,
1961 const blender::ImplicitSharingInfo *sharing_info,
1962 const blender::FunctionRef<void()> write_fn)
1963{
1964 if (data == nullptr) {
1965 return;
1966 }
1967 if (BLO_write_is_undo(writer)) {
1968 MemFile &memfile = *writer->wd->mem.written_memfile;
1969 if (sharing_info != nullptr) {
1970 if (memfile.shared_storage == nullptr) {
1971 memfile.shared_storage = MEM_new<MemFileSharedStorage>(__func__);
1972 }
1973 if (memfile.shared_storage->map.add(data, sharing_info)) {
1974 /* The undo-step takes (shared) ownership of the data, which also makes it immutable. */
1975 sharing_info->add_user();
1976 /* This size is an estimate, but good enough to count data with many users less. */
1977 memfile.size += approximate_size_in_bytes / sharing_info->strong_users();
1978 return;
1979 }
1980 }
1981 }
1982 if (sharing_info != nullptr) {
1983 if (!writer->wd->per_id_written_shared_addresses.add(data)) {
1984 /* Was written already. */
1985 return;
1986 }
1987 }
1988 write_fn();
1989}
1990
1992{
1993 return writer->wd->use_memfile;
1994}
1995
void BKE_asset_catalog_path_list_blend_write(BlendWriter *writer, const ListBase &catalog_path_list)
#define BLENDER_FILE_SUBVERSION
#define BLENDER_FILE_MIN_VERSION
#define BLENDER_FILE_VERSION
#define BLENDER_FILE_MIN_SUBVERSION
void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports, BPathSummary *r_summary=nullptr)
Definition bpath.cc:615
void * BKE_bpath_list_backup(Main *bmain, eBPathForeachFlag flag)
Definition bpath.cc:689
void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports, BPathSummary *r_summary=nullptr)
Definition bpath.cc:624
void BKE_bpath_list_restore(Main *bmain, eBPathForeachFlag flag, void *path_list_handle)
Definition bpath.cc:703
void BKE_bpath_relative_rebase(Main *bmain, const char *basedir_src, const char *basedir_dst, ReportList *reports, BPathSummary *r_summary=nullptr)
Definition bpath.cc:475
eBPathForeachFlag
Definition BKE_bpath.hh:26
@ BKE_BPATH_FOREACH_PATH_SKIP_LINKED
Definition BKE_bpath.hh:35
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
Definition BKE_bpath.hh:55
void BKE_bpath_list_free(void *path_list_handle)
Definition bpath.cc:715
@ G_FILE_ASSET_EDIT_FILE
@ G_FILE_RECOVER_WRITE
@ G_FILE_COMPRESS
@ G_DEBUG_IO
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
Definition idprop.cc:1437
@ IDTYPE_FLAGS_NEVER_UNUSED
Definition BKE_idtype.hh:64
const IDTypeInfo * BKE_idtype_get_info_from_id(const ID *id)
Definition idtype.cc:150
bool BKE_idtype_idcode_is_linkable(short idcode)
Definition idtype.cc:201
ViewLayer * BKE_view_layer_find(const Scene *scene, const char *layer_name)
void id_lib_extern(ID *id)
Definition lib_id.cc:283
#define MAIN_ID_SESSION_UID_UNSET
void id_lib_indirect_weak_link(ID *id)
Definition lib_id.cc:296
void BKE_id_blend_write(BlendWriter *writer, ID *id)
Definition lib_id.cc:2560
void BKE_main_id_refcount_recompute(Main *bmain, bool do_linked_only)
Definition lib_id.cc:1981
OverrideLibraryStorage * BKE_lib_override_library_operations_store_init()
ID * BKE_lib_override_library_operations_store_start(Main *bmain, OverrideLibraryStorage *liboverride_storage, ID *local)
void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *liboverride_storage)
void BKE_lib_override_library_operations_store_end(OverrideLibraryStorage *liboverride_storage, ID *local)
@ IDWALK_RET_NOP
@ IDWALK_CB_DIRECT_WEAK_LINK
@ IDWALK_CB_INDIRECT_USAGE
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, int flag)
Definition lib_query.cc:416
@ IDWALK_INCLUDE_UI
@ IDWALK_READONLY
#define FOREACH_MAIN_ID_END
Definition BKE_main.hh:500
int set_listbasepointers(Main *bmain, ListBase *lb[])
Definition main.cc:929
#define FOREACH_MAIN_ID_BEGIN(_bmain, _id)
Definition BKE_main.hh:494
bool BKE_main_namemap_validate_and_fix(Main *bmain) ATTR_NONNULL()
void BKE_packedfile_blend_write(BlendWriter *writer, const PackedFile *pf)
void BKE_preferences_extension_repo_write_data(struct BlendWriter *writer, const bUserExtensionRepo *repo)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:613
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_STATIC_ASSERT(a, msg)
Definition BLI_assert.h:87
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define B_ENDIAN
#define ENDIAN_ORDER
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
#define O_BINARY
int BLI_rename_overwrite(const char *from, const char *to) ATTR_NONNULL()
Definition fileops_c.cc:505
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int min_ii(int a, int b)
MINLINE int max_ii(int a, int b)
#define FILE_MAX
int BLI_path_normalize(char *path) ATTR_NONNULL(1)
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
#define BLI_path_cmp
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
unsigned char uchar
unsigned long ulong
unsigned int uint
void BLI_condition_notify_all(ThreadCondition *cond)
Definition threads.cc:597
void BLI_mutex_end(ThreadMutex *mutex)
Definition threads.cc:360
void BLI_threadpool_remove(struct ListBase *threadbase, void *callerdata)
Definition threads.cc:197
void BLI_condition_wait(ThreadCondition *cond, ThreadMutex *mutex)
Definition threads.cc:582
void BLI_threadpool_init(struct ListBase *threadbase, void *(*do_thread)(void *), int tot)
Definition threads.cc:121
void BLI_mutex_init(ThreadMutex *mutex)
Definition threads.cc:340
pthread_cond_t ThreadCondition
void BLI_condition_end(ThreadCondition *cond)
Definition threads.cc:602
int BLI_system_thread_count(void)
Definition threads.cc:253
void BLI_threadpool_end(struct ListBase *threadbase)
Definition threads.cc:234
void BLI_condition_init(ThreadCondition *cond)
Definition threads.cc:577
void BLI_mutex_lock(ThreadMutex *mutex)
Definition threads.cc:345
void BLI_mutex_unlock(ThreadMutex *mutex)
Definition threads.cc:350
int BLI_available_threads(struct ListBase *threadbase)
Definition threads.cc:146
void BLI_threadpool_insert(struct ListBase *threadbase, void *callerdata)
Definition threads.cc:184
pthread_mutex_t ThreadMutex
Definition BLI_threads.h:83
#define UNLIKELY(x)
#define ELEM(...)
Compatibility-like things for windows.
defines for blend-file codes.
#define BLEN_THUMB_MEMSIZE_FILE(_x, _y)
@ BLO_CODE_ENDB
@ BLO_CODE_REND
@ BLO_CODE_TEST
@ BLO_CODE_GLOB
@ BLO_CODE_DATA
@ BLO_CODE_DNA1
@ BLO_CODE_USER
Utilities ensuring .blend file (i.e. Main) is in valid state during write and/or read process.
bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports)
#define BLO_write_struct(writer, struct_name, data_ptr)
external readfile function prototypes.
void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t size)
Definition undofile.cc:131
void BLO_memfile_write_init(MemFileWriteData *mem_data, MemFile *written_memfile, MemFile *reference_memfile)
Definition undofile.cc:100
void BLO_memfile_write_finalize(MemFileWriteData *mem_data)
Definition undofile.cc:126
external writefile.cc function prototypes.
eBLO_WritePathRemap
@ BLO_WRITE_PATH_REMAP_NONE
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
@ BLO_WRITE_PATH_REMAP_RELATIVE
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition DNA_ID.h:676
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
#define MAX_ID_NAME
Definition DNA_ID.h:377
@ ID_FLAG_INDIRECT_WEAK_LINK
Definition DNA_ID.h:731
@ ID_TAG_NO_USER_REFCOUNT
Definition DNA_ID.h:985
@ ID_TAG_INDIRECT
Definition DNA_ID.h:794
@ ID_TAG_RUNTIME
Definition DNA_ID.h:805
@ ID_TAG_EXTERN
Definition DNA_ID.h:788
@ ID_TAG_NOT_ALLOCATED
Definition DNA_ID.h:992
@ ID_TAG_NO_MAIN
Definition DNA_ID.h:945
#define INDEX_ID_MAX
Definition DNA_ID.h:1328
#define ID_TAG_KEEP_ON_UNDO
Definition DNA_ID.h:1013
@ ID_LI
@ ID_KE
@ ID_SCE
#define ID_LINK_PLACEHOLDER
Object groups, one object can be in many groups at once.
blenloader genfile private function prototypes
const struct SDNA * DNA_sdna_current_get(void)
int DNA_struct_find_with_alias(const struct SDNA *sdna, const char *str)
int DNA_struct_size(const struct SDNA *sdna, int struct_index)
@ R_BG_RENDER
#define SDNA_RAW_DATA_STRUCT_INDEX
@ USER_MENU_TYPE_OPERATOR
@ USER_MENU_TYPE_PROP
@ USER_MENU_TYPE_MENU
#define USER_EXPERIMENTAL_TEST(userdef, member)
DrawDataList * DRW_drawdatalist_from_id(ID *id)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
ulong build_commit_timestamp
Definition bpy_app.cc:65
unsigned int U
Definition btGjkEpa3.h:78
bool write(const void *buf, size_t buf_len) override
Definition writefile.cc:202
bool open(const char *filepath) override
Definition writefile.cc:185
bool close() override
Definition writefile.cc:198
virtual bool open(const char *filepath)=0
virtual bool write(const void *buf, size_t buf_len)=0
bool use_buf
Definition writefile.cc:172
virtual bool close()=0
ZstdWriteWrap(WriteWrap &base_wrap)
Definition writefile.cc:222
bool open(const char *filepath) override
Definition writefile.cc:289
bool close() override
Definition writefile.cc:345
bool write(const void *buf, size_t buf_len) override
Definition writefile.cc:359
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:271
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:531
bool add(const Key &key)
Definition BLI_set.hh:248
bool is_empty() const
Definition BLI_set.hh:572
void clear()
Definition BLI_set.hh:532
#define printf
FILE * file
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
char build_hash[16]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define GS(x)
Definition iris.cc:202
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
#define G(x, y, z)
int main()
struct blender::compositor::@172::@174 task
void blo_split_main(ListBase *mainlist, Main *main)
Definition readfile.cc:397
void blo_join_main(ListBase *mainlist)
Definition readfile.cc:350
unsigned int uint32_t
Definition stdint.h:80
signed int int32_t
Definition stdint.h:77
unsigned char uint8_t
Definition stdint.h:78
signed char int8_t
Definition stdint.h:75
const void * old
const IDTypeInfo * id_type
char id_buffer_static[ID_BUFFER_STATIC_SIZE]
WriteData * wd
Definition writefile.cc:459
struct ViewLayer * cur_view_layer
struct Scene * curscene
uint64_t build_commit_timestamp
struct bScreen * curscreen
char filepath[1024]
const char * name
IDTypeBlendWriteFunction blend_write
uint32_t flags
size_t struct_size
Definition DNA_ID.h:413
void * py_instance
Definition DNA_ID.h:482
int tag
Definition DNA_ID.h:434
int us
Definition DNA_ID.h:435
int icon_id
Definition DNA_ID.h:436
struct ID * newid
Definition DNA_ID.h:417
void * prev
Definition DNA_ID.h:416
struct ID * orig_id
Definition DNA_ID.h:466
void * next
Definition DNA_ID.h:416
char name[66]
Definition DNA_ID.h:425
ID * from
void * first
ListBase scenes
Definition BKE_main.hh:210
ListBase wm
Definition BKE_main.hh:239
bool is_asset_edit_file
Definition BKE_main.hh:151
char filepath[1024]
Definition BKE_main.hh:136
MainLock * lock
Definition BKE_main.hh:272
Main * next
Definition BKE_main.hh:123
blender::Map< const void *, const blender::ImplicitSharingInfo * > map
blender::Map< uint, MemFileChunk * > id_session_uid_mapping
MemFile * written_memfile
MemFileChunk * reference_current_chunk
MemFileSharedStorage * shared_storage
size_t size
char scene_name[MAX_ID_NAME - 2]
Definition writefile.cc:886
const char * data
int data_size
struct ListBase addons
ListBase script_directories
struct ListBase uistyles
struct ListBase asset_shelves_settings
struct ListBase user_keymaps
struct ListBase themes
struct ListBase autoexec_paths
struct ListBase user_keyconfig_prefs
struct ListBase extension_repos
struct ListBase user_menus
struct ListBase asset_libraries
blender::Set< const void * > per_id_addresses_set
Definition writefile.cc:436
const SDNA * sdna
Definition writefile.cc:403
WriteWrap * ww
Definition writefile.cc:455
uchar * buf
Definition writefile.cc:407
bool critical_error
Definition writefile.cc:431
bool use_memfile
Definition writefile.cc:448
blender::Set< const void * > per_id_written_shared_addresses
Definition writefile.cc:443
bool is_writing_id
Definition writefile.cc:423
MemFileWriteData mem
Definition writefile.cc:446
struct WriteData::@131 validation_data
size_t chunk_size
Definition writefile.cc:414
size_t max_size
Definition writefile.cc:412
size_t used_len
Definition writefile.cc:409
struct WriteData::@130 buffer
uint32_t uncompressed_size
Definition writefile.cc:162
uint32_t compressed_size
Definition writefile.cc:161
ZstdFrame * next
Definition writefile.cc:159
ZstdFrame * prev
Definition writefile.cc:159
static void * write_task(void *userdata)
Definition writefile.cc:242
struct IDProperty * prop
struct Scene * scene
struct wmWindow * next
struct WorkSpaceInstanceHook * workspace_hook
void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, const ListBase *list)
int BLO_get_struct_id_by_name(const BlendWriter *writer, const char *struct_name)
static void mywrite_id_begin(WriteData *wd, ID *id)
Definition writefile.cc:638
#define ZSTD_COMPRESSION_LEVEL
Definition writefile.cc:147
static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
#define writestruct(wd, filecode, struct_id, nr, adr)
Definition writefile.cc:830
void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
Definition writefile.cc:797
void BLO_write_struct_at_address_by_id_with_filecode(BlendWriter *writer, int filecode, int struct_id, const void *address, const void *data_ptr)
static bool write_at_address_validate(WriteData *wd, int filecode, const void *address)
Definition writefile.cc:704
static void writestruct_at_address_nr(WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr, const void *data)
Definition writefile.cc:722
static void id_buffer_init_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
BLO_Write_IDBuffer * BLO_write_allocate_id_buffer()
static void id_buffer_init_for_id_type(BLO_Write_IDBuffer *id_buffer, const IDTypeInfo *id_type)
static void writedata_free(WriteData *wd)
Definition writefile.cc:511
void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
Definition writefile.cc:918
void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
void BLO_write_uint8_array(BlendWriter *writer, uint num, const uint8_t *data_ptr)
#define ZSTD_CHUNK_SIZE
Definition writefile.cc:145
static bool do_history(const char *filepath, ReportList *reports)
static void write_renderinfo(WriteData *wd, Main *mainvar)
Definition writefile.cc:895
static void write_userdef(BlendWriter *writer, const UserDef *userdef)
Definition writefile.cc:926
static void write_libraries(WriteData *wd, Main *main)
static void write_file_main_validate_pre(Main *bmain, ReportList *reports)
#define ID_BUFFER_STATIC_SIZE
void BLO_write_init_id_buffer_from_id(BLO_Write_IDBuffer *id_buffer, ID *id, const bool is_undo)
void BLO_write_struct_array_by_id(BlendWriter *writer, int struct_id, int array_size, const void *data_ptr)
static void write_file_main_validate_post(Main *bmain, ReportList *reports)
static void write_global(WriteData *wd, int fileflags, Main *mainvar)
#define MEM_CHUNK_SIZE
Definition writefile.cc:142
static void writestruct_nr(WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
Definition writefile.cc:753
void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
static WriteData * writedata_new(WriteWrap *ww)
Definition writefile.cc:462
void BLO_write_destroy_id_buffer(BLO_Write_IDBuffer **id_buffer)
ID * BLO_write_get_id_buffer_temp_id(BLO_Write_IDBuffer *id_buffer)
void BLO_write_int8_array(BlendWriter *writer, uint num, const int8_t *data_ptr)
void BLO_write_struct_array_by_name(BlendWriter *writer, const char *struct_name, int array_size, const void *data_ptr)
void BLO_write_char_array(BlendWriter *writer, uint num, const char *data_ptr)
static bool mywrite_end(WriteData *wd)
Definition writefile.cc:616
static void mywrite_flush(WriteData *wd)
Definition writefile.cc:529
static void mywrite_id_end(WriteData *wd, ID *)
Definition writefile.cc:675
#define ZSTD_BUFFER_SIZE
Definition writefile.cc:144
static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
Definition writefile.cc:762
static CLG_LogRef LOG
Definition writefile.cc:149
void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
void BLO_write_struct_at_address_by_id(BlendWriter *writer, int struct_id, const void *address, const void *data_ptr)
static void current_screen_compat(Main *mainvar, bool use_active_win, bScreen **r_screen, Scene **r_scene, ViewLayer **r_view_layer)
Definition writefile.cc:845
void BLO_write_struct_array_at_address_by_id(BlendWriter *writer, int struct_id, int array_size, const void *address, const void *data_ptr)
bool BLO_write_file(Main *mainvar, const char *filepath, const int write_flags, const BlendFileWriteParams *params, ReportList *reports)
static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
Definition writefile.cc:485
bool BLO_write_is_undo(BlendWriter *writer)
#define MEM_BUFFER_SIZE
Definition writefile.cc:141
static int write_id_direct_linked_data_process_cb(LibraryIDLinkCallbackData *cb_data)
static void mywrite(WriteData *wd, const void *adr, size_t len)
Definition writefile.cc:542
bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
static WriteData * mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
Definition writefile.cc:598
static bool write_file_handle(Main *mainvar, WriteWrap *ww, MemFile *compare, MemFile *current, int write_flags, bool use_userdef, const BlendThumbnail *thumb)
void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
static bool BLO_write_file_impl(Main *mainvar, const char *filepath, const int write_flags, const BlendFileWriteParams *params, ReportList *reports, WriteWrap &ww)
void BLO_write_shared(BlendWriter *writer, const void *data, const size_t approximate_size_in_bytes, const blender::ImplicitSharingInfo *sharing_info, const blender::FunctionRef< void()> write_fn)
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)