Blender  V2.93
writefile.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
75 #include <fcntl.h>
76 #include <limits.h>
77 #include <math.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 
82 #ifdef WIN32
83 # include "BLI_winstuff.h"
84 # include "winsock2.h"
85 # include <io.h>
86 # include <zlib.h> /* odd include order-issue */
87 #else
88 # include <unistd.h> /* FreeBSD, for write() and close(). */
89 #endif
90 
91 #include "BLI_utildefines.h"
92 
93 /* allow writefile to use deprecated functionality (for forward compatibility code) */
94 #define DNA_DEPRECATED_ALLOW
95 
96 #include "DNA_collection_types.h"
97 #include "DNA_fileglobal_types.h"
98 #include "DNA_genfile.h"
99 #include "DNA_sdna_types.h"
100 
101 #include "BLI_bitmap.h"
102 #include "BLI_blenlib.h"
103 #include "BLI_mempool.h"
104 #include "MEM_guardedalloc.h" /* MEM_freeN */
105 
106 #include "BKE_blender_version.h"
107 #include "BKE_bpath.h"
108 #include "BKE_global.h" /* for G */
109 #include "BKE_idprop.h"
110 #include "BKE_idtype.h"
111 #include "BKE_layer.h"
112 #include "BKE_lib_id.h"
113 #include "BKE_lib_override.h"
114 #include "BKE_main.h"
115 #include "BKE_node.h"
116 #include "BKE_packedFile.h"
117 #include "BKE_report.h"
118 #include "BKE_workspace.h"
119 
120 #include "BLO_blend_defs.h"
121 #include "BLO_blend_validate.h"
122 #include "BLO_read_write.h"
123 #include "BLO_readfile.h"
124 #include "BLO_undofile.h"
125 #include "BLO_writefile.h"
126 
127 #include "readfile.h"
128 
129 #include <errno.h>
130 
131 /* Make preferences read-only. */
132 #define U (*((const UserDef *)&U))
133 
134 /* ********* my write, buffered writing with minimum size chunks ************ */
135 
136 /* Use optimal allocation since blocks of this size are kept in memory for undo. */
137 #define MYWRITE_BUFFER_SIZE (MEM_SIZE_OPTIMAL(1 << 17)) /* 128kb */
138 #define MYWRITE_MAX_CHUNK (MEM_SIZE_OPTIMAL(1 << 15)) /* ~32kb */
139 
141 // #define USE_WRITE_DATA_LEN
142 
143 /* -------------------------------------------------------------------- */
147 typedef enum {
151 
152 typedef struct WriteWrap WriteWrap;
153 struct WriteWrap {
154  /* callbacks */
155  bool (*open)(WriteWrap *ww, const char *filepath);
156  bool (*close)(WriteWrap *ww);
157  size_t (*write)(WriteWrap *ww, const char *data, size_t data_len);
158 
159  /* Buffer output (we only want when output isn't already buffered). */
160  bool use_buf;
161 
162  /* internal */
163  union {
165  gzFile gz_handle;
167 };
168 
169 /* none */
170 #define FILE_HANDLE(ww) (ww)->_user_data.file_handle
171 
172 static bool ww_open_none(WriteWrap *ww, const char *filepath)
173 {
174  int file;
175 
176  file = BLI_open(filepath, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
177 
178  if (file != -1) {
179  FILE_HANDLE(ww) = file;
180  return true;
181  }
182 
183  return false;
184 }
185 static bool ww_close_none(WriteWrap *ww)
186 {
187  return (close(FILE_HANDLE(ww)) != -1);
188 }
189 static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
190 {
191  return write(FILE_HANDLE(ww), buf, buf_len);
192 }
193 #undef FILE_HANDLE
194 
195 /* zlib */
196 #define FILE_HANDLE(ww) (ww)->_user_data.gz_handle
197 
198 static bool ww_open_zlib(WriteWrap *ww, const char *filepath)
199 {
200  gzFile file;
201 
202  file = BLI_gzopen(filepath, "wb1");
203 
204  if (file != Z_NULL) {
205  FILE_HANDLE(ww) = file;
206  return true;
207  }
208 
209  return false;
210 }
211 static bool ww_close_zlib(WriteWrap *ww)
212 {
213  return (gzclose(FILE_HANDLE(ww)) == Z_OK);
214 }
215 static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len)
216 {
217  return gzwrite(FILE_HANDLE(ww), buf, buf_len);
218 }
219 #undef FILE_HANDLE
220 
221 /* --- end compression types --- */
222 
223 static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
224 {
225  memset(r_ww, 0, sizeof(*r_ww));
226 
227  switch (ww_type) {
228  case WW_WRAP_ZLIB: {
229  r_ww->open = ww_open_zlib;
230  r_ww->close = ww_close_zlib;
231  r_ww->write = ww_write_zlib;
232  r_ww->use_buf = false;
233  break;
234  }
235  default: {
236  r_ww->open = ww_open_none;
237  r_ww->close = ww_close_none;
238  r_ww->write = ww_write_none;
239  r_ww->use_buf = true;
240  break;
241  }
242  }
243 }
244 
247 /* -------------------------------------------------------------------- */
251 typedef struct {
252  const struct SDNA *sdna;
253 
257  size_t buf_used_len;
258 
259 #ifdef USE_WRITE_DATA_LEN
261  size_t write_len;
262 #endif
263 
265  bool error;
266 
271 
278 } WriteData;
279 
280 typedef struct BlendWriter {
283 
285 {
286  WriteData *wd = MEM_callocN(sizeof(*wd), "writedata");
287 
288  wd->sdna = DNA_sdna_current_get();
289 
290  wd->ww = ww;
291 
292  if ((ww == NULL) || (ww->use_buf)) {
293  wd->buf = MEM_mallocN(MYWRITE_BUFFER_SIZE, "wd->buf");
294  }
295 
296  return wd;
297 }
298 
299 static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
300 {
301  if ((wd == NULL) || wd->error || (mem == NULL) || memlen < 1) {
302  return;
303  }
304 
305  if (memlen > INT_MAX) {
306  BLI_assert(!"Cannot write chunks bigger than INT_MAX.");
307  return;
308  }
309 
310  if (UNLIKELY(wd->error)) {
311  return;
312  }
313 
314  /* memory based save */
315  if (wd->use_memfile) {
316  BLO_memfile_chunk_add(&wd->mem, mem, memlen);
317  }
318  else {
319  if (wd->ww->write(wd->ww, mem, memlen) != memlen) {
320  wd->error = true;
321  }
322  }
323 }
324 
325 static void writedata_free(WriteData *wd)
326 {
327  if (wd->buf) {
328  MEM_freeN(wd->buf);
329  }
330  MEM_freeN(wd);
331 }
332 
335 /* -------------------------------------------------------------------- */
343 static void mywrite_flush(WriteData *wd)
344 {
345  if (wd->buf_used_len != 0) {
346  writedata_do_write(wd, wd->buf, wd->buf_used_len);
347  wd->buf_used_len = 0;
348  }
349 }
350 
356 static void mywrite(WriteData *wd, const void *adr, size_t len)
357 {
358  if (UNLIKELY(wd->error)) {
359  return;
360  }
361 
362  if (UNLIKELY(adr == NULL)) {
363  BLI_assert(0);
364  return;
365  }
366 
367 #ifdef USE_WRITE_DATA_LEN
368  wd->write_len += len;
369 #endif
370 
371  if (wd->buf == NULL) {
372  writedata_do_write(wd, adr, len);
373  }
374  else {
375  /* if we have a single big chunk, write existing data in
376  * buffer and write out big chunk in smaller pieces */
377  if (len > MYWRITE_MAX_CHUNK) {
378  if (wd->buf_used_len != 0) {
379  writedata_do_write(wd, wd->buf, wd->buf_used_len);
380  wd->buf_used_len = 0;
381  }
382 
383  do {
384  size_t writelen = MIN2(len, MYWRITE_MAX_CHUNK);
385  writedata_do_write(wd, adr, writelen);
386  adr = (const char *)adr + writelen;
387  len -= writelen;
388  } while (len > 0);
389 
390  return;
391  }
392 
393  /* if data would overflow buffer, write out the buffer */
394  if (len + wd->buf_used_len > MYWRITE_BUFFER_SIZE - 1) {
395  writedata_do_write(wd, wd->buf, wd->buf_used_len);
396  wd->buf_used_len = 0;
397  }
398 
399  /* append data at end of buffer */
400  memcpy(&wd->buf[wd->buf_used_len], adr, len);
401  wd->buf_used_len += len;
402  }
403 }
404 
412 static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
413 {
414  WriteData *wd = writedata_new(ww);
415 
416  if (current != NULL) {
417  BLO_memfile_write_init(&wd->mem, current, compare);
418  wd->use_memfile = true;
419  }
420 
421  return wd;
422 }
423 
430 static bool mywrite_end(WriteData *wd)
431 {
432  if (wd->buf_used_len != 0) {
433  writedata_do_write(wd, wd->buf, wd->buf_used_len);
434  wd->buf_used_len = 0;
435  }
436 
437  if (wd->use_memfile) {
439  }
440 
441  const bool err = wd->error;
442  writedata_free(wd);
443 
444  return err;
445 }
446 
452 static void mywrite_id_begin(WriteData *wd, ID *id)
453 {
454  if (wd->use_memfile) {
455  wd->mem.current_id_session_uuid = id->session_uuid;
456 
457  /* If current next memchunk does not match the ID we are about to write, or is not the _first_
458  * one for said ID, try to find the correct memchunk in the mapping using ID's session_uuid. */
459  MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk;
460  MemFileChunk *prev_memchunk = curr_memchunk != NULL ? curr_memchunk->prev : NULL;
461  if (wd->mem.id_session_uuid_mapping != NULL &&
462  (curr_memchunk == NULL || curr_memchunk->id_session_uuid != id->session_uuid ||
463  (prev_memchunk != NULL &&
464  (prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) {
467  if (ref != NULL) {
468  wd->mem.reference_current_chunk = ref;
469  }
470  /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */
471  }
472  /* Otherwise, we try with the current memchunk in any case, whether it is matching current
473  * ID's session_uuid or not. */
474  }
475 }
476 
482 static void mywrite_id_end(WriteData *wd, ID *UNUSED(id))
483 {
484  if (wd->use_memfile) {
485  /* Very important to do it after every ID write now, otherwise we cannot know whether a
486  * specific ID changed or not. */
487  mywrite_flush(wd);
489  }
490 }
491 
494 /* -------------------------------------------------------------------- */
499  WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr, const void *data)
500 {
501  BHead bh;
502 
503  BLI_assert(struct_nr > 0 && struct_nr < SDNA_TYPE_MAX);
504 
505  if (adr == NULL || data == NULL || nr == 0) {
506  return;
507  }
508 
509  /* init BHead */
510  bh.code = filecode;
511  bh.old = adr;
512  bh.nr = nr;
513 
514  bh.SDNAnr = struct_nr;
515  const SDNA_Struct *struct_info = wd->sdna->structs[bh.SDNAnr];
516 
517  bh.len = nr * wd->sdna->types_size[struct_info->type];
518 
519  if (bh.len == 0) {
520  return;
521  }
522 
523  mywrite(wd, &bh, sizeof(BHead));
524  mywrite(wd, data, (size_t)bh.len);
525 }
526 
527 static void writestruct_nr(
528  WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
529 {
530  writestruct_at_address_nr(wd, filecode, struct_nr, nr, adr, adr);
531 }
532 
533 /* do not use for structs */
534 static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
535 {
536  BHead bh;
537 
538  if (adr == NULL || len == 0) {
539  return;
540  }
541 
542  if (len > INT_MAX) {
543  BLI_assert(!"Cannot write chunks bigger than INT_MAX.");
544  return;
545  }
546 
547  /* align to 4 (writes uninitialized bytes in some cases) */
548  len = (len + 3) & ~((size_t)3);
549 
550  /* init BHead */
551  bh.code = filecode;
552  bh.old = adr;
553  bh.nr = 1;
554  bh.SDNAnr = 0;
555  bh.len = (int)len;
556 
557  mywrite(wd, &bh, sizeof(BHead));
558  mywrite(wd, adr, len);
559 }
560 
561 /* use this to force writing of lists in same order as reading (using link_list) */
562 static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
563 {
564  const Link *link = lb->first;
565 
566  while (link) {
567  writestruct_nr(wd, filecode, struct_nr, 1, link);
568  link = link->next;
569  }
570 }
571 
572 #if 0
573 static void writelist_id(WriteData *wd, int filecode, const char *structname, const ListBase *lb)
574 {
575  const Link *link = lb->first;
576  if (link) {
577 
578  const int struct_nr = DNA_struct_find_nr(wd->sdna, structname);
579  if (struct_nr == -1) {
580  printf("error: can't find SDNA code <%s>\n", structname);
581  return;
582  }
583 
584  while (link) {
585  writestruct_nr(wd, filecode, struct_nr, 1, link);
586  link = link->next;
587  }
588  }
589 }
590 #endif
591 
592 #define writestruct_at_address(wd, filecode, struct_id, nr, adr, data) \
593  writestruct_at_address_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr, data)
594 
595 #define writestruct(wd, filecode, struct_id, nr, adr) \
596  writestruct_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), nr, adr)
597 
598 #define writelist(wd, filecode, struct_id, lb) \
599  writelist_nr(wd, filecode, SDNA_TYPE_FROM_STRUCT(struct_id), lb)
600 
603 /* -------------------------------------------------------------------- */
613 static void current_screen_compat(Main *mainvar,
614  bool use_active_win,
615  bScreen **r_screen,
616  Scene **r_scene,
617  ViewLayer **r_view_layer)
618 {
619  wmWindowManager *wm;
620  wmWindow *window = NULL;
621 
622  /* find a global current screen in the first open window, to have
623  * a reasonable default for reading in older versions */
624  wm = mainvar->wm.first;
625 
626  if (wm) {
627  if (use_active_win) {
628  /* write the active window into the file, needed for multi-window undo T43424 */
629  for (window = wm->windows.first; window; window = window->next) {
630  if (window->active) {
631  break;
632  }
633  }
634 
635  /* fallback */
636  if (window == NULL) {
637  window = wm->windows.first;
638  }
639  }
640  else {
641  window = wm->windows.first;
642  }
643  }
644 
645  *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : NULL;
646  *r_scene = (window) ? window->scene : NULL;
647  *r_view_layer = (window && *r_scene) ? BKE_view_layer_find(*r_scene, window->view_layer_name) :
648  NULL;
649 }
650 
651 typedef struct RenderInfo {
652  int sfra;
653  int efra;
656 
663 static void write_renderinfo(WriteData *wd, Main *mainvar)
664 {
665  bScreen *curscreen;
666  Scene *curscene = NULL;
667  ViewLayer *view_layer;
668 
669  /* XXX in future, handle multiple windows with multiple screens? */
670  current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer);
671 
672  LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) {
673  if (sce->id.lib == NULL && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) {
675  data.sfra = sce->r.sfra;
676  data.efra = sce->r.efra;
677  memset(data.scene_name, 0, sizeof(data.scene_name));
678 
679  BLI_strncpy(data.scene_name, sce->id.name + 2, sizeof(data.scene_name));
680 
681  writedata(wd, REND, sizeof(data), &data);
682  }
683  }
684 }
685 
686 static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
687 {
688  BLO_write_struct(writer, wmKeyMapItem, kmi);
689  if (kmi->properties) {
690  IDP_BlendWrite(writer, kmi->properties);
691  }
692 }
693 
694 static void write_userdef(BlendWriter *writer, const UserDef *userdef)
695 {
696  writestruct(writer->wd, USER, UserDef, 1, userdef);
697 
698  LISTBASE_FOREACH (const bTheme *, btheme, &userdef->themes) {
699  BLO_write_struct(writer, bTheme, btheme);
700  }
701 
702  LISTBASE_FOREACH (const wmKeyMap *, keymap, &userdef->user_keymaps) {
703  BLO_write_struct(writer, wmKeyMap, keymap);
704 
705  LISTBASE_FOREACH (const wmKeyMapDiffItem *, kmdi, &keymap->diff_items) {
706  BLO_write_struct(writer, wmKeyMapDiffItem, kmdi);
707  if (kmdi->remove_item) {
708  write_keymapitem(writer, kmdi->remove_item);
709  }
710  if (kmdi->add_item) {
711  write_keymapitem(writer, kmdi->add_item);
712  }
713  }
714 
715  LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) {
716  write_keymapitem(writer, kmi);
717  }
718  }
719 
720  LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
721  BLO_write_struct(writer, wmKeyConfigPref, kpt);
722  if (kpt->prop) {
723  IDP_BlendWrite(writer, kpt->prop);
724  }
725  }
726 
727  LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
728  BLO_write_struct(writer, bUserMenu, um);
729  LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
730  if (umi->type == USER_MENU_TYPE_OPERATOR) {
731  const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
732  BLO_write_struct(writer, bUserMenuItem_Op, umi_op);
733  if (umi_op->prop) {
734  IDP_BlendWrite(writer, umi_op->prop);
735  }
736  }
737  else if (umi->type == USER_MENU_TYPE_MENU) {
738  const bUserMenuItem_Menu *umi_mt = (const bUserMenuItem_Menu *)umi;
739  BLO_write_struct(writer, bUserMenuItem_Menu, umi_mt);
740  }
741  else if (umi->type == USER_MENU_TYPE_PROP) {
742  const bUserMenuItem_Prop *umi_pr = (const bUserMenuItem_Prop *)umi;
743  BLO_write_struct(writer, bUserMenuItem_Prop, umi_pr);
744  }
745  else {
746  BLO_write_struct(writer, bUserMenuItem, umi);
747  }
748  }
749  }
750 
751  LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
752  BLO_write_struct(writer, bAddon, bext);
753  if (bext->prop) {
754  IDP_BlendWrite(writer, bext->prop);
755  }
756  }
757 
758  LISTBASE_FOREACH (const bPathCompare *, path_cmp, &userdef->autoexec_paths) {
759  BLO_write_struct(writer, bPathCompare, path_cmp);
760  }
761 
762  LISTBASE_FOREACH (const bUserAssetLibrary *, asset_library, &userdef->asset_libraries) {
763  BLO_write_struct(writer, bUserAssetLibrary, asset_library);
764  }
765 
766  LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
767  BLO_write_struct(writer, uiStyle, style);
768  }
769 }
770 
771 /* Keep it last of write_foodata functions. */
772 static void write_libraries(WriteData *wd, Main *main)
773 {
774  ListBase *lbarray[INDEX_ID_MAX];
775  ID *id;
776  int a, tot;
777  bool found_one;
778 
779  for (; main; main = main->next) {
780  a = tot = set_listbasepointers(main, lbarray);
781 
782  /* test: is lib being used */
783  if (main->curlib && main->curlib->packedfile) {
784  found_one = true;
785  }
786  else if (wd->use_memfile) {
787  /* When writing undo step we always write all existing libraries, makes reading undo step
788  * much easier when dealing with purely indirectly used libraries. */
789  found_one = true;
790  }
791  else {
792  found_one = false;
793  while (!found_one && tot--) {
794  for (id = lbarray[tot]->first; id; id = id->next) {
795  if (id->us > 0 &&
796  ((id->tag & LIB_TAG_EXTERN) ||
798  found_one = true;
799  break;
800  }
801  }
802  }
803  }
804 
805  /* To be able to restore 'quit.blend' and temp saves,
806  * the packed blend has to be in undo buffers... */
807  /* XXX needs rethink, just like save UI in undo files now -
808  * would be nice to append things only for the 'quit.blend' and temp saves. */
809  if (found_one) {
810  /* Not overridable. */
811 
812  BlendWriter writer = {wd};
813  writestruct(wd, ID_LI, Library, 1, main->curlib);
814  BKE_id_blend_write(&writer, &main->curlib->id);
815 
816  if (main->curlib->packedfile) {
817  BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
818  if (wd->use_memfile == false) {
819  printf("write packed .blend: %s\n", main->curlib->filepath);
820  }
821  }
822 
823  /* Write link placeholders for all direct linked IDs. */
824  while (a--) {
825  for (id = lbarray[a]->first; id; id = id->next) {
826  if (id->us > 0 &&
827  ((id->tag & LIB_TAG_EXTERN) ||
830  printf(
831  "ERROR: write file: data-block '%s' from lib '%s' is not linkable "
832  "but is flagged as directly linked",
833  id->name,
834  main->curlib->filepath_abs);
835  BLI_assert(0);
836  }
837  writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id);
838  }
839  }
840  }
841  }
842  }
843 
844  mywrite_flush(wd);
845 }
846 
847 /* context is usually defined by WM, two cases where no WM is available:
848  * - for forward compatibility, curscreen has to be saved
849  * - for undofile, curscene needs to be saved */
850 static void write_global(WriteData *wd, int fileflags, Main *mainvar)
851 {
852  const bool is_undo = wd->use_memfile;
853  FileGlobal fg;
854  bScreen *screen;
855  Scene *scene;
856  ViewLayer *view_layer;
857  char subvstr[8];
858 
859  /* prevent mem checkers from complaining */
860  memset(fg._pad, 0, sizeof(fg._pad));
861  memset(fg.filename, 0, sizeof(fg.filename));
862  memset(fg.build_hash, 0, sizeof(fg.build_hash));
863  fg._pad1 = NULL;
864 
865  current_screen_compat(mainvar, is_undo, &screen, &scene, &view_layer);
866 
867  /* XXX still remap G */
868  fg.curscreen = screen;
869  fg.curscene = scene;
870  fg.cur_view_layer = view_layer;
871 
872  /* prevent to save this, is not good convention, and feature with concerns... */
873  fg.fileflags = (fileflags & ~G_FILE_FLAG_ALL_RUNTIME);
874 
875  fg.globalf = G.f;
876  /* Write information needed for recovery. */
877  if (fileflags & G_FILE_RECOVER_WRITE) {
878  BLI_strncpy(fg.filename, mainvar->name, sizeof(fg.filename));
879  }
880  sprintf(subvstr, "%4d", BLENDER_FILE_SUBVERSION);
881  memcpy(fg.subvstr, subvstr, 4);
882 
886 #ifdef WITH_BUILDINFO
887  {
888  extern unsigned long build_commit_timestamp;
889  extern char build_hash[];
890  /* TODO(sergey): Add branch name to file as well? */
892  BLI_strncpy(fg.build_hash, build_hash, sizeof(fg.build_hash));
893  }
894 #else
895  fg.build_commit_timestamp = 0;
896  BLI_strncpy(fg.build_hash, "unknown", sizeof(fg.build_hash));
897 #endif
898  writestruct(wd, GLOB, FileGlobal, 1, &fg);
899 }
900 
901 /* preview image, first 2 values are width and height
902  * second are an RGBA image (uchar)
903  * note, this uses 'TEST' since new types will segfault on file load for older blender versions.
904  */
905 static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
906 {
907  if (thumb) {
908  writedata(wd, TEST, BLEN_THUMB_MEMSIZE_FILE(thumb->width, thumb->height), thumb);
909  }
910 }
911 
914 /* -------------------------------------------------------------------- */
918 /* if MemFile * there's filesave to memory */
919 static bool write_file_handle(Main *mainvar,
920  WriteWrap *ww,
921  MemFile *compare,
922  MemFile *current,
923  int write_flags,
924  bool use_userdef,
925  const BlendThumbnail *thumb)
926 {
927  BHead bhead;
928  ListBase mainlist;
929  char buf[16];
930  WriteData *wd;
931 
932  blo_split_main(&mainlist, mainvar);
933 
934  wd = mywrite_begin(ww, compare, current);
935  BlendWriter writer = {wd};
936 
937  sprintf(buf,
938  "BLENDER%c%c%.3d",
939  (sizeof(void *) == 8) ? '-' : '_',
940  (ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
942 
943  mywrite(wd, buf, 12);
944 
945  write_renderinfo(wd, mainvar);
946  write_thumb(wd, thumb);
947  write_global(wd, write_flags, mainvar);
948 
949  /* The window-manager and screen often change,
950  * avoid thumbnail detecting changes because of this. */
951  mywrite_flush(wd);
952 
953  OverrideLibraryStorage *override_storage = wd->use_memfile ?
954  NULL :
956 
957 #define ID_BUFFER_STATIC_SIZE 8192
958  /* This outer loop allows to save first data-blocks from real mainvar,
959  * then the temp ones from override process,
960  * if needed, without duplicating whole code. */
961  Main *bmain = mainvar;
962  do {
963  ListBase *lbarray[INDEX_ID_MAX];
964  int a = set_listbasepointers(bmain, lbarray);
965  while (a--) {
966  ID *id = lbarray[a]->first;
967 
968  if (id == NULL || GS(id->name) == ID_LI) {
969  continue; /* Libraries are handled separately below. */
970  }
971 
972  char id_buffer_static[ID_BUFFER_STATIC_SIZE];
973  void *id_buffer = id_buffer_static;
974  const size_t idtype_struct_size = BKE_idtype_get_info_from_id(id)->struct_size;
975  if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
976  BLI_assert(0);
977  id_buffer = MEM_mallocN(idtype_struct_size, __func__);
978  }
979 
980  for (; id; id = id->next) {
981  /* We should never attempt to write non-regular IDs
982  * (i.e. all kind of temp/runtime ones). */
983  BLI_assert(
985 
986  const bool do_override = !ELEM(override_storage, NULL, bmain) &&
988 
989  if (do_override) {
990  BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
991  }
992 
993  if (wd->use_memfile) {
994  /* Record the changes that happened up to this undo push in
995  * recalc_up_to_undo_push, and clear recalc_after_undo_push again
996  * to start accumulating for the next undo push. */
997  id->recalc_up_to_undo_push = id->recalc_after_undo_push;
998  id->recalc_after_undo_push = 0;
999 
1000  bNodeTree *nodetree = ntreeFromID(id);
1001  if (nodetree != NULL) {
1002  nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
1003  nodetree->id.recalc_after_undo_push = 0;
1004  }
1005  if (GS(id->name) == ID_SCE) {
1006  Scene *scene = (Scene *)id;
1007  if (scene->master_collection != NULL) {
1011  }
1012  }
1013  }
1014 
1015  mywrite_id_begin(wd, id);
1016 
1017  memcpy(id_buffer, id, idtype_struct_size);
1018 
1019  ((ID *)id_buffer)->tag = 0;
1020  /* Those listbase data change every time we add/remove an ID, and also often when
1021  * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
1022  * detections between undo steps. */
1023  ((ID *)id_buffer)->prev = NULL;
1024  ((ID *)id_buffer)->next = NULL;
1025 
1026  const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
1027  if (id_type->blend_write != NULL) {
1028  id_type->blend_write(&writer, (ID *)id_buffer, id);
1029  }
1030 
1031  if (do_override) {
1032  BKE_lib_override_library_operations_store_end(override_storage, id);
1033  }
1034 
1035  mywrite_id_end(wd, id);
1036  }
1037 
1038  if (id_buffer != id_buffer_static) {
1039  MEM_SAFE_FREE(id_buffer);
1040  }
1041 
1042  mywrite_flush(wd);
1043  }
1044  } while ((bmain != override_storage) && (bmain = override_storage));
1045 
1046  if (override_storage) {
1048  override_storage = NULL;
1049  }
1050 
1051  /* Special handling, operating over split Mains... */
1052  write_libraries(wd, mainvar->next);
1053 
1054  /* So changes above don't cause a 'DNA1' to be detected as changed on undo. */
1055  mywrite_flush(wd);
1056 
1057  if (use_userdef) {
1058  write_userdef(&writer, &U);
1059  }
1060 
1061  /* Write DNA last, because (to be implemented) test for which structs are written.
1062  *
1063  * Note that we *borrow* the pointer to 'DNAstr',
1064  * so writing each time uses the same address and doesn't cause unnecessary undo overhead. */
1065  writedata(wd, DNA1, (size_t)wd->sdna->data_len, wd->sdna->data);
1066 
1067  /* end of file */
1068  memset(&bhead, 0, sizeof(BHead));
1069  bhead.code = ENDB;
1070  mywrite(wd, &bhead, sizeof(BHead));
1071 
1072  blo_join_main(&mainlist);
1073 
1074  return mywrite_end(wd);
1075 }
1076 
1077 /* do reverse file history: .blend1 -> .blend2, .blend -> .blend1 */
1078 /* return: success(0), failure(1) */
1079 static bool do_history(const char *name, ReportList *reports)
1080 {
1081  char tempname1[FILE_MAX], tempname2[FILE_MAX];
1082  int hisnr = U.versions;
1083 
1084  if (U.versions == 0) {
1085  return 0;
1086  }
1087 
1088  if (strlen(name) < 2) {
1089  BKE_report(reports, RPT_ERROR, "Unable to make version backup: filename too short");
1090  return 1;
1091  }
1092 
1093  while (hisnr > 1) {
1094  BLI_snprintf(tempname1, sizeof(tempname1), "%s%d", name, hisnr - 1);
1095  if (BLI_exists(tempname1)) {
1096  BLI_snprintf(tempname2, sizeof(tempname2), "%s%d", name, hisnr);
1097 
1098  if (BLI_rename(tempname1, tempname2)) {
1099  BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1100  return true;
1101  }
1102  }
1103  hisnr--;
1104  }
1105 
1106  /* is needed when hisnr==1 */
1107  if (BLI_exists(name)) {
1108  BLI_snprintf(tempname1, sizeof(tempname1), "%s%d", name, hisnr);
1109 
1110  if (BLI_rename(name, tempname1)) {
1111  BKE_report(reports, RPT_ERROR, "Unable to make version backup");
1112  return true;
1113  }
1114  }
1115 
1116  return 0;
1117 }
1118 
1121 /* -------------------------------------------------------------------- */
1128 bool BLO_write_file(Main *mainvar,
1129  const char *filepath,
1130  const int write_flags,
1131  const struct BlendFileWriteParams *params,
1132  ReportList *reports)
1133 {
1134  char tempname[FILE_MAX + 1];
1135  eWriteWrapType ww_type;
1136  WriteWrap ww;
1137 
1138  eBLO_WritePathRemap remap_mode = params->remap_mode;
1139  const bool use_save_versions = params->use_save_versions;
1140  const bool use_save_as_copy = params->use_save_as_copy;
1141  const bool use_userdef = params->use_userdef;
1142  const BlendThumbnail *thumb = params->thumb;
1143 
1144  /* path backup/restore */
1145  void *path_list_backup = NULL;
1147 
1148  if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
1149  BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* save to disk");
1150  BLO_main_validate_libraries(mainvar, reports);
1151  BLO_main_validate_shapekeys(mainvar, reports);
1152  }
1153 
1154  /* open temporary file, so we preserve the original in case we crash */
1155  BLI_snprintf(tempname, sizeof(tempname), "%s@", filepath);
1156 
1157  if (write_flags & G_FILE_COMPRESS) {
1158  ww_type = WW_WRAP_ZLIB;
1159  }
1160  else {
1161  ww_type = WW_WRAP_NONE;
1162  }
1163 
1164  ww_handle_init(ww_type, &ww);
1165 
1166  if (ww.open(&ww, tempname) == false) {
1167  BKE_reportf(
1168  reports, RPT_ERROR, "Cannot open file %s for writing: %s", tempname, strerror(errno));
1169  return 0;
1170  }
1171 
1172  /* Remapping of relative paths to new file location. */
1173  if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1174 
1175  if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1176  /* Make all relative as none of the existing paths can be relative in an unsaved document.
1177  */
1178  if (G.relbase_valid == false) {
1179  remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL;
1180  }
1181  }
1182 
1183  char dir_src[FILE_MAX];
1184  char dir_dst[FILE_MAX];
1185  BLI_split_dir_part(mainvar->name, dir_src, sizeof(dir_src));
1186  BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
1187 
1188  /* Just in case there is some subtle difference. */
1189  BLI_path_normalize(mainvar->name, dir_dst);
1190  BLI_path_normalize(mainvar->name, dir_src);
1191 
1192  /* Only for relative, not relative-all, as this means making existing paths relative. */
1193  if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
1194  if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
1195  /* Saved to same path. Nothing to do. */
1196  remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1197  }
1198  }
1199  else if (remap_mode == BLO_WRITE_PATH_REMAP_ABSOLUTE) {
1200  if (G.relbase_valid == false) {
1201  /* Unsaved, all paths are absolute.Even if the user manages to set a relative path,
1202  * there is no base-path that can be used to make it absolute. */
1203  remap_mode = BLO_WRITE_PATH_REMAP_NONE;
1204  }
1205  }
1206 
1207  if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) {
1208  /* Check if we need to backup and restore paths. */
1209  if (UNLIKELY(use_save_as_copy)) {
1210  path_list_backup = BKE_bpath_list_backup(mainvar, path_list_flag);
1211  }
1212 
1213  switch (remap_mode) {
1215  /* Saved, make relative paths relative to new location (if possible). */
1216  BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL);
1217  break;
1219  /* Make all relative (when requested or unsaved). */
1220  BKE_bpath_relative_convert(mainvar, dir_dst, NULL);
1221  break;
1223  /* Make all absolute (when requested or unsaved). */
1224  BKE_bpath_absolute_convert(mainvar, dir_src, NULL);
1225  break;
1227  BLI_assert(0); /* Unreachable. */
1228  break;
1229  }
1230  }
1231  }
1232 
1233  /* actual file writing */
1234  const bool err = write_file_handle(mainvar, &ww, NULL, NULL, write_flags, use_userdef, thumb);
1235 
1236  ww.close(&ww);
1237 
1238  if (UNLIKELY(path_list_backup)) {
1239  BKE_bpath_list_restore(mainvar, path_list_flag, path_list_backup);
1240  BKE_bpath_list_free(path_list_backup);
1241  }
1242 
1243  if (err) {
1244  BKE_report(reports, RPT_ERROR, strerror(errno));
1245  remove(tempname);
1246 
1247  return 0;
1248  }
1249 
1250  /* file save to temporary file was successful */
1251  /* now do reverse file history (move .blend1 -> .blend2, .blend -> .blend1) */
1252  if (use_save_versions) {
1253  const bool err_hist = do_history(filepath, reports);
1254  if (err_hist) {
1255  BKE_report(reports, RPT_ERROR, "Version backup failed (file saved with @)");
1256  return 0;
1257  }
1258  }
1259 
1260  if (BLI_rename(tempname, filepath) != 0) {
1261  BKE_report(reports, RPT_ERROR, "Cannot change old file (file saved with @)");
1262  return 0;
1263  }
1264 
1265  if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
1266  BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* save to disk");
1267  BLO_main_validate_libraries(mainvar, reports);
1268  }
1269 
1270  return 1;
1271 }
1272 
1276 bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
1277 {
1278  bool use_userdef = false;
1279 
1280  const bool err = write_file_handle(
1281  mainvar, NULL, compare, current, write_flags, use_userdef, NULL);
1282 
1283  return (err == 0);
1284 }
1285 
1286 void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
1287 {
1288  writedata(writer->wd, DATA, size_in_bytes, data_ptr);
1289 }
1290 
1291 void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
1292 {
1293  BLO_write_struct_array_by_name(writer, struct_name, 1, data_ptr);
1294 }
1295 
1297  const char *struct_name,
1298  int array_size,
1299  const void *data_ptr)
1300 {
1301  int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1302  if (UNLIKELY(struct_id == -1)) {
1303  printf("error: can't find SDNA code <%s>\n", struct_name);
1304  return;
1305  }
1306  BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr);
1307 }
1308 
1309 void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
1310 {
1311  writestruct_nr(writer->wd, DATA, struct_id, 1, data_ptr);
1312 }
1313 
1315  int struct_id,
1316  const void *address,
1317  const void *data_ptr)
1318 {
1319  BLO_write_struct_at_address_by_id_with_filecode(writer, DATA, struct_id, address, data_ptr);
1320 }
1321 
1323  BlendWriter *writer, int filecode, int struct_id, const void *address, const void *data_ptr)
1324 {
1325  writestruct_at_address_nr(writer->wd, filecode, struct_id, 1, address, data_ptr);
1326 }
1327 
1329  int struct_id,
1330  int array_size,
1331  const void *data_ptr)
1332 {
1333  writestruct_nr(writer->wd, DATA, struct_id, array_size, data_ptr);
1334 }
1335 
1337  BlendWriter *writer, int struct_id, int array_size, const void *address, const void *data_ptr)
1338 {
1339  writestruct_at_address_nr(writer->wd, DATA, struct_id, array_size, address, data_ptr);
1340 }
1341 
1342 void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list)
1343 {
1344  writelist_nr(writer->wd, DATA, struct_id, list);
1345 }
1346 
1347 void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
1348 {
1349  int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
1350  if (UNLIKELY(struct_id == -1)) {
1351  printf("error: can't find SDNA code <%s>\n", struct_name);
1352  return;
1353  }
1354  BLO_write_struct_list_by_id(writer, struct_id, list);
1355 }
1356 
1357 void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
1358 {
1359  writestruct_at_address_nr(writer->wd, GS(id->name), struct_id, 1, id_address, id);
1360 }
1361 
1362 int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name)
1363 {
1364  int struct_id = DNA_struct_find_nr(writer->wd->sdna, struct_name);
1365  return struct_id;
1366 }
1367 
1368 void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
1369 {
1370  BLO_write_raw(writer, sizeof(int32_t) * (size_t)num, data_ptr);
1371 }
1372 
1373 void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
1374 {
1375  BLO_write_raw(writer, sizeof(uint32_t) * (size_t)num, data_ptr);
1376 }
1377 
1378 void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
1379 {
1380  BLO_write_raw(writer, sizeof(float) * (size_t)num, data_ptr);
1381 }
1382 
1383 void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
1384 {
1385  BLO_write_raw(writer, sizeof(double) * (size_t)num, data_ptr);
1386 }
1387 
1388 void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
1389 {
1390  BLO_write_raw(writer, sizeof(void *) * (size_t)num, data_ptr);
1391 }
1392 
1393 void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
1394 {
1395  BLO_write_raw(writer, sizeof(float[3]) * (size_t)num, data_ptr);
1396 }
1397 
1401 void BLO_write_string(BlendWriter *writer, const char *data_ptr)
1402 {
1403  if (data_ptr != NULL) {
1404  BLO_write_raw(writer, strlen(data_ptr) + 1, data_ptr);
1405  }
1406 }
1407 
1413 {
1414  return writer->wd->use_memfile;
1415 }
1416 
#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:162
void BKE_bpath_list_restore(struct Main *bmain, const int flag, void *ls_handle)
Definition: bpath.c:882
void BKE_bpath_relative_convert(struct Main *bmain, const char *basedir, struct ReportList *reports)
Definition: bpath.c:224
void BKE_bpath_list_free(void *ls_handle)
Definition: bpath.c:889
@ BKE_BPATH_TRAVERSE_SKIP_MULTIFILE
Definition: BKE_bpath.h:70
@ BKE_BPATH_TRAVERSE_SKIP_LIBRARY
Definition: BKE_bpath.h:63
void * BKE_bpath_list_backup(struct Main *bmain, const int flag)
Definition: bpath.c:873
void BKE_bpath_absolute_convert(struct Main *bmain, const char *basedir, struct ReportList *reports)
Definition: bpath.c:276
@ G_FILE_RECOVER_WRITE
Definition: BKE_global.h:190
@ G_FILE_COMPRESS
Definition: BKE_global.h:167
#define B_ENDIAN
Definition: BKE_global.h:208
@ G_DEBUG_IO
Definition: BKE_global.h:152
#define ENDIAN_ORDER
Definition: BKE_global.h:213
#define G_FILE_FLAG_ALL_RUNTIME
Definition: BKE_global.h:200
void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop)
bool BKE_idtype_idcode_is_linkable(const short idcode)
Definition: idtype.c:232
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:2395
#define MAIN_ID_SESSION_UUID_UNSET
Definition: BKE_lib_id.h:73
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[])
struct bNodeTree * ntreeFromID(struct ID *id)
Definition: node.cc:3147
void BKE_packedfile_blend_write(struct BlendWriter *writer, struct PackedFile *pf)
Definition: packedFile.c:876
void BKE_report(ReportList *reports, ReportType type, const char *message)
Definition: report.c:104
void BKE_reportf(ReportList *reports, ReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
struct bScreen * BKE_workspace_active_screen_get(const struct WorkSpaceInstanceHook *hook) GETTER_ATTRS
#define BLI_assert(a)
Definition: BLI_assert.h:58
int BLI_open(const char *filename, int oflag, int pmode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1017
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
#define O_BINARY
Definition: BLI_fileops.h:182
int BLI_rename(const char *from, const char *to) ATTR_NONNULL()
Definition: fileops.c:1381
void * BLI_gzopen(const char *filename, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:1010
void * BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:803
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
Definition: path_util.c:1682
#define FILE_MAX
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:173
#define BLI_path_cmp
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
unsigned char uchar
Definition: BLI_sys_types.h:86
unsigned int uint
Definition: BLI_sys_types.h:83
#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)
Utils 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:157
void BLO_memfile_write_init(MemFileWriteData *mem_data, MemFile *written_memfile, MemFile *reference_memfile)
Definition: undofile.c:116
void BLO_memfile_write_finalize(MemFileWriteData *mem_data)
Definition: undofile.c:150
external writefile function prototypes.
eBLO_WritePathRemap
Definition: BLO_writefile.h:41
@ BLO_WRITE_PATH_REMAP_NONE
Definition: BLO_writefile.h:43
@ BLO_WRITE_PATH_REMAP_RELATIVE_ALL
Definition: BLO_writefile.h:47
@ BLO_WRITE_PATH_REMAP_ABSOLUTE
Definition: BLO_writefile.h:49
@ BLO_WRITE_PATH_REMAP_RELATIVE
Definition: BLO_writefile.h:45
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition: DNA_ID.h:438
@ LIB_TAG_NO_USER_REFCOUNT
Definition: DNA_ID.h:573
@ LIB_TAG_NOT_ALLOCATED
Definition: DNA_ID.h:576
@ LIB_TAG_INDIRECT
Definition: DNA_ID.h:524
@ LIB_TAG_EXTERN
Definition: DNA_ID.h:521
@ LIB_TAG_NO_MAIN
Definition: DNA_ID.h:572
#define MAX_ID_NAME
Definition: DNA_ID.h:269
@ LIB_INDIRECT_WEAK_LINK
Definition: DNA_ID.h:488
@ INDEX_ID_MAX
Definition: DNA_ID.h:859
@ ID_LI
Definition: DNA_ID_enums.h:58
@ ID_SCE
Definition: DNA_ID_enums.h:57
#define ID_LINK_PLACEHOLDER
Definition: DNA_ID_enums.h:100
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:597
#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)
char build_hash[]
Definition: buildinfo.c:47
ulong build_commit_timestamp
Definition: buildinfo.c:48
unsigned int U
Definition: btGjkEpa3.h:78
FILE * file
Scene scene
static FT_Error err
Definition: freetypefont.c:52
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
#define GS(x)
Definition: iris.c:241
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
int main(int argc, char **argv)
Definition: msgfmt.c:457
static unsigned a[3]
Definition: RandGen.cpp:92
static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
Definition: png.c:86
void blo_split_main(ListBase *mainlist, Main *main)
Definition: readfile.c:496
void blo_join_main(ListBase *mainlist)
Definition: readfile.c:462
unsigned int uint32_t
Definition: stdint.h:83
signed int int32_t
Definition: stdint.h:80
int SDNAnr
const void * old
WriteData * wd
Definition: writefile.c:281
struct ViewLayer * cur_view_layer
struct Scene * curscene
uint64_t build_commit_timestamp
struct bScreen * curscreen
char filename[1024]
IDTypeBlendWriteFunction blend_write
Definition: BKE_idtype.h:196
size_t struct_size
Definition: BKE_idtype.h:135
Definition: DNA_ID.h:273
int tag
Definition: DNA_ID.h:292
int recalc_after_undo_push
Definition: DNA_ID.h:306
int us
Definition: DNA_ID.h:293
short flag
Definition: DNA_ID.h:288
unsigned int session_uuid
Definition: DNA_ID.h:312
void * next
Definition: DNA_ID.h:274
char name[66]
Definition: DNA_ID.h:283
int recalc_up_to_undo_push
Definition: DNA_ID.h:305
void * first
Definition: DNA_listBase.h:47
Definition: BKE_main.h:116
ListBase scenes
Definition: BKE_main.h:146
ListBase wm
Definition: BKE_main.h:175
struct MainLock * lock
Definition: BKE_main.h:194
struct Main * next
Definition: BKE_main.h:117
char name[1024]
Definition: BKE_main.h:118
uint id_session_uuid
Definition: BLO_undofile.h:43
struct GHash * id_session_uuid_mapping
Definition: BLO_undofile.h:59
MemFileChunk * reference_current_chunk
Definition: BLO_undofile.h:56
uint current_id_session_uuid
Definition: BLO_undofile.h:55
char scene_name[MAX_ID_NAME - 2]
Definition: writefile.c:654
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:265
WriteWrap * ww
Definition: writefile.c:277
uchar * buf
Definition: writefile.c:255
bool use_memfile
Definition: writefile.c:270
const struct SDNA * sdna
Definition: writefile.c:252
MemFileWriteData mem
Definition: writefile.c:268
size_t buf_used_len
Definition: writefile.c:257
bool(* close)(WriteWrap *ww)
Definition: writefile.c:156
size_t(* write)(WriteWrap *ww, const char *data, size_t data_len)
Definition: writefile.c:157
union WriteWrap::@130 _user_data
int file_handle
Definition: writefile.c:164
bool(* open)(WriteWrap *ww, const char *filepath)
Definition: writefile.c:155
bool use_buf
Definition: writefile.c:160
gzFile gz_handle
Definition: writefile.c:165
struct IDProperty * prop
struct Scene * scene
char view_layer_name[64]
struct wmWindow * next
struct WorkSpaceInstanceHook * workspace_hook
#define G(x, y, z)
uint len
void BLO_write_double_array(BlendWriter *writer, uint num, const double *data_ptr)
Definition: writefile.c:1383
void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr)
Definition: writefile.c:1373
static void mywrite_id_begin(WriteData *wd, ID *id)
Definition: writefile.c:452
static void write_thumb(WriteData *wd, const BlendThumbnail *thumb)
Definition: writefile.c:905
static WriteData * mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
Definition: writefile.c:412
#define MYWRITE_BUFFER_SIZE
Definition: writefile.c:137
#define writestruct(wd, filecode, struct_id, nr, adr)
Definition: writefile.c:595
void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr)
Definition: writefile.c:1393
static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
Definition: writefile.c:562
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:1322
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:498
static bool ww_open_none(WriteWrap *ww, const char *filepath)
Definition: writefile.c:172
bool BLO_write_file(Main *mainvar, const char *filepath, const int write_flags, const struct BlendFileWriteParams *params, ReportList *reports)
Definition: writefile.c:1128
void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
Definition: writefile.c:1291
static void writedata_free(WriteData *wd)
Definition: writefile.c:325
void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr)
Definition: writefile.c:1368
static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi)
Definition: writefile.c:686
static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
Definition: writefile.c:189
void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
Definition: writefile.c:1357
struct BlendWriter BlendWriter
static void write_renderinfo(WriteData *wd, Main *mainvar)
Definition: writefile.c:663
static bool ww_close_zlib(WriteWrap *ww)
Definition: writefile.c:211
static void write_userdef(BlendWriter *writer, const UserDef *userdef)
Definition: writefile.c:694
static void write_libraries(WriteData *wd, Main *main)
Definition: writefile.c:772
#define ID_BUFFER_STATIC_SIZE
static size_t ww_write_zlib(WriteWrap *ww, const char *buf, size_t buf_len)
Definition: writefile.c:215
void BLO_write_struct_array_by_id(BlendWriter *writer, int struct_id, int array_size, const void *data_ptr)
Definition: writefile.c:1328
eWriteWrapType
Definition: writefile.c:147
@ WW_WRAP_ZLIB
Definition: writefile.c:149
@ WW_WRAP_NONE
Definition: writefile.c:148
static void write_global(WriteData *wd, int fileflags, Main *mainvar)
Definition: writefile.c:850
static WriteData * writedata_new(WriteWrap *ww)
Definition: writefile.c:284
void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list)
Definition: writefile.c:1342
static void writestruct_nr(WriteData *wd, int filecode, const int struct_nr, int nr, const void *adr)
Definition: writefile.c:527
void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr)
Definition: writefile.c:1378
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
Definition: writefile.c:1401
static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww)
Definition: writefile.c:223
int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name)
Definition: writefile.c:1362
void BLO_write_struct_array_by_name(BlendWriter *writer, const char *struct_name, int array_size, const void *data_ptr)
Definition: writefile.c:1296
static bool mywrite_end(WriteData *wd)
Definition: writefile.c:430
static void mywrite_flush(WriteData *wd)
Definition: writefile.c:343
static bool ww_open_zlib(WriteWrap *ww, const char *filepath)
Definition: writefile.c:198
static bool do_history(const char *name, ReportList *reports)
Definition: writefile.c:1079
static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
Definition: writefile.c:534
#define FILE_HANDLE(ww)
Definition: writefile.c:196
void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
Definition: writefile.c:1286
void BLO_write_struct_at_address_by_id(BlendWriter *writer, int struct_id, const void *address, const void *data_ptr)
Definition: writefile.c:1314
static void current_screen_compat(Main *mainvar, bool use_active_win, bScreen **r_screen, Scene **r_scene, ViewLayer **r_view_layer)
Definition: writefile.c:613
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:1336
static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
Definition: writefile.c:299
bool BLO_write_is_undo(BlendWriter *writer)
Definition: writefile.c:1412
static void mywrite(WriteData *wd, const void *adr, size_t len)
Definition: writefile.c:356
bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int write_flags)
Definition: writefile.c:1276
static void mywrite_id_end(WriteData *wd, ID *UNUSED(id))
Definition: writefile.c:482
static bool ww_close_none(WriteWrap *ww)
Definition: writefile.c:185
#define MYWRITE_MAX_CHUNK
Definition: writefile.c:138
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:919
void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
Definition: writefile.c:1309
void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
Definition: writefile.c:1347
void BLO_write_pointer_array(BlendWriter *writer, uint num, const void *data_ptr)
Definition: writefile.c:1388
struct RenderInfo RenderInfo