Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenloader/intern/writefile.c')
-rw-r--r--source/blender/blenloader/intern/writefile.c833
1 files changed, 562 insertions, 271 deletions
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 599e592c77d..73bab07a008 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -133,6 +133,7 @@
#include "DNA_sdna_types.h"
#include "DNA_sequence_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_simulation_types.h"
#include "DNA_sound_types.h"
#include "DNA_space_types.h"
#include "DNA_speaker_types.h"
@@ -156,14 +157,17 @@
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_fcurve.h"
+#include "BKE_fcurve_driver.h"
#include "BKE_global.h" // for G
#include "BKE_gpencil_modifier.h"
#include "BKE_idtype.h"
#include "BKE_layer.h"
+#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
+#include "BKE_object.h"
#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_sequencer.h"
@@ -173,6 +177,7 @@
#include "BLO_blend_defs.h"
#include "BLO_blend_validate.h"
+#include "BLO_read_write.h"
#include "BLO_readfile.h"
#include "BLO_undofile.h"
#include "BLO_writefile.h"
@@ -323,12 +328,7 @@ typedef struct {
bool error;
/** #MemFile writing (used for undo). */
- struct {
- MemFile *current;
- MemFile *compare;
- /** Use to de-duplicate chunks when writing. */
- MemFileChunk *compare_chunk;
- } mem;
+ MemFileWriteData mem;
/** When true, write to #WriteData.current, could also call 'is_undo'. */
bool use_memfile;
@@ -340,6 +340,10 @@ typedef struct {
WriteWrap *ww;
} WriteData;
+typedef struct BlendWriter {
+ WriteData *wd;
+} BlendWriter;
+
static WriteData *writedata_new(WriteWrap *ww)
{
WriteData *wd = MEM_callocN(sizeof(*wd), "writedata");
@@ -367,7 +371,7 @@ static void writedata_do_write(WriteData *wd, const void *mem, int memlen)
/* memory based save */
if (wd->use_memfile) {
- memfile_chunk_add(wd->mem.current, mem, memlen, &wd->mem.compare_chunk);
+ BLO_memfile_chunk_add(&wd->mem, mem, memlen);
}
else {
if (wd->ww->write(wd->ww, mem, memlen) != memlen) {
@@ -468,9 +472,7 @@ static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *curren
WriteData *wd = writedata_new(ww);
if (current != NULL) {
- wd->mem.current = current;
- wd->mem.compare = compare;
- wd->mem.compare_chunk = compare ? compare->chunks.first : NULL;
+ BLO_memfile_write_init(&wd->mem, current, compare);
wd->use_memfile = true;
}
@@ -490,12 +492,58 @@ static bool mywrite_end(WriteData *wd)
wd->buf_used_len = 0;
}
+ if (wd->use_memfile) {
+ BLO_memfile_write_finalize(&wd->mem);
+ }
+
const bool err = wd->error;
writedata_free(wd);
return err;
}
+/**
+ * Start writing of data related to a single ID.
+ *
+ * Only does something when storing an undo step.
+ */
+static void mywrite_id_begin(WriteData *wd, ID *id)
+{
+ if (wd->use_memfile) {
+ wd->mem.current_id_session_uuid = id->session_uuid;
+
+ /* If current next memchunk does not match the ID we are about to write, try to find the
+ * correct memchunk in the mapping using ID's session_uuid. */
+ if (wd->mem.id_session_uuid_mapping != NULL &&
+ (wd->mem.reference_current_chunk == NULL ||
+ wd->mem.reference_current_chunk->id_session_uuid != id->session_uuid)) {
+ void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping,
+ POINTER_FROM_UINT(id->session_uuid));
+ if (ref != NULL) {
+ wd->mem.reference_current_chunk = ref;
+ }
+ /* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */
+ }
+ /* Otherwise, we try with the current memchunk in any case, whether it is matching current
+ * ID's session_uuid or not. */
+ }
+}
+
+/**
+ * Start writing of data related to a single ID.
+ *
+ * Only does something when storing an undo step.
+ */
+static void mywrite_id_end(WriteData *wd, ID *UNUSED(id))
+{
+ if (wd->use_memfile) {
+ /* Very important to do it after every ID write now, otherwise we cannot know whether a
+ * specific ID changed or not. */
+ mywrite_flush(wd);
+ wd->mem.current_id_session_uuid = MAIN_ID_SESSION_UUID_UNSET;
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -631,79 +679,86 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co
* These functions are used by blender's .blend system for file saving/loading.
* \{ */
-void IDP_WriteProperty_OnlyData(const IDProperty *prop, void *wd);
-void IDP_WriteProperty(const IDProperty *prop, void *wd);
+void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
+void IDP_WriteProperty(const IDProperty *prop, WriteData *wd);
+void IDP_WriteProperty_new_api(const IDProperty *prop, BlendWriter *writer);
-static void IDP_WriteArray(const IDProperty *prop, void *wd)
+static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
{
/*REMEMBER to set totalen to len in the linking code!!*/
if (prop->data.pointer) {
- writedata(wd, DATA, MEM_allocN_len(prop->data.pointer), prop->data.pointer);
+ BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer);
if (prop->subtype == IDP_GROUP) {
IDProperty **array = prop->data.pointer;
int a;
for (a = 0; a < prop->len; a++) {
- IDP_WriteProperty(array[a], wd);
+ IDP_WriteProperty_new_api(array[a], writer);
}
}
}
}
-static void IDP_WriteIDPArray(const IDProperty *prop, void *wd)
+static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer)
{
/*REMEMBER to set totalen to len in the linking code!!*/
if (prop->data.pointer) {
const IDProperty *array = prop->data.pointer;
int a;
- writestruct(wd, DATA, IDProperty, prop->len, array);
+ BLO_write_struct_array(writer, IDProperty, prop->len, array);
for (a = 0; a < prop->len; a++) {
- IDP_WriteProperty_OnlyData(&array[a], wd);
+ IDP_WriteProperty_OnlyData(&array[a], writer);
}
}
}
-static void IDP_WriteString(const IDProperty *prop, void *wd)
+static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer)
{
/*REMEMBER to set totalen to len in the linking code!!*/
- writedata(wd, DATA, prop->len, prop->data.pointer);
+ BLO_write_raw(writer, prop->len, prop->data.pointer);
}
-static void IDP_WriteGroup(const IDProperty *prop, void *wd)
+static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer)
{
IDProperty *loop;
for (loop = prop->data.group.first; loop; loop = loop->next) {
- IDP_WriteProperty(loop, wd);
+ IDP_WriteProperty_new_api(loop, writer);
}
}
/* Functions to read/write ID Properties */
-void IDP_WriteProperty_OnlyData(const IDProperty *prop, void *wd)
+void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
{
switch (prop->type) {
case IDP_GROUP:
- IDP_WriteGroup(prop, wd);
+ IDP_WriteGroup(prop, writer);
break;
case IDP_STRING:
- IDP_WriteString(prop, wd);
+ IDP_WriteString(prop, writer);
break;
case IDP_ARRAY:
- IDP_WriteArray(prop, wd);
+ IDP_WriteArray(prop, writer);
break;
case IDP_IDPARRAY:
- IDP_WriteIDPArray(prop, wd);
+ IDP_WriteIDPArray(prop, writer);
break;
}
}
-void IDP_WriteProperty(const IDProperty *prop, void *wd)
+void IDP_WriteProperty_new_api(const IDProperty *prop, BlendWriter *writer)
{
- writestruct(wd, DATA, IDProperty, 1, prop);
- IDP_WriteProperty_OnlyData(prop, wd);
+ BLO_write_struct(writer, IDProperty, prop);
+ IDP_WriteProperty_OnlyData(prop, writer);
+}
+
+void IDP_WriteProperty(const IDProperty *prop, WriteData *wd)
+{
+ BlendWriter writer = {wd};
+ IDP_WriteProperty_new_api(prop, &writer);
}
static void write_iddata(WriteData *wd, ID *id)
@@ -717,13 +772,11 @@ static void write_iddata(WriteData *wd, ID *id)
writestruct(wd, DATA, IDOverrideLibrary, 1, id->override_library);
writelist(wd, DATA, IDOverrideLibraryProperty, &id->override_library->properties);
- for (IDOverrideLibraryProperty *op = id->override_library->properties.first; op;
- op = op->next) {
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
writedata(wd, DATA, strlen(op->rna_path) + 1, op->rna_path);
writelist(wd, DATA, IDOverrideLibraryPropertyOperation, &op->operations);
- for (IDOverrideLibraryPropertyOperation *opop = op->operations.first; opop;
- opop = opop->next) {
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->subitem_reference_name) {
writedata(
wd, DATA, strlen(opop->subitem_reference_name) + 1, opop->subitem_reference_name);
@@ -734,11 +787,6 @@ static void write_iddata(WriteData *wd, ID *id)
}
}
}
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- if (wd->use_memfile) {
- id->recalc_undo_accumulated = 0;
- }
}
static void write_previews(WriteData *wd, const PreviewImage *prv_orig)
@@ -859,19 +907,19 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves)
}
}
-static void write_action(WriteData *wd, bAction *act)
+static void write_action(WriteData *wd, bAction *act, const void *id_address)
{
if (act->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_AC, bAction, 1, act);
+ writestruct_at_address(wd, ID_AC, bAction, 1, id_address, act);
write_iddata(wd, &act->id);
write_fcurves(wd, &act->curves);
- for (bActionGroup *grp = act->groups.first; grp; grp = grp->next) {
+ LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) {
writestruct(wd, DATA, bActionGroup, 1, grp);
}
- for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) {
+ LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
writestruct(wd, DATA, TimeMarker, 1, marker);
}
}
@@ -996,9 +1044,19 @@ static void write_node_socket_default_value(WriteData *wd, bNodeSocket *sock)
case SOCK_STRING:
writestruct(wd, DATA, bNodeSocketValueString, 1, sock->default_value);
break;
+ case SOCK_OBJECT:
+ writestruct(wd, DATA, bNodeSocketValueObject, 1, sock->default_value);
+ break;
+ case SOCK_IMAGE:
+ writestruct(wd, DATA, bNodeSocketValueImage, 1, sock->default_value);
+ break;
case __SOCK_MESH:
case SOCK_CUSTOM:
case SOCK_SHADER:
+ case SOCK_EMITTERS:
+ case SOCK_EVENTS:
+ case SOCK_FORCES:
+ case SOCK_CONTROL_FLOW:
BLI_assert(false);
break;
}
@@ -1139,11 +1197,6 @@ static void write_nodetree_nolib(WriteData *wd, bNodeTree *ntree)
for (sock = ntree->outputs.first; sock; sock = sock->next) {
write_node_socket_interface(wd, sock);
}
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- if (wd->use_memfile) {
- ntree->id.recalc_undo_accumulated = 0;
- }
}
/**
@@ -1235,14 +1288,14 @@ static void write_userdef(WriteData *wd, const UserDef *userdef)
{
writestruct(wd, USER, UserDef, 1, userdef);
- for (const bTheme *btheme = userdef->themes.first; btheme; btheme = btheme->next) {
+ LISTBASE_FOREACH (const bTheme *, btheme, &userdef->themes) {
writestruct(wd, DATA, bTheme, 1, btheme);
}
- for (const wmKeyMap *keymap = userdef->user_keymaps.first; keymap; keymap = keymap->next) {
+ LISTBASE_FOREACH (const wmKeyMap *, keymap, &userdef->user_keymaps) {
writestruct(wd, DATA, wmKeyMap, 1, keymap);
- for (const wmKeyMapDiffItem *kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
+ LISTBASE_FOREACH (const wmKeyMapDiffItem *, kmdi, &keymap->diff_items) {
writestruct(wd, DATA, wmKeyMapDiffItem, 1, kmdi);
if (kmdi->remove_item) {
write_keymapitem(wd, kmdi->remove_item);
@@ -1252,21 +1305,21 @@ static void write_userdef(WriteData *wd, const UserDef *userdef)
}
}
- for (const wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) {
+ LISTBASE_FOREACH (const wmKeyMapItem *, kmi, &keymap->items) {
write_keymapitem(wd, kmi);
}
}
- for (const wmKeyConfigPref *kpt = userdef->user_keyconfig_prefs.first; kpt; kpt = kpt->next) {
+ LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) {
writestruct(wd, DATA, wmKeyConfigPref, 1, kpt);
if (kpt->prop) {
IDP_WriteProperty(kpt->prop, wd);
}
}
- for (const bUserMenu *um = userdef->user_menus.first; um; um = um->next) {
+ LISTBASE_FOREACH (const bUserMenu *, um, &userdef->user_menus) {
writestruct(wd, DATA, bUserMenu, 1, um);
- for (const bUserMenuItem *umi = um->items.first; umi; umi = umi->next) {
+ LISTBASE_FOREACH (const bUserMenuItem *, umi, &um->items) {
if (umi->type == USER_MENU_TYPE_OPERATOR) {
const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi;
writestruct(wd, DATA, bUserMenuItem_Op, 1, umi_op);
@@ -1288,19 +1341,18 @@ static void write_userdef(WriteData *wd, const UserDef *userdef)
}
}
- for (const bAddon *bext = userdef->addons.first; bext; bext = bext->next) {
+ LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) {
writestruct(wd, DATA, bAddon, 1, bext);
if (bext->prop) {
IDP_WriteProperty(bext->prop, wd);
}
}
- for (const bPathCompare *path_cmp = userdef->autoexec_paths.first; path_cmp;
- path_cmp = path_cmp->next) {
+ LISTBASE_FOREACH (const bPathCompare *, path_cmp, &userdef->autoexec_paths) {
writestruct(wd, DATA, bPathCompare, 1, path_cmp);
}
- for (const uiStyle *style = userdef->uistyles.first; style; style = style->next) {
+ LISTBASE_FOREACH (const uiStyle *, style, &userdef->uistyles) {
writestruct(wd, DATA, uiStyle, 1, style);
}
}
@@ -1396,11 +1448,11 @@ static void write_pointcaches(WriteData *wd, ListBase *ptcaches)
}
}
-static void write_particlesettings(WriteData *wd, ParticleSettings *part)
+static void write_particlesettings(WriteData *wd, ParticleSettings *part, const void *id_address)
{
if (part->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_PA, ParticleSettings, 1, part);
+ writestruct_at_address(wd, ID_PA, ParticleSettings, 1, id_address, part);
write_iddata(wd, &part->id);
if (part->adt) {
@@ -1420,7 +1472,7 @@ static void write_particlesettings(WriteData *wd, ParticleSettings *part)
write_curvemapping(wd, part->twistcurve);
}
- for (ParticleDupliWeight *dw = part->instance_weights.first; dw; dw = dw->next) {
+ LISTBASE_FOREACH (ParticleDupliWeight *, dw, &part->instance_weights) {
/* update indices, but only if dw->ob is set (can be NULL after loading e.g.) */
if (dw->ob != NULL) {
dw->index = 0;
@@ -1440,7 +1492,7 @@ static void write_particlesettings(WriteData *wd, ParticleSettings *part)
if (part->boids && part->phystype == PART_PHYS_BOIDS) {
writestruct(wd, DATA, BoidSettings, 1, part->boids);
- for (BoidState *state = part->boids->states.first; state; state = state->next) {
+ LISTBASE_FOREACH (BoidState *, state, &part->boids->states) {
write_boid_state(wd, state);
}
}
@@ -1625,14 +1677,14 @@ static void write_pose(WriteData *wd, bPose *pose)
static void write_defgroups(WriteData *wd, ListBase *defbase)
{
- for (bDeformGroup *defgroup = defbase->first; defgroup; defgroup = defgroup->next) {
+ LISTBASE_FOREACH (bDeformGroup *, defgroup, defbase) {
writestruct(wd, DATA, bDeformGroup, 1, defgroup);
}
}
static void write_fmaps(WriteData *wd, ListBase *fbase)
{
- for (bFaceMap *fmap = fbase->first; fmap; fmap = fmap->next) {
+ LISTBASE_FOREACH (bFaceMap *, fmap, fbase) {
writestruct(wd, DATA, bFaceMap, 1, fmap);
}
}
@@ -1646,7 +1698,7 @@ static void write_modifiers(WriteData *wd, ListBase *modbase)
}
for (md = modbase->first; md; md = md->next) {
- const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
if (mti == NULL) {
return;
}
@@ -1829,7 +1881,7 @@ static void write_gpencil_modifiers(WriteData *wd, ListBase *modbase)
}
for (md = modbase->first; md; md = md->next) {
- const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type);
+ const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type);
if (mti == NULL) {
return;
}
@@ -1896,7 +1948,7 @@ static void write_shaderfxs(WriteData *wd, ListBase *fxbase)
}
for (fx = fxbase->first; fx; fx = fx->next) {
- const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(fx->type);
+ const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type);
if (fxi == NULL) {
return;
}
@@ -1905,11 +1957,14 @@ static void write_shaderfxs(WriteData *wd, ListBase *fxbase)
}
}
-static void write_object(WriteData *wd, Object *ob)
+static void write_object(WriteData *wd, Object *ob, const void *id_address)
{
if (ob->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BKE_object_runtime_reset(ob);
+
/* write LibData */
- writestruct(wd, ID_OB, Object, 1, ob);
+ writestruct_at_address(wd, ID_OB, Object, 1, id_address, ob);
write_iddata(wd, &ob->id);
if (ob->adt) {
@@ -1970,11 +2025,15 @@ static void write_object(WriteData *wd, Object *ob)
}
}
-static void write_vfont(WriteData *wd, VFont *vf)
+static void write_vfont(WriteData *wd, VFont *vf, const void *id_address)
{
if (vf->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ vf->data = NULL;
+ vf->temp_pf = NULL;
+
/* write LibData */
- writestruct(wd, ID_VF, VFont, 1, vf);
+ writestruct_at_address(wd, ID_VF, VFont, 1, id_address, vf);
write_iddata(wd, &vf->id);
/* direct data */
@@ -1986,11 +2045,11 @@ static void write_vfont(WriteData *wd, VFont *vf)
}
}
-static void write_key(WriteData *wd, Key *key)
+static void write_key(WriteData *wd, Key *key, const void *id_address)
{
if (key->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_KE, Key, 1, key);
+ writestruct_at_address(wd, ID_KE, Key, 1, id_address, key);
write_iddata(wd, &key->id);
if (key->adt) {
@@ -1998,7 +2057,7 @@ static void write_key(WriteData *wd, Key *key)
}
/* direct data */
- for (KeyBlock *kb = key->block.first; kb; kb = kb->next) {
+ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
writestruct(wd, DATA, KeyBlock, 1, kb);
if (kb->data) {
writedata(wd, DATA, kb->totelem * key->elemsize, kb->data);
@@ -2007,28 +2066,36 @@ static void write_key(WriteData *wd, Key *key)
}
}
-static void write_camera(WriteData *wd, Camera *cam)
+static void write_camera(WriteData *wd, Camera *cam, const void *id_address)
{
if (cam->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_CA, Camera, 1, cam);
+ writestruct_at_address(wd, ID_CA, Camera, 1, id_address, cam);
write_iddata(wd, &cam->id);
if (cam->adt) {
write_animdata(wd, cam->adt);
}
- for (CameraBGImage *bgpic = cam->bg_images.first; bgpic; bgpic = bgpic->next) {
+ LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
writestruct(wd, DATA, CameraBGImage, 1, bgpic);
}
}
}
-static void write_mball(WriteData *wd, MetaBall *mb)
+static void write_mball(WriteData *wd, MetaBall *mb, const void *id_address)
{
if (mb->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&mb->disp);
+ mb->editelems = NULL;
+ /* Must always be cleared (meta's don't have their own edit-data). */
+ mb->needs_flush_to_id = 0;
+ mb->lastelem = NULL;
+ mb->batch_cache = NULL;
+
/* write LibData */
- writestruct(wd, ID_MB, MetaBall, 1, mb);
+ writestruct_at_address(wd, ID_MB, MetaBall, 1, id_address, mb);
write_iddata(wd, &mb->id);
/* direct data */
@@ -2037,17 +2104,22 @@ static void write_mball(WriteData *wd, MetaBall *mb)
write_animdata(wd, mb->adt);
}
- for (MetaElem *ml = mb->elems.first; ml; ml = ml->next) {
+ LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) {
writestruct(wd, DATA, MetaElem, 1, ml);
}
}
}
-static void write_curve(WriteData *wd, Curve *cu)
+static void write_curve(WriteData *wd, Curve *cu, const void *id_address)
{
if (cu->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ cu->editnurb = NULL;
+ cu->editfont = NULL;
+ cu->batch_cache = NULL;
+
/* write LibData */
- writestruct(wd, ID_CU, Curve, 1, cu);
+ writestruct_at_address(wd, ID_CU, Curve, 1, id_address, cu);
write_iddata(wd, &cu->id);
/* direct data */
@@ -2063,10 +2135,10 @@ static void write_curve(WriteData *wd, Curve *cu)
}
else {
/* is also the order of reading */
- for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
+ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
writestruct(wd, DATA, Nurb, 1, nu);
}
- for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
+ LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) {
if (nu->type == CU_BEZIER) {
writestruct(wd, DATA, BezTriple, nu->pntsu, nu->bezt);
}
@@ -2186,7 +2258,7 @@ static void write_customdata(WriteData *wd,
datasize = structnum * count;
writestruct_id(wd, DATA, structname, datasize, layer->data);
}
- else {
+ else if (!wd->use_memfile) { /* Do not warn on undo. */
printf("%s error: layer '%s':%d - can't be written to file\n",
__func__,
structname,
@@ -2200,19 +2272,14 @@ static void write_customdata(WriteData *wd,
}
}
-static void write_mesh(WriteData *wd, Mesh *mesh)
+static void write_mesh(WriteData *wd, Mesh *mesh, const void *id_address)
{
if (mesh->id.us > 0 || wd->use_memfile) {
- /* Write a copy of the mesh with possibly reduced number of data layers.
- * Don't edit the original since other threads might be reading it. */
- Mesh *old_mesh = mesh;
- Mesh copy_mesh = *mesh;
- mesh = &copy_mesh;
-
/* cache only - don't write */
mesh->mface = NULL;
mesh->totface = 0;
memset(&mesh->fdata, 0, sizeof(mesh->fdata));
+ memset(&mesh->runtime, 0, sizeof(mesh->runtime));
/* Reduce xdata layers, fill xlayers with layers to be written.
* This makes xdata invalid for Blender, which is why we made a
@@ -2229,7 +2296,7 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff));
CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
- writestruct_at_address(wd, ID_ME, Mesh, 1, old_mesh, mesh);
+ writestruct_at_address(wd, ID_ME, Mesh, 1, id_address, mesh);
write_iddata(wd, &mesh->id);
/* direct data */
@@ -2247,9 +2314,6 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
write_customdata(wd, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask);
write_customdata(wd, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask);
- /* restore pointer */
- mesh = old_mesh;
-
/* free temporary data */
if (vlayers && vlayers != vlayers_buff) {
MEM_freeN(vlayers);
@@ -2269,11 +2333,15 @@ static void write_mesh(WriteData *wd, Mesh *mesh)
}
}
-static void write_lattice(WriteData *wd, Lattice *lt)
+static void write_lattice(WriteData *wd, Lattice *lt, const void *id_address)
{
if (lt->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ lt->editlatt = NULL;
+ lt->batch_cache = NULL;
+
/* write LibData */
- writestruct(wd, ID_LT, Lattice, 1, lt);
+ writestruct_at_address(wd, ID_LT, Lattice, 1, id_address, lt);
write_iddata(wd, &lt->id);
/* write animdata */
@@ -2288,7 +2356,7 @@ static void write_lattice(WriteData *wd, Lattice *lt)
}
}
-static void write_image(WriteData *wd, Image *ima)
+static void write_image(WriteData *wd, Image *ima, const void *id_address)
{
if (ima->id.us > 0 || wd->use_memfile) {
ImagePackedFile *imapf;
@@ -2301,7 +2369,7 @@ static void write_image(WriteData *wd, Image *ima)
}
/* write LibData */
- writestruct(wd, ID_IM, Image, 1, ima);
+ writestruct_at_address(wd, ID_IM, Image, 1, id_address, ima);
write_iddata(wd, &ima->id);
for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
@@ -2315,7 +2383,7 @@ static void write_image(WriteData *wd, Image *ima)
write_previews(wd, ima->preview);
- for (ImageView *iv = ima->views.first; iv; iv = iv->next) {
+ LISTBASE_FOREACH (ImageView *, iv, &ima->views) {
writestruct(wd, DATA, ImageView, 1, iv);
}
writestruct(wd, DATA, Stereo3dFormat, 1, ima->stereo3d_format);
@@ -2328,11 +2396,11 @@ static void write_image(WriteData *wd, Image *ima)
}
}
-static void write_texture(WriteData *wd, Tex *tex)
+static void write_texture(WriteData *wd, Tex *tex, const void *id_address)
{
if (tex->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_TE, Tex, 1, tex);
+ writestruct_at_address(wd, ID_TE, Tex, 1, id_address, tex);
write_iddata(wd, &tex->id);
if (tex->adt) {
@@ -2354,11 +2422,15 @@ static void write_texture(WriteData *wd, Tex *tex)
}
}
-static void write_material(WriteData *wd, Material *ma)
+static void write_material(WriteData *wd, Material *ma, const void *id_address)
{
if (ma->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ ma->texpaintslot = NULL;
+ BLI_listbase_clear(&ma->gpumaterial);
+
/* write LibData */
- writestruct(wd, ID_MA, Material, 1, ma);
+ writestruct_at_address(wd, ID_MA, Material, 1, id_address, ma);
write_iddata(wd, &ma->id);
if (ma->adt) {
@@ -2380,11 +2452,14 @@ static void write_material(WriteData *wd, Material *ma)
}
}
-static void write_world(WriteData *wd, World *wrld)
+static void write_world(WriteData *wd, World *wrld, const void *id_address)
{
if (wrld->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&wrld->gpumaterial);
+
/* write LibData */
- writestruct(wd, ID_WO, World, 1, wrld);
+ writestruct_at_address(wd, ID_WO, World, 1, id_address, wrld);
write_iddata(wd, &wrld->id);
if (wrld->adt) {
@@ -2401,11 +2476,11 @@ static void write_world(WriteData *wd, World *wrld)
}
}
-static void write_light(WriteData *wd, Light *la)
+static void write_light(WriteData *wd, Light *la, const void *id_address)
{
if (la->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_LA, Light, 1, la);
+ writestruct_at_address(wd, ID_LA, Light, 1, id_address, la);
write_iddata(wd, &la->id);
if (la->adt) {
@@ -2431,25 +2506,26 @@ static void write_collection_nolib(WriteData *wd, Collection *collection)
/* Shared function for collection data-blocks and scene master collection. */
write_previews(wd, collection->preview);
- for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
+ LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
writestruct(wd, DATA, CollectionObject, 1, cob);
}
- for (CollectionChild *child = collection->children.first; child; child = child->next) {
+ LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
writestruct(wd, DATA, CollectionChild, 1, child);
}
-
- /* Clear the accumulated recalc flags in case of undo step saving. */
- if (wd->use_memfile) {
- collection->id.recalc_undo_accumulated = 0;
- }
}
-static void write_collection(WriteData *wd, Collection *collection)
+static void write_collection(WriteData *wd, Collection *collection, const void *id_address)
{
if (collection->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
+ collection->tag = 0;
+ BLI_listbase_clear(&collection->object_cache);
+ BLI_listbase_clear(&collection->parents);
+
/* write LibData */
- writestruct(wd, ID_GR, Collection, 1, collection);
+ writestruct_at_address(wd, ID_GR, Collection, 1, id_address, collection);
write_iddata(wd, &collection->id);
write_collection_nolib(wd, collection);
@@ -2507,7 +2583,7 @@ static void write_paint(WriteData *wd, Paint *p)
static void write_layer_collections(WriteData *wd, ListBase *lb)
{
- for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
+ LISTBASE_FOREACH (LayerCollection *, lc, lb) {
writestruct(wd, DATA, LayerCollection, 1, lc);
write_layer_collections(wd, &lc->layer_collections);
@@ -2523,12 +2599,11 @@ static void write_view_layer(WriteData *wd, ViewLayer *view_layer)
IDP_WriteProperty(view_layer->id_properties, wd);
}
- for (FreestyleModuleConfig *fmc = view_layer->freestyle_config.modules.first; fmc;
- fmc = fmc->next) {
+ LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) {
writestruct(wd, DATA, FreestyleModuleConfig, 1, fmc);
}
- for (FreestyleLineSet *fls = view_layer->freestyle_config.linesets.first; fls; fls = fls->next) {
+ LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) {
writestruct(wd, DATA, FreestyleLineSet, 1, fls);
}
write_layer_collections(wd, &view_layer->layer_collections);
@@ -2564,10 +2639,16 @@ static void write_lightcache(WriteData *wd, LightCache *cache)
writestruct(wd, DATA, LightProbeCache, cache->cube_len, cache->cube_data);
}
-static void write_scene(WriteData *wd, Scene *sce)
+static void write_scene(WriteData *wd, Scene *sce, const void *id_address)
{
+ if (wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ /* XXX This UI data should not be stored in Scene at all... */
+ memset(&sce->cursor, 0, sizeof(sce->cursor));
+ }
+
/* write LibData */
- writestruct(wd, ID_SCE, Scene, 1, sce);
+ writestruct_at_address(wd, ID_SCE, Scene, 1, id_address, sce);
write_iddata(wd, &sce->id);
if (sce->adt) {
@@ -2714,7 +2795,7 @@ static void write_scene(WriteData *wd, Scene *sce)
SEQ_END;
/* new; meta stack too, even when its nasty restore code */
- for (MetaStack *ms = ed->metastack.first; ms; ms = ms->next) {
+ LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) {
writestruct(wd, DATA, MetaStack, 1, ms);
}
}
@@ -2733,17 +2814,17 @@ static void write_scene(WriteData *wd, Scene *sce)
}
/* writing dynamic list of TimeMarkers to the blend file */
- for (TimeMarker *marker = sce->markers.first; marker; marker = marker->next) {
+ LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
writestruct(wd, DATA, TimeMarker, 1, marker);
}
/* writing dynamic list of TransformOrientations to the blend file */
- for (TransformOrientation *ts = sce->transform_spaces.first; ts; ts = ts->next) {
+ LISTBASE_FOREACH (TransformOrientation *, ts, &sce->transform_spaces) {
writestruct(wd, DATA, TransformOrientation, 1, ts);
}
/* writing MultiView to the blend file */
- for (SceneRenderView *srv = sce->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &sce->r.views) {
writestruct(wd, DATA, SceneRenderView, 1, srv);
}
@@ -2769,7 +2850,7 @@ static void write_scene(WriteData *wd, Scene *sce)
write_previews(wd, sce->preview);
write_curvemapping_curves(wd, &sce->r.mblur_shutter_curve);
- for (ViewLayer *view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
write_view_layer(wd, view_layer);
}
@@ -2790,11 +2871,19 @@ static void write_scene(WriteData *wd, Scene *sce)
BLI_assert(sce->layer_properties == NULL);
}
-static void write_gpencil(WriteData *wd, bGPdata *gpd)
+static void write_gpencil(WriteData *wd, bGPdata *gpd, const void *id_address)
{
if (gpd->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed data-blocks. */
+ /* XXX not sure why the whole run-time data is not cleared in reading code,
+ * for now mimicking it here. */
+ gpd->runtime.sbuffer = NULL;
+ gpd->runtime.sbuffer_used = 0;
+ gpd->runtime.sbuffer_size = 0;
+ gpd->runtime.tot_cp_points = 0;
+
/* write gpd data block to file */
- writestruct(wd, ID_GD, bGPdata, 1, gpd);
+ writestruct_at_address(wd, ID_GD, bGPdata, 1, id_address, gpd);
write_iddata(wd, &gpd->id);
if (gpd->adt) {
@@ -2914,35 +3003,33 @@ static void write_soops(WriteData *wd, SpaceOutliner *so)
static void write_panel_list(WriteData *wd, ListBase *lb)
{
- for (Panel *pa = lb->first; pa; pa = pa->next) {
- writestruct(wd, DATA, Panel, 1, pa);
- write_panel_list(wd, &pa->children);
+ LISTBASE_FOREACH (Panel *, panel, lb) {
+ writestruct(wd, DATA, Panel, 1, panel);
+ write_panel_list(wd, &panel->children);
}
}
static void write_area_regions(WriteData *wd, ScrArea *area)
{
- for (ARegion *region = area->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
write_region(wd, region, area->spacetype);
write_panel_list(wd, &region->panels);
- for (PanelCategoryStack *pc_act = region->panels_category_active.first; pc_act;
- pc_act = pc_act->next) {
+ LISTBASE_FOREACH (PanelCategoryStack *, pc_act, &region->panels_category_active) {
writestruct(wd, DATA, PanelCategoryStack, 1, pc_act);
}
- for (uiList *ui_list = region->ui_lists.first; ui_list; ui_list = ui_list->next) {
+ LISTBASE_FOREACH (uiList *, ui_list, &region->ui_lists) {
write_uilist(wd, ui_list);
}
- for (uiPreview *ui_preview = region->ui_previews.first; ui_preview;
- ui_preview = ui_preview->next) {
+ LISTBASE_FOREACH (uiPreview *, ui_preview, &region->ui_previews) {
writestruct(wd, DATA, uiPreview, 1, ui_preview);
}
}
- for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
- for (ARegion *region = sl->regionbase.first; region; region = region->next) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) {
write_region(wd, region, sl->spacetype);
}
@@ -3055,7 +3142,7 @@ static void write_area_map(WriteData *wd, ScrAreaMap *area_map)
{
writelist(wd, DATA, ScrVert, &area_map->vertbase);
writelist(wd, DATA, ScrEdge, &area_map->edgebase);
- for (ScrArea *area = area_map->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &area_map->areabase) {
area->butspacetype = area->spacetype; /* Just for compatibility, will be reset below. */
writestruct(wd, DATA, ScrArea, 1, area);
@@ -3070,13 +3157,13 @@ static void write_area_map(WriteData *wd, ScrAreaMap *area_map)
}
}
-static void write_windowmanager(WriteData *wd, wmWindowManager *wm)
+static void write_windowmanager(BlendWriter *writer, wmWindowManager *wm, const void *id_address)
{
- writestruct(wd, ID_WM, wmWindowManager, 1, wm);
- write_iddata(wd, &wm->id);
- write_wm_xr_data(wd, &wm->xr);
+ BLO_write_id_struct(writer, wmWindowManager, id_address, &wm->id);
+ write_iddata(writer->wd, &wm->id);
+ write_wm_xr_data(writer->wd, &wm->xr);
- for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
#ifndef WITH_GLOBAL_AREA_WRITING
/* Don't write global areas yet, while we make changes to them. */
ScrAreaMap global_areas = win->global_areas;
@@ -3086,12 +3173,12 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm)
/* update deprecated screen member (for so loading in 2.7x uses the correct screen) */
win->screen = BKE_workspace_active_screen_get(win->workspace_hook);
- writestruct(wd, DATA, wmWindow, 1, win);
- writestruct(wd, DATA, WorkSpaceInstanceHook, 1, win->workspace_hook);
- writestruct(wd, DATA, Stereo3dFormat, 1, win->stereo3d_format);
+ BLO_write_struct(writer, wmWindow, win);
+ BLO_write_struct(writer, WorkSpaceInstanceHook, win->workspace_hook);
+ BLO_write_struct(writer, Stereo3dFormat, win->stereo3d_format);
#ifdef WITH_GLOBAL_AREA_WRITING
- write_area_map(wd, &win->global_areas);
+ write_area_map(writer->wd, &win->global_areas);
#else
win->global_areas = global_areas;
#endif
@@ -3101,19 +3188,19 @@ static void write_windowmanager(WriteData *wd, wmWindowManager *wm)
}
}
-static void write_screen(WriteData *wd, bScreen *sc)
+static void write_screen(WriteData *wd, bScreen *screen, const void *id_address)
{
/* Screens are reference counted, only saved if used by a workspace. */
- if (sc->id.us > 0 || wd->use_memfile) {
+ if (screen->id.us > 0 || wd->use_memfile) {
/* write LibData */
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
- writestruct(wd, ID_SCRN, bScreen, 1, sc);
- write_iddata(wd, &sc->id);
+ writestruct_at_address(wd, ID_SCRN, bScreen, 1, id_address, screen);
+ write_iddata(wd, &screen->id);
- write_previews(wd, sc->preview);
+ write_previews(wd, screen->preview);
/* direct data */
- write_area_map(wd, AREAMAP_FROM_SCREEN(sc));
+ write_area_map(wd, AREAMAP_FROM_SCREEN(screen));
}
}
@@ -3132,15 +3219,22 @@ static void write_bone(WriteData *wd, Bone *bone)
}
/* Write Children */
- for (Bone *cbone = bone->childbase.first; cbone; cbone = cbone->next) {
+ LISTBASE_FOREACH (Bone *, cbone, &bone->childbase) {
write_bone(wd, cbone);
}
}
-static void write_armature(WriteData *wd, bArmature *arm)
+static void write_armature(WriteData *wd, bArmature *arm, const void *id_address)
{
if (arm->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_AR, bArmature, 1, arm);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ arm->bonehash = NULL;
+ arm->edbo = NULL;
+ /* Must always be cleared (armatures don't have their own edit-data). */
+ arm->needs_flush_to_id = 0;
+ arm->act_edbone = NULL;
+
+ writestruct_at_address(wd, ID_AR, bArmature, 1, id_address, arm);
write_iddata(wd, &arm->id);
if (arm->adt) {
@@ -3148,20 +3242,24 @@ static void write_armature(WriteData *wd, bArmature *arm)
}
/* Direct data */
- for (Bone *bone = arm->bonebase.first; bone; bone = bone->next) {
+ LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
write_bone(wd, bone);
}
}
}
-static void write_text(WriteData *wd, Text *text)
+static void write_text(WriteData *wd, Text *text, const void *id_address)
{
+ /* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
if ((text->flags & TXT_ISMEM) && (text->flags & TXT_ISEXT)) {
text->flags &= ~TXT_ISEXT;
}
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ text->compiled = NULL;
+
/* write LibData */
- writestruct(wd, ID_TXT, Text, 1, text);
+ writestruct_at_address(wd, ID_TXT, Text, 1, id_address, text);
write_iddata(wd, &text->id);
if (text->name) {
@@ -3170,21 +3268,21 @@ static void write_text(WriteData *wd, Text *text)
if (!(text->flags & TXT_ISEXT)) {
/* now write the text data, in two steps for optimization in the readfunction */
- for (TextLine *tmp = text->lines.first; tmp; tmp = tmp->next) {
+ LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
writestruct(wd, DATA, TextLine, 1, tmp);
}
- for (TextLine *tmp = text->lines.first; tmp; tmp = tmp->next) {
+ LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
writedata(wd, DATA, tmp->len + 1, tmp->line);
}
}
}
-static void write_speaker(WriteData *wd, Speaker *spk)
+static void write_speaker(WriteData *wd, Speaker *spk, const void *id_address)
{
if (spk->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_SPK, Speaker, 1, spk);
+ writestruct_at_address(wd, ID_SPK, Speaker, 1, id_address, spk);
write_iddata(wd, &spk->id);
if (spk->adt) {
@@ -3193,11 +3291,17 @@ static void write_speaker(WriteData *wd, Speaker *spk)
}
}
-static void write_sound(WriteData *wd, bSound *sound)
+static void write_sound(WriteData *wd, bSound *sound, const void *id_address)
{
if (sound->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ sound->tags = 0;
+ sound->handle = NULL;
+ sound->playback_handle = NULL;
+ sound->spinlock = NULL;
+
/* write LibData */
- writestruct(wd, ID_SO, bSound, 1, sound);
+ writestruct_at_address(wd, ID_SO, bSound, 1, id_address, sound);
write_iddata(wd, &sound->id);
if (sound->packedfile) {
@@ -3208,11 +3312,11 @@ static void write_sound(WriteData *wd, bSound *sound)
}
}
-static void write_probe(WriteData *wd, LightProbe *prb)
+static void write_probe(WriteData *wd, LightProbe *prb, const void *id_address)
{
if (prb->id.us > 0 || wd->use_memfile) {
/* write LibData */
- writestruct(wd, ID_LP, LightProbe, 1, prb);
+ writestruct_at_address(wd, ID_LP, LightProbe, 1, id_address, prb);
write_iddata(wd, &prb->id);
if (prb->adt) {
@@ -3221,10 +3325,18 @@ static void write_probe(WriteData *wd, LightProbe *prb)
}
}
-static void write_nodetree(WriteData *wd, bNodeTree *ntree)
+static void write_nodetree(WriteData *wd, bNodeTree *ntree, const void *id_address)
{
if (ntree->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_NT, bNodeTree, 1, ntree);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ ntree->init = 0; /* to set callbacks and force setting types */
+ ntree->is_updating = false;
+ ntree->typeinfo = NULL;
+ ntree->interface_type = NULL;
+ ntree->progress = NULL;
+ ntree->execdata = NULL;
+
+ writestruct_at_address(wd, ID_NT, bNodeTree, 1, id_address, ntree);
/* Note that trees directly used by other IDs (materials etc.) are not 'real' ID, they cannot
* be linked, etc., so we write actual id data here only, for 'real' ID trees. */
write_iddata(wd, &ntree->id);
@@ -3233,10 +3345,10 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree)
}
}
-static void write_brush(WriteData *wd, Brush *brush)
+static void write_brush(WriteData *wd, Brush *brush, const void *id_address)
{
if (brush->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_BR, Brush, 1, brush);
+ writestruct_at_address(wd, ID_BR, Brush, 1, id_address, brush);
write_iddata(wd, &brush->id);
if (brush->curve) {
@@ -3255,6 +3367,24 @@ static void write_brush(WriteData *wd, Brush *brush)
if (brush->gpencil_settings->curve_jitter) {
write_curvemapping(wd, brush->gpencil_settings->curve_jitter);
}
+ if (brush->gpencil_settings->curve_rand_pressure) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_pressure);
+ }
+ if (brush->gpencil_settings->curve_rand_strength) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_strength);
+ }
+ if (brush->gpencil_settings->curve_rand_uv) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_uv);
+ }
+ if (brush->gpencil_settings->curve_rand_hue) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_hue);
+ }
+ if (brush->gpencil_settings->curve_rand_saturation) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_saturation);
+ }
+ if (brush->gpencil_settings->curve_rand_value) {
+ write_curvemapping(wd, brush->gpencil_settings->curve_rand_value);
+ }
}
if (brush->gradient) {
writestruct(wd, DATA, ColorBand, 1, brush->gradient);
@@ -3262,11 +3392,11 @@ static void write_brush(WriteData *wd, Brush *brush)
}
}
-static void write_palette(WriteData *wd, Palette *palette)
+static void write_palette(WriteData *wd, Palette *palette, const void *id_address)
{
if (palette->id.us > 0 || wd->use_memfile) {
PaletteColor *color;
- writestruct(wd, ID_PAL, Palette, 1, palette);
+ writestruct_at_address(wd, ID_PAL, Palette, 1, id_address, palette);
write_iddata(wd, &palette->id);
for (color = palette->colors.first; color; color = color->next) {
@@ -3275,10 +3405,10 @@ static void write_palette(WriteData *wd, Palette *palette)
}
}
-static void write_paintcurve(WriteData *wd, PaintCurve *pc)
+static void write_paintcurve(WriteData *wd, PaintCurve *pc, const void *id_address)
{
if (pc->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_PC, PaintCurve, 1, pc);
+ writestruct_at_address(wd, ID_PC, PaintCurve, 1, id_address, pc);
write_iddata(wd, &pc->id);
writestruct(wd, DATA, PaintCurvePoint, pc->tot_points, pc->points);
@@ -3324,13 +3454,18 @@ static void write_movieReconstruction(WriteData *wd, MovieTrackingReconstruction
}
}
-static void write_movieclip(WriteData *wd, MovieClip *clip)
+static void write_movieclip(WriteData *wd, MovieClip *clip, const void *id_address)
{
if (clip->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ clip->anim = NULL;
+ clip->tracking_context = NULL;
+ clip->tracking.stats = NULL;
+
MovieTracking *tracking = &clip->tracking;
MovieTrackingObject *object;
- writestruct(wd, ID_MC, MovieClip, 1, clip);
+ writestruct_at_address(wd, ID_MC, MovieClip, 1, id_address, clip);
write_iddata(wd, &clip->id);
if (clip->adt) {
@@ -3354,12 +3489,12 @@ static void write_movieclip(WriteData *wd, MovieClip *clip)
}
}
-static void write_mask(WriteData *wd, Mask *mask)
+static void write_mask(WriteData *wd, Mask *mask, const void *id_address)
{
if (mask->id.us > 0 || wd->use_memfile) {
MaskLayer *masklay;
- writestruct(wd, ID_MSK, Mask, 1, mask);
+ writestruct_at_address(wd, ID_MSK, Mask, 1, id_address, mask);
write_iddata(wd, &mask->id);
if (mask->adt) {
@@ -3661,10 +3796,10 @@ static void write_linestyle_geometry_modifiers(WriteData *wd, ListBase *modifier
}
}
-static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle)
+static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle, const void *id_address)
{
if (linestyle->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_LS, FreestyleLineStyle, 1, linestyle);
+ writestruct_at_address(wd, ID_LS, FreestyleLineStyle, 1, id_address, linestyle);
write_iddata(wd, &linestyle->id);
if (linestyle->adt) {
@@ -3687,10 +3822,16 @@ static void write_linestyle(WriteData *wd, FreestyleLineStyle *linestyle)
}
}
-static void write_cachefile(WriteData *wd, CacheFile *cache_file)
+static void write_cachefile(WriteData *wd, CacheFile *cache_file, const void *id_address)
{
if (cache_file->id.us > 0 || wd->use_memfile) {
- writestruct(wd, ID_CF, CacheFile, 1, cache_file);
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ BLI_listbase_clear(&cache_file->object_paths);
+ cache_file->handle = NULL;
+ memset(cache_file->handle_filepath, 0, sizeof(cache_file->handle_filepath));
+ cache_file->handle_readers = NULL;
+
+ writestruct_at_address(wd, ID_CF, CacheFile, 1, id_address, cache_file);
if (cache_file->adt) {
write_animdata(wd, cache_file->adt);
@@ -3698,39 +3839,31 @@ static void write_cachefile(WriteData *wd, CacheFile *cache_file)
}
}
-static void write_workspace(WriteData *wd, WorkSpace *workspace)
+static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const void *id_address)
{
- ListBase *layouts = BKE_workspace_layouts_get(workspace);
-
- writestruct(wd, ID_WS, WorkSpace, 1, workspace);
- write_iddata(wd, &workspace->id);
- writelist(wd, DATA, WorkSpaceLayout, layouts);
- writelist(wd, DATA, WorkSpaceDataRelation, &workspace->hook_layout_relations);
- writelist(wd, DATA, wmOwnerID, &workspace->owner_ids);
- writelist(wd, DATA, bToolRef, &workspace->tools);
- for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
+ BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id);
+ write_iddata(writer->wd, &workspace->id);
+ BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts);
+ BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations);
+ BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids);
+ BLO_write_struct_list(writer, bToolRef, &workspace->tools);
+ LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
if (tref->properties) {
- IDP_WriteProperty(tref->properties, wd);
+ IDP_WriteProperty_new_api(tref->properties, writer);
}
}
}
-static void write_hair(WriteData *wd, Hair *hair)
+static void write_hair(WriteData *wd, Hair *hair, const void *id_address)
{
if (hair->id.us > 0 || wd->use_memfile) {
- /* Write a copy of the hair with possibly reduced number of data layers.
- * Don't edit the original since other threads might be reading it. */
- Hair *old_hair = hair;
- Hair copy_hair = *hair;
- hair = &copy_hair;
-
CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE];
CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
/* Write LibData */
- writestruct_at_address(wd, ID_HA, Hair, 1, old_hair, hair);
+ writestruct_at_address(wd, ID_HA, Hair, 1, id_address, hair);
write_iddata(wd, &hair->id);
/* Direct data */
@@ -3748,27 +3881,18 @@ static void write_hair(WriteData *wd, Hair *hair)
if (clayers && clayers != clayers_buff) {
MEM_freeN(clayers);
}
-
- /* restore pointer */
- hair = old_hair;
}
}
-static void write_pointcloud(WriteData *wd, PointCloud *pointcloud)
+static void write_pointcloud(WriteData *wd, PointCloud *pointcloud, const void *id_address)
{
if (pointcloud->id.us > 0 || wd->use_memfile) {
- /* Write a copy of the pointcloud with possibly reduced number of data layers.
- * Don't edit the original since other threads might be reading it. */
- PointCloud *old_pointcloud = pointcloud;
- PointCloud copy_pointcloud = *pointcloud;
- pointcloud = &copy_pointcloud;
-
CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
CustomData_file_write_prepare(
&pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
/* Write LibData */
- writestruct_at_address(wd, ID_PT, PointCloud, 1, old_pointcloud, pointcloud);
+ writestruct_at_address(wd, ID_PT, PointCloud, 1, id_address, pointcloud);
write_iddata(wd, &pointcloud->id);
/* Direct data */
@@ -3786,11 +3910,14 @@ static void write_pointcloud(WriteData *wd, PointCloud *pointcloud)
}
}
-static void write_volume(WriteData *wd, Volume *volume)
+static void write_volume(WriteData *wd, Volume *volume, const void *id_address)
{
if (volume->id.us > 0 || wd->use_memfile) {
+ /* Clean up, important in undo case to reduce false detection of changed datablocks. */
+ volume->runtime.grids = 0;
+
/* write LibData */
- writestruct(wd, ID_VO, Volume, 1, volume);
+ writestruct_at_address(wd, ID_VO, Volume, 1, id_address, volume);
write_iddata(wd, &volume->id);
/* direct data */
@@ -3807,6 +3934,24 @@ static void write_volume(WriteData *wd, Volume *volume)
}
}
+static void write_simulation(WriteData *wd, Simulation *simulation)
+{
+ if (simulation->id.us > 0 || wd->use_memfile) {
+ writestruct(wd, ID_SIM, Simulation, 1, simulation);
+ write_iddata(wd, &simulation->id);
+
+ if (simulation->adt) {
+ write_animdata(wd, simulation->adt);
+ }
+
+ /* nodetree is integral part of simulation, no libdata */
+ if (simulation->nodetree) {
+ writestruct(wd, DATA, bNodeTree, 1, simulation->nodetree);
+ write_nodetree_nolib(wd, simulation->nodetree);
+ }
+ }
+}
+
/* Keep it last of write_foodata functions. */
static void write_libraries(WriteData *wd, Main *main)
{
@@ -3822,6 +3967,11 @@ static void write_libraries(WriteData *wd, Main *main)
if (main->curlib && main->curlib->packedfile) {
found_one = true;
}
+ else if (wd->use_memfile) {
+ /* When writing undo step we always write all existing libraries, makes reading undo step
+ * much easier when dealing with purely indirectly used libraries. */
+ found_one = true;
+ }
else {
found_one = false;
while (!found_one && tot--) {
@@ -3909,12 +4059,12 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
fg.globalf = G.f;
BLI_strncpy(fg.filename, mainvar->name, sizeof(fg.filename));
- sprintf(subvstr, "%4d", BLENDER_SUBVERSION);
+ sprintf(subvstr, "%4d", BLENDER_FILE_SUBVERSION);
memcpy(fg.subvstr, subvstr, 4);
- fg.subversion = BLENDER_SUBVERSION;
- fg.minversion = BLENDER_MINVERSION;
- fg.minsubversion = BLENDER_MINSUBVERSION;
+ fg.subversion = BLENDER_FILE_SUBVERSION;
+ fg.minversion = BLENDER_FILE_MIN_VERSION;
+ fg.minsubversion = BLENDER_FILE_MIN_SUBVERSION;
#ifdef WITH_BUILDINFO
{
extern unsigned long build_commit_timestamp;
@@ -3968,7 +4118,7 @@ static bool write_file_handle(Main *mainvar,
"BLENDER%c%c%.3d",
(sizeof(void *) == 8) ? '-' : '_',
(ENDIAN_ORDER == B_ENDIAN) ? 'V' : 'v',
- BLENDER_VERSION);
+ BLENDER_FILE_VERSION);
mywrite(wd, buf, 12);
@@ -3983,6 +4133,7 @@ static bool write_file_handle(Main *mainvar,
OverrideLibraryStorage *override_storage =
wd->use_memfile ? NULL : BKE_lib_override_library_operations_store_initialize();
+#define ID_BUFFER_STATIC_SIZE 8192
/* This outer loop allows to save first data-blocks from real mainvar,
* then the temp ones from override process,
* if needed, without duplicating whole code. */
@@ -3993,10 +4144,18 @@ static bool write_file_handle(Main *mainvar,
while (a--) {
ID *id = lbarray[a]->first;
- if (id && GS(id->name) == ID_LI) {
+ if (id == NULL || GS(id->name) == ID_LI) {
continue; /* Libraries are handled separately below. */
}
+ char id_buffer_static[ID_BUFFER_STATIC_SIZE];
+ void *id_buffer = id_buffer_static;
+ const size_t idtype_struct_size = BKE_idtype_get_info_from_id(id)->struct_size;
+ if (idtype_struct_size > ID_BUFFER_STATIC_SIZE) {
+ BLI_assert(0);
+ id_buffer = MEM_mallocN(idtype_struct_size, __func__);
+ }
+
for (; id; id = id->next) {
/* We should never attempt to write non-regular IDs
* (i.e. all kind of temp/runtime ones). */
@@ -4009,117 +4168,155 @@ static bool write_file_handle(Main *mainvar,
BKE_lib_override_library_operations_store_start(bmain, override_storage, id);
}
+ if (wd->use_memfile) {
+ /* Record the changes that happened up to this undo push in
+ * recalc_up_to_undo_push, and clear recalc_after_undo_push again
+ * to start accumulating for the next undo push. */
+ id->recalc_up_to_undo_push = id->recalc_after_undo_push;
+ id->recalc_after_undo_push = 0;
+
+ bNodeTree *nodetree = ntreeFromID(id);
+ if (nodetree != NULL) {
+ nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
+ nodetree->id.recalc_after_undo_push = 0;
+ }
+ if (GS(id->name) == ID_SCE) {
+ Scene *scene = (Scene *)id;
+ if (scene->master_collection != NULL) {
+ scene->master_collection->id.recalc_up_to_undo_push =
+ scene->master_collection->id.recalc_after_undo_push;
+ scene->master_collection->id.recalc_after_undo_push = 0;
+ }
+ }
+ }
+
+ mywrite_id_begin(wd, id);
+
+ memcpy(id_buffer, id, idtype_struct_size);
+
+ ((ID *)id_buffer)->tag = 0;
+ /* Those listbase data change every time we add/remove an ID, and also often when renaming
+ * one (due to re-sorting). This avoids generating a lot of false 'is changed' detections
+ * between undo steps. */
+ ((ID *)id_buffer)->prev = NULL;
+ ((ID *)id_buffer)->next = NULL;
+
+ BlendWriter writer = {wd};
+
switch ((ID_Type)GS(id->name)) {
case ID_WM:
- write_windowmanager(wd, (wmWindowManager *)id);
+ write_windowmanager(&writer, (wmWindowManager *)id_buffer, id);
break;
case ID_WS:
- write_workspace(wd, (WorkSpace *)id);
+ write_workspace(&writer, (WorkSpace *)id_buffer, id);
break;
case ID_SCR:
- write_screen(wd, (bScreen *)id);
+ write_screen(wd, (bScreen *)id_buffer, id);
break;
case ID_MC:
- write_movieclip(wd, (MovieClip *)id);
+ write_movieclip(wd, (MovieClip *)id_buffer, id);
break;
case ID_MSK:
- write_mask(wd, (Mask *)id);
+ write_mask(wd, (Mask *)id_buffer, id);
break;
case ID_SCE:
- write_scene(wd, (Scene *)id);
+ write_scene(wd, (Scene *)id_buffer, id);
break;
case ID_CU:
- write_curve(wd, (Curve *)id);
+ write_curve(wd, (Curve *)id_buffer, id);
break;
case ID_MB:
- write_mball(wd, (MetaBall *)id);
+ write_mball(wd, (MetaBall *)id_buffer, id);
break;
case ID_IM:
- write_image(wd, (Image *)id);
+ write_image(wd, (Image *)id_buffer, id);
break;
case ID_CA:
- write_camera(wd, (Camera *)id);
+ write_camera(wd, (Camera *)id_buffer, id);
break;
case ID_LA:
- write_light(wd, (Light *)id);
+ write_light(wd, (Light *)id_buffer, id);
break;
case ID_LT:
- write_lattice(wd, (Lattice *)id);
+ write_lattice(wd, (Lattice *)id_buffer, id);
break;
case ID_VF:
- write_vfont(wd, (VFont *)id);
+ write_vfont(wd, (VFont *)id_buffer, id);
break;
case ID_KE:
- write_key(wd, (Key *)id);
+ write_key(wd, (Key *)id_buffer, id);
break;
case ID_WO:
- write_world(wd, (World *)id);
+ write_world(wd, (World *)id_buffer, id);
break;
case ID_TXT:
- write_text(wd, (Text *)id);
+ write_text(wd, (Text *)id_buffer, id);
break;
case ID_SPK:
- write_speaker(wd, (Speaker *)id);
+ write_speaker(wd, (Speaker *)id_buffer, id);
break;
case ID_LP:
- write_probe(wd, (LightProbe *)id);
+ write_probe(wd, (LightProbe *)id_buffer, id);
break;
case ID_SO:
- write_sound(wd, (bSound *)id);
+ write_sound(wd, (bSound *)id_buffer, id);
break;
case ID_GR:
- write_collection(wd, (Collection *)id);
+ write_collection(wd, (Collection *)id_buffer, id);
break;
case ID_AR:
- write_armature(wd, (bArmature *)id);
+ write_armature(wd, (bArmature *)id_buffer, id);
break;
case ID_AC:
- write_action(wd, (bAction *)id);
+ write_action(wd, (bAction *)id_buffer, id);
break;
case ID_OB:
- write_object(wd, (Object *)id);
+ write_object(wd, (Object *)id_buffer, id);
break;
case ID_MA:
- write_material(wd, (Material *)id);
+ write_material(wd, (Material *)id_buffer, id);
break;
case ID_TE:
- write_texture(wd, (Tex *)id);
+ write_texture(wd, (Tex *)id_buffer, id);
break;
case ID_ME:
- write_mesh(wd, (Mesh *)id);
+ write_mesh(wd, (Mesh *)id_buffer, id);
break;
case ID_PA:
- write_particlesettings(wd, (ParticleSettings *)id);
+ write_particlesettings(wd, (ParticleSettings *)id_buffer, id);
break;
case ID_NT:
- write_nodetree(wd, (bNodeTree *)id);
+ write_nodetree(wd, (bNodeTree *)id_buffer, id);
break;
case ID_BR:
- write_brush(wd, (Brush *)id);
+ write_brush(wd, (Brush *)id_buffer, id);
break;
case ID_PAL:
- write_palette(wd, (Palette *)id);
+ write_palette(wd, (Palette *)id_buffer, id);
break;
case ID_PC:
- write_paintcurve(wd, (PaintCurve *)id);
+ write_paintcurve(wd, (PaintCurve *)id_buffer, id);
break;
case ID_GD:
- write_gpencil(wd, (bGPdata *)id);
+ write_gpencil(wd, (bGPdata *)id_buffer, id);
break;
case ID_LS:
- write_linestyle(wd, (FreestyleLineStyle *)id);
+ write_linestyle(wd, (FreestyleLineStyle *)id_buffer, id);
break;
case ID_CF:
- write_cachefile(wd, (CacheFile *)id);
+ write_cachefile(wd, (CacheFile *)id_buffer, id);
break;
case ID_HA:
- write_hair(wd, (Hair *)id);
+ write_hair(wd, (Hair *)id_buffer, id);
break;
case ID_PT:
- write_pointcloud(wd, (PointCloud *)id);
+ write_pointcloud(wd, (PointCloud *)id_buffer, id);
break;
case ID_VO:
- write_volume(wd, (Volume *)id);
+ write_volume(wd, (Volume *)id_buffer, id);
+ break;
+ case ID_SIM:
+ write_simulation(wd, (Simulation *)id);
break;
case ID_LI:
/* Do nothing, handled below - and should never be reached. */
@@ -4138,11 +4335,11 @@ static bool write_file_handle(Main *mainvar,
BKE_lib_override_library_operations_store_end(override_storage, id);
}
- if (wd->use_memfile) {
- /* Very important to do it after every ID write now, otherwise we cannot know whether a
- * specific ID changed or not. */
- mywrite_flush(wd);
- }
+ mywrite_id_end(wd, id);
+ }
+
+ if (id_buffer != id_buffer_static) {
+ MEM_SAFE_FREE(id_buffer);
}
mywrite_flush(wd);
@@ -4277,8 +4474,8 @@ bool BLO_write_file(Main *mainvar,
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
/* Just in case there is some subtle difference. */
- BLI_cleanup_path(mainvar->name, dir_dst);
- BLI_cleanup_path(mainvar->name, dir_src);
+ BLI_path_normalize(mainvar->name, dir_dst);
+ BLI_path_normalize(mainvar->name, dir_src);
if (G.relbase_valid && (BLI_path_cmp(dir_dst, dir_src) == 0)) {
/* Saved to same path. Nothing to do. */
@@ -4353,4 +4550,98 @@ bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int w
return (err == 0);
}
+void BLO_write_raw(BlendWriter *writer, int size_in_bytes, const void *data_ptr)
+{
+ writedata(writer->wd, DATA, size_in_bytes, data_ptr);
+}
+
+void BLO_write_struct_by_name(BlendWriter *writer, const char *struct_name, const void *data_ptr)
+{
+ int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
+ BLO_write_struct_by_id(writer, struct_id, data_ptr);
+}
+
+void BLO_write_struct_array_by_name(BlendWriter *writer,
+ const char *struct_name,
+ int array_size,
+ const void *data_ptr)
+{
+ int struct_id = BLO_get_struct_id_by_name(writer, struct_name);
+ BLO_write_struct_array_by_id(writer, struct_id, array_size, data_ptr);
+}
+
+void BLO_write_struct_by_id(BlendWriter *writer, int struct_id, const void *data_ptr)
+{
+ writestruct_nr(writer->wd, DATA, struct_id, 1, data_ptr);
+}
+
+void BLO_write_struct_array_by_id(BlendWriter *writer,
+ int struct_id,
+ int array_size,
+ const void *data_ptr)
+{
+ writestruct_nr(writer->wd, DATA, struct_id, array_size, data_ptr);
+}
+
+void BLO_write_struct_list_by_id(BlendWriter *writer, int struct_id, ListBase *list)
+{
+ writelist_nr(writer->wd, DATA, struct_id, list);
+}
+
+void BLO_write_struct_list_by_name(BlendWriter *writer, const char *struct_name, ListBase *list)
+{
+ BLO_write_struct_list_by_id(writer, BLO_get_struct_id_by_name(writer, struct_name), list);
+}
+
+void blo_write_id_struct(BlendWriter *writer, int struct_id, const void *id_address, const ID *id)
+{
+ writestruct_at_address_nr(writer->wd, GS(id->name), struct_id, 1, id_address, id);
+}
+
+int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name)
+{
+ int struct_id = DNA_struct_find_nr(writer->wd->sdna, struct_name);
+ BLI_assert(struct_id >= 0);
+ return struct_id;
+}
+
+void BLO_write_int32_array(BlendWriter *writer, int size, const int32_t *data_ptr)
+{
+ BLO_write_raw(writer, sizeof(int32_t) * size, data_ptr);
+}
+
+void BLO_write_uint32_array(BlendWriter *writer, int size, const uint32_t *data_ptr)
+{
+ BLO_write_raw(writer, sizeof(uint32_t) * size, data_ptr);
+}
+
+void BLO_write_float_array(BlendWriter *writer, int size, const float *data_ptr)
+{
+ BLO_write_raw(writer, sizeof(float) * size, data_ptr);
+}
+
+void BLO_write_float3_array(BlendWriter *writer, int size, const float *data_ptr)
+{
+ BLO_write_raw(writer, sizeof(float) * 3 * size, data_ptr);
+}
+
+/**
+ * Write a null terminated string.
+ */
+void BLO_write_string(BlendWriter *writer, const char *str)
+{
+ if (str != NULL) {
+ BLO_write_raw(writer, strlen(str) + 1, str);
+ }
+}
+
+/**
+ * Sometimes different data is written depending on whether the file is saved to disk or used for
+ * undo. This function returns true when the current file-writing is done for undo.
+ */
+bool BLO_write_is_undo(BlendWriter *writer)
+{
+ return writer->wd->use_memfile;
+}
+
/** \} */