Blender  V3.3
writefile.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
59 #include <fcntl.h>
60 #include <limits.h>
61 #include <math.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 
66 #ifdef WIN32
67 # include "BLI_winstuff.h"
68 # include "winsock2.h"
69 # include <io.h>
70 #else
71 # include <unistd.h> /* FreeBSD, for write() and close(). */
72 #endif
73 
74 #include "BLI_utildefines.h"
75 
76 #include "CLG_log.h"
77 
78 /* allow writefile to use deprecated functionality (for forward compatibility code) */
79 #define DNA_DEPRECATED_ALLOW
80 
81 #include "DNA_collection_types.h"
82 #include "DNA_fileglobal_types.h"
83 #include "DNA_genfile.h"
84 #include "DNA_sdna_types.h"
85 
86 #include "BLI_bitmap.h"
87 #include "BLI_blenlib.h"
88 #include "BLI_endian_defines.h"
89 #include "BLI_endian_switch.h"
90 #include "BLI_link_utils.h"
91 #include "BLI_linklist.h"
92 #include "BLI_math_base.h"
93 #include "BLI_mempool.h"
94 #include "BLI_threads.h"
95 #include "MEM_guardedalloc.h" /* MEM_freeN */
96 
97 #include "BKE_blender_version.h"
98 #include "BKE_bpath.h"
99 #include "BKE_global.h" /* for G */
100 #include "BKE_idprop.h"
101 #include "BKE_idtype.h"
102 #include "BKE_layer.h"
103 #include "BKE_lib_id.h"
104 #include "BKE_lib_override.h"
105 #include "BKE_main.h"
106 #include "BKE_node.h"
107 #include "BKE_packedFile.h"
108 #include "BKE_report.h"
109 #include "BKE_workspace.h"
110 
111 #include "BLO_blend_defs.h"
112 #include "BLO_blend_validate.h"
113 #include "BLO_read_write.h"
114 #include "BLO_readfile.h"
115 #include "BLO_undofile.h"
116 #include "BLO_writefile.h"
117 
118 #include "readfile.h"
119 
120 #include <errno.h>
121 
122 #include <zstd.h>
123 
124 /* Make preferences read-only. */
125 #define U (*((const UserDef *)&U))
126 
127 /* ********* my write, buffered writing with minimum size chunks ************ */
128 
129 /* Use optimal allocation since blocks of this size are kept in memory for undo. */
130 #define MEM_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
131 #define MEM_CHUNK_SIZE (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
132 
133 #define ZSTD_BUFFER_SIZE (1 << 21) /* 2mb */
134 #define ZSTD_CHUNK_SIZE (1 << 20) /* 1mb */
135 
136 #define ZSTD_COMPRESSION_LEVEL 3
137 
138 static CLG_LogRef LOG = {"blo.writefile"};
139 
141 // #define USE_WRITE_DATA_LEN
142 
143 /* -------------------------------------------------------------------- */
147 typedef enum {
151 
152 typedef struct ZstdFrame {
153  struct ZstdFrame *next, *prev;
154 
158 
159 typedef struct WriteWrap WriteWrap;
160 struct WriteWrap {
161  /* callbacks */
162  bool (*open)(WriteWrap *ww, const char *filepath);
164  size_t (*write)(WriteWrap *ww, const char *data, size_t data_len);
165 
166  /* Buffer output (we only want when output isn't already buffered). */
167  bool use_buf;
168 
169  /* internal */
171  struct {
178 
179  int level;
181 
183  } zstd;
184 };
185 
186 /* none */
187 static bool ww_open_none(WriteWrap *ww, const char *filepath)
188 {
189  int file;
190 
191  file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
192 
193  if (file != -1) {
194  ww->file_handle = file;
195  return true;
196  }
197 
198  return false;
199 }
200 static bool ww_close_none(WriteWrap *ww)
201 {
202  return (close(ww->file_handle) != -1);
203 }
204 static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
205 {
206  return write(ww->file_handle, buf, buf_len);
207 }
208 
209 /* zstd */
210 
211 typedef struct {
213  void *data;
214  size_t size;
218 
219 static void *zstd_write_task(void *userdata)
220 {
221  ZstdWriteBlockTask *task = userdata;
222  WriteWrap *ww = task->ww;
223 
224  size_t out_buf_len = ZSTD_compressBound(task->size);
225  void *out_buf = MEM_mallocN(out_buf_len, "Zstd out buffer");
226  size_t out_size = ZSTD_compress(
227  out_buf, out_buf_len, task->data, task->size, ZSTD_COMPRESSION_LEVEL);
228 
229  MEM_freeN(task->data);
230 
232 
233  while (ww->zstd.next_frame != task->frame_number) {
235  }
236 
237  if (ZSTD_isError(out_size)) {
238  ww->zstd.write_error = true;
239  }
240  else {
241  if (ww_write_none(ww, out_buf, out_size) == out_size) {
242  ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo");
243  frameinfo->uncompressed_size = task->size;
244  frameinfo->compressed_size = out_size;
245  BLI_addtail(&ww->zstd.frames, frameinfo);
246  }
247  else {
248  ww->zstd.write_error = true;
249  }
250  }
251 
252  ww->zstd.next_frame++;
253 
256 
257  MEM_freeN(out_buf);
258  return NULL;
259 }
260 
261 static bool ww_open_zstd(WriteWrap *ww, const char *filepath)
262 {
263  if (!ww_open_none(ww, filepath)) {
264  return false;
265  }
266 
267  /* Leave one thread open for the main writing logic, unless we only have one HW thread. */
268  int num_threads = max_ii(1, BLI_system_thread_count() - 1);
272 
273  return true;
274 }
275 
277 {
278 #ifdef __BIG_ENDIAN__
280 #endif
281  ww_write_none(ww, (char *)&val, sizeof(uint32_t));
282 }
283 
284 /* In order to implement efficient seeking when reading the .blend, we append
285  * a skippable frame that encodes information about the other frames present
286  * in the file.
287  * The format here follows the upstream spec for seekable files:
288  * https://github.com/facebook/zstd/blob/master/contrib/seekable_format/zstd_seekable_compression_format.md
289  * If this information is not present in a file (e.g. if it was compressed
290  * with external tools), it can still be opened in Blender, but seeking will
291  * not be supported, so more memory might be needed. */
293 {
294  /* Write seek table header (magic number and frame size). */
295  zstd_write_u32_le(ww, 0x184D2A5E);
296 
297  /* The actual frame number might not match ww->zstd.num_frames if there was a write error. */
298  const uint32_t num_frames = BLI_listbase_count(&ww->zstd.frames);
299  /* Each frame consists of two u32, so 8 bytes each.
300  * After the frames, a footer containing two u32 and one byte (9 bytes total) is written. */
301  const uint32_t frame_size = num_frames * 8 + 9;
302  zstd_write_u32_le(ww, frame_size);
303 
304  /* Write seek table entries. */
305  LISTBASE_FOREACH (ZstdFrame *, frame, &ww->zstd.frames) {
306  zstd_write_u32_le(ww, frame->compressed_size);
307  zstd_write_u32_le(ww, frame->uncompressed_size);
308  }
309 
310  /* Write seek table footer (number of frames, option flags and second magic number). */
311  zstd_write_u32_le(ww, num_frames);
312  const char flags = 0; /* We don't store checksums for each frame. */
313  ww_write_none(ww, &flags, 1);
314  zstd_write_u32_le(ww, 0x8F92EAB1);
315 }
316 
318 {
321 
324 
327 
328  return ww_close_none(ww) && !ww->zstd.write_error;
329 }
330 
331 static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
332 {
333  if (ww->zstd.write_error) {
334  return 0;
335  }
336 
338  task->data = MEM_mallocN(buf_len, __func__);
339  memcpy(task->data, buf, buf_len);
340  task->size = buf_len;
341  task->frame_number = ww->zstd.num_frames++;
342  task->ww = ww;
343 
346 
347  /* If there's a free worker thread, just push the block into that thread.
348  * Otherwise, we wait for the earliest thread to finish.
349  * We look up the earliest thread while holding the mutex, but release it
350  * before joining the thread to prevent a deadlock. */
351  ZstdWriteBlockTask *first_task = ww->zstd.tasks.first;
354  BLI_threadpool_remove(&ww->zstd.threadpool, first_task);
355 
356  /* If the task list was empty before we pushed our task, there should
357  * always be a free thread. */
358  BLI_assert(first_task != task);
359  BLI_remlink(&ww->zstd.tasks, first_task);
360  MEM_freeN(first_task);
361  }
363 
364  return buf_len;
365 }
366 
367 /* --- end compression types --- */
368 
369 static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
370 {
371  memset(r_ww, 0, sizeof(*r_ww));
372 
373  switch (ww_type) {
374  case WW_WRAP_ZSTD: {
375  r_ww->open = ww_open_zstd;
376  r_ww->close = ww_close_zstd;
377  r_ww->write = ww_write_zstd;
378  r_ww->use_buf = true;
379  break;
380  }
381  default: {
382  r_ww->open = ww_open_none;
383  r_ww->close = ww_close_none;
384  r_ww->write = ww_write_none;
385  r_ww->use_buf = true;
386  break;
387  }
388  }
389 }
390 
393 /* -------------------------------------------------------------------- */
397 typedef struct {
398  const struct SDNA *sdna;
399 
400  struct {
404  size_t used_len;
405 
407  size_t max_size;
409  size_t chunk_size;
411 
412 #ifdef USE_WRITE_DATA_LEN
414  size_t write_len;
415 #endif
416 
418  bool error;
419 
424 
431 } WriteData;
432 
433 typedef struct BlendWriter {
436 
438 {
439  WriteData *wd = MEM_callocN(sizeof(*wd), "writedata");
440 
441  wd->sdna = DNA_sdna_current_get();
442 
443  wd->ww = ww;
444 
445  if ((ww == NULL) || (ww->use_buf)) {
446  if (ww == NULL) {
449  }
450  else {
453  }
454  wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf");
455  }
456 
457  return wd;
458 }
459 
460 static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
461 {
462  if ((wd == NULL) || wd->error || (mem == NULL) || memlen < 1) {
463  return;
464  }
465 
466  if (memlen > INT_MAX) {
467  BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX.");
468  return;
469  }
470 
471  if (UNLIKELY(wd->error)) {
472  return;
473  }
474 
475  /* memory based save */
476  if (wd->use_memfile) {
477  BLO_memfile_chunk_add(&wd->mem, mem, memlen);
478  }
479  else {
480  if (wd->ww->write(wd->ww, mem, memlen) != memlen) {
481  wd->error = true;
482  }
483  }
484 }
485 
486 static void writedata_free(WriteData *wd)
487 {
488  if (wd->buffer.buf) {
489  MEM_freeN(wd->buffer.buf);
490  }
491  MEM_freeN(wd);
492 }
493 
496 /* -------------------------------------------------------------------- */
504 static void mywrite_flush(WriteData *wd)
505 {
506  if (wd->buffer.used_len != 0) {
508  wd->buffer.used_len = 0;
509  }
510 }
511 
517 static void mywrite(WriteData *wd, const void *adr, size_t len)
518 {
519  if (UNLIKELY(wd->error)) {
520  return;
521  }
522 
523  if (UNLIKELY(adr == NULL)) {
524  BLI_assert(0);
525  return;
526  }
527 
528 #ifdef USE_WRITE_DATA_LEN
529  wd->write_len += len;
530 #endif
531 
532  if (wd->buffer.buf == NULL) {
533  writedata_do_write(wd, adr, len);
534  }
535  else {
536  /* if we have a single big chunk, write existing data in
537  * buffer and write out big chunk in smaller pieces */
538  if (len > wd->buffer.chunk_size) {
539  if (wd->buffer.used_len != 0) {
541  wd->buffer.used_len = 0;
542  }
543 
544  do {
545  size_t writelen = MIN2(len, wd->buffer.chunk_size);
546  writedata_do_write(wd, adr, writelen);
547  adr = (const char *)adr + writelen;
548  len -= writelen;
549  } while (len > 0);
550 
551  return;
552  }
553 
554  /* if data would overflow buffer, write out the buffer */
555  if (len + wd->buffer.used_len > wd->buffer.max_size - 1) {
557  wd->buffer.used_len = 0;
558  }
559 
560  /* append data at end of buffer */
561  memcpy(&wd->buffer.buf[wd->buffer.used_len], adr, len);
562  wd->buffer.used_len += len;
563  }
564 }
565 
573 static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
574 {
575  WriteData *wd = writedata_new(ww);
576 
577  if (current != NULL) {
578  BLO_memfile_write_init(&wd->mem, current, compare);
579  wd->use_memfile = true;
580  }
581 
582  return wd;
583 }
584 
591 static bool mywrite_end(WriteData *wd)
592 {
593  if (wd->buffer.used_len != 0) {
595  wd->buffer.used_len = 0;
596  }
597 
598  if (wd->use_memfile) {
600  }
601 
602  const bool err = wd->error;
603  writedata_free(wd);
604 
605  return err;
606 }
607 
613 static void mywrite_id_begin(WriteData *wd, ID *id)
614 {
615  if (wd->use_memfile) {
616  wd->mem.current_id_session_uuid = id->session_uuid;
617 
618  /* If current next memchunk does not match the ID we are about to write, or is not the _first_
619  * one for said ID, try to find the correct memchunk in the mapping using ID's session_uuid. */
620  MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk;
621  MemFileChunk *prev_memchunk = curr_memchunk != NULL ? curr_memchunk->prev : NULL;
622  if (wd->mem.id_session_uuid_mapping != NULL &&
623  (curr_memchunk == NULL || curr_memchunk->id_session_uuid != id->session_uuid ||
624  (prev_memchunk != NULL &&
625  (prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) {
628  if (ref != NULL) {
629  wd->mem.reference_current_chunk = ref;
630  }
631  /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */
632  }
633  /* Otherwise, we try with the current memchunk in any case, whether it is matching current
634  * ID's session_uuid or not. */
635  }
636 }
637 
643 static void mywrite_id_end(WriteData *wd, ID *UNUSED(id))
644 {
645  if (wd->use_memfile) {
646  /* Very important to do it after every ID write now, otherwise we cannot know whether a
647  * specific ID changed or not. */
648  mywrite_flush(wd);
650  }
651 }
652 
655 /* -------------------------------------------------------------------- */
660  WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr, const void *data)
661 {
662  BHead bh;
663 
664  BLI_assert(struct_nr > 0 && struct_nr < SDNA_TYPE_MAX);
665 
666  if (adr == NULL || data == NULL || nr == 0) {
667  return;
668  }
669 
670  /* init BHead */
671  bh.code = filecode;
672  bh.old = adr;
673  bh.nr = nr;
674 
675  bh.SDNAnr = struct_nr;
676  const SDNA_Struct *struct_info = wd->sdna->structs[bh.SDNAnr];
677 
678  bh.len = nr * wd->sdna->types_size[struct_info->type];
679 
680  if (bh.len == 0) {
681  return;
682  }
683 
684  mywrite(wd, &bh, sizeof(BHead));
685  mywrite(wd, data, (size_t)bh.len);
686 }
687 
688 static void writestruct_nr(
689  WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
690 {
691  writestruct_at_address_nr(wd, filecode, struct_nr, nr, adr, adr);
692 }
693 
694 /* do not use for structs */
695 static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
696 {
697  BHead bh;
698 
699  if (adr == NULL || len == 0) {
700  return;
701  }
702 
703  if (len > INT_MAX) {
704  BLI_assert_msg(0, "Cannot write chunks bigger than INT_MAX.");
705  return;
706  }
707 
708  /* align to 4 (writes uninitialized bytes in some cases) */
709  len = (len + 3) & ~((size_t)3);
710 
711  /* init BHead */
712  bh.code = filecode;
713  bh.old = adr;
714  bh.nr = 1;
715  bh.SDNAnr = 0;
716  bh.len = (int)len;
717 
718  mywrite(wd, &bh, sizeof(BHead));
719  mywrite(wd, adr, len);
720 }
721 
722 /* use this to force writing of lists in same order as reading (using link_list) */
723 static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
724 {
725  const Link *link = lb->first;
726 
727  while (link) {
728  writestruct_nr(wd, filecode, struct_nr, 1, link);
729  link = link->next;
730  }
731 }
732 
733 #if 0
734 static void writelist_id(WriteData *wd, int filecode, const char *structname, const ListBase *lb)
735 {
736  const Link *link = lb->first;
737  if (link) {
738 
739  const int struct_nr = DNA_struct_find_nr(wd->sdna, structname);
740  if (struct_nr == -1) {
741  printf("error: can't find SDNA code <%s>\n", structname);
742  return;
743  }
744 
745  while (link) {
746  writestruct_nr(wd, filecode, struct_nr, 1, link);
747  link = link->next;
748  }
749  }
750 }
751 #endif
752 
753 #define writestruct_at_address(wd, filecode, struct_id, nr, adr, data) \
754  writestruct_at_address_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr, data)
755 
756 #define writestruct(wd, filecode, struct_id, nr, adr) \
757  writestruct_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr)
758 
759 #define writelist(wd, filecode, struct_id, lb) \
760  writelist_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), lb)
761 
764 /* -------------------------------------------------------------------- */
774 static void current_screen_compat(Main *mainvar,
775  bool use_active_win,
776  bScreen **r_screen,
777  Scene **r_scene,
778  ViewLayer **r_view_layer)
779 {
780  wmWindowManager *wm;
781  wmWindow *window = NULL;
782 
783  /* find a global current screen in the first open window, to have
784  * a reasonable default for reading in older versions */
785  wm = mainvar->wm.first;
786 
787  if (wm) {
788  if (use_active_win) {
789  /* write the active window into the file, needed for multi-window undo T43424 */
790  for (window = wm->windows.first; window; window = window->next) {
791  if (window->active) {
792  break;
793  }
794  }
795 
796  /* fallback */
797  if (window == NULL) {
798  window = wm->windows.first;
799  }
800  }
801  else {
802  window = wm->windows.first;
803  }
804  }
805 
806  *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : NULL;
807  *r_scene = (window) ? window->scene : NULL;
808  *r_view_layer = (window && *r_scene) ? BKE_view_layer_find(*r_scene, window->view_layer_name) :
809  NULL;
810 }
811 
812 typedef struct RenderInfo {
813  int sfra;
814  int efra;
817 
824 static void write_renderinfo(WriteData *wd, Main *mainvar)
825 {
826  bScreen *curscreen;
827  Scene *curscene = NULL;
828  ViewLayer *view_layer;
829 
830  /* XXX in future, handle multiple windows with multiple screens? */
831  current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer);
832 
833  LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) {
834  if (!ID_IS_LINKED(sce) && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) {
836  data.sfra = sce->r.sfra;
837  data.efra = sce->r.efra;
838  memset(data.scene_name, 0, sizeof(data.scene_name));
839 
840  BLI_strncpy(data.scene_name, sce->id.name + 2, sizeof(data.scene_name));
841 
842  writedata(wd, REND, sizeof(data), &data);
843  }
844  }
845 }
846 
847 static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
848 {
849  BLO_write_struct(writer, wmKeyMapItem, kmi);
850  if (kmi->properties) {
851  IDP_BlendWrite(writer, kmi->properties);
852  }
853 }
854 
855 static void write_userdef(BlendWriter *writer, const UserDef *userdef)
856 {
857  writestruct(writer->wd, USER, UserDef, 1, userdef);
858 
859  LISTBASE_FOREACH (const bTheme *, btheme, &userdef->themes) {
860  BLO_write_struct(writer, bTheme, btheme);
861  }
862 
863  LISTBASE_FOREACH (const wmKeyMap *, keymap, &userdef->user_keymaps) {
864  BLO_write_struct(writer, wmKeyMap, keymap);
865 
866  LISTBASE_FOREACH (const wmKeyMapDiffItem *, kmdi, &keymap->diff_items) {
867  BLO_write_struct(writer, wmKeyMapDiffItem, kmdi);
868  if (kmdi->remove_item) {
869  write_keymapitem(writer, kmdi->remove_item);
870  }
871  if (kmdi->add_item) {
872  write_keymapitem(writer, kmdi->add_item);
873  }
874  }
875 
876  LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) {
877  write_keymapitem(writer, kmi);
878  }
879  }
880 
881  LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
882  BLO_write_struct(writer, wmKeyConfigPref, kpt);
883  if (kpt->prop) {
884  IDP_BlendWrite(writer, kpt->prop);
885  }
886  }
887 
888  LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
889  BLO_write_struct(writer, bUserMenu, um);
890  LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
891  if (umi->type == USER_MENU_TYPE_OPERATOR) {
892  const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
893  BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
894  if (umi_op->prop) {
895  IDP_BlendWrite(writer, umi_op->prop);
896  }
897  }
898  else if (umi->type == USER_MENU_TYPE_MENU) {
899  const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
900  BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
901  }
902  else if (umi->type == USER_MENU_TYPE_PROP) {
903  const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
904  BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
905  }
906  else {
907  BLO_write_struct(writer, bUserMenuItem, umi);
908  }
909  }
910  }
911 
912  LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
913  BLO_write_struct(writer, bAddon, bext);
914  if (bext->prop) {
915  IDP_BlendWrite(writer, bext->prop);
916  }
917  }
918 
919  LISTBASE_FOREACH (const bPathCompare *, path_cmp, &userdef->autoexec_paths) {
920  BLO_write_struct(writer, bPathCompare, path_cmp);
921  }
922 
923  LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library_ref, &userdef->asset_libraries) {
924  BLO_write_struct(writer, bUserAssetLibrary, asset_library_ref);
925  }
926 
927  LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
928  BLO_write_struct(writer, uiStyle, style);
929  }
930 }
931 
932 /* Keep it last of write_foodata functions. */
933 static void write_libraries(WriteData *wd, Main *main)
934 {
935  ListBase *lbarray[INDEX_ID_MAX];
936  ID *id;
937  int a, tot;
938  bool found_one;
939 
940  for (; main; main = main->next) {
941  a = tot = set_listbasepointers(main, lbarray);
942 
943  /* test: is lib being used */
944  if (main->curlib && main->curlib->packedfile) {
945  found_one = true;
946  }
947  else if (wd->use_memfile) {
948  /* When writing undo step we always write all existing libraries, makes reading undo step
949  * much easier when dealing with purely indirectly used libraries. */
950  found_one = true;
951  }
952  else {
953  found_one = false;
954  while (!found_one && tot--) {
955  for (id = lbarray[tot]->first; id; id = id->next) {
956  if (id->us > 0 &&
957  ((id->tag & LIB_TAG_EXTERN) ||
959  found_one = true;
960  break;
961  }
962  }
963  }
964  }
965 
966  /* To be able to restore 'quit.blend' and temp saves,
967  * the packed blend has to be in undo buffers... */
968  /* XXX needs rethink, just like save UI in undo files now -
969  * would be nice to append things only for the 'quit.blend' and temp saves. */
970  if (found_one) {
971  /* Not overridable. */
972 
973  void *runtime_name_data = main->curlib->runtime.name_map;
974  main->curlib->runtime.name_map = NULL;
975 
976  BlendWriter writer = {wd};
977  writestruct(wd, ID_LI, Library, 1, main->curlib);
978  BKE_id_blend_write(&writer, &main->curlib->id);
979 
980  main->curlib->runtime.name_map = runtime_name_data;
981 
982  if (main->curlib->packedfile) {
983  BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
984  if (wd->use_memfile == false) {
985  CLOG_INFO(&LOG, 2, "Write packed .blend: %s", main->curlib->filepath);
986  }
987  }
988 
989  /* Write link placeholders for all direct linked IDs. */
990  while (a--) {
991  for (id = lbarray[a]->first; id; id = id->next) {
992  if (id->us > 0 &&
993  ((id->tag & LIB_TAG_EXTERN) ||
996  CLOG_ERROR(&LOG,
997  "Data-block '%s' from lib '%s' is not linkable, but is flagged as "
998  "directly linked",
999  id->name,
1000  main->curlib->filepath_abs);
1001  }
1002  writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id);
1003  }
1004  }
1005  }
1006  }
1007  }
1008 
1009  mywrite_flush(wd);
1010 }
1011 
1012 /* context is usually defined by WM, two cases where no WM is available:
1013  * - for forward compatibility, curscreen has to be saved
1014  * - for undofile, curscene needs to be saved */
1015 static void write_global(WriteData *wd, int fileflags, Main *mainvar)
1016 {
1017  const bool is_undo = wd->use_memfile;
1018  FileGlobal fg;
1019  bScreen *screen;
1020  Scene *scene;
1021  ViewLayer *view_layer;
1022  char subvstr[8];
1023 
1024  /* prevent mem checkers from complaining */
1025  memset(fg._pad, 0, sizeof(fg._pad));
1026  memset(fg.filepath, 0, sizeof(fg.filepath));
1027  memset(fg.build_hash, 0, sizeof(fg.build_hash));
1028  fg._pad1 = NULL;
1029 
1030  current_screen_compat(mainvar, is_undo, &screen, &scene, &view_layer);
1031 
1032  /* XXX still remap G */
1033  fg.curscreen = screen;
1034  fg.curscene = scene;
1035  fg.cur_view_layer = view_layer;
1036 
1037  /* prevent to save this, is not good convention, and feature with concerns... */
1038  fg.fileflags = (fileflags & ~G_FILE_FLAG_ALL_RUNTIME);
1039 
1040  fg.globalf = G.f;
1041  /* Write information needed for recovery. */
1042  if (fileflags & G_FILE_RECOVER_WRITE) {
1043  STRNCPY(fg.filepath, mainvar->filepath);
1044  }
1045  sprintf(subvstr, "%4d", BLENDER_FILE_SUBVERSION);
1046  memcpy(fg.subvstr, subvstr, 4);
1047 
1051 #ifdef WITH_BUILDINFO
1052  {
1053  extern unsigned long build_commit_timestamp;
1054  extern char build_hash[];
1055  /* TODO(sergey): Add branch name to file as well? */
1057  BLI_strncpy(fg.build_hash, build_hash, sizeof(fg.build_hash));
1058  }
1059 #else
1060  fg.build_commit_timestamp = 0;
1061  BLI_strncpy(fg.build_hash, "unknown", sizeof(fg.build_hash));
1062 #endif
1063  writestruct(wd, GLOB, FileGlobal, 1, &fg);
1064 }
1065 
1071 static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
1072 {
1073  if (thumb) {
1074  writedata(wd, TEST, BLEN_THUMB_MEMSIZE_FILE(thumb->width, thumb->height), thumb);
1075  }
1076 }
1077 
1080 /* -------------------------------------------------------------------- */
1084 /* if MemFile * there's filesave to memory */
1085 static bool write_file_handle(Main *mainvar,
1086  WriteWrap *ww,
1087  MemFile *compare,
1088  MemFile *current,
1089  int write_flags,
1090  bool use_userdef,
1091  const BlendThumbnail *thumb)
1092 {
1093  BHead bhead;
1094  ListBase mainlist;
1095  char buf[16];
1096  WriteData *wd;
1097 
1098  blo_split_main(&mainlist, mainvar);
1099 
1100  wd = mywrite_begin(ww, compare, current);
1101  BlendWriter writer = {wd};
1102 
1103  sprintf(buf,
1104  "BLENDER%c%c%.3d",
1105  (sizeof(void *) == 8) ? '-' : '_',
1106  (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
1108 
1109  mywrite(wd, buf, 12);
1110 
1111  write_renderinfo(wd, mainvar);
1112  write_thumb(wd, thumb);
1113  write_global(wd, write_flags, mainvar);
1114 
1115  /* The window-manager and screen often change,
1116  * avoid thumbnail detecting changes because of this. */
1117  mywrite_flush(wd);
1118 
1119  OverrideLibraryStorage *override_storage = wd->use_memfile ?
1120  NULL :
1122 
1123 #define ID_BUFFER_STATIC_SIZE 8192
1124  /* This outer loop allows to save first data-blocks from real mainvar,
1125  * then the temp ones from override process,
1126  * if needed, without duplicating whole code. */
1127  Main *bmain = mainvar;
1128  do {
1129  ListBase *lbarray[INDEX_ID_MAX];
1130  int a = set_listbasepointers(bmain, lbarray);
1131  while (a--) {
1132  ID *id = lbarray[a]->first;
1133 
1134  if (id == NULL || GS(id->name) == ID_LI) {
1135  continue; /* Libraries are handled separately below. */
1136  }
1137 
1138  char id_buffer_static[ID_BUFFER_STATIC_SIZE];
1139  void *id_buffer = id_buffer_static;
1140  const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
1141  const size_t idtype_struct_size = id_type->struct_size;
1142  if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
1143  CLOG_ERROR(&LOG,
1144  "ID maximum buffer size (%d bytes) is not big enough to fit IDs of type %s, "
1145  "which needs %lu bytes",
1147  id_type->name,
1148  id_type->struct_size);
1149  id_buffer = MEM_mallocN(idtype_struct_size, __func__);
1150  }
1151 
1152  for (; id; id = id->next) {
1153  /* We should never attempt to write non-regular IDs
1154  * (i.e. all kind of temp/runtime ones). */
1155  BLI_assert(
1157 
1158  /* We only write unused IDs in undo case.
1159  * NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so
1160  * their usercount should never be NULL currently. */
1161  if (id->us == 0 && !wd->use_memfile) {
1163  continue;
1164  }
1165 
1166  const bool do_override = !ELEM(override_storage, NULL, bmain) &&
1168 
1169  if (do_override) {
1170  BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
1171  }
1172 
1173  if (wd->use_memfile) {
1174  /* Record the changes that happened up to this undo push in
1175  * recalc_up_to_undo_push, and clear recalc_after_undo_push again
1176  * to start accumulating for the next undo push. */
1177  id->recalc_up_to_undo_push = id->recalc_after_undo_push;
1178  id->recalc_after_undo_push = 0;
1179 
1180  bNodeTree *nodetree = ntreeFromID(id);
1181  if (nodetree != NULL) {
1182  nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
1183  nodetree->id.recalc_after_undo_push = 0;
1184  }
1185  if (GS(id->name) == ID_SCE) {
1186  Scene *scene = (Scene *)id;
1187  if (scene->master_collection != NULL) {
1191  }
1192  }
1193  }
1194 
1195  mywrite_id_begin(wd, id);
1196 
1197  memcpy(id_buffer, id, idtype_struct_size);
1198 
1199  /* Clear runtime data to reduce false detection of changed data in undo/redo context. */
1200  ((ID *)id_buffer)->tag = 0;
1201  ((ID *)id_buffer)->us = 0;
1202  ((ID *)id_buffer)->icon_id = 0;
1203  /* Those listbase data change every time we add/remove an ID, and also often when
1204  * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
1205  * detections between undo steps. */
1206  ((ID *)id_buffer)->prev = NULL;
1207  ((ID *)id_buffer)->next = NULL;
1208  /* Those runtime pointers should never be set during writing stage, but just in case clear
1209  * them too. */
1210  ((ID *)id_buffer)->orig_id = NULL;
1211  ((ID *)id_buffer)->newid = NULL;
1212  /* Even though in theory we could be able to preserve this python instance across undo even
1213  * when we need to re-read the ID into its original address, this is currently cleared in
1214  * #direct_link_id_common in `readfile.c` anyway, */
1215  ((ID *)id_buffer)->py_instance = NULL;
1216 
1217  if (id_type->blend_write != NULL) {
1218  id_type->blend_write(&writer, (ID *)id_buffer, id);
1219  }
1220 
1221  if (do_override) {
1222  BKE_lib_override_library_operations_store_end(override_storage, id);
1223  }
1224 
1225  mywrite_id_end(wd, id);
1226  }
1227 
1228  if (id_buffer != id_buffer_static) {
1229  MEM_SAFE_FREE(id_buffer);
1230  }
1231 
1232  mywrite_flush(wd);
1233  }
1234  } while ((bmain != override_storage) && (bmain = override_storage));
1235 
1236  if (override_storage) {
1238  override_storage = NULL;
1239  }
1240 
1241  /* Special handling, operating over split Mains... */
1242  write_libraries(wd, mainvar->next);
1243 
1244  /* So changes above don't cause a 'DNA1' to be detected as changed on undo. */
1245  mywrite_flush(wd);
1246 
1247  if (use_userdef) {
1248  write_userdef(&writer, &U);
1249  }
1250 
1251  /* Write DNA last, because (to be implemented) test for which structs are written.
1252  *
1253  * Note that we *borrow* the pointer to 'DNAstr',
1254  * so writing each time uses the same address and doesn't cause unnecessary undo overhead. */
1255  writedata(wd, DNA1, (size_t)wd->sdna->data_len, wd->sdna->data);
1256 
1257  /* end of file */
1258  memset(&bhead, 0, sizeof(BHead));
1259  bhead.code = ENDB;
1260  mywrite(wd, &bhead, sizeof(BHead));
1261 
1262  blo_join_main(&mainlist);
1263 
1264  return mywrite_end(wd);
1265 }
1266 
1267 /* do reverse file history: .blend1 -> .blend2, .blend -> .blend1 */
1268 /* return: success(0), failure(1) */
1269 static bool do_history(const char *name, ReportList *reports)
1270 {
1271  char tempname1[FILE_MAX], tempname2[FILE_MAX];
1272  int hisnr = U.versions;
1273 
1274  if (U.versions == 0) {
1275  return false;
1276  }
1277 
1278  if (strlen(name) < 2) {
1279  BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short");
1280  return true;
1281  }
1282 
1283  while (hisnr > 1) {
1284  BLI_snprintf(tempname1, sizeof(tempname1), "%s%d", name, hisnr - 1);
1285  if (BLI_exists(tempname1)) {
1286  BLI_snprintf(tempname2, sizeof(tempname2), "%s%d", name, hisnr);
1287 
1288  if (BLI_rename(tempname1, tempname2)) {
1289  BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1290  return true;
1291  }
1292  }
1293  hisnr--;
1294  }
1295 
1296  /* is needed when hisnr==1 */
1297  if (BLI_exists(name)) {
1298  BLI_snprintf(tempname1, sizeof(tempname1), "%s%d", name, hisnr);
1299 
1300  if (BLI_rename(name, tempname1)) {
1301  BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1302  return true;
1303  }
1304  }
1305 
1306  return false;
1307 }
1308 
1311 /* -------------------------------------------------------------------- */
1315 bool BLO_write_file(Main *mainvar,
1316  const char *filepath,
1317  const int write_flags,
1318  const struct BlendFileWriteParams *params,
1319  ReportList *reports)
1320 {
1321  BLI_assert(!BLI_path_is_rel(filepath));
1323 
1324  char tempname[FILE_MAX + 1];
1325  WriteWrap ww;
1326 
1327  eBLO_WritePathRemap remap_mode = params->remap_mode;
1328  const bool use_save_versions = params->use_save_versions;
1329  const bool use_save_as_copy = params->use_save_as_copy;
1330  const bool use_userdef = params->use_userdef;
1331  const BlendThumbnail *thumb = params->thumb;
1332  const bool relbase_valid = (mainvar->filepath[0] != '\0');
1333 
1334  /* path backup/restore */
1335  void *path_list_backup = NULL;
1336  const eBPathForeachFlag path_list_flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED |
1338 
1339  if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
1340  BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* save to disk");
1341  BLO_main_validate_libraries(mainvar, reports);
1342  BLO_main_validate_shapekeys(mainvar, reports);
1343  }
1344 
1345  /* open temporary file, so we preserve the original in case we crash */
1346  BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
1347 
1348  ww_handle_init((write_flags & G_FILE_COMPRESS) ? WW_WRAP_ZSTD : WW_WRAP_NONE, &ww);
1349 
1350  if (ww.open(&ww, tempname) == false) {
1351  BKE_reportf(
1352  reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno));
1353  return false;
1354  }
1355 
1356  if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
1357  /* Paths will already be absolute, no remapping to do. */
1358  if (relbase_valid == false) {
1359  remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1360  }
1361  }
1362 
1363  /* Remapping of relative paths to new file location. */
1364  if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1365  if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1366  /* Make all relative as none of the existing paths can be relative in an unsaved document. */
1367  if (relbase_valid == false) {
1368  remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL;
1369  }
1370  }
1371 
1372  /* The source path only makes sense to set if the file was saved (`relbase_valid`). */
1373  char dir_src[FILE_MAX];
1374  char dir_dst[FILE_MAX];
1375 
1376  /* Normalize the paths in case there is some subtle difference (so they can be compared). */
1377  if (relbase_valid) {
1378  BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
1379  BLI_path_normalize(NULL, dir_src);
1380  }
1381  else {
1382  dir_src[0] = '\0';
1383  }
1384  BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
1385  BLI_path_normalize(NULL, dir_dst);
1386 
1387  /* Only for relative, not relative-all, as this means making existing paths relative. */
1388  if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1389  if (relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
1390  /* Saved to same path. Nothing to do. */
1391  remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1392  }
1393  }
1394  else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
1395  if (relbase_valid == false) {
1396  /* Unsaved, all paths are absolute.Even if the user manages to set a relative path,
1397  * there is no base-path that can be used to make it absolute. */
1398  remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1399  }
1400  }
1401 
1402  if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1403  /* Some path processing (e.g. with libraries) may use the current `main->filepath`, if this
1404  * is not matching the path currently used for saving, unexpected paths corruptions can
1405  * happen. See T98201. */
1406  char mainvar_filepath_orig[FILE_MAX];
1407  STRNCPY(mainvar_filepath_orig, mainvar->filepath);
1408  STRNCPY(mainvar->filepath, filepath);
1409 
1410  /* Check if we need to backup and restore paths. */
1411  if (UNLIKELY(use_save_as_copy)) {
1412  path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag);
1413  }
1414 
1415  switch (remap_mode) {
1417  /* Saved, make relative paths relative to new location (if possible). */
1418  BLI_assert(relbase_valid);
1419  BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL);
1420  break;
1422  /* Make all relative (when requested or unsaved). */
1423  BKE_bpath_relative_convert(mainvar, dir_dst, NULL);
1424  break;
1426  /* Make all absolute (when requested or unsaved). */
1427  BLI_assert(relbase_valid);
1428  BKE_bpath_absolute_convert(mainvar, dir_src, NULL);
1429  break;
1431  BLI_assert_unreachable(); /* Unreachable. */
1432  break;
1433  }
1434 
1435  STRNCPY(mainvar->filepath, mainvar_filepath_orig);
1436  }
1437  }
1438 
1439  /* actual file writing */
1440  const bool err = write_file_handle(mainvar, &ww, NULL, NULL, write_flags, use_userdef, thumb);
1441 
1442  ww.close(&ww);
1443 
1444  if (UNLIKELY(path_list_backup)) {
1445  BKE_bpath_list_restore(mainvar, path_list_flag, path_list_backup);
1446  BKE_bpath_list_free(path_list_backup);
1447  }
1448 
1449  if (err) {
1450  BKE_report(reports, RPT_ERROR, strerror(errno));
1451  remove(tempname);
1452 
1453  return false;
1454  }
1455 
1456  /* file save to temporary file was successful */
1457  /* now do reverse file history (move .blend1 -> .blend2, .blend -> .blend1) */
1458  if (use_save_versions) {
1459  const bool err_hist = do_history(filepath, reports);
1460  if (err_hist) {
1461  BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)");
1462  return false;
1463  }
1464  }
1465 
1466  if (BLI_rename(tempname, filepath) != 0) {
1467  BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)");
1468  return false;
1469  }
1470 
1471  if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
1472  BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* save to disk");
1473  BLO_main_validate_libraries(mainvar, reports);
1474  }
1475 
1476  return true;
1477 }
1478 
1479 bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
1480 {
1481  bool use_userdef = false;
1482 
1483  const bool err = write_file_handle(
1484  mainvar, NULL, compare, current, write_flags, use_userdef, NULL);
1485 
1486  return (err == 0);
1487 }
1488 
1489 void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
1490 {
1491  writedata(writer->wd, DATA, size_in_bytes, data_ptr);
1492 }
1493 
1494 void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
1495 {
1496  BLO_write_struct_array_by_name(writer, struct_name, 1, data_ptr);
1497 }
1498 
1500  const char *struct_name,
1501  int array_size,
1502  const void *data_ptr)
1503 {
1504  int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1505  if (UNLIKELY(struct_id == -1)) {
1506  CLOG_ERROR(&LOG, "Can't find SDNA code <%s>", struct_name);
1507  return;
1508  }
1509  BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr);
1510 }
1511 
1512 void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
1513 {
1514  writestruct_nr(writer->wd, DATA, struct_id, 1, data_ptr);
1515 }
1516 
1518  int struct_id,
1519  const void *address,
1520  const void *data_ptr)
1521 {
1522  BLO_write_struct_at_address_by_id_with_filecode(writer, DATA, struct_id, address, data_ptr);
1523 }
1524 
1526  BlendWriter *writer, int filecode, int struct_id, const void *address, const void *data_ptr)
1527 {
1528  writestruct_at_address_nr(writer->wd, filecode, struct_id, 1, address, data_ptr);
1529 }
1530 
1532  int struct_id,
1533  int array_size,
1534  const void *data_ptr)
1535 {
1536  writestruct_nr(writer->wd, DATA, struct_id, array_size, data_ptr);
1537 }
1538 
1540  BlendWriter *writer, int struct_id, int array_size, const void *address, const void *data_ptr)
1541 {
1542  writestruct_at_address_nr(writer->wd, DATA, struct_id, array_size, address, data_ptr);
1543 }
1544 
1545 void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list)
1546 {
1547  writelist_nr(writer->wd, DATA, struct_id, list);
1548 }
1549 
1550 void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
1551 {
1552  int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1553  if (UNLIKELY(struct_id == -1)) {
1554  CLOG_ERROR(&LOG, "Can't find SDNA code <%s>", struct_name);
1555  return;
1556  }
1557  BLO_write_struct_list_by_id(writer, struct_id, list);
1558 }
1559 
1560 void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
1561 {
1562  writestruct_at_address_nr(writer->wd, GS(id->name), struct_id, 1, id_address, id);
1563 }
1564 
1565 int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name)
1566 {
1567  int struct_id = DNA_struct_find_nr(writer->wd->sdna, struct_name);
1568  return struct_id;
1569 }
1570 
1571 void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
1572 {
1573  BLO_write_raw(writer, sizeof(int32_t) * (size_t)num, data_ptr);
1574 }
1575 
1576 void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
1577 {
1578  BLO_write_raw(writer, sizeof(uint32_t) * (size_t)num, data_ptr);
1579 }
1580 
1581 void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
1582 {
1583  BLO_write_raw(writer, sizeof(float) * (size_t)num, data_ptr);
1584 }
1585 
1586 void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
1587 {
1588  BLO_write_raw(writer, sizeof(double) * (size_t)num, data_ptr);
1589 }
1590 
1591 void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
1592 {
1593  BLO_write_raw(writer, sizeof(void *) * (size_t)num, data_ptr);
1594 }
1595 
1596 void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
1597 {
1598  BLO_write_raw(writer, sizeof(float[3]) * (size_t)num, data_ptr);
1599 }
1600 
1601 void BLO_write_string(BlendWriter *writer, const char *data_ptr)
1602 {
1603  if (data_ptr != NULL) {
1604  BLO_write_raw(writer, strlen(data_ptr) + 1, data_ptr);
1605  }
1606 }
1607 
1609 {
1610  return writer->wd->use_memfile;
1611 }
1612 
#define BLENDER_FILE_SUBVERSION
#define BLENDER_FILE_MIN_VERSION
#define BLENDER_FILE_VERSION
#define BLENDER_FILE_MIN_SUBVERSION
void BKE_bpath_relative_rebase(struct Main *bmain, const char *basedir_src, const char *basedir_dst, struct ReportList *reports)
Definition: bpath.c:439
void BKE_bpath_relative_convert(struct Main *bmain, const char *basedir, struct ReportList *reports)
Definition: bpath.c:559
void * BKE_bpath_list_backup(struct Main *bmain, eBPathForeachFlag flag)
Definition: bpath.c:619
void BKE_bpath_list_restore(struct Main *bmain, eBPathForeachFlag flag, void *path_list_handle)
Definition: bpath.c:631
eBPathForeachFlag
Definition: BKE_bpath.h:27
@ BKE_BPATH_FOREACH_PATH_SKIP_LINKED
Definition: BKE_bpath.h:35
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
Definition: BKE_bpath.h:55
void BKE_bpath_list_free(void *path_list_handle)
Definition: bpath.c:641
void BKE_bpath_absolute_convert(struct Main *bmain, const char *basedir, struct ReportList *reports)
Definition: bpath.c:564
@ G_FILE_RECOVER_WRITE
Definition: BKE_global.h:233
@ G_FILE_COMPRESS
Definition: BKE_global.h:210
@ G_DEBUG_IO
Definition: BKE_global.h:194
#define G_FILE_FLAG_ALL_RUNTIME
Definition: BKE_global.h:243
void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop)
bool BKE_idtype_idcode_is_linkable(short idcode)
Definition: idtype.c:175
const struct IDTypeInfo * BKE_idtype_get_info_from_id(const struct ID *id)
struct ViewLayer * BKE_view_layer_find(const struct Scene *scene, const char *layer_name)
void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id)
Definition: lib_id.c:2008
#define MAIN_ID_SESSION_UUID_UNSET
Definition: BKE_lib_id.h:82
void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage)
void BKE_lib_override_library_operations_store_end(OverrideLibraryStorage *override_storage, struct ID *local)
struct ID * BKE_lib_override_library_operations_store_start(struct Main *bmain, OverrideLibraryStorage *override_storage, struct ID *local)
OverrideLibraryStorage * BKE_lib_override_library_operations_store_init(void)
int set_listbasepointers(struct Main *main, struct ListBase *lb[])
Definition: main.c:654
struct bNodeTree * ntreeFromID(struct ID *id)
Definition: node.cc:3231
void BKE_packedfile_blend_write(struct BlendWriter *writer, struct PackedFile *pf)
Definition: packedFile.c:855
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.c:83
struct bScreen * BKE_workspace_active_screen_get(const struct WorkSpaceInstanceHook *hook) GETTER_ATTRS
#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
#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.c:314
#define O_BINARY
Definition: BLI_fileops.h:319
int BLI_rename(const char *from, const char *to) ATTR_NONNULL()
Definition: fileops.c:1268
int BLI_open(const char *filepath, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:920
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition: listbase.c:466
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE int max_ii(int a, int b)
void BLI_split_dir_part(const char *string, char *dir, size_t dirlen)
Definition: path_util.c:1490
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:347
#define FILE_MAX
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:131
bool BLI_path_is_abs_from_cwd(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:998
#define BLI_path_cmp
#define STRNCPY(dst, src)
Definition: BLI_string.h:483
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
unsigned char uchar
Definition: BLI_sys_types.h:70
unsigned int uint
Definition: BLI_sys_types.h:67
void BLI_condition_notify_all(ThreadCondition *cond)
Definition: threads.cc:594
void BLI_mutex_end(ThreadMutex *mutex)
Definition: threads.cc:388
void BLI_threadpool_remove(struct ListBase *threadbase, void *callerdata)
Definition: threads.cc:225
void BLI_condition_wait(ThreadCondition *cond, ThreadMutex *mutex)
Definition: threads.cc:579
void BLI_threadpool_init(struct ListBase *threadbase, void *(*do_thread)(void *), int tot)
Definition: threads.cc:134
void BLI_mutex_init(ThreadMutex *mutex)
Definition: threads.cc:368
pthread_cond_t ThreadCondition
Definition: BLI_threads.h:150
void BLI_condition_end(ThreadCondition *cond)
Definition: threads.cc:599
int BLI_system_thread_count(void)
Definition: threads.cc:281
void BLI_threadpool_end(struct ListBase *threadbase)
Definition: threads.cc:262
void BLI_condition_init(ThreadCondition *cond)
Definition: threads.cc:574
void BLI_mutex_lock(ThreadMutex *mutex)
Definition: threads.cc:373
void BLI_mutex_unlock(ThreadMutex *mutex)
Definition: threads.cc:378
int BLI_available_threads(struct ListBase *threadbase)
Definition: threads.cc:167
void BLI_threadpool_insert(struct ListBase *threadbase, void *callerdata)
Definition: threads.cc:212
pthread_mutex_t ThreadMutex
Definition: BLI_threads.h:82
#define UNUSED(x)
#define UNLIKELY(x)
#define ELEM(...)
#define MIN2(a, b)
#define POINTER_FROM_UINT(i)
Compatibility-like things for windows.
defines for blend-file codes.
@ GLOB
@ DNA1
@ ENDB
@ DATA
@ TEST
@ USER
@ REND
#define BLEN_THUMB_MEMSIZE_FILE(_x, _y)
Utilities ensuring .blend file (i.e. Main) is in valid state during write and/or read process.
bool BLO_main_validate_shapekeys(struct Main *bmain, struct ReportList *reports)
bool BLO_main_validate_libraries(struct Main *bmain, struct 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.c:137
void BLO_memfile_write_init(MemFileWriteData *mem_data, MemFile *written_memfile, MemFile *reference_memfile)
Definition: undofile.c:96
void BLO_memfile_write_finalize(MemFileWriteData *mem_data)
Definition: undofile.c:130
external writefile.c function prototypes.
eBLO_WritePathRemap
Definition: BLO_writefile.h:29
@ BLO_WRITE_PATH_REMAP_NONE
Definition: BLO_writefile.h:31
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
Definition: BLO_writefile.h:35
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
Definition: BLO_writefile.h:37
@ BLO_WRITE_PATH_REMAP_RELATIVE
Definition: BLO_writefile.h:33
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:190
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition: DNA_ID.h:581
@ INDEX_ID_MAX
Definition: DNA_ID.h:1058
#define ID_IS_LINKED(_id)
Definition: DNA_ID.h:566
#define MAX_ID_NAME
Definition: DNA_ID.h:337
@ LIB_TAG_NO_USER_REFCOUNT
Definition: DNA_ID.h:745
@ LIB_TAG_NOT_ALLOCATED
Definition: DNA_ID.h:748
@ LIB_TAG_INDIRECT
Definition: DNA_ID.h:677
@ LIB_TAG_EXTERN
Definition: DNA_ID.h:674
@ LIB_TAG_NO_MAIN
Definition: DNA_ID.h:744
@ LIB_INDIRECT_WEAK_LINK
Definition: DNA_ID.h:641
@ ID_WM
Definition: DNA_ID_enums.h:72
@ ID_LI
Definition: DNA_ID_enums.h:46
@ ID_WS
Definition: DNA_ID_enums.h:79
@ ID_SCE
Definition: DNA_ID_enums.h:45
#define ID_LINK_PLACEHOLDER
Definition: DNA_ID_enums.h:88
Object groups, one object can be in many groups at once.
blenloader genfile private function prototypes
int DNA_struct_find_nr(const struct SDNA *sdna, const char *str)
const struct SDNA * DNA_sdna_current_get(void)
Definition: dna_genfile.c:566
#define R_BG_RENDER
@ USER_MENU_TYPE_OPERATOR
@ USER_MENU_TYPE_PROP
@ USER_MENU_TYPE_MENU
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
int main(int argc, char *argv[])
char build_hash[]
Definition: buildinfo.c:31
ulong build_commit_timestamp
Definition: buildinfo.c:32
unsigned int U
Definition: btGjkEpa3.h:78
FILE * file
Scene scene
int len
Definition: draw_manager.c:108
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define GS(x)
Definition: iris.c:225
ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define G(x, y, z)
static unsigned a[3]
Definition: RandGen.cpp:78
bool remove(void *owner, const AttributeIDRef &attribute_id)
struct blender::compositor::@179::@181 task
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: png.c:70
void blo_split_main(ListBase *mainlist, Main *main)
Definition: readfile.c:496
void blo_join_main(ListBase *mainlist)
Definition: readfile.c:455
unsigned int uint32_t
Definition: stdint.h:80
signed int int32_t
Definition: stdint.h:77
int SDNAnr
int code
const void * old
WriteData * wd
Definition: writefile.c:434
struct ViewLayer * cur_view_layer
struct Scene * curscene
uint64_t build_commit_timestamp
struct bScreen * curscreen
char filepath[1024]
const char * name
Definition: BKE_idtype.h:132
IDTypeBlendWriteFunction blend_write
Definition: BKE_idtype.h:196
size_t struct_size
Definition: BKE_idtype.h:129
Definition: DNA_ID.h:368
int tag
Definition: DNA_ID.h:387
int recalc_after_undo_push
Definition: DNA_ID.h:401
int us
Definition: DNA_ID.h:388
short flag
Definition: DNA_ID.h:383
unsigned int session_uuid
Definition: DNA_ID.h:407
void * next
Definition: DNA_ID.h:369
char name[66]
Definition: DNA_ID.h:378
int recalc_up_to_undo_push
Definition: DNA_ID.h:400
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
ListBase scenes
Definition: BKE_main.h:168
ListBase wm
Definition: BKE_main.h:197
char filepath[1024]
Definition: BKE_main.h:124
struct MainLock * lock
Definition: BKE_main.h:226
struct Main * next
Definition: BKE_main.h:122
uint id_session_uuid
Definition: BLO_undofile.h:29
struct GHash * id_session_uuid_mapping
Definition: BLO_undofile.h:45
MemFileChunk * reference_current_chunk
Definition: BLO_undofile.h:42
uint current_id_session_uuid
Definition: BLO_undofile.h:41
char scene_name[MAX_ID_NAME - 2]
Definition: writefile.c:815
short * types_size
SDNA_Struct ** structs
int data_len
const char * data
struct Collection * master_collection
struct ListBase addons
struct ListBase uistyles
struct ListBase user_keymaps
struct ListBase themes
struct ListBase autoexec_paths
struct ListBase user_keyconfig_prefs
struct ListBase user_menus
struct ListBase asset_libraries
bool error
Definition: writefile.c:418
WriteWrap * ww
Definition: writefile.c:430
uchar * buf
Definition: writefile.c:402
struct WriteData::@137 buffer
bool use_memfile
Definition: writefile.c:423
const struct SDNA * sdna
Definition: writefile.c:398
MemFileWriteData mem
Definition: writefile.c:421
size_t chunk_size
Definition: writefile.c:409
size_t max_size
Definition: writefile.c:407
size_t used_len
Definition: writefile.c:404
bool(* close)(WriteWrap *ww)
Definition: writefile.c:163
int num_frames
Definition: writefile.c:177
ThreadMutex mutex
Definition: writefile.c:174
ListBase threadpool
Definition: writefile.c:172
ListBase frames
Definition: writefile.c:180
int next_frame
Definition: writefile.c:176
size_t(* write)(WriteWrap *ww, const char *data, size_t data_len)
Definition: writefile.c:164
int level
Definition: writefile.c:179
int file_handle
Definition: writefile.c:170
bool(* open)(WriteWrap *ww, const char *filepath)
Definition: writefile.c:162
bool use_buf
Definition: writefile.c:167
bool write_error
Definition: writefile.c:182
ListBase tasks
Definition: writefile.c:173
ThreadCondition condition
Definition: writefile.c:175
struct WriteWrap::@136 zstd
uint32_t uncompressed_size
Definition: writefile.c:156
struct ZstdFrame * next
Definition: writefile.c:153
uint32_t compressed_size
Definition: writefile.c:155
struct ZstdFrame * prev
Definition: writefile.c:153
struct ZstdWriteBlockTask * next
Definition: writefile.c:212
WriteWrap * ww
Definition: writefile.c:216
struct IDProperty * prop
struct Scene * scene
char view_layer_name[64]
struct wmWindow * next
struct WorkSpaceInstanceHook * workspace_hook
static FT_Error err
void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
Definition: writefile.c:1586
void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
Definition: writefile.c:1576
struct ZstdFrame ZstdFrame
static void mywrite_id_begin(WriteData *wd, ID *id)
Definition: writefile.c:613
#define ZSTD_COMPRESSION_LEVEL
Definition: writefile.c:136
static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
Definition: writefile.c:1071
static WriteData * mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
Definition: writefile.c:573
#define writestruct(wd, filecode, struct_id, nr, adr)
Definition: writefile.c:756
void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
Definition: writefile.c:1596
static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
Definition: writefile.c:723
void BLO_write_struct_at_address_by_id_with_filecode(BlendWriter *writer, int filecode, int struct_id, const void *address, const void *data_ptr)
Definition: writefile.c:1525
static void writestruct_at_address_nr(WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr, const void *data)
Definition: writefile.c:659
static bool ww_open_none(WriteWrap *ww, const char *filepath)
Definition: writefile.c:187
bool BLO_write_file(Main *mainvar, const char *filepath, const int write_flags, const struct BlendFileWriteParams *params, ReportList *reports)
Definition: writefile.c:1315
void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
Definition: writefile.c:1494
static void writedata_free(WriteData *wd)
Definition: writefile.c:486
void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
Definition: writefile.c:1571
static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
Definition: writefile.c:847
static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
Definition: writefile.c:204
void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
Definition: writefile.c:1560
#define ZSTD_CHUNK_SIZE
Definition: writefile.c:134
struct BlendWriter BlendWriter
static void write_renderinfo(WriteData *wd, Main *mainvar)
Definition: writefile.c:824
static void write_userdef(BlendWriter *writer, const UserDef *userdef)
Definition: writefile.c:855
static void write_libraries(WriteData *wd, Main *main)
Definition: writefile.c:933
static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
Definition: writefile.c:331
#define ID_BUFFER_STATIC_SIZE
static bool ww_open_zstd(WriteWrap *ww, const char *filepath)
Definition: writefile.c:261
void BLO_write_struct_array_by_id(BlendWriter *writer, int struct_id, int array_size, const void *data_ptr)
Definition: writefile.c:1531
eWriteWrapType
Definition: writefile.c:147
@ WW_WRAP_ZSTD
Definition: writefile.c:149
@ WW_WRAP_NONE
Definition: writefile.c:148
static void * zstd_write_task(void *userdata)
Definition: writefile.c:219
static void write_global(WriteData *wd, int fileflags, Main *mainvar)
Definition: writefile.c:1015
static bool ww_close_zstd(WriteWrap *ww)
Definition: writefile.c:317
static void zstd_write_seekable_frames(WriteWrap *ww)
Definition: writefile.c:292
static WriteData * writedata_new(WriteWrap *ww)
Definition: writefile.c:437
void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list)
Definition: writefile.c:1545
#define MEM_CHUNK_SIZE
Definition: writefile.c:131
static void writestruct_nr(WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
Definition: writefile.c:688
void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
Definition: writefile.c:1581
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
Definition: writefile.c:1601
static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
Definition: writefile.c:369
int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name)
Definition: writefile.c:1565
void BLO_write_struct_array_by_name(BlendWriter *writer, const char *struct_name, int array_size, const void *data_ptr)
Definition: writefile.c:1499
static bool mywrite_end(WriteData *wd)
Definition: writefile.c:591
static void mywrite_flush(WriteData *wd)
Definition: writefile.c:504
static bool do_history(const char *name, ReportList *reports)
Definition: writefile.c:1269
#define ZSTD_BUFFER_SIZE
Definition: writefile.c:133
static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
Definition: writefile.c:695
static CLG_LogRef LOG
Definition: writefile.c:138
void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
Definition: writefile.c:1489
void BLO_write_struct_at_address_by_id(BlendWriter *writer, int struct_id, const void *address, const void *data_ptr)
Definition: writefile.c:1517
static void current_screen_compat(Main *mainvar, bool use_active_win, bScreen **r_screen, Scene **r_scene, ViewLayer **r_view_layer)
Definition: writefile.c:774
void BLO_write_struct_array_at_address_by_id(BlendWriter *writer, int struct_id, int array_size, const void *address, const void *data_ptr)
Definition: writefile.c:1539
static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
Definition: writefile.c:460
bool BLO_write_is_undo(BlendWriter *writer)
Definition: writefile.c:1608
#define MEM_BUFFER_SIZE
Definition: writefile.c:130
static void mywrite(WriteData *wd, const void *adr, size_t len)
Definition: writefile.c:517
bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
Definition: writefile.c:1479
static void mywrite_id_end(WriteData *wd, ID *UNUSED(id))
Definition: writefile.c:643
static bool ww_close_none(WriteWrap *ww)
Definition: writefile.c:200
static bool write_file_handle(Main *mainvar, WriteWrap *ww, MemFile *compare, MemFile *current, int write_flags, bool use_userdef, const BlendThumbnail *thumb)
Definition: writefile.c:1085
void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
Definition: writefile.c:1512
void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
Definition: writefile.c:1550
static void zstd_write_u32_le(WriteWrap *ww, uint32_t val)
Definition: writefile.c:276
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
Definition: writefile.c:1591
struct RenderInfo RenderInfo