This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenloader/intern/readfile.c
Bastien Montagne 887d052e56 Fix T62570: Append Particles System not working properly.
T62570 and T61796 show that we need two slightly different behaviors in
post-linking collection process:
* For linking, we never want to instantiate indirectly-linked
collections or objects.
* For appending however, since all collections and objects will become
local and hence need instantiation, we want to 'link to scene' all
collections first, better than instantiating the objects in the master
collection opf current scene.
2019-03-17 18:07:13 +01:00

11533 lines
324 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*/
/** \file
* \ingroup blenloader
*/
#include "zlib.h"
#include <limits.h>
#include <stdio.h> // for printf fopen fwrite fclose sprintf FILE
#include <stdlib.h> // for getenv atoi
#include <stddef.h> // for offsetof
#include <fcntl.h> // for open
#include <string.h> // for strrchr strncmp strstr
#include <math.h> // for fabs
#include <stdarg.h> /* for va_start/end */
#include <time.h> /* for gmtime */
#include <ctype.h> /* for isdigit */
#include "BLI_utildefines.h"
#ifndef WIN32
# include <unistd.h> // for read close
#else
# include <io.h> // for open close read
# include "winsock2.h"
# include "BLI_winstuff.h"
#endif
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
/* Allow using DNA struct members that are marked as private for read/write.
* Note: Each header that uses this needs to define its own way of handling
* it. There's no generic implementation, direct use does nothing. */
#define DNA_PRIVATE_READ_WRITE_ALLOW
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_cloth_types.h"
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_dynamicpaint_types.h"
#include "DNA_effect_types.h"
#include "DNA_fileglobal_types.h"
#include "DNA_genfile.h"
#include "DNA_gpencil_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_layer_types.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_meta_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_nla_types.h"
#include "DNA_node_types.h"
#include "DNA_object_fluidsim_types.h"
#include "DNA_object_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_particle_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_text_types.h"
#include "DNA_view3d_types.h"
#include "DNA_screen_types.h"
#include "DNA_sdna_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_smoke_types.h"
#include "DNA_speaker_types.h"
#include "DNA_sound_types.h"
#include "DNA_space_types.h"
#include "DNA_vfont_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
#include "RNA_access.h"
#include "MEM_guardedalloc.h"
#include "BLI_endian_switch.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_threads.h"
#include "BLI_mempool.h"
#include "BLI_ghash.h"
#include "BLT_translation.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_brush.h"
#include "BKE_cachefile.h"
#include "BKE_cloth.h"
#include "BKE_collection.h"
#include "BKE_colortools.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_effect.h"
#include "BKE_fcurve.h"
#include "BKE_global.h" // for G
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_library_idmap.h"
#include "BKE_library_override.h"
#include "BKE_library_query.h"
#include "BKE_main.h" // for Main
#include "BKE_material.h"
#include "BKE_mesh.h" // for ME_ defines (patching)
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_node.h" // for tree type defines
#include "BKE_object.h"
#include "BKE_ocean.h"
#include "BKE_outliner_treehash.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_sequencer.h"
#include "BKE_shader_fx.h"
#include "BKE_sound.h"
#include "BKE_workspace.h"
#include "DRW_engine.h"
#include "DEG_depsgraph.h"
#include "NOD_common.h"
#include "NOD_socket.h"
#include "BLO_blend_defs.h"
#include "BLO_blend_validate.h"
#include "BLO_readfile.h"
#include "BLO_undofile.h"
#include "RE_engine.h"
#include "readfile.h"
#include <errno.h>
/**
* READ
* ====
*
* - Existing Library (#Main) push or free
* - allocate new #Main
* - load file
* - read #SDNA
* - for each LibBlock
* - read LibBlock
* - if a Library
* - make a new #Main
* - attach ID's to it
* - else
* - read associated 'direct data'
* - link direct data (internal and to LibBlock)
* - read #FileGlobal
* - read #USER data, only when indicated (file is ``~/X.XX/startup.blend``)
* - free file
* - per Library (per #Main)
* - read file
* - read #SDNA
* - find LibBlocks and attach #ID's to #Main
* - if external LibBlock
* - search all #Main's
* - or it's already read,
* - or not read yet
* - or make new #Main
* - per LibBlock
* - read recursive
* - read associated direct data
* - link direct data (internal and to LibBlock)
* - free file
* - per Library with unread LibBlocks
* - read file
* - read #SDNA
* - per LibBlock
* - read recursive
* - read associated direct data
* - link direct data (internal and to LibBlock)
* - free file
* - join all #Main's
* - link all LibBlocks and indirect pointers to libblocks
* - initialize #FileGlobal and copy pointers to #Global
*
* \note Still a weak point is the new-address function, that doesnt solve reading from
* multiple files at the same time.
* (added remark: oh, i thought that was solved? will look at that... (ton).
*/
/**
* Delay reading blocks we might not use (especially applies to library linking).
* which keeps large arrays in memory from data-blocks we may not even use.
*
* \note This is disabled when using compression,
* while zlib supports seek ist's unusably slow, see: T61880.
*/
#define USE_BHEAD_READ_ON_DEMAND
/* use GHash for BHead name-based lookups (speeds up linking) */
#define USE_GHASH_BHEAD
/* Use GHash for restoring pointers by name */
#define USE_GHASH_RESTORE_POINTER
/* Define this to have verbose debug prints. */
#define USE_DEBUG_PRINT
#ifdef USE_DEBUG_PRINT
# define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
# define DEBUG_PRINTF(...)
#endif
/* local prototypes */
static void read_libraries(FileData *basefd, ListBase *mainlist);
static void *read_struct(FileData *fd, BHead *bh, const char *blockname);
static void direct_link_modifiers(FileData *fd, ListBase *lb);
static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name);
static BHead *find_bhead_from_idname(FileData *fd, const char *idname);
#ifdef USE_COLLECTION_COMPAT_28
static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection *sc);
#endif
static void direct_link_animdata(FileData *fd, AnimData *adt);
static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt);
typedef struct BHeadN {
struct BHeadN *next, *prev;
#ifdef USE_BHEAD_READ_ON_DEMAND
/** Use to read the data from the file directly into memory as needed. */
off_t file_offset;
/** When set, the remainder of this allocation is the data, otherwise it needs to be read. */
bool has_data;
#endif
struct BHead bhead;
} BHeadN;
#define BHEADN_FROM_BHEAD(bh) ((BHeadN *)POINTER_OFFSET(bh, -offsetof(BHeadN, bhead)))
/* We could change this in the future, for now it's simplest if only data is delayed
* because ID names are used in lookup tables. */
#define BHEAD_USE_READ_ON_DEMAND(bhead) \
((bhead)->code == DATA)
/* this function ensures that reports are printed,
* in the case of libraray linking errors this is important!
*
* bit kludge but better then doubling up on prints,
* we could alternatively have a versions of a report function which forces printing - campbell
*/
void blo_reportf_wrap(ReportList *reports, ReportType type, const char *format, ...)
{
char fixed_buf[1024]; /* should be long enough */
va_list args;
va_start(args, format);
vsnprintf(fixed_buf, sizeof(fixed_buf), format, args);
va_end(args);
fixed_buf[sizeof(fixed_buf) - 1] = '\0';
BKE_report(reports, type, fixed_buf);
if (G.background == 0) {
printf("%s: %s\n", BKE_report_type_str(type), fixed_buf);
}
}
/* for reporting linking messages */
static const char *library_parent_filepath(Library *lib)
{
return lib->parent ? lib->parent->filepath : "<direct>";
}
/* -------------------------------------------------------------------- */
/** \name OldNewMap API
* \{ */
typedef struct OldNew {
const void *oldp;
void *newp;
/* `nr` is "user count" for data, and ID code for libdata. */
int nr;
} OldNew;
typedef struct OldNewMap {
/* Array that stores the actual entries. */
OldNew *entries;
int nentries;
/* Hashmap that stores indices into the `entries` array. */
int32_t *map;
int capacity_exp;
} OldNewMap;
#define ENTRIES_CAPACITY(onm) (1 << (onm)->capacity_exp)
#define MAP_CAPACITY(onm) (1 << ((onm)->capacity_exp + 1))
#define SLOT_MASK(onm) (MAP_CAPACITY(onm) - 1)
#define DEFAULT_SIZE_EXP 6
#define PERTURB_SHIFT 5
/* based on the probing algorithm used in Python dicts. */
#define ITER_SLOTS(onm, KEY, SLOT_NAME, INDEX_NAME) \
uint32_t hash = BLI_ghashutil_ptrhash(KEY); \
uint32_t mask = SLOT_MASK(onm); \
uint perturb = hash; \
int SLOT_NAME = mask & hash; \
int INDEX_NAME = onm->map[SLOT_NAME]; \
for (;;SLOT_NAME = mask & ((5 * SLOT_NAME) + 1 + perturb), perturb >>= PERTURB_SHIFT, INDEX_NAME = onm->map[SLOT_NAME])
static void oldnewmap_insert_index_in_map(OldNewMap *onm, const void *ptr, int index)
{
ITER_SLOTS(onm, ptr, slot, stored_index) {
if (stored_index == -1) {
onm->map[slot] = index;
break;
}
}
}
static void oldnewmap_insert_or_replace(OldNewMap *onm, OldNew entry)
{
ITER_SLOTS(onm, entry.oldp, slot, index) {
if (index == -1) {
onm->entries[onm->nentries] = entry;
onm->map[slot] = onm->nentries;
onm->nentries++;
break;
}
else if (onm->entries[index].oldp == entry.oldp) {
onm->entries[index] = entry;
break;
}
}
}
static OldNew *oldnewmap_lookup_entry(const OldNewMap *onm, const void *addr)
{
ITER_SLOTS(onm, addr, slot, index) {
if (index >= 0) {
OldNew *entry = &onm->entries[index];
if (entry->oldp == addr) {
return entry;
}
}
else {
return NULL;
}
}
}
static void oldnewmap_clear_map(OldNewMap *onm)
{
memset(onm->map, 0xFF, MAP_CAPACITY(onm) * sizeof(*onm->map));
}
static void oldnewmap_increase_size(OldNewMap *onm)
{
onm->capacity_exp++;
onm->entries = MEM_reallocN(onm->entries, sizeof(*onm->entries) * ENTRIES_CAPACITY(onm));
onm->map = MEM_reallocN(onm->map, sizeof(*onm->map) * MAP_CAPACITY(onm));
oldnewmap_clear_map(onm);
for (int i = 0; i < onm->nentries; i++) {
oldnewmap_insert_index_in_map(onm, onm->entries[i].oldp, i);
}
}
/* Public OldNewMap API */
static OldNewMap *oldnewmap_new(void)
{
OldNewMap *onm = MEM_callocN(sizeof(*onm), "OldNewMap");
onm->capacity_exp = DEFAULT_SIZE_EXP;
onm->entries = MEM_malloc_arrayN(ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries");
onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map");
oldnewmap_clear_map(onm);
return onm;
}
static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
{
if (oldaddr == NULL || newaddr == NULL) return;
if (UNLIKELY(onm->nentries == ENTRIES_CAPACITY(onm))) {
oldnewmap_increase_size(onm);
}
OldNew entry;
entry.oldp = oldaddr;
entry.newp = newaddr;
entry.nr = nr;
oldnewmap_insert_or_replace(onm, entry);
}
void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
{
oldnewmap_insert(onm, oldaddr, newaddr, nr);
}
static void *oldnewmap_lookup_and_inc(OldNewMap *onm, const void *addr, bool increase_users)
{
OldNew *entry = oldnewmap_lookup_entry(onm, addr);
if (entry == NULL) return NULL;
if (increase_users) entry->nr++;
return entry->newp;
}
/* for libdata, OldNew.nr has ID code, no increment */
static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *lib)
{
if (addr == NULL) return NULL;
ID *id = oldnewmap_lookup_and_inc(onm, addr, false);
if (id == NULL) return NULL;
if (!lib || id->lib) return id;
return NULL;
}
static void oldnewmap_free_unused(OldNewMap *onm)
{
for (int i = 0; i < onm->nentries; i++) {
OldNew *entry = &onm->entries[i];
if (entry->nr == 0) {
MEM_freeN(entry->newp);
entry->newp = NULL;
}
}
}
static void oldnewmap_clear(OldNewMap *onm)
{
onm->capacity_exp = DEFAULT_SIZE_EXP;
oldnewmap_clear_map(onm);
onm->nentries = 0;
}
static void oldnewmap_free(OldNewMap *onm)
{
MEM_freeN(onm->entries);
MEM_freeN(onm->map);
MEM_freeN(onm);
}
#undef ENTRIES_CAPACITY
#undef MAP_CAPACITY
#undef SLOT_MASK
#undef DEFAULT_SIZE_EXP
#undef PERTURB_SHIFT
#undef ITER_SLOTS
/** \} */
/* -------------------------------------------------------------------- */
/** \name Helper Functions
* \{ */
static void add_main_to_main(Main *mainvar, Main *from)
{
ListBase *lbarray[MAX_LIBARRAY], *fromarray[MAX_LIBARRAY];
int a;
set_listbasepointers(mainvar, lbarray);
a = set_listbasepointers(from, fromarray);
while (a--) {
BLI_movelisttolist(lbarray[a], fromarray[a]);
}
}
void blo_join_main(ListBase *mainlist)
{
Main *tojoin, *mainl;
mainl = mainlist->first;
while ((tojoin = mainl->next)) {
add_main_to_main(mainl, tojoin);
BLI_remlink(mainlist, tojoin);
BKE_main_free(tojoin);
}
}
static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint lib_main_array_len)
{
for (ID *id = lb_src->first, *idnext; id; id = idnext) {
idnext = id->next;
if (id->lib) {
if (((uint)id->lib->temp_index < lib_main_array_len) &&
/* this check should never fail, just incase 'id->lib' is a dangling pointer. */
(lib_main_array[id->lib->temp_index]->curlib == id->lib))
{
Main *mainvar = lib_main_array[id->lib->temp_index];
ListBase *lb_dst = which_libbase(mainvar, GS(id->name));
BLI_remlink(lb_src, id);
BLI_addtail(lb_dst, id);
}
else {
printf("%s: invalid library for '%s'\n", __func__, id->name);
BLI_assert(0);
}
}
}
}
void blo_split_main(ListBase *mainlist, Main *main)
{
mainlist->first = mainlist->last = main;
main->next = NULL;
if (BLI_listbase_is_empty(&main->libraries))
return;
/* (Library.temp_index -> Main), lookup table */
const uint lib_main_array_len = BLI_listbase_count(&main->libraries);
Main **lib_main_array = MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__);
int i = 0;
for (Library *lib = main->libraries.first; lib; lib = lib->id.next, i++) {
Main *libmain = BKE_main_new();
libmain->curlib = lib;
libmain->versionfile = lib->versionfile;
libmain->subversionfile = lib->subversionfile;
BLI_addtail(mainlist, libmain);
lib->temp_index = i;
lib_main_array[i] = libmain;
}
ListBase *lbarray[MAX_LIBARRAY];
i = set_listbasepointers(main, lbarray);
while (i--) {
ID *id = lbarray[i]->first;
if (id == NULL || GS(id->name) == ID_LI) {
continue; /* no ID_LI datablock should ever be linked anyway, but just in case, better be explicit. */
}
split_libdata(lbarray[i], lib_main_array, lib_main_array_len);
}
MEM_freeN(lib_main_array);
}
static void read_file_version(FileData *fd, Main *main)
{
BHead *bhead;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == GLOB) {
FileGlobal *fg = read_struct(fd, bhead, "Global");
if (fg) {
main->subversionfile = fg->subversion;
main->minversionfile = fg->minversion;
main->minsubversionfile = fg->minsubversion;
MEM_freeN(fg);
}
else if (bhead->code == ENDB)
break;
}
}
if (main->curlib) {
main->curlib->versionfile = main->versionfile;
main->curlib->subversionfile = main->subversionfile;
}
}
#ifdef USE_GHASH_BHEAD
static void read_file_bhead_idname_map_create(FileData *fd)
{
BHead *bhead;
/* dummy values */
bool is_link = false;
int code_prev = ENDB;
uint reserve = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (code_prev != bhead->code) {
code_prev = bhead->code;
is_link = BKE_idcode_is_valid(code_prev) ? BKE_idcode_is_linkable(code_prev) : false;
}
if (is_link) {
reserve += 1;
}
}
BLI_assert(fd->bhead_idname_hash == NULL);
fd->bhead_idname_hash = BLI_ghash_str_new_ex(__func__, reserve);
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (code_prev != bhead->code) {
code_prev = bhead->code;
is_link = BKE_idcode_is_valid(code_prev) ? BKE_idcode_is_linkable(code_prev) : false;
}
if (is_link) {
BLI_ghash_insert(fd->bhead_idname_hash, (void *)blo_bhead_id_name(fd, bhead), bhead);
}
}
}
#endif
static Main *blo_find_main(FileData *fd, const char *filepath, const char *relabase)
{
ListBase *mainlist = fd->mainlist;
Main *m;
Library *lib;
char name1[FILE_MAX];
BLI_strncpy(name1, filepath, sizeof(name1));
BLI_cleanup_path(relabase, name1);
// printf("blo_find_main: relabase %s\n", relabase);
// printf("blo_find_main: original in %s\n", filepath);
// printf("blo_find_main: converted to %s\n", name1);
for (m = mainlist->first; m; m = m->next) {
const char *libname = (m->curlib) ? m->curlib->filepath : m->name;
if (BLI_path_cmp(name1, libname) == 0) {
if (G.debug & G_DEBUG) printf("blo_find_main: found library %s\n", libname);
return m;
}
}
m = BKE_main_new();
BLI_addtail(mainlist, m);
/* Add library datablock itself to 'main' Main, since libraries are **never** linked data.
* Fixes bug where you could end with all ID_LI datablocks having the same name... */
lib = BKE_libblock_alloc(mainlist->first, ID_LI, BLI_path_basename(filepath), 0);
lib->id.us = ID_FAKE_USERS(lib); /* Important, consistency with main ID reading code from read_libblock(). */
BLI_strncpy(lib->name, filepath, sizeof(lib->name));
BLI_strncpy(lib->filepath, name1, sizeof(lib->filepath));
m->curlib = lib;
read_file_version(fd, m);
if (G.debug & G_DEBUG) printf("blo_find_main: added new lib %s\n", filepath);
return m;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name File Parsing
* \{ */
static void switch_endian_bh4(BHead4 *bhead)
{
/* the ID_.. codes */
if ((bhead->code & 0xFFFF) == 0) bhead->code >>= 16;
if (bhead->code != ENDB) {
BLI_endian_switch_int32(&bhead->len);
BLI_endian_switch_int32(&bhead->SDNAnr);
BLI_endian_switch_int32(&bhead->nr);
}
}
static void switch_endian_bh8(BHead8 *bhead)
{
/* the ID_.. codes */
if ((bhead->code & 0xFFFF) == 0) bhead->code >>= 16;
if (bhead->code != ENDB) {
BLI_endian_switch_int32(&bhead->len);
BLI_endian_switch_int32(&bhead->SDNAnr);
BLI_endian_switch_int32(&bhead->nr);
}
}
static void bh4_from_bh8(BHead *bhead, BHead8 *bhead8, int do_endian_swap)
{
BHead4 *bhead4 = (BHead4 *)bhead;
int64_t old;
bhead4->code = bhead8->code;
bhead4->len = bhead8->len;
if (bhead4->code != ENDB) {
/* perform a endian swap on 64bit pointers, otherwise the pointer might map to zero
* 0x0000000000000000000012345678 would become 0x12345678000000000000000000000000
*/
if (do_endian_swap) {
BLI_endian_switch_int64(&bhead8->old);
}
/* this patch is to avoid a long long being read from not-eight aligned positions
* is necessary on any modern 64bit architecture) */
memcpy(&old, &bhead8->old, 8);
bhead4->old = (int)(old >> 3);
bhead4->SDNAnr = bhead8->SDNAnr;
bhead4->nr = bhead8->nr;
}
}
static void bh8_from_bh4(BHead *bhead, BHead4 *bhead4)
{
BHead8 *bhead8 = (BHead8 *)bhead;
bhead8->code = bhead4->code;
bhead8->len = bhead4->len;
if (bhead8->code != ENDB) {
bhead8->old = bhead4->old;
bhead8->SDNAnr = bhead4->SDNAnr;
bhead8->nr = bhead4->nr;
}
}
static BHeadN *get_bhead(FileData *fd)
{
BHeadN *new_bhead = NULL;
int readsize;
if (fd) {
if (!fd->is_eof) {
/* initializing to zero isn't strictly needed but shuts valgrind up
* since uninitialized memory gets compared */
BHead8 bhead8 = {0};
BHead4 bhead4 = {0};
BHead bhead = {0};
/* First read the bhead structure.
* Depending on the platform the file was written on this can
* be a big or little endian BHead4 or BHead8 structure.
*
* As usual 'ENDB' (the last *partial* bhead of the file)
* needs some special handling. We don't want to EOF just yet.
*/
if (fd->flags & FD_FLAGS_FILE_POINTSIZE_IS_4) {
bhead4.code = DATA;
readsize = fd->read(fd, &bhead4, sizeof(bhead4));
if (readsize == sizeof(bhead4) || bhead4.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
switch_endian_bh4(&bhead4);
}
if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) {
bh8_from_bh4(&bhead, &bhead4);
}
else {
/* MIN2 is only to quiet '-Warray-bounds' compiler warning. */
BLI_assert(sizeof(bhead) == sizeof(bhead4));
memcpy(&bhead, &bhead4, MIN2(sizeof(bhead), sizeof(bhead4)));
}
}
else {
fd->is_eof = true;
bhead.len = 0;
}
}
else {
bhead8.code = DATA;
readsize = fd->read(fd, &bhead8, sizeof(bhead8));
if (readsize == sizeof(bhead8) || bhead8.code == ENDB) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
switch_endian_bh8(&bhead8);
}
if (fd->flags & FD_FLAGS_POINTSIZE_DIFFERS) {
bh4_from_bh8(&bhead, &bhead8, (fd->flags & FD_FLAGS_SWITCH_ENDIAN));
}
else {
/* MIN2 is only to quiet '-Warray-bounds' compiler warning. */
BLI_assert(sizeof(bhead) == sizeof(bhead8));
memcpy(&bhead, &bhead8, MIN2(sizeof(bhead), sizeof(bhead8)));
}
}
else {
fd->is_eof = true;
bhead.len = 0;
}
}
/* make sure people are not trying to pass bad blend files */
if (bhead.len < 0) {
fd->is_eof = true;
}
/* bhead now contains the (converted) bhead structure. Now read
* the associated data and put everything in a BHeadN (creative naming !)
*/
if (fd->is_eof) {
/* pass */
}
#ifdef USE_BHEAD_READ_ON_DEMAND
else if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
/* Delay reading bhead content. */
new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead");
if (new_bhead) {
new_bhead->next = new_bhead->prev = NULL;
new_bhead->file_offset = fd->file_offset;
new_bhead->has_data = false;
new_bhead->bhead = bhead;
off_t seek_new = fd->seek(fd, bhead.len, SEEK_CUR);
if (seek_new == -1) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
BLI_assert(fd->file_offset == seek_new);
}
else {
fd->is_eof = true;
}
}
#endif
else {
new_bhead = MEM_mallocN(sizeof(BHeadN) + bhead.len, "new_bhead");
if (new_bhead) {
new_bhead->next = new_bhead->prev = NULL;
#ifdef USE_BHEAD_READ_ON_DEMAND
new_bhead->file_offset = 0; /* don't seek. */
new_bhead->has_data = true;
#endif
new_bhead->bhead = bhead;
readsize = fd->read(fd, new_bhead + 1, bhead.len);
if (readsize != bhead.len) {
fd->is_eof = true;
MEM_freeN(new_bhead);
new_bhead = NULL;
}
}
else {
fd->is_eof = true;
}
}
}
}
/* We've read a new block. Now add it to the list
* of blocks.
*/
if (new_bhead) {
BLI_addtail(&fd->bhead_list, new_bhead);
}
return new_bhead;
}
BHead *blo_bhead_first(FileData *fd)
{
BHeadN *new_bhead;
BHead *bhead = NULL;
/* Rewind the file
* Read in a new block if necessary
*/
new_bhead = fd->bhead_list.first;
if (new_bhead == NULL) {
new_bhead = get_bhead(fd);
}
if (new_bhead) {
bhead = &new_bhead->bhead;
}
return bhead;
}
BHead *blo_bhead_prev(FileData *UNUSED(fd), BHead *thisblock)
{
BHeadN *bheadn = BHEADN_FROM_BHEAD(thisblock);
BHeadN *prev = bheadn->prev;
return (prev) ? &prev->bhead : NULL;
}
BHead *blo_bhead_next(FileData *fd, BHead *thisblock)
{
BHeadN *new_bhead = NULL;
BHead *bhead = NULL;
if (thisblock) {
/* bhead is actually a sub part of BHeadN
* We calculate the BHeadN pointer from the BHead pointer below */
new_bhead = BHEADN_FROM_BHEAD(thisblock);
/* get the next BHeadN. If it doesn't exist we read in the next one */
new_bhead = new_bhead->next;
if (new_bhead == NULL) {
new_bhead = get_bhead(fd);
}
}
if (new_bhead) {
/* here we do the reverse:
* go from the BHeadN pointer to the BHead pointer */
bhead = &new_bhead->bhead;
}
return bhead;
}
#ifdef USE_BHEAD_READ_ON_DEMAND
static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf)
{
bool success = true;
BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock);
BLI_assert(new_bhead->has_data == false && new_bhead->file_offset != 0);
off_t offset_backup = fd->file_offset;
if (UNLIKELY(fd->seek(fd, new_bhead->file_offset, SEEK_SET) == -1)) {
success = false;
}
else {
if (fd->read(fd, buf, new_bhead->bhead.len) != new_bhead->bhead.len) {
success = false;
}
}
if (fd->seek(fd, offset_backup, SEEK_SET) == -1) {
success = false;
}
return success;
}
static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock)
{
BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock);
BHeadN *new_bhead_data = MEM_mallocN(sizeof(BHeadN) + new_bhead->bhead.len, "new_bhead");
new_bhead_data->bhead = new_bhead->bhead;
new_bhead_data->file_offset = new_bhead->file_offset;
new_bhead_data->has_data = true;
if (!blo_bhead_read_data(fd, thisblock, new_bhead_data + 1)) {
MEM_freeN(new_bhead_data);
return NULL;
}
return &new_bhead_data->bhead;
}
#endif /* USE_BHEAD_READ_ON_DEMAND */
/* Warning! Caller's responsibility to ensure given bhead **is** and ID one! */
const char *blo_bhead_id_name(const FileData *fd, const BHead *bhead)
{
return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs);
}
static void decode_blender_header(FileData *fd)
{
char header[SIZEOFBLENDERHEADER], num[4];
int readsize;
/* read in the header data */
readsize = fd->read(fd, header, sizeof(header));
if (readsize == sizeof(header) &&
STREQLEN(header, "BLENDER", 7) &&
ELEM(header[7], '_', '-') &&
ELEM(header[8], 'v', 'V') &&
(isdigit(header[9]) && isdigit(header[10]) && isdigit(header[11])))
{
fd->flags |= FD_FLAGS_FILE_OK;
/* what size are pointers in the file ? */
if (header[7] == '_') {
fd->flags |= FD_FLAGS_FILE_POINTSIZE_IS_4;
if (sizeof(void *) != 4) {
fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS;
}
}
else {
if (sizeof(void *) != 8) {
fd->flags |= FD_FLAGS_POINTSIZE_DIFFERS;
}
}
/* is the file saved in a different endian
* than we need ?
*/
if (((header[8] == 'v') ? L_ENDIAN : B_ENDIAN) != ENDIAN_ORDER) {
fd->flags |= FD_FLAGS_SWITCH_ENDIAN;
}
/* get the version number */
memcpy(num, header + 9, 3);
num[3] = 0;
fd->fileversion = atoi(num);
}
}
/**
* \return Success if the file is read correctly, else set \a r_error_message.
*/
static bool read_file_dna(FileData *fd, const char **r_error_message)
{
BHead *bhead;
int subversion = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == GLOB) {
/* Before this, the subversion didn't exist in 'FileGlobal' so the subversion
* value isn't accessible for the purpose of DNA versioning in this case. */
if (fd->fileversion <= 242) {
continue;
}
/* We can't use read_global because this needs 'DNA1' to be decoded,
* however the first 4 chars are _always_ the subversion. */
FileGlobal *fg = (void *)&bhead[1];
BLI_STATIC_ASSERT(offsetof(FileGlobal, subvstr) == 0, "Must be first: subvstr");
char num[5];
memcpy(num, fg->subvstr, 4);
num[4] = 0;
subversion = atoi(num);
}
else if (bhead->code == DNA1) {
const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0;
fd->filesdna = DNA_sdna_from_data(&bhead[1], bhead->len, do_endian_swap, true, r_error_message);
if (fd->filesdna) {
blo_do_versions_dna(fd->filesdna, fd->fileversion, subversion);
fd->compflags = DNA_struct_get_compareflags(fd->filesdna, fd->memsdna);
/* used to retrieve ID names from (bhead+1) */
fd->id_name_offs = DNA_elem_offset(fd->filesdna, "ID", "char", "name[]");
return true;
}
else {
return false;
}
}
else if (bhead->code == ENDB)
break;
}
*r_error_message = "Missing DNA block";
return false;
}
static int *read_file_thumbnail(FileData *fd)
{
BHead *bhead;
int *blend_thumb = NULL;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == TEST) {
const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0;
int *data = (int *)(bhead + 1);
if (bhead->len < (2 * sizeof(int))) {
break;
}
if (do_endian_swap) {
BLI_endian_switch_int32(&data[0]);
BLI_endian_switch_int32(&data[1]);
}
const int width = data[0];
const int height = data[1];
if (!BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
break;
}
if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(width, height)) {
break;
}
blend_thumb = data;
break;
}
else if (bhead->code != REND) {
/* Thumbnail is stored in TEST immediately after first REND... */
break;
}
}
return blend_thumb;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name File Data API
* \{ */
/* Regular file reading. */
static int fd_read_data_from_file(FileData *filedata, void *buffer, uint size)
{
int readsize = read(filedata->filedes, buffer, size);
if (readsize < 0) {
readsize = EOF;
}
else {
filedata->file_offset += readsize;
}
return (readsize);
}
static off_t fd_seek_data_from_file(FileData *filedata, off_t offset, int whence)
{
filedata->file_offset = lseek(filedata->filedes, offset, whence);
return filedata->file_offset;
}
/* GZip file reading. */
static int fd_read_gzip_from_file(FileData *filedata, void *buffer, uint size)
{
int readsize = gzread(filedata->gzfiledes, buffer, size);
if (readsize < 0) {
readsize = EOF;
}
else {
filedata->file_offset += readsize;
}
return (readsize);
}
/* Memory reading. */
static int fd_read_from_memory(FileData *filedata, void *buffer, uint size)
{
/* don't read more bytes then there are available in the buffer */
int readsize = (int)MIN2(size, (uint)(filedata->buffersize - filedata->file_offset));
memcpy(buffer, filedata->buffer + filedata->file_offset, readsize);
filedata->file_offset += readsize;
return (readsize);
}
/* MemFile reading. */
static int fd_read_from_memfile(FileData *filedata, void *buffer, uint size)
{
static size_t seek = SIZE_MAX; /* the current position */
static size_t offset = 0; /* size of previous chunks */
static MemFileChunk *chunk = NULL;
size_t chunkoffset, readsize, totread;
if (size == 0) return 0;
if (seek != (size_t)filedata->file_offset) {
chunk = filedata->memfile->chunks.first;
seek = 0;
while (chunk) {
if (seek + chunk->size > (size_t)filedata->file_offset) {
break;
}
seek += chunk->size;
chunk = chunk->next;
}
offset = seek;
seek = filedata->file_offset;
}
if (chunk) {
totread = 0;
do {
/* first check if it's on the end if current chunk */
if (seek - offset == chunk->size) {
offset += chunk->size;
chunk = chunk->next;
}
/* debug, should never happen */
if (chunk == NULL) {
printf("illegal read, chunk zero\n");
return 0;
}
chunkoffset = seek - offset;
readsize = size - totread;
/* data can be spread over multiple chunks, so clamp size
* to within this chunk, and then it will read further in
* the next chunk */
if (chunkoffset + readsize > chunk->size)
readsize = chunk->size - chunkoffset;
memcpy(POINTER_OFFSET(buffer, totread), chunk->buf + chunkoffset, readsize);
totread += readsize;
filedata->file_offset += readsize;
seek += readsize;
} while (totread < size);
return totread;
}
return 0;
}
static FileData *filedata_new(void)
{
FileData *fd = MEM_callocN(sizeof(FileData), "FileData");
fd->filedes = -1;
fd->gzfiledes = NULL;
fd->memsdna = DNA_sdna_current_get();
fd->datamap = oldnewmap_new();
fd->globmap = oldnewmap_new();
fd->libmap = oldnewmap_new();
return fd;
}
static FileData *blo_decode_and_check(FileData *fd, ReportList *reports)
{
decode_blender_header(fd);
if (fd->flags & FD_FLAGS_FILE_OK) {
const char *error_message = NULL;
if (read_file_dna(fd, &error_message) == false) {
BKE_reportf(reports, RPT_ERROR,
"Failed to read blend file '%s': %s",
fd->relabase, error_message);
blo_filedata_free(fd);
fd = NULL;
}
}
else {
BKE_reportf(reports, RPT_ERROR, "Failed to read blend file '%s', not a blend file", fd->relabase);
blo_filedata_free(fd);
fd = NULL;
}
return fd;
}
static FileData *blo_filedata_from_file_descriptor(const char *filepath, ReportList *reports, int file)
{
FileDataReadFn *read_fn = NULL;
FileDataSeekFn *seek_fn = NULL; /* Optional. */
gzFile gzfile = (gzFile)Z_NULL;
char header[7];
/* Regular file. */
errno = 0;
if (read(file, header, sizeof(header)) != sizeof(header)) {
BKE_reportf(reports, RPT_WARNING, "Unable to read '%s': %s",
filepath, errno ? strerror(errno) : TIP_("insufficient content"));
return NULL;
}
else {
lseek(file, 0, SEEK_SET);
}
/* Regular file. */
if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
read_fn = fd_read_data_from_file;
seek_fn = fd_seek_data_from_file;
}
/* Gzip file. */
errno = 0;
if ((read_fn == NULL) &&
/* Check header magic. */
(header[0] == 0x1f && header[1] == 0x8b))
{
gzfile = BLI_gzopen(filepath, "rb");
if (gzfile == (gzFile)Z_NULL) {
BKE_reportf(reports, RPT_WARNING, "Unable to open '%s': %s",
filepath, errno ? strerror(errno) : TIP_("unknown error reading file"));
return NULL;
}
else {
/* 'seek_fn' is too slow for gzip, don't set it. */
read_fn = fd_read_gzip_from_file;
/* Caller must close. */
file = -1;
}
}
if (read_fn == NULL) {
BKE_reportf(reports, RPT_WARNING, "Unrecognized file format '%s'", filepath);
return NULL;
}
FileData *fd = filedata_new();
fd->filedes = file;
fd->gzfiledes = gzfile;
fd->read = read_fn;
fd->seek = seek_fn;
return fd;
}
static FileData *blo_filedata_from_file_open(const char *filepath, ReportList *reports)
{
errno = 0;
const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
if (file == -1) {
BKE_reportf(reports, RPT_WARNING, "Unable to open '%s': %s",
filepath, errno ? strerror(errno) : TIP_("unknown error reading file"));
return NULL;
}
FileData *fd = blo_filedata_from_file_descriptor(filepath, reports, file);
if ((fd == NULL) || (fd->filedes == -1)) {
close(file);
}
return fd;
}
/* cannot be called with relative paths anymore! */
/* on each new library added, it now checks for the current FileData and expands relativeness */
FileData *blo_filedata_from_file(const char *filepath, ReportList *reports)
{
FileData *fd = blo_filedata_from_file_open(filepath, reports);
if (fd != NULL) {
/* needed for library_append and read_libraries */
BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase));
return blo_decode_and_check(fd, reports);
}
return NULL;
}
/**
* Same as blo_filedata_from_file(), but does not reads DNA data, only header. Use it for light access
* (e.g. thumbnail reading).
*/
static FileData *blo_filedata_from_file_minimal(const char *filepath)
{
FileData *fd = blo_filedata_from_file_open(filepath, NULL);
if (fd != NULL) {
decode_blender_header(fd);
if (fd->flags & FD_FLAGS_FILE_OK) {
return fd;
}
blo_filedata_free(fd);
}
return NULL;
}
static int fd_read_gzip_from_memory(FileData *filedata, void *buffer, uint size)
{
int err;
filedata->strm.next_out = (Bytef *)buffer;
filedata->strm.avail_out = size;
// Inflate another chunk.
err = inflate(&filedata->strm, Z_SYNC_FLUSH);
if (err == Z_STREAM_END) {
return 0;
}
else if (err != Z_OK) {
printf("fd_read_gzip_from_memory: zlib error\n");
return 0;
}
filedata->file_offset += size;
return (size);
}
static int fd_read_gzip_from_memory_init(FileData *fd)
{
fd->strm.next_in = (Bytef *)fd->buffer;
fd->strm.avail_in = fd->buffersize;
fd->strm.total_out = 0;
fd->strm.zalloc = Z_NULL;
fd->strm.zfree = Z_NULL;
if (inflateInit2(&fd->strm, (16 + MAX_WBITS)) != Z_OK)
return 0;
fd->read = fd_read_gzip_from_memory;
return 1;
}
FileData *blo_filedata_from_memory(const void *mem, int memsize, ReportList *reports)
{
if (!mem || memsize < SIZEOFBLENDERHEADER) {
BKE_report(reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open"));
return NULL;
}
else {
FileData *fd = filedata_new();
const char *cp = mem;
fd->buffer = mem;
fd->buffersize = memsize;
/* test if gzip */
if (cp[0] == 0x1f && cp[1] == 0x8b) {
if (0 == fd_read_gzip_from_memory_init(fd)) {
blo_filedata_free(fd);
return NULL;
}
}
else
fd->read = fd_read_from_memory;
fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
return blo_decode_and_check(fd, reports);
}
}
FileData *blo_filedata_from_memfile(MemFile *memfile, ReportList *reports)
{
if (!memfile) {
BKE_report(reports, RPT_WARNING, "Unable to open blend <memory>");
return NULL;
}
else {
FileData *fd = filedata_new();
fd->memfile = memfile;
fd->read = fd_read_from_memfile;
fd->flags |= FD_FLAGS_NOT_MY_BUFFER;
return blo_decode_and_check(fd, reports);
}
}
void blo_filedata_free(FileData *fd)
{
if (fd) {
if (fd->filedes != -1) {
close(fd->filedes);
}
if (fd->gzfiledes != NULL) {
gzclose(fd->gzfiledes);
}
if (fd->strm.next_in) {
if (inflateEnd(&fd->strm) != Z_OK) {
printf("close gzip stream error\n");
}
}
if (fd->buffer && !(fd->flags & FD_FLAGS_NOT_MY_BUFFER)) {
MEM_freeN((void *)fd->buffer);
fd->buffer = NULL;
}
/* Free all BHeadN data blocks */
#ifndef NDEBUG
BLI_freelistN(&fd->bhead_list);
#else
/* Sanity check we're not keeping memory we don't need. */
LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) {
if (fd->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
BLI_assert(new_bhead->has_data == 0);
}
MEM_freeN(new_bhead);
}
#endif
if (fd->filesdna)
DNA_sdna_free(fd->filesdna);
if (fd->compflags)
MEM_freeN((void *)fd->compflags);
if (fd->datamap)
oldnewmap_free(fd->datamap);
if (fd->globmap)
oldnewmap_free(fd->globmap);
if (fd->imamap)
oldnewmap_free(fd->imamap);
if (fd->movieclipmap)
oldnewmap_free(fd->movieclipmap);
if (fd->scenemap)
oldnewmap_free(fd->scenemap);
if (fd->soundmap)
oldnewmap_free(fd->soundmap);
if (fd->packedmap)
oldnewmap_free(fd->packedmap);
if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP))
oldnewmap_free(fd->libmap);
if (fd->bheadmap)
MEM_freeN(fd->bheadmap);
#ifdef USE_GHASH_BHEAD
if (fd->bhead_idname_hash) {
BLI_ghash_free(fd->bhead_idname_hash, NULL, NULL);
}
#endif
MEM_freeN(fd);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public Utilities
* \{ */
/**
* Check whether given path ends with a blend file compatible extension
* (`.blend`, `.ble` or `.blend.gz`).
*
* \param str: The path to check.
* \return true is this path ends with a blender file extension.
*/
bool BLO_has_bfile_extension(const char *str)
{
const char *ext_test[4] = {".blend", ".ble", ".blend.gz", NULL};
return BLI_path_extension_check_array(str, ext_test);
}
/**
* Try to explode given path into its 'library components'
* (i.e. a .blend file, id type/group, and data-block itself).
*
* \param path: the full path to explode.
* \param r_dir: the string that'll contain path up to blend file itself ('library' path).
* WARNING! Must be #FILE_MAX_LIBEXTRA long (it also stores group and name strings)!
* \param r_group: the string that'll contain 'group' part of the path, if any. May be NULL.
* \param r_name: the string that'll contain data's name part of the path, if any. May be NULL.
* \return true if path contains a blend file.
*/
bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name)
{
/* We might get some data names with slashes, so we have to go up in path until we find blend file itself,
* then we now next path item is group, and everything else is data name. */
char *slash = NULL, *prev_slash = NULL, c = '\0';
r_dir[0] = '\0';
if (r_group) {
*r_group = NULL;
}
if (r_name) {
*r_name = NULL;
}
/* if path leads to an existing directory, we can be sure we're not (in) a library */
if (BLI_is_dir(path)) {
return false;
}
strcpy(r_dir, path);
while ((slash = (char *)BLI_last_slash(r_dir))) {
char tc = *slash;
*slash = '\0';
if (BLO_has_bfile_extension(r_dir) && BLI_is_file(r_dir)) {
break;
}
else if (STREQ(r_dir, BLO_EMBEDDED_STARTUP_BLEND)) {
break;
}
if (prev_slash) {
*prev_slash = c;
}
prev_slash = slash;
c = tc;
}
if (!slash) {
return false;
}
if (slash[1] != '\0') {
BLI_assert(strlen(slash + 1) < BLO_GROUP_MAX);
if (r_group) {
*r_group = slash + 1;
}
}
if (prev_slash && (prev_slash[1] != '\0')) {
BLI_assert(strlen(prev_slash + 1) < MAX_ID_NAME - 2);
if (r_name) {
*r_name = prev_slash + 1;
}
}
return true;
}
/**
* Does a very light reading of given .blend file to extract its stored thumbnail.
*
* \param filepath: The path of the file to extract thumbnail from.
* \return The raw thumbnail
* (MEM-allocated, as stored in file, use BKE_main_thumbnail_to_imbuf() to convert it to ImBuf image).
*/
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
{
FileData *fd;
BlendThumbnail *data = NULL;
int *fd_data;
fd = blo_filedata_from_file_minimal(filepath);
fd_data = fd ? read_file_thumbnail(fd) : NULL;
if (fd_data) {
const int width = fd_data[0];
const int height = fd_data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
const size_t sz = BLEN_THUMB_MEMSIZE(width, height);
data = MEM_mallocN(sz, __func__);
if (data) {
BLI_assert((sz - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2)));
data->width = width;
data->height = height;
memcpy(data->rect, &fd_data[2], sz - sizeof(*data));
}
}
}
blo_filedata_free(fd);
return data;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Old/New Pointer Map
* \{ */
static void *newdataadr(FileData *fd, const void *adr) /* only direct databocks */
{
return oldnewmap_lookup_and_inc(fd->datamap, adr, true);
}
static void *newdataadr_no_us(FileData *fd, const void *adr) /* only direct databocks */
{
return oldnewmap_lookup_and_inc(fd->datamap, adr, false);
}
static void *newglobadr(FileData *fd, const void *adr) /* direct datablocks with global linking */
{
return oldnewmap_lookup_and_inc(fd->globmap, adr, true);
}
static void *newimaadr(FileData *fd, const void *adr) /* used to restore image data after undo */
{
if (fd->imamap && adr)
return oldnewmap_lookup_and_inc(fd->imamap, adr, true);
return NULL;
}
static void *newsceadr(FileData *fd, const void *adr) /* used to restore scene data after undo */
{
if (fd->scenemap && adr)
return oldnewmap_lookup_and_inc(fd->scenemap, adr, true);
return NULL;
}
static void *newmclipadr(FileData *fd, const void *adr) /* used to restore movie clip data after undo */
{
if (fd->movieclipmap && adr)
return oldnewmap_lookup_and_inc(fd->movieclipmap, adr, true);
return NULL;
}
static void *newsoundadr(FileData *fd, const void *adr) /* used to restore sound data after undo */
{
if (fd->soundmap && adr)
return oldnewmap_lookup_and_inc(fd->soundmap, adr, true);
return NULL;
}
static void *newpackedadr(FileData *fd, const void *adr) /* used to restore packed data after undo */
{
if (fd->packedmap && adr)
return oldnewmap_lookup_and_inc(fd->packedmap, adr, true);
return oldnewmap_lookup_and_inc(fd->datamap, adr, true);
}
static void *newlibadr(FileData *fd, const void *lib, const void *adr) /* only lib data */
{
return oldnewmap_liblookup(fd->libmap, adr, lib);
}
void *blo_do_versions_newlibadr(FileData *fd, const void *lib, const void *adr) /* only lib data */
{
return newlibadr(fd, lib, adr);
}
static void *newlibadr_us(FileData *fd, const void *lib, const void *adr) /* increases user number */
{
ID *id = newlibadr(fd, lib, adr);
id_us_plus_no_lib(id);
return id;
}
void *blo_do_versions_newlibadr_us(FileData *fd, const void *lib, const void *adr) /* increases user number */
{
return newlibadr_us(fd, lib, adr);
}
static void *newlibadr_real_us(FileData *fd, const void *lib, const void *adr) /* ensures real user */
{
ID *id = newlibadr(fd, lib, adr);
id_us_ensure_real(id);
return id;
}
static void change_link_placeholder_to_real_ID_pointer_fd(FileData *fd, const void *old, void *new)
{
for (int i = 0; i < fd->libmap->nentries; i++) {
OldNew *entry = &fd->libmap->entries[i];
if (old == entry->newp && entry->nr == ID_LINK_PLACEHOLDER) {
entry->newp = new;
if (new) entry->nr = GS( ((ID *)new)->name);
}
}
}
static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, FileData *basefd, void *old, void *new)
{
Main *mainptr;
for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) {
FileData *fd;
if (mainptr->curlib)
fd = mainptr->curlib->filedata;
else
fd = basefd;
if (fd) {
change_link_placeholder_to_real_ID_pointer_fd(fd, old, new);
}
}
}
/* lib linked proxy objects point to our local data, we need
* to clear that pointer before reading the undo memfile since
* the object might be removed, it is set again in reading
* if the local object still exists.
* This is only valid for local proxy objects though, linked ones should not be affected here.
*/
void blo_clear_proxy_pointers_from_lib(Main *oldmain)
{
Object *ob = oldmain->objects.first;
for (; ob; ob = ob->id.next) {
if (ob->id.lib != NULL && ob->proxy_from != NULL && ob->proxy_from->id.lib == NULL) {
ob->proxy_from = NULL;
}
}
}
void blo_make_scene_pointer_map(FileData *fd, Main *oldmain)
{
Scene *sce = oldmain->scenes.first;
fd->scenemap = oldnewmap_new();
for (; sce; sce = sce->id.next) {
if (sce->eevee.light_cache) {
struct LightCache *light_cache = sce->eevee.light_cache;
oldnewmap_insert(fd->scenemap, light_cache, light_cache, 0);
}
}
}
void blo_end_scene_pointer_map(FileData *fd, Main *oldmain)
{
OldNew *entry = fd->scenemap->entries;
Scene *sce = oldmain->scenes.first;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->scenemap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (; sce; sce = sce->id.next) {
sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache);
}
}
void blo_make_image_pointer_map(FileData *fd, Main *oldmain)
{
Image *ima = oldmain->images.first;
Scene *sce = oldmain->scenes.first;
int a;
fd->imamap = oldnewmap_new();
for (; ima; ima = ima->id.next) {
if (ima->cache) {
oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0);
}
for (a = 0; a < TEXTARGET_COUNT; a++) {
if (ima->gputexture[a]) {
oldnewmap_insert(fd->imamap, ima->gputexture[a], ima->gputexture[a], 0);
}
}
if (ima->rr) {
oldnewmap_insert(fd->imamap, ima->rr, ima->rr, 0);
}
LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) {
if (slot->render) {
oldnewmap_insert(fd->imamap, slot->render, slot->render, 0);
}
}
}
for (; sce; sce = sce->id.next) {
if (sce->nodetree && sce->nodetree->previews) {
bNodeInstanceHashIterator iter;
NODE_INSTANCE_HASH_ITER(iter, sce->nodetree->previews) {
bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter);
oldnewmap_insert(fd->imamap, preview, preview, 0);
}
}
}
}
/* set old main image ibufs to zero if it has been restored */
/* this works because freeing old main only happens after this call */
void blo_end_image_pointer_map(FileData *fd, Main *oldmain)
{
OldNew *entry = fd->imamap->entries;
Image *ima = oldmain->images.first;
Scene *sce = oldmain->scenes.first;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->imamap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (; ima; ima = ima->id.next) {
ima->cache = newimaadr(fd, ima->cache);
if (ima->cache == NULL) {
ima->gpuflag = 0;
for (i = 0; i < TEXTARGET_COUNT; i++) {
ima->gputexture[i] = NULL;
}
ima->rr = NULL;
}
LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) {
slot->render = newimaadr(fd, slot->render);
}
for (i = 0; i < TEXTARGET_COUNT; i++)
ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]);
ima->rr = newimaadr(fd, ima->rr);
}
for (; sce; sce = sce->id.next) {
if (sce->nodetree && sce->nodetree->previews) {
bNodeInstanceHash *new_previews = BKE_node_instance_hash_new("node previews");
bNodeInstanceHashIterator iter;
/* reconstruct the preview hash, only using remaining pointers */
NODE_INSTANCE_HASH_ITER(iter, sce->nodetree->previews) {
bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter);
if (preview) {
bNodePreview *new_preview = newimaadr(fd, preview);
if (new_preview) {
bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter);
BKE_node_instance_hash_insert(new_previews, key, new_preview);
}
}
}
BKE_node_instance_hash_free(sce->nodetree->previews, NULL);
sce->nodetree->previews = new_previews;
}
}
}
void blo_make_movieclip_pointer_map(FileData *fd, Main *oldmain)
{
MovieClip *clip = oldmain->movieclips.first;
Scene *sce = oldmain->scenes.first;
fd->movieclipmap = oldnewmap_new();
for (; clip; clip = clip->id.next) {
if (clip->cache)
oldnewmap_insert(fd->movieclipmap, clip->cache, clip->cache, 0);
if (clip->tracking.camera.intrinsics)
oldnewmap_insert(fd->movieclipmap, clip->tracking.camera.intrinsics, clip->tracking.camera.intrinsics, 0);
}
for (; sce; sce = sce->id.next) {
if (sce->nodetree) {
bNode *node;
for (node = sce->nodetree->nodes.first; node; node = node->next)
if (node->type == CMP_NODE_MOVIEDISTORTION)
oldnewmap_insert(fd->movieclipmap, node->storage, node->storage, 0);
}
}
}
/* set old main movie clips caches to zero if it has been restored */
/* this works because freeing old main only happens after this call */
void blo_end_movieclip_pointer_map(FileData *fd, Main *oldmain)
{
OldNew *entry = fd->movieclipmap->entries;
MovieClip *clip = oldmain->movieclips.first;
Scene *sce = oldmain->scenes.first;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->movieclipmap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (; clip; clip = clip->id.next) {
clip->cache = newmclipadr(fd, clip->cache);
clip->tracking.camera.intrinsics = newmclipadr(fd, clip->tracking.camera.intrinsics);
}
for (; sce; sce = sce->id.next) {
if (sce->nodetree) {
bNode *node;
for (node = sce->nodetree->nodes.first; node; node = node->next)
if (node->type == CMP_NODE_MOVIEDISTORTION)
node->storage = newmclipadr(fd, node->storage);
}
}
}
void blo_make_sound_pointer_map(FileData *fd, Main *oldmain)
{
bSound *sound = oldmain->sounds.first;
fd->soundmap = oldnewmap_new();
for (; sound; sound = sound->id.next) {
if (sound->waveform)
oldnewmap_insert(fd->soundmap, sound->waveform, sound->waveform, 0);
}
}
/* set old main sound caches to zero if it has been restored */
/* this works because freeing old main only happens after this call */
void blo_end_sound_pointer_map(FileData *fd, Main *oldmain)
{
OldNew *entry = fd->soundmap->entries;
bSound *sound = oldmain->sounds.first;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->soundmap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (; sound; sound = sound->id.next) {
sound->waveform = newsoundadr(fd, sound->waveform);
}
}
/* XXX disabled this feature - packed files also belong in temp saves and quit.blend, to make restore work */
static void insert_packedmap(FileData *fd, PackedFile *pf)
{
oldnewmap_insert(fd->packedmap, pf, pf, 0);
oldnewmap_insert(fd->packedmap, pf->data, pf->data, 0);
}
void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
{
Image *ima;
VFont *vfont;
bSound *sound;
Library *lib;
fd->packedmap = oldnewmap_new();
for (ima = oldmain->images.first; ima; ima = ima->id.next) {
ImagePackedFile *imapf;
if (ima->packedfile)
insert_packedmap(fd, ima->packedfile);
for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next)
if (imapf->packedfile)
insert_packedmap(fd, imapf->packedfile);
}
for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next)
if (vfont->packedfile)
insert_packedmap(fd, vfont->packedfile);
for (sound = oldmain->sounds.first; sound; sound = sound->id.next)
if (sound->packedfile)
insert_packedmap(fd, sound->packedfile);
for (lib = oldmain->libraries.first; lib; lib = lib->id.next)
if (lib->packedfile)
insert_packedmap(fd, lib->packedfile);
}
/* set old main packed data to zero if it has been restored */
/* this works because freeing old main only happens after this call */
void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
{
Image *ima;
VFont *vfont;
bSound *sound;
Library *lib;
OldNew *entry = fd->packedmap->entries;
int i;
/* used entries were restored, so we put them to zero */
for (i = 0; i < fd->packedmap->nentries; i++, entry++) {
if (entry->nr > 0)
entry->newp = NULL;
}
for (ima = oldmain->images.first; ima; ima = ima->id.next) {
ImagePackedFile *imapf;
ima->packedfile = newpackedadr(fd, ima->packedfile);
for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next)
imapf->packedfile = newpackedadr(fd, imapf->packedfile);
}
for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next)
vfont->packedfile = newpackedadr(fd, vfont->packedfile);
for (sound = oldmain->sounds.first; sound; sound = sound->id.next)
sound->packedfile = newpackedadr(fd, sound->packedfile);
for (lib = oldmain->libraries.first; lib; lib = lib->id.next)
lib->packedfile = newpackedadr(fd, lib->packedfile);
}
/* undo file support: add all library pointers in lookup */
void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
{
Main *ptr = old_mainlist->first;
ListBase *lbarray[MAX_LIBARRAY];
for (ptr = ptr->next; ptr; ptr = ptr->next) {
int i = set_listbasepointers(ptr, lbarray);
while (i--) {
ID *id;
for (id = lbarray[i]->first; id; id = id->next)
oldnewmap_insert(fd->libmap, id, id, GS(id->name));
}
}
fd->old_mainlist = old_mainlist;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name DNA Struct Loading
* \{ */
static void switch_endian_structs(const struct SDNA *filesdna, BHead *bhead)
{
int blocksize, nblocks;
char *data;
data = (char *)(bhead + 1);
blocksize = filesdna->types_size[filesdna->structs[bhead->SDNAnr][0]];
nblocks = bhead->nr;
while (nblocks--) {
DNA_struct_switch_endian(filesdna, bhead->SDNAnr, data);
data += blocksize;
}
}
static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
{
void *temp = NULL;
if (bh->len) {
#ifdef USE_BHEAD_READ_ON_DEMAND
BHead *bh_orig = bh;
#endif
/* switch is based on file dna */
if (bh->SDNAnr && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) {
#ifdef USE_BHEAD_READ_ON_DEMAND
if (BHEADN_FROM_BHEAD(bh)->has_data == false) {
bh = blo_bhead_read_full(fd, bh);
if (UNLIKELY(bh == NULL)) {
fd->flags &= ~FD_FLAGS_FILE_OK;
return NULL;
}
}
#endif
switch_endian_structs(fd->filesdna, bh);
}
if (fd->compflags[bh->SDNAnr] != SDNA_CMP_REMOVED) {
if (fd->compflags[bh->SDNAnr] == SDNA_CMP_NOT_EQUAL) {
#ifdef USE_BHEAD_READ_ON_DEMAND
if (BHEADN_FROM_BHEAD(bh)->has_data == false) {
bh = blo_bhead_read_full(fd, bh);
if (UNLIKELY(bh == NULL)) {
fd->flags &= ~FD_FLAGS_FILE_OK;
return NULL;
}
}
#endif
temp = DNA_struct_reconstruct(fd->memsdna, fd->filesdna, fd->compflags, bh->SDNAnr, bh->nr, (bh + 1));
}
else {
/* SDNA_CMP_EQUAL */
temp = MEM_mallocN(bh->len, blockname);
#ifdef USE_BHEAD_READ_ON_DEMAND
if (BHEADN_FROM_BHEAD(bh)->has_data) {
memcpy(temp, (bh + 1), bh->len);
}
else {
/* Instead of allocating the bhead, then copying it,
* read the data from the file directly into the memory. */
if (UNLIKELY(!blo_bhead_read_data(fd, bh, temp))) {
fd->flags &= ~FD_FLAGS_FILE_OK;
MEM_freeN(temp);
temp = NULL;
}
}
#else
memcpy(temp, (bh + 1), bh->len);
#endif
}
}
#ifdef USE_BHEAD_READ_ON_DEMAND
if (bh_orig != bh) {
MEM_freeN(BHEADN_FROM_BHEAD(bh));
}
#endif
}
return temp;
}
typedef void (*link_list_cb)(FileData *fd, void *data);
static void link_list_ex(FileData *fd, ListBase *lb, link_list_cb callback) /* only direct data */
{
Link *ln, *prev;
if (BLI_listbase_is_empty(lb)) return;
lb->first = newdataadr(fd, lb->first);
if (callback != NULL) {
callback(fd, lb->first);
}
ln = lb->first;
prev = NULL;
while (ln) {
ln->next = newdataadr(fd, ln->next);
if (ln->next != NULL && callback != NULL) {
callback(fd, ln->next);
}
ln->prev = prev;
prev = ln;
ln = ln->next;
}
lb->last = prev;
}
static void link_list(FileData *fd, ListBase *lb) /* only direct data */
{
link_list_ex(fd, lb, NULL);
}
static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */
{
Link *ln, *prev;
void *poin;
if (BLI_listbase_is_empty(lb)) return;
poin = newdataadr(fd, lb->first);
if (lb->first) {
oldnewmap_insert(fd->globmap, lb->first, poin, 0);
}
lb->first = poin;
ln = lb->first;
prev = NULL;
while (ln) {
poin = newdataadr(fd, ln->next);
if (ln->next) {
oldnewmap_insert(fd->globmap, ln->next, poin, 0);
}
ln->next = poin;
ln->prev = prev;
prev = ln;
ln = ln->next;
}
lb->last = prev;
}
static void test_pointer_array(FileData *fd, void **mat)
{
int64_t *lpoin, *lmat;
int *ipoin, *imat;
size_t len;
/* manually convert the pointer array in
* the old dna format to a pointer array in
* the new dna format.
*/
if (*mat) {
len = MEM_allocN_len(*mat) / fd->filesdna->pointer_size;
if (fd->filesdna->pointer_size == 8 && fd->memsdna->pointer_size == 4) {
ipoin = imat = MEM_malloc_arrayN(len, 4, "newmatar");
lpoin = *mat;
while (len-- > 0) {
if ((fd->flags & FD_FLAGS_SWITCH_ENDIAN))
BLI_endian_switch_int64(lpoin);
*ipoin = (int)((*lpoin) >> 3);
ipoin++;
lpoin++;
}
MEM_freeN(*mat);
*mat = imat;
}
if (fd->filesdna->pointer_size == 4 && fd->memsdna->pointer_size == 8) {
lpoin = lmat = MEM_malloc_arrayN(len, 8, "newmatar");
ipoin = *mat;
while (len-- > 0) {
*lpoin = *ipoin;
ipoin++;
lpoin++;
}
MEM_freeN(*mat);
*mat = lmat;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID Properties
* \{ */
static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd);
static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd);
static void IDP_DirectLinkIDPArray(IDProperty *prop, int switch_endian, FileData *fd)
{
IDProperty *array;
int i;
/* since we didn't save the extra buffer, set totallen to len */
prop->totallen = prop->len;
prop->data.pointer = newdataadr(fd, prop->data.pointer);
array = (IDProperty *)prop->data.pointer;
/* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared
* theres not really anything we can do to correct this, at least don't crash */
if (array == NULL) {
prop->len = 0;
prop->totallen = 0;
}
for (i = 0; i < prop->len; i++)
IDP_DirectLinkProperty(&array[i], switch_endian, fd);
}
static void IDP_DirectLinkArray(IDProperty *prop, int switch_endian, FileData *fd)
{
IDProperty **array;
int i;
/* since we didn't save the extra buffer, set totallen to len */
prop->totallen = prop->len;
prop->data.pointer = newdataadr(fd, prop->data.pointer);
if (prop->subtype == IDP_GROUP) {
test_pointer_array(fd, prop->data.pointer);
array = prop->data.pointer;
for (i = 0; i < prop->len; i++)
IDP_DirectLinkProperty(array[i], switch_endian, fd);
}
else if (prop->subtype == IDP_DOUBLE) {
if (switch_endian) {
BLI_endian_switch_double_array(prop->data.pointer, prop->len);
}
}
else {
if (switch_endian) {
/* also used for floats */
BLI_endian_switch_int32_array(prop->data.pointer, prop->len);
}
}
}
static void IDP_DirectLinkString(IDProperty *prop, FileData *fd)
{
/*since we didn't save the extra string buffer, set totallen to len.*/
prop->totallen = prop->len;
prop->data.pointer = newdataadr(fd, prop->data.pointer);
}
static void IDP_DirectLinkGroup(IDProperty *prop, int switch_endian, FileData *fd)
{
ListBase *lb = &prop->data.group;
IDProperty *loop;
link_list(fd, lb);
/*Link child id properties now*/
for (loop = prop->data.group.first; loop; loop = loop->next) {
IDP_DirectLinkProperty(loop, switch_endian, fd);
}
}
static void IDP_DirectLinkProperty(IDProperty *prop, int switch_endian, FileData *fd)
{
switch (prop->type) {
case IDP_GROUP:
IDP_DirectLinkGroup(prop, switch_endian, fd);
break;
case IDP_STRING:
IDP_DirectLinkString(prop, fd);
break;
case IDP_ARRAY:
IDP_DirectLinkArray(prop, switch_endian, fd);
break;
case IDP_IDPARRAY:
IDP_DirectLinkIDPArray(prop, switch_endian, fd);
break;
case IDP_DOUBLE:
/* erg, stupid doubles. since I'm storing them
* in the same field as int val; val2 in the
* IDPropertyData struct, they have to deal with
* endianness specifically
*
* in theory, val and val2 would've already been swapped
* if switch_endian is true, so we have to first unswap
* them then reswap them as a single 64-bit entity.
*/
if (switch_endian) {
BLI_endian_switch_int32(&prop->data.val);
BLI_endian_switch_int32(&prop->data.val2);
BLI_endian_switch_int64((int64_t *)&prop->data.val);
}
break;
case IDP_INT:
case IDP_FLOAT:
case IDP_ID:
break; /* Nothing special to do here. */
default:
/* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code,
* IDP are way too polymorphic to do it safely. */
printf("%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type);
/* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */
prop->type = IDP_INT;
prop->subtype = 0;
IDP_Int(prop) = 0;
}
}
#define IDP_DirectLinkGroup_OrFree(prop, switch_endian, fd) \
_IDP_DirectLinkGroup_OrFree(prop, switch_endian, fd, __func__)
static void _IDP_DirectLinkGroup_OrFree(
IDProperty **prop, int switch_endian, FileData *fd,
const char *caller_func_id)
{
if (*prop) {
if ((*prop)->type == IDP_GROUP) {
IDP_DirectLinkGroup(*prop, switch_endian, fd);
}
else {
/* corrupt file! */
printf("%s: found non group data, freeing type %d!\n",
caller_func_id, (*prop)->type);
/* don't risk id, data's likely corrupt. */
// IDP_FreeProperty(*prop);
*prop = NULL;
}
}
}
static void IDP_LibLinkProperty(IDProperty *prop, FileData *fd)
{
if (!prop)
return;
switch (prop->type) {
case IDP_ID: /* PointerProperty */
{
void *newaddr = newlibadr_us(fd, NULL, IDP_Id(prop));
if (IDP_Id(prop) && !newaddr && G.debug) {
printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
}
prop->data.pointer = newaddr;
break;
}
case IDP_IDPARRAY: /* CollectionProperty */
{
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
IDP_LibLinkProperty(&(idp_array[i]), fd);
}
break;
}
case IDP_GROUP: /* PointerProperty */
{
for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
IDP_LibLinkProperty(loop, fd);
}
break;
}
default:
break; /* Nothing to do for other IDProps. */
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Image Preview
* \{ */
static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_prv)
{
PreviewImage *prv = newdataadr(fd, old_prv);
if (prv) {
int i;
for (i = 0; i < NUM_ICON_SIZES; ++i) {
if (prv->rect[i]) {
prv->rect[i] = newdataadr(fd, prv->rect[i]);
}
prv->gputexture[i] = NULL;
}
prv->icon_id = 0;
prv->tag = 0;
}
return prv;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID
* \{ */
static void lib_link_id(FileData *fd, Main *main)
{
ListBase *lbarray[MAX_LIBARRAY];
int base_count, i;
base_count = set_listbasepointers(main, lbarray);
for (i = 0; i < base_count; i++) {
ListBase *lb = lbarray[i];
ID *id;
for (id = lb->first; id; id = id->next) {
if (id->override_static) {
id->override_static->reference = newlibadr_us(fd, id->lib, id->override_static->reference);
id->override_static->storage = newlibadr_us(fd, id->lib, id->override_static->storage);
}
}
}
}
static void direct_link_id_override_property_operation_cb(FileData *fd, void *data)
{
IDOverrideStaticPropertyOperation *opop = data;
opop->subitem_reference_name = newdataadr(fd, opop->subitem_reference_name);
opop->subitem_local_name = newdataadr(fd, opop->subitem_local_name);
}
static void direct_link_id_override_property_cb(FileData *fd, void *data)
{
IDOverrideStaticProperty *op = data;
op->rna_path = newdataadr(fd, op->rna_path);
link_list_ex(fd, &op->operations, direct_link_id_override_property_operation_cb);
}
static void direct_link_id(FileData *fd, ID *id)
{
/*link direct data of ID properties*/
if (id->properties) {
id->properties = newdataadr(fd, id->properties);
/* this case means the data was written incorrectly, it should not happen */
IDP_DirectLinkGroup_OrFree(&id->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
id->py_instance = NULL;
/* That way datablock reading not going through main read_libblock() function are still in a clear tag state.
* (glowering at certain nodetree fake datablock here...). */
id->tag = 0;
/* Link direct data of overrides. */
if (id->override_static) {
id->override_static = newdataadr(fd, id->override_static);
link_list_ex(fd, &id->override_static->properties, direct_link_id_override_property_cb);
}
DrawDataList *drawdata = DRW_drawdatalist_from_id(id);
if (drawdata) {
BLI_listbase_clear((ListBase *)drawdata);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read CurveMapping
* \{ */
/* cuma itself has been read! */
static void direct_link_curvemapping(FileData *fd, CurveMapping *cumap)
{
int a;
/* flag seems to be able to hang? Maybe old files... not bad to clear anyway */
cumap->flag &= ~CUMA_PREMULLED;
for (a = 0; a < CM_TOT; a++) {
cumap->cm[a].curve = newdataadr(fd, cumap->cm[a].curve);
cumap->cm[a].table = NULL;
cumap->cm[a].premultable = NULL;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Brush
* \{ */
/* library brush linking after fileread */
static void lib_link_brush(FileData *fd, Main *main)
{
/* only link ID pointers */
for (Brush *brush = main->brushes.first; brush; brush = brush->id.next) {
if (brush->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(brush->id.properties, fd);
/* brush->(mask_)mtex.obj is ignored on purpose? */
brush->mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mtex.tex);
brush->mask_mtex.tex = newlibadr_us(fd, brush->id.lib, brush->mask_mtex.tex);
brush->clone.image = newlibadr(fd, brush->id.lib, brush->clone.image);
brush->toggle_brush = newlibadr(fd, brush->id.lib, brush->toggle_brush);
brush->paint_curve = newlibadr_us(fd, brush->id.lib, brush->paint_curve);
/* link default grease pencil palette */
if (brush->gpencil_settings != NULL) {
brush->gpencil_settings->material = newlibadr_us(fd, brush->id.lib, brush->gpencil_settings->material);
}
brush->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_brush(FileData *fd, Brush *brush)
{
/* brush itself has been read */
/* fallof curve */
brush->curve = newdataadr(fd, brush->curve);
brush->gradient = newdataadr(fd, brush->gradient);
if (brush->curve)
direct_link_curvemapping(fd, brush->curve);
else
BKE_brush_curve_preset(brush, CURVE_PRESET_SHARP);
/* grease pencil */
brush->gpencil_settings = newdataadr(fd, brush->gpencil_settings);
if (brush->gpencil_settings != NULL) {
brush->gpencil_settings->curve_sensitivity = newdataadr(fd, brush->gpencil_settings->curve_sensitivity);
brush->gpencil_settings->curve_strength = newdataadr(fd, brush->gpencil_settings->curve_strength);
brush->gpencil_settings->curve_jitter = newdataadr(fd, brush->gpencil_settings->curve_jitter);
if (brush->gpencil_settings->curve_sensitivity)
direct_link_curvemapping(fd, brush->gpencil_settings->curve_sensitivity);
if (brush->gpencil_settings->curve_strength)
direct_link_curvemapping(fd, brush->gpencil_settings->curve_strength);
if (brush->gpencil_settings->curve_jitter)
direct_link_curvemapping(fd, brush->gpencil_settings->curve_jitter);
}
brush->preview = NULL;
brush->icon_imbuf = NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Palette
* \{ */
static void lib_link_palette(FileData *fd, Main *main)
{
/* only link ID pointers */
for (Palette *palette = main->palettes.first; palette; palette = palette->id.next) {
if (palette->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(palette->id.properties, fd);
palette->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_palette(FileData *fd, Palette *palette)
{
/* palette itself has been read */
link_list(fd, &palette->colors);
}
static void lib_link_paint_curve(FileData *fd, Main *main)
{
/* only link ID pointers */
for (PaintCurve *pc = main->paintcurves.first; pc; pc = pc->id.next) {
if (pc->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(pc->id.properties, fd);
pc->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_paint_curve(FileData *fd, PaintCurve *pc)
{
pc->points = newdataadr(fd, pc->points);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read PackedFile
* \{ */
static PackedFile *direct_link_packedfile(FileData *fd, PackedFile *oldpf)
{
PackedFile *pf = newpackedadr(fd, oldpf);
if (pf) {
pf->data = newpackedadr(fd, pf->data);
}
return pf;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Animation (legacy for version patching)
* \{ */
// XXX deprecated - old animation system
static void lib_link_ipo(FileData *fd, Main *main)
{
Ipo *ipo;
for (ipo = main->ipo.first; ipo; ipo = ipo->id.next) {
if (ipo->id.tag & LIB_TAG_NEED_LINK) {
IpoCurve *icu;
for (icu = ipo->curve.first; icu; icu = icu->next) {
if (icu->driver)
icu->driver->ob = newlibadr(fd, ipo->id.lib, icu->driver->ob);
}
ipo->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
// XXX deprecated - old animation system
static void direct_link_ipo(FileData *fd, Ipo *ipo)
{
IpoCurve *icu;
link_list(fd, &(ipo->curve));
for (icu = ipo->curve.first; icu; icu = icu->next) {
icu->bezt = newdataadr(fd, icu->bezt);
icu->bp = newdataadr(fd, icu->bp);
icu->driver = newdataadr(fd, icu->driver);
}
}
// XXX deprecated - old animation system
static void lib_link_nlastrips(FileData *fd, ID *id, ListBase *striplist)
{
bActionStrip *strip;
bActionModifier *amod;
for (strip = striplist->first; strip; strip = strip->next) {
strip->object = newlibadr(fd, id->lib, strip->object);
strip->act = newlibadr_us(fd, id->lib, strip->act);
strip->ipo = newlibadr(fd, id->lib, strip->ipo);
for (amod = strip->modifiers.first; amod; amod = amod->next)
amod->ob = newlibadr(fd, id->lib, amod->ob);
}
}
// XXX deprecated - old animation system
static void direct_link_nlastrips(FileData *fd, ListBase *strips)
{
bActionStrip *strip;
link_list(fd, strips);
for (strip = strips->first; strip; strip = strip->next)
link_list(fd, &strip->modifiers);
}
// XXX deprecated - old animation system
static void lib_link_constraint_channels(FileData *fd, ID *id, ListBase *chanbase)
{
bConstraintChannel *chan;
for (chan = chanbase->first; chan; chan = chan->next) {
chan->ipo = newlibadr_us(fd, id->lib, chan->ipo);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Action
* \{ */
static void lib_link_fmodifiers(FileData *fd, ID *id, ListBase *list)
{
FModifier *fcm;
for (fcm = list->first; fcm; fcm = fcm->next) {
/* data for specific modifiers */
switch (fcm->type) {
case FMODIFIER_TYPE_PYTHON:
{
FMod_Python *data = (FMod_Python *)fcm->data;
data->script = newlibadr(fd, id->lib, data->script);
break;
}
}
}
}
static void lib_link_fcurves(FileData *fd, ID *id, ListBase *list)
{
FCurve *fcu;
if (list == NULL)
return;
/* relink ID-block references... */
for (fcu = list->first; fcu; fcu = fcu->next) {
/* driver data */
if (fcu->driver) {
ChannelDriver *driver = fcu->driver;
DriverVar *dvar;
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
DRIVER_TARGETS_LOOPER_BEGIN(dvar)
{
/* only relink if still used */
if (tarIndex < dvar->num_targets)
dtar->id = newlibadr(fd, id->lib, dtar->id);
else
dtar->id = NULL;
}
DRIVER_TARGETS_LOOPER_END;
}
}
/* modifiers */
lib_link_fmodifiers(fd, id, &fcu->modifiers);
}
}
/* NOTE: this assumes that link_list has already been called on the list */
static void direct_link_fmodifiers(FileData *fd, ListBase *list, FCurve *curve)
{
FModifier *fcm;
for (fcm = list->first; fcm; fcm = fcm->next) {
/* relink general data */
fcm->data = newdataadr(fd, fcm->data);
fcm->curve = curve;
/* do relinking of data for specific types */
switch (fcm->type) {
case FMODIFIER_TYPE_GENERATOR:
{
FMod_Generator *data = (FMod_Generator *)fcm->data;
data->coefficients = newdataadr(fd, data->coefficients);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_float_array(data->coefficients, data->arraysize);
}
break;
}
case FMODIFIER_TYPE_ENVELOPE:
{
FMod_Envelope *data = (FMod_Envelope *)fcm->data;
data->data = newdataadr(fd, data->data);
break;
}
case FMODIFIER_TYPE_PYTHON:
{
FMod_Python *data = (FMod_Python *)fcm->data;
data->prop = newdataadr(fd, data->prop);
IDP_DirectLinkGroup_OrFree(&data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
break;
}
}
}
}
/* NOTE: this assumes that link_list has already been called on the list */
static void direct_link_fcurves(FileData *fd, ListBase *list)
{
FCurve *fcu;
/* link F-Curve data to F-Curve again (non ID-libs) */
for (fcu = list->first; fcu; fcu = fcu->next) {
/* curve data */
fcu->bezt = newdataadr(fd, fcu->bezt);
fcu->fpt = newdataadr(fd, fcu->fpt);
/* rna path */
fcu->rna_path = newdataadr(fd, fcu->rna_path);
/* group */
fcu->grp = newdataadr(fd, fcu->grp);
/* clear disabled flag - allows disabled drivers to be tried again ([#32155]),
* but also means that another method for "reviving disabled F-Curves" exists
*/
fcu->flag &= ~FCURVE_DISABLED;
/* driver */
fcu->driver = newdataadr(fd, fcu->driver);
if (fcu->driver) {
ChannelDriver *driver = fcu->driver;
DriverVar *dvar;
/* compiled expression data will need to be regenerated (old pointer may still be set here) */
driver->expr_comp = NULL;
driver->expr_simple = NULL;
/* give the driver a fresh chance - the operating environment may be different now
* (addons, etc. may be different) so the driver namespace may be sane now [#32155]
*/
driver->flag &= ~DRIVER_FLAG_INVALID;
/* relink variables, targets and their paths */
link_list(fd, &driver->variables);
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
DRIVER_TARGETS_LOOPER_BEGIN(dvar)
{
/* only relink the targets being used */
if (tarIndex < dvar->num_targets)
dtar->rna_path = newdataadr(fd, dtar->rna_path);
else
dtar->rna_path = NULL;
}
DRIVER_TARGETS_LOOPER_END;
}
}
/* modifiers */
link_list(fd, &fcu->modifiers);
direct_link_fmodifiers(fd, &fcu->modifiers, fcu);
}
}
static void lib_link_action(FileData *fd, Main *main)
{
for (bAction *act = main->actions.first; act; act = act->id.next) {
if (act->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(act->id.properties, fd);
// XXX deprecated - old animation system <<<
for (bActionChannel *chan = act->chanbase.first; chan; chan = chan->next) {
chan->ipo = newlibadr_us(fd, act->id.lib, chan->ipo);
lib_link_constraint_channels(fd, &act->id, &chan->constraintChannels);
}
// >>> XXX deprecated - old animation system
lib_link_fcurves(fd, &act->id, &act->curves);
for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) {
if (marker->camera) {
marker->camera = newlibadr(fd, act->id.lib, marker->camera);
}
}
act->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_action(FileData *fd, bAction *act)
{
bActionChannel *achan; // XXX deprecated - old animation system
bActionGroup *agrp;
link_list(fd, &act->curves);
link_list(fd, &act->chanbase); // XXX deprecated - old animation system
link_list(fd, &act->groups);
link_list(fd, &act->markers);
// XXX deprecated - old animation system <<<
for (achan = act->chanbase.first; achan; achan = achan->next) {
achan->grp = newdataadr(fd, achan->grp);
link_list(fd, &achan->constraintChannels);
}
// >>> XXX deprecated - old animation system
direct_link_fcurves(fd, &act->curves);
for (agrp = act->groups.first; agrp; agrp = agrp->next) {
agrp->channels.first = newdataadr(fd, agrp->channels.first);
agrp->channels.last = newdataadr(fd, agrp->channels.last);
}
}
static void lib_link_nladata_strips(FileData *fd, ID *id, ListBase *list)
{
NlaStrip *strip;
for (strip = list->first; strip; strip = strip->next) {
/* check strip's children */
lib_link_nladata_strips(fd, id, &strip->strips);
/* check strip's F-Curves */
lib_link_fcurves(fd, id, &strip->fcurves);
/* reassign the counted-reference to action */
strip->act = newlibadr_us(fd, id->lib, strip->act);
/* fix action id-root (i.e. if it comes from a pre 2.57 .blend file) */
if ((strip->act) && (strip->act->idroot == 0))
strip->act->idroot = GS(id->name);
}
}
static void lib_link_nladata(FileData *fd, ID *id, ListBase *list)
{
NlaTrack *nlt;
/* we only care about the NLA strips inside the tracks */
for (nlt = list->first; nlt; nlt = nlt->next) {
lib_link_nladata_strips(fd, id, &nlt->strips);
}
}
/* This handles Animato NLA-Strips linking
* NOTE: this assumes that link_list has already been called on the list
*/
static void direct_link_nladata_strips(FileData *fd, ListBase *list)
{
NlaStrip *strip;
for (strip = list->first; strip; strip = strip->next) {
/* strip's child strips */
link_list(fd, &strip->strips);
direct_link_nladata_strips(fd, &strip->strips);
/* strip's F-Curves */
link_list(fd, &strip->fcurves);
direct_link_fcurves(fd, &strip->fcurves);
/* strip's F-Modifiers */
link_list(fd, &strip->modifiers);
direct_link_fmodifiers(fd, &strip->modifiers, NULL);
}
}
/* NOTE: this assumes that link_list has already been called on the list */
static void direct_link_nladata(FileData *fd, ListBase *list)
{
NlaTrack *nlt;
for (nlt = list->first; nlt; nlt = nlt->next) {
/* relink list of strips */
link_list(fd, &nlt->strips);
/* relink strip data */
direct_link_nladata_strips(fd, &nlt->strips);
}
}
/* ------- */
static void lib_link_keyingsets(FileData *fd, ID *id, ListBase *list)
{
KeyingSet *ks;
KS_Path *ksp;
/* here, we're only interested in the ID pointer stored in some of the paths */
for (ks = list->first; ks; ks = ks->next) {
for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
ksp->id = newlibadr(fd, id->lib, ksp->id);
}
}
}
/* NOTE: this assumes that link_list has already been called on the list */
static void direct_link_keyingsets(FileData *fd, ListBase *list)
{
KeyingSet *ks;
KS_Path *ksp;
/* link KeyingSet data to KeyingSet again (non ID-libs) */
for (ks = list->first; ks; ks = ks->next) {
/* paths */
link_list(fd, &ks->paths);
for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
/* rna path */
ksp->rna_path = newdataadr(fd, ksp->rna_path);
}
}
}
/* ------- */
static void lib_link_animdata(FileData *fd, ID *id, AnimData *adt)
{
if (adt == NULL)
return;
/* link action data */
adt->action = newlibadr_us(fd, id->lib, adt->action);
adt->tmpact = newlibadr_us(fd, id->lib, adt->tmpact);
/* fix action id-roots (i.e. if they come from a pre 2.57 .blend file) */
if ((adt->action) && (adt->action->idroot == 0))
adt->action->idroot = GS(id->name);
if ((adt->tmpact) && (adt->tmpact->idroot == 0))
adt->tmpact->idroot = GS(id->name);
/* link drivers */
lib_link_fcurves(fd, id, &adt->drivers);
/* overrides don't have lib-link for now, so no need to do anything */
/* link NLA-data */
lib_link_nladata(fd, id, &adt->nla_tracks);
}
static void direct_link_animdata(FileData *fd, AnimData *adt)
{
/* NOTE: must have called newdataadr already before doing this... */
if (adt == NULL)
return;
/* link drivers */
link_list(fd, &adt->drivers);
direct_link_fcurves(fd, &adt->drivers);
adt->driver_array = NULL;
/* link overrides */
// TODO...
/* link NLA-data */
link_list(fd, &adt->nla_tracks);
direct_link_nladata(fd, &adt->nla_tracks);
/* relink active track/strip - even though strictly speaking this should only be used
* if we're in 'tweaking mode', we need to be able to have this loaded back for
* undo, but also since users may not exit tweakmode before saving (#24535)
*/
// TODO: it's not really nice that anyone should be able to save the file in this
// state, but it's going to be too hard to enforce this single case...
adt->act_track = newdataadr(fd, adt->act_track);
adt->actstrip = newdataadr(fd, adt->actstrip);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: CacheFiles
* \{ */
static void lib_link_cachefiles(FileData *fd, Main *bmain)
{
/* only link ID pointers */
for (CacheFile *cache_file = bmain->cachefiles.first; cache_file; cache_file = cache_file->id.next) {
if (cache_file->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(cache_file->id.properties, fd);
lib_link_animdata(fd, &cache_file->id, cache_file->adt);
cache_file->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_cachefile(FileData *fd, CacheFile *cache_file)
{
BLI_listbase_clear(&cache_file->object_paths);
cache_file->handle = NULL;
cache_file->handle_mutex = NULL;
/* relink animdata */
cache_file->adt = newdataadr(fd, cache_file->adt);
direct_link_animdata(fd, cache_file->adt);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: WorkSpace
* \{ */
static void lib_link_workspaces(FileData *fd, Main *bmain)
{
for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
ListBase *layouts = BKE_workspace_layouts_get(workspace);
ID *id = (ID *)workspace;
if ((id->tag & LIB_TAG_NEED_LINK) == 0) {
continue;
}
IDP_LibLinkProperty(id->properties, fd);
id_us_ensure_real(id);
for (WorkSpaceLayout *layout = layouts->first, *layout_next; layout; layout = layout_next) {
layout->screen = newlibadr_us(fd, id->lib, layout->screen);
layout_next = layout->next;
if (layout->screen) {
if (ID_IS_LINKED(id)) {
layout->screen->winid = 0;
if (layout->screen->temp) {
/* delete temp layouts when appending */
BKE_workspace_layout_remove(bmain, workspace, layout);
}
}
}
}
id->tag &= ~LIB_TAG_NEED_LINK;
}
}
static void direct_link_workspace(FileData *fd, WorkSpace *workspace, const Main *main)
{
link_list(fd, BKE_workspace_layouts_get(workspace));
link_list(fd, &workspace->hook_layout_relations);
link_list(fd, &workspace->owner_ids);
link_list(fd, &workspace->tools);
for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first;
relation;
relation = relation->next)
{
relation->parent = newglobadr(fd, relation->parent); /* data from window - need to access through global oldnew-map */
relation->value = newdataadr(fd, relation->value);
}
/* Same issue/fix as in direct_link_workspace_link_scene_data: Can't read workspace data
* when reading windows, so have to update windows after/when reading workspaces. */
for (wmWindowManager *wm = main->wm.first; wm; wm = wm->id.next) {
for (wmWindow *win = wm->windows.first; win; win = win->next) {
WorkSpaceLayout *act_layout = newdataadr(fd, BKE_workspace_active_layout_get(win->workspace_hook));
if (act_layout) {
BKE_workspace_active_layout_set(win->workspace_hook, act_layout);
}
}
}
for (bToolRef *tref = workspace->tools.first; tref; tref = tref->next) {
tref->runtime = NULL;
tref->properties = newdataadr(fd, tref->properties);
IDP_DirectLinkGroup_OrFree(&tref->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
workspace->status_text = NULL;
}
static void lib_link_workspace_instance_hook(FileData *fd, WorkSpaceInstanceHook *hook, ID *id)
{
WorkSpace *workspace = BKE_workspace_active_get(hook);
BKE_workspace_active_set(hook, newlibadr(fd, id->lib, workspace));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Node Tree
* \{ */
/* Single node tree (also used for material/scene trees), ntree is not NULL */
static void lib_link_ntree(FileData *fd, ID *id, bNodeTree *ntree)
{
bNode *node;
bNodeSocket *sock;
IDP_LibLinkProperty(ntree->id.properties, fd);
lib_link_animdata(fd, &ntree->id, ntree->adt);
ntree->gpd = newlibadr_us(fd, id->lib, ntree->gpd);
for (node = ntree->nodes.first; node; node = node->next) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(node->prop, fd);
node->id = newlibadr_us(fd, id->lib, node->id);
for (sock = node->inputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = node->outputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, fd);
}
}
for (sock = ntree->inputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, fd);
}
for (sock = ntree->outputs.first; sock; sock = sock->next) {
IDP_LibLinkProperty(sock->prop, fd);
}
}
/* library ntree linking after fileread */
static void lib_link_nodetree(FileData *fd, Main *main)
{
/* only link ID pointers */
for (bNodeTree *ntree = main->nodetrees.first; ntree; ntree = ntree->id.next) {
if (ntree->id.tag & LIB_TAG_NEED_LINK) {
lib_link_ntree(fd, &ntree->id, ntree);
ntree->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/* updates group node socket identifier so that
* external links to/from the group node are preserved.
*/
static void lib_node_do_versions_group_indices(bNode *gnode)
{
bNodeTree *ngroup = (bNodeTree *)gnode->id;
bNodeSocket *sock;
bNodeLink *link;
for (sock = gnode->outputs.first; sock; sock = sock->next) {
int old_index = sock->to_index;
for (link = ngroup->links.first; link; link = link->next) {
if (link->tonode == NULL && link->fromsock->own_index == old_index) {
strcpy(sock->identifier, link->fromsock->identifier);
/* deprecated */
sock->own_index = link->fromsock->own_index;
sock->to_index = 0;
sock->groupsock = NULL;
}
}
}
for (sock = gnode->inputs.first; sock; sock = sock->next) {
int old_index = sock->to_index;
for (link = ngroup->links.first; link; link = link->next) {
if (link->fromnode == NULL && link->tosock->own_index == old_index) {
strcpy(sock->identifier, link->tosock->identifier);
/* deprecated */
sock->own_index = link->tosock->own_index;
sock->to_index = 0;
sock->groupsock = NULL;
}
}
}
}
/* verify types for nodes and groups, all data has to be read */
/* open = 0: appending/linking, open = 1: open new file (need to clean out dynamic
* typedefs */
static void lib_verify_nodetree(Main *main, int UNUSED(open))
{
/* this crashes blender on undo/redo */
#if 0
if (open == 1) {
reinit_nodesystem();
}
#endif
/* set node->typeinfo pointers */
FOREACH_NODETREE_BEGIN (main, ntree, id) {
ntreeSetTypes(NULL, ntree);
} FOREACH_NODETREE_END;
/* verify static socket templates */
FOREACH_NODETREE_BEGIN (main, ntree, id) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next)
node_verify_socket_templates(ntree, node);
} FOREACH_NODETREE_END;
{
bool has_old_groups = false;
/* XXX this should actually be part of do_versions, but since we need
* finished library linking, it is not possible there. Instead in do_versions
* we have set the NTREE_DO_VERSIONS_GROUP_EXPOSE_2_56_2 flag, so at this point we can do the
* actual group node updates.
*/
for (bNodeTree *ntree = main->nodetrees.first; ntree; ntree = ntree->id.next) {
if (ntree->flag & NTREE_DO_VERSIONS_GROUP_EXPOSE_2_56_2) {
has_old_groups = 1;
}
}
if (has_old_groups) {
FOREACH_NODETREE_BEGIN (main, ntree, id) {
/* updates external links for all group nodes in a tree */
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (node->type == NODE_GROUP) {
bNodeTree *ngroup = (bNodeTree *)node->id;
if (ngroup && (ngroup->flag & NTREE_DO_VERSIONS_GROUP_EXPOSE_2_56_2))
lib_node_do_versions_group_indices(node);
}
}
} FOREACH_NODETREE_END;
}
for (bNodeTree *ntree = main->nodetrees.first; ntree; ntree = ntree->id.next) {
ntree->flag &= ~NTREE_DO_VERSIONS_GROUP_EXPOSE_2_56_2;
}
}
{
/* Convert the previously used ntree->inputs/ntree->outputs lists to interface nodes.
* Pre 2.56.2 node trees automatically have all unlinked sockets exposed already
* (see NTREE_DO_VERSIONS_GROUP_EXPOSE_2_56_2).
*
* XXX this should actually be part of do_versions,
* but needs valid typeinfo pointers to create interface nodes.
*
* Note: theoretically only needed in node groups (main->nodetree),
* but due to a temporary bug such links could have been added in all trees,
* so have to clean up all of them ...
*/
FOREACH_NODETREE_BEGIN(main, ntree, id) {
if (ntree->flag & NTREE_DO_VERSIONS_CUSTOMNODES_GROUP) {
bNode *input_node = NULL, *output_node = NULL;
int num_inputs = 0, num_outputs = 0;
bNodeLink *link, *next_link;
/* Only create new interface nodes for actual older files.
* New file versions already have input/output nodes with duplicate links,
* in that case just remove the invalid links.
*/
const bool create_io_nodes = (ntree->flag & NTREE_DO_VERSIONS_CUSTOMNODES_GROUP_CREATE_INTERFACE) != 0;
float input_locx = 1000000.0f, input_locy = 0.0f;
float output_locx = -1000000.0f, output_locy = 0.0f;
/* rough guess, not nice but we don't have access to UI constants here ... */
static const float offsetx = 42 + 3 * 20 + 20;
/*static const float offsety = 0.0f;*/
if (create_io_nodes) {
if (ntree->inputs.first)
input_node = nodeAddStaticNode(NULL, ntree, NODE_GROUP_INPUT);
if (ntree->outputs.first)
output_node = nodeAddStaticNode(NULL, ntree, NODE_GROUP_OUTPUT);
}
/* Redirect links from/to the node tree interface to input/output node.
* If the fromnode/tonode pointers are NULL, this means a link from/to
* the ntree interface sockets, which need to be redirected to new interface nodes.
*/
for (link = ntree->links.first; link; link = next_link) {
bool free_link = false;
next_link = link->next;
if (link->fromnode == NULL) {
if (input_node) {
link->fromnode = input_node;
link->fromsock = node_group_input_find_socket(input_node, link->fromsock->identifier);
++num_inputs;
if (link->tonode) {
if (input_locx > link->tonode->locx - offsetx)
input_locx = link->tonode->locx - offsetx;
input_locy += link->tonode->locy;
}
}
else {
free_link = true;
}
}
if (link->tonode == NULL) {
if (output_node) {
link->tonode = output_node;
link->tosock = node_group_output_find_socket(output_node, link->tosock->identifier);
++num_outputs;
if (link->fromnode) {
if (output_locx < link->fromnode->locx + offsetx)
output_locx = link->fromnode->locx + offsetx;
output_locy += link->fromnode->locy;
}
}
else {
free_link = true;
}
}
if (free_link)
nodeRemLink(ntree, link);
}
if (num_inputs > 0) {
input_locy /= num_inputs;
input_node->locx = input_locx;
input_node->locy = input_locy;
}
if (num_outputs > 0) {
output_locy /= num_outputs;
output_node->locx = output_locx;
output_node->locy = output_locy;
}
/* clear do_versions flags */
ntree->flag &= ~(NTREE_DO_VERSIONS_CUSTOMNODES_GROUP | NTREE_DO_VERSIONS_CUSTOMNODES_GROUP_CREATE_INTERFACE);
}
}
FOREACH_NODETREE_END;
}
/* verify all group user nodes */
for (bNodeTree *ntree = main->nodetrees.first; ntree; ntree = ntree->id.next) {
ntreeVerifyNodes(main, &ntree->id);
}
/* make update calls where necessary */
{
FOREACH_NODETREE_BEGIN(main, ntree, id) {
/* make an update call for the tree */
ntreeUpdateTree(main, ntree);
} FOREACH_NODETREE_END;
}
}
static void direct_link_node_socket(FileData *fd, bNodeSocket *sock)
{
sock->prop = newdataadr(fd, sock->prop);
IDP_DirectLinkGroup_OrFree(&sock->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
sock->link = newdataadr(fd, sock->link);
sock->typeinfo = NULL;
sock->storage = newdataadr(fd, sock->storage);
sock->default_value = newdataadr(fd, sock->default_value);
sock->cache = NULL;
}
/* ntree itself has been read! */
static void direct_link_nodetree(FileData *fd, bNodeTree *ntree)
{
/* note: writing and reading goes in sync, for speed */
bNode *node;
bNodeSocket *sock;
bNodeLink *link;
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;
ntree->duplilock = NULL;
ntree->adt = newdataadr(fd, ntree->adt);
direct_link_animdata(fd, ntree->adt);
ntree->id.recalc &= ~ID_RECALC_ALL;
link_list(fd, &ntree->nodes);
for (node = ntree->nodes.first; node; node = node->next) {
node->typeinfo = NULL;
link_list(fd, &node->inputs);
link_list(fd, &node->outputs);
node->prop = newdataadr(fd, node->prop);
IDP_DirectLinkGroup_OrFree(&node->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
link_list(fd, &node->internal_links);
for (link = node->internal_links.first; link; link = link->next) {
link->fromnode = newdataadr(fd, link->fromnode);
link->fromsock = newdataadr(fd, link->fromsock);
link->tonode = newdataadr(fd, link->tonode);
link->tosock = newdataadr(fd, link->tosock);
}
if (node->type == CMP_NODE_MOVIEDISTORTION) {
node->storage = newmclipadr(fd, node->storage);
}
else {
node->storage = newdataadr(fd, node->storage);
}
if (node->storage) {
/* could be handlerized at some point */
if (ntree->type == NTREE_SHADER) {
if (node->type == SH_NODE_CURVE_VEC || node->type == SH_NODE_CURVE_RGB) {
direct_link_curvemapping(fd, node->storage);
}
else if (node->type == SH_NODE_SCRIPT) {
NodeShaderScript *nss = (NodeShaderScript *)node->storage;
nss->bytecode = newdataadr(fd, nss->bytecode);
}
else if (node->type == SH_NODE_TEX_POINTDENSITY) {
NodeShaderTexPointDensity *npd = (NodeShaderTexPointDensity *)node->storage;
memset(&npd->pd, 0, sizeof(npd->pd));
}
else if (node->type == SH_NODE_TEX_IMAGE) {
NodeTexImage *tex = (NodeTexImage *)node->storage;
tex->iuser.ok = 1;
tex->iuser.scene = NULL;
}
else if (node->type == SH_NODE_TEX_ENVIRONMENT) {
NodeTexEnvironment *tex = (NodeTexEnvironment *)node->storage;
tex->iuser.ok = 1;
tex->iuser.scene = NULL;
}
}
else if (ntree->type == NTREE_COMPOSIT) {
if (ELEM(node->type, CMP_NODE_TIME, CMP_NODE_CURVE_VEC, CMP_NODE_CURVE_RGB, CMP_NODE_HUECORRECT)) {
direct_link_curvemapping(fd, node->storage);
}
else if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
ImageUser *iuser = node->storage;
iuser->ok = 1;
iuser->scene = NULL;
}
else if (node->type == CMP_NODE_CRYPTOMATTE) {
NodeCryptomatte *nc = (NodeCryptomatte *)node->storage;
nc->matte_id = newdataadr(fd, nc->matte_id);
}
}
else if (ntree->type == NTREE_TEXTURE) {
if (node->type == TEX_NODE_CURVE_RGB || node->type == TEX_NODE_CURVE_TIME) {
direct_link_curvemapping(fd, node->storage);
}
else if (node->type == TEX_NODE_IMAGE) {
ImageUser *iuser = node->storage;
iuser->ok = 1;
iuser->scene = NULL;
}
}
}
}
link_list(fd, &ntree->links);
/* and we connect the rest */
for (node = ntree->nodes.first; node; node = node->next) {
node->parent = newdataadr(fd, node->parent);
node->lasty = 0;
for (sock = node->inputs.first; sock; sock = sock->next)
direct_link_node_socket(fd, sock);
for (sock = node->outputs.first; sock; sock = sock->next)
direct_link_node_socket(fd, sock);
}
/* interface socket lists */
link_list(fd, &ntree->inputs);
link_list(fd, &ntree->outputs);
for (sock = ntree->inputs.first; sock; sock = sock->next)
direct_link_node_socket(fd, sock);
for (sock = ntree->outputs.first; sock; sock = sock->next)
direct_link_node_socket(fd, sock);
for (link = ntree->links.first; link; link = link->next) {
link->fromnode = newdataadr(fd, link->fromnode);
link->tonode = newdataadr(fd, link->tonode);
link->fromsock = newdataadr(fd, link->fromsock);
link->tosock = newdataadr(fd, link->tosock);
}
#if 0
if (ntree->previews) {
bNodeInstanceHash *new_previews = BKE_node_instance_hash_new("node previews");
bNodeInstanceHashIterator iter;
NODE_INSTANCE_HASH_ITER(iter, ntree->previews) {
bNodePreview *preview = BKE_node_instance_hash_iterator_get_value(&iter);
if (preview) {
bNodePreview *new_preview = newimaadr(fd, preview);
if (new_preview) {
bNodeInstanceKey key = BKE_node_instance_hash_iterator_get_key(&iter);
BKE_node_instance_hash_insert(new_previews, key, new_preview);
}
}
}
BKE_node_instance_hash_free(ntree->previews, NULL);
ntree->previews = new_previews;
}
#else
/* XXX TODO */
ntree->previews = NULL;
#endif
/* type verification is in lib-link */
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Armature
* \{ */
/* temp struct used to transport needed info to lib_link_constraint_cb() */
typedef struct tConstraintLinkData {
FileData *fd;
ID *id;
} tConstraintLinkData;
/* callback function used to relink constraint ID-links */
static void lib_link_constraint_cb(bConstraint *UNUSED(con), ID **idpoin, bool is_reference, void *userdata)
{
tConstraintLinkData *cld = (tConstraintLinkData *)userdata;
/* for reference types, we need to increment the usercounts on load... */
if (is_reference) {
/* reference type - with usercount */
*idpoin = newlibadr_us(cld->fd, cld->id->lib, *idpoin);
}
else {
/* target type - no usercount needed */
*idpoin = newlibadr(cld->fd, cld->id->lib, *idpoin);
}
}
static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist)
{
tConstraintLinkData cld;
bConstraint *con;
/* legacy fixes */
for (con = conlist->first; con; con = con->next) {
/* patch for error introduced by changing constraints (dunno how) */
/* if con->data type changes, dna cannot resolve the pointer! (ton) */
if (con->data == NULL) {
con->type = CONSTRAINT_TYPE_NULL;
}
/* own ipo, all constraints have it */
con->ipo = newlibadr_us(fd, id->lib, con->ipo); // XXX deprecated - old animation system
/* If linking from a library, clear 'local' static override flag. */
if (id->lib != NULL) {
con->flag &= ~CONSTRAINT_STATICOVERRIDE_LOCAL;
}
}
/* relink all ID-blocks used by the constraints */
cld.fd = fd;
cld.id = id;
BKE_constraints_id_loop(conlist, lib_link_constraint_cb, &cld);
}
static void direct_link_constraints(FileData *fd, ListBase *lb)
{
bConstraint *con;
link_list(fd, lb);
for (con = lb->first; con; con = con->next) {
con->data = newdataadr(fd, con->data);
switch (con->type) {
case CONSTRAINT_TYPE_PYTHON:
{
bPythonConstraint *data = con->data;
link_list(fd, &data->targets);
data->prop = newdataadr(fd, data->prop);
IDP_DirectLinkGroup_OrFree(&data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
break;
}
case CONSTRAINT_TYPE_ARMATURE:
{
bArmatureConstraint *data = con->data;
link_list(fd, &data->targets);
break;
}
case CONSTRAINT_TYPE_SPLINEIK:
{
bSplineIKConstraint *data = con->data;
data->points = newdataadr(fd, data->points);
break;
}
case CONSTRAINT_TYPE_KINEMATIC:
{
bKinematicConstraint *data = con->data;
con->lin_error = 0.f;
con->rot_error = 0.f;
/* version patch for runtime flag, was not cleared in some case */
data->flag &= ~CONSTRAINT_IK_AUTO;
break;
}
case CONSTRAINT_TYPE_CHILDOF:
{
/* XXX version patch, in older code this flag wasn't always set, and is inherent to type */
if (con->ownspace == CONSTRAINT_SPACE_POSE)
con->flag |= CONSTRAINT_SPACEONCE;
break;
}
case CONSTRAINT_TYPE_TRANSFORM_CACHE:
{
bTransformCacheConstraint *data = con->data;
data->reader = NULL;
}
}
}
}
static void lib_link_pose(FileData *fd, Main *bmain, Object *ob, bPose *pose)
{
bArmature *arm = ob->data;
if (!pose || !arm)
return;
/* always rebuild to match proxy or lib changes, but on Undo */
bool rebuild = false;
if (fd->memfile == NULL) {
if (ob->proxy || ob->id.lib != arm->id.lib) {
rebuild = true;
}
}
/* avoid string */
GHash *bone_hash = BKE_armature_bone_from_name_map(arm);
if (ob->proxy) {
/* sync proxy layer */
if (pose->proxy_layer)
arm->layer = pose->proxy_layer;
/* sync proxy active bone */
if (pose->proxy_act_bone[0]) {
Bone *bone = BLI_ghash_lookup(bone_hash, pose->proxy_act_bone);
if (bone) {
arm->act_bone = bone;
}
}
}
for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
lib_link_constraints(fd, (ID *)ob, &pchan->constraints);
pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name);
IDP_LibLinkProperty(pchan->prop, fd);
pchan->custom = newlibadr_us(fd, arm->id.lib, pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
rebuild = true;
}
else if ((ob->id.lib == NULL) && arm->id.lib) {
/* local pose selection copied to armature, bit hackish */
pchan->bone->flag &= ~BONE_SELECTED;
pchan->bone->flag |= pchan->selectflag;
}
}
BLI_ghash_free(bone_hash, NULL, NULL);
if (rebuild) {
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
BKE_pose_tag_recalc(bmain, pose);
}
}
static void lib_link_bones(FileData *fd, Bone *bone)
{
IDP_LibLinkProperty(bone->prop, fd);
for (Bone *curbone = bone->childbase.first; curbone; curbone = curbone->next) {
lib_link_bones(fd, curbone);
}
}
static void lib_link_armature(FileData *fd, Main *main)
{
for (bArmature *arm = main->armatures.first; arm; arm = arm->id.next) {
if (arm->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(arm->id.properties, fd);
lib_link_animdata(fd, &arm->id, arm->adt);
for (Bone *curbone = arm->bonebase.first; curbone; curbone = curbone->next) {
lib_link_bones(fd, curbone);
}
arm->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_bones(FileData *fd, Bone *bone)
{
Bone *child;
bone->parent = newdataadr(fd, bone->parent);
bone->prop = newdataadr(fd, bone->prop);
IDP_DirectLinkGroup_OrFree(&bone->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
bone->bbone_next = newdataadr(fd, bone->bbone_next);
bone->bbone_prev = newdataadr(fd, bone->bbone_prev);
bone->flag &= ~BONE_DRAW_ACTIVE;
link_list(fd, &bone->childbase);
for (child = bone->childbase.first; child; child = child->next)
direct_link_bones(fd, child);
}
static void direct_link_armature(FileData *fd, bArmature *arm)
{
Bone *bone;
link_list(fd, &arm->bonebase);
arm->edbo = NULL;
arm->adt = newdataadr(fd, arm->adt);
direct_link_animdata(fd, arm->adt);
for (bone = arm->bonebase.first; bone; bone = bone->next) {
direct_link_bones(fd, bone);
}
arm->act_bone = newdataadr(fd, arm->act_bone);
arm->act_edbone = NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Camera
* \{ */
static void lib_link_camera(FileData *fd, Main *main)
{
for (Camera *ca = main->cameras.first; ca; ca = ca->id.next) {
if (ca->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ca->id.properties, fd);
lib_link_animdata(fd, &ca->id, ca->adt);
ca->ipo = newlibadr_us(fd, ca->id.lib, ca->ipo); // XXX deprecated - old animation system
ca->dof_ob = newlibadr(fd, ca->id.lib, ca->dof_ob);
for (CameraBGImage *bgpic = ca->bg_images.first; bgpic; bgpic = bgpic->next) {
bgpic->ima = newlibadr_us(fd, ca->id.lib, bgpic->ima);
bgpic->clip = newlibadr_us(fd, ca->id.lib, bgpic->clip);
}
ca->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_camera(FileData *fd, Camera *ca)
{
ca->adt = newdataadr(fd, ca->adt);
direct_link_animdata(fd, ca->adt);
link_list(fd, &ca->bg_images);
for (CameraBGImage *bgpic = ca->bg_images.first; bgpic; bgpic = bgpic->next) {
bgpic->iuser.ok = 1;
bgpic->iuser.scene = NULL;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Light
* \{ */
static void lib_link_light(FileData *fd, Main *main)
{
for (Light *la = main->lights.first; la; la = la->id.next) {
if (la->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(la->id.properties, fd);
lib_link_animdata(fd, &la->id, la->adt);
la->ipo = newlibadr_us(fd, la->id.lib, la->ipo); // XXX deprecated - old animation system
if (la->nodetree) {
lib_link_ntree(fd, &la->id, la->nodetree);
la->nodetree->id.lib = la->id.lib;
}
la->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_light(FileData *fd, Light *la)
{
la->adt = newdataadr(fd, la->adt);
direct_link_animdata(fd, la->adt);
la->curfalloff = newdataadr(fd, la->curfalloff);
if (la->curfalloff)
direct_link_curvemapping(fd, la->curfalloff);
la->nodetree = newdataadr(fd, la->nodetree);
if (la->nodetree) {
direct_link_id(fd, &la->nodetree->id);
direct_link_nodetree(fd, la->nodetree);
}
la->preview = direct_link_preview_image(fd, la->preview);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Shape Keys
* \{ */
void blo_do_versions_key_uidgen(Key *key)
{
KeyBlock *block;
key->uidgen = 1;
for (block = key->block.first; block; block = block->next) {
block->uid = key->uidgen++;
}
}
static void lib_link_key(FileData *fd, Main *main)
{
for (Key *key = main->shapekeys.first; key; key = key->id.next) {
BLI_assert((key->id.tag & LIB_TAG_EXTERN) == 0);
if (key->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(key->id.properties, fd);
lib_link_animdata(fd, &key->id, key->adt);
key->ipo = newlibadr_us(fd, key->id.lib, key->ipo); // XXX deprecated - old animation system
key->from = newlibadr(fd, key->id.lib, key->from);
key->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void switch_endian_keyblock(Key *key, KeyBlock *kb)
{
int elemsize, a, b;
char *data;
elemsize = key->elemsize;
data = kb->data;
for (a = 0; a < kb->totelem; a++) {
const char *cp = key->elemstr;
char *poin = data;
while (cp[0]) { /* cp[0] == amount */
switch (cp[1]) { /* cp[1] = type */
case IPO_FLOAT:
case IPO_BPOINT:
case IPO_BEZTRIPLE:
b = cp[0];
BLI_endian_switch_float_array((float *)poin, b);
poin += sizeof(float) * b;
break;
}
cp += 2;
}
data += elemsize;
}
}
static void direct_link_key(FileData *fd, Key *key)
{
KeyBlock *kb;
link_list(fd, &(key->block));
key->adt = newdataadr(fd, key->adt);
direct_link_animdata(fd, key->adt);
key->refkey = newdataadr(fd, key->refkey);
for (kb = key->block.first; kb; kb = kb->next) {
kb->data = newdataadr(fd, kb->data);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN)
switch_endian_keyblock(key, kb);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Meta Ball
* \{ */
static void lib_link_mball(FileData *fd, Main *main)
{
for (MetaBall *mb = main->metaballs.first; mb; mb = mb->id.next) {
if (mb->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(mb->id.properties, fd);
lib_link_animdata(fd, &mb->id, mb->adt);
for (int a = 0; a < mb->totcol; a++) {
mb->mat[a] = newlibadr_us(fd, mb->id.lib, mb->mat[a]);
}
mb->ipo = newlibadr_us(fd, mb->id.lib, mb->ipo); // XXX deprecated - old animation system
mb->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_mball(FileData *fd, MetaBall *mb)
{
mb->adt = newdataadr(fd, mb->adt);
direct_link_animdata(fd, mb->adt);
mb->mat = newdataadr(fd, mb->mat);
test_pointer_array(fd, (void **)&mb->mat);
link_list(fd, &(mb->elems));
BLI_listbase_clear(&mb->disp);
mb->editelems = NULL;
/* mb->edit_elems.first= mb->edit_elems.last= NULL;*/
mb->lastelem = NULL;
mb->batch_cache = NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: World
* \{ */
static void lib_link_world(FileData *fd, Main *main)
{
for (World *wrld = main->worlds.first; wrld; wrld = wrld->id.next) {
if (wrld->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(wrld->id.properties, fd);
lib_link_animdata(fd, &wrld->id, wrld->adt);
wrld->ipo = newlibadr_us(fd, wrld->id.lib, wrld->ipo); // XXX deprecated - old animation system
if (wrld->nodetree) {
lib_link_ntree(fd, &wrld->id, wrld->nodetree);
wrld->nodetree->id.lib = wrld->id.lib;
}
wrld->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_world(FileData *fd, World *wrld)
{
wrld->adt = newdataadr(fd, wrld->adt);
direct_link_animdata(fd, wrld->adt);
wrld->nodetree = newdataadr(fd, wrld->nodetree);
if (wrld->nodetree) {
direct_link_id(fd, &wrld->nodetree->id);
direct_link_nodetree(fd, wrld->nodetree);
}
wrld->preview = direct_link_preview_image(fd, wrld->preview);
BLI_listbase_clear(&wrld->gpumaterial);
}
/* ************ READ VFONT ***************** */
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: VFont
* \{ */
static void lib_link_vfont(FileData *fd, Main *main)
{
for (VFont *vf = main->fonts.first; vf; vf = vf->id.next) {
if (vf->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(vf->id.properties, fd);
vf->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_vfont(FileData *fd, VFont *vf)
{
vf->data = NULL;
vf->temp_pf = NULL;
vf->packedfile = direct_link_packedfile(fd, vf->packedfile);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Text
* \{ */
static void lib_link_text(FileData *fd, Main *main)
{
for (Text *text = main->texts.first; text; text = text->id.next) {
if (text->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(text->id.properties, fd);
text->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_text(FileData *fd, Text *text)
{
TextLine *ln;
text->name = newdataadr(fd, text->name);
text->compiled = NULL;
#if 0
if (text->flags & TXT_ISEXT) {
BKE_text_reload(text);
}
/* else { */
#endif
link_list(fd, &text->lines);
text->curl = newdataadr(fd, text->curl);
text->sell = newdataadr(fd, text->sell);
for (ln = text->lines.first; ln; ln = ln->next) {
ln->line = newdataadr(fd, ln->line);
ln->format = NULL;
if (ln->len != (int)strlen(ln->line)) {
printf("Error loading text, line lengths differ\n");
ln->len = strlen(ln->line);
}
}
text->flags = (text->flags) & ~TXT_ISEXT;
id_us_ensure_real(&text->id);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Image
* \{ */
static void lib_link_image(FileData *fd, Main *main)
{
for (Image *ima = main->images.first; ima; ima = ima->id.next) {
if (ima->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ima->id.properties, fd);
ima->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_image(FileData *fd, Image *ima)
{
ImagePackedFile *imapf;
/* for undo system, pointers could be restored */
if (fd->imamap)
ima->cache = newimaadr(fd, ima->cache);
else
ima->cache = NULL;
/* if not restored, we keep the binded opengl index */
if (!ima->cache) {
ima->gpuflag = 0;
for (int i = 0; i < TEXTARGET_COUNT; i++) {
ima->gputexture[i] = NULL;
}
ima->rr = NULL;
}
else {
for (int i = 0; i < TEXTARGET_COUNT; i++) {
ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]);
}
ima->rr = newimaadr(fd, ima->rr);
}
/* undo system, try to restore render buffers */
link_list(fd, &(ima->renderslots));
if (fd->imamap) {
LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) {
slot->render = newimaadr(fd, slot->render);
}
}
else {
LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) {
slot->render = NULL;
}
ima->last_render_slot = ima->render_slot;
}
link_list(fd, &(ima->views));
link_list(fd, &(ima->packedfiles));
if (ima->packedfiles.first) {
for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
imapf->packedfile = direct_link_packedfile(fd, imapf->packedfile);
}
ima->packedfile = NULL;
}
else {
ima->packedfile = direct_link_packedfile(fd, ima->packedfile);
}
BLI_listbase_clear(&ima->anims);
ima->preview = direct_link_preview_image(fd, ima->preview);
ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format);
ima->ok = 1;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Curve
* \{ */
static void lib_link_curve(FileData *fd, Main *main)
{
for (Curve *cu = main->curves.first; cu; cu = cu->id.next) {
if (cu->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(cu->id.properties, fd);
lib_link_animdata(fd, &cu->id, cu->adt);
for (int a = 0; a < cu->totcol; a++) {
cu->mat[a] = newlibadr_us(fd, cu->id.lib, cu->mat[a]);
}
cu->bevobj = newlibadr(fd, cu->id.lib, cu->bevobj);
cu->taperobj = newlibadr(fd, cu->id.lib, cu->taperobj);
cu->textoncurve = newlibadr(fd, cu->id.lib, cu->textoncurve);
cu->vfont = newlibadr_us(fd, cu->id.lib, cu->vfont);
cu->vfontb = newlibadr_us(fd, cu->id.lib, cu->vfontb);
cu->vfonti = newlibadr_us(fd, cu->id.lib, cu->vfonti);
cu->vfontbi = newlibadr_us(fd, cu->id.lib, cu->vfontbi);
cu->ipo = newlibadr_us(fd, cu->id.lib, cu->ipo); // XXX deprecated - old animation system
cu->key = newlibadr_us(fd, cu->id.lib, cu->key);
cu->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void switch_endian_knots(Nurb *nu)
{
if (nu->knotsu) {
BLI_endian_switch_float_array(nu->knotsu, KNOTSU(nu));
}
if (nu->knotsv) {
BLI_endian_switch_float_array(nu->knotsv, KNOTSV(nu));
}
}
static void direct_link_curve(FileData *fd, Curve *cu)
{
Nurb *nu;
TextBox *tb;
cu->adt = newdataadr(fd, cu->adt);
direct_link_animdata(fd, cu->adt);
/* Protect against integer overflow vulnerability. */
CLAMP(cu->len_wchar, 0, INT_MAX - 4);
cu->mat = newdataadr(fd, cu->mat);
test_pointer_array(fd, (void **)&cu->mat);
cu->str = newdataadr(fd, cu->str);
cu->strinfo = newdataadr(fd, cu->strinfo);
cu->tb = newdataadr(fd, cu->tb);
if (cu->vfont == NULL) {
link_list(fd, &(cu->nurb));
}
else {
cu->nurb.first = cu->nurb.last = NULL;
tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread");
if (cu->tb) {
memcpy(tb, cu->tb, cu->totbox * sizeof(TextBox));
MEM_freeN(cu->tb);
cu->tb = tb;
}
else {
cu->totbox = 1;
cu->actbox = 1;
cu->tb = tb;
cu->tb[0].w = cu->linewidth;
}
if (cu->wordspace == 0.0f) cu->wordspace = 1.0f;
}
cu->editnurb = NULL;
cu->editfont = NULL;
cu->batch_cache = NULL;
for (nu = cu->nurb.first; nu; nu = nu->next) {
nu->bezt = newdataadr(fd, nu->bezt);
nu->bp = newdataadr(fd, nu->bp);
nu->knotsu = newdataadr(fd, nu->knotsu);
nu->knotsv = newdataadr(fd, nu->knotsv);
if (cu->vfont == NULL) nu->charidx = 0;
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
switch_endian_knots(nu);
}
}
cu->bb = NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Texture
* \{ */
static void lib_link_texture(FileData *fd, Main *main)
{
for (Tex *tex = main->textures.first; tex; tex = tex->id.next) {
if (tex->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(tex->id.properties, fd);
lib_link_animdata(fd, &tex->id, tex->adt);
tex->ima = newlibadr_us(fd, tex->id.lib, tex->ima);
tex->ipo = newlibadr_us(fd, tex->id.lib, tex->ipo); // XXX deprecated - old animation system
if (tex->nodetree) {
lib_link_ntree(fd, &tex->id, tex->nodetree);
tex->nodetree->id.lib = tex->id.lib;
}
tex->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_texture(FileData *fd, Tex *tex)
{
tex->adt = newdataadr(fd, tex->adt);
direct_link_animdata(fd, tex->adt);
tex->coba = newdataadr(fd, tex->coba);
tex->nodetree = newdataadr(fd, tex->nodetree);
if (tex->nodetree) {
direct_link_id(fd, &tex->nodetree->id);
direct_link_nodetree(fd, tex->nodetree);
}
tex->preview = direct_link_preview_image(fd, tex->preview);
tex->iuser.ok = 1;
tex->iuser.scene = NULL;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Material
* \{ */
static void lib_link_material(FileData *fd, Main *main)
{
for (Material *ma = main->materials.first; ma; ma = ma->id.next) {
if (ma->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(ma->id.properties, fd);
lib_link_animdata(fd, &ma->id, ma->adt);
ma->ipo = newlibadr_us(fd, ma->id.lib, ma->ipo); // XXX deprecated - old animation system
if (ma->nodetree) {
lib_link_ntree(fd, &ma->id, ma->nodetree);
ma->nodetree->id.lib = ma->id.lib;
}
/* relink grease pencil settings */
if (ma->gp_style != NULL) {
MaterialGPencilStyle *gp_style = ma->gp_style;
if (gp_style->sima != NULL) {
gp_style->sima = newlibadr_us(fd, ma->id.lib, gp_style->sima);
}
if (gp_style->ima != NULL) {
gp_style->ima = newlibadr_us(fd, ma->id.lib, gp_style->ima);
}
}
ma->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_material(FileData *fd, Material *ma)
{
ma->adt = newdataadr(fd, ma->adt);
direct_link_animdata(fd, ma->adt);
ma->texpaintslot = NULL;
ma->nodetree = newdataadr(fd, ma->nodetree);
if (ma->nodetree) {
direct_link_id(fd, &ma->nodetree->id);
direct_link_nodetree(fd, ma->nodetree);
}
ma->preview = direct_link_preview_image(fd, ma->preview);
BLI_listbase_clear(&ma->gpumaterial);
ma->gp_style = newdataadr(fd, ma->gp_style);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Particle Settings
* \{ */
/* update this also to writefile.c */
static const char *ptcache_data_struct[] = {
"", // BPHYS_DATA_INDEX
"", // BPHYS_DATA_LOCATION
"", // BPHYS_DATA_VELOCITY
"", // BPHYS_DATA_ROTATION
"", // BPHYS_DATA_AVELOCITY / BPHYS_DATA_XCONST */
"", // BPHYS_DATA_SIZE:
"", // BPHYS_DATA_TIMES:
"BoidData", // case BPHYS_DATA_BOIDS:
};
static void direct_link_pointcache_cb(FileData *fd, void *data)
{
PTCacheMem *pm = data;
PTCacheExtra *extra;
int i;
for (i = 0; i < BPHYS_TOT_DATA; i++) {
pm->data[i] = newdataadr(fd, pm->data[i]);
/* the cache saves non-struct data without DNA */
if (pm->data[i] && ptcache_data_struct[i][0] == '\0' && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) {
int tot = (BKE_ptcache_data_size(i) * pm->totpoint) / sizeof(int); /* data_size returns bytes */
int *poin = pm->data[i];
BLI_endian_switch_int32_array(poin, tot);
}
}
link_list(fd, &pm->extradata);
for (extra = pm->extradata.first; extra; extra = extra->next)
extra->data = newdataadr(fd, extra->data);
}
static void direct_link_pointcache(FileData *fd, PointCache *cache)
{
if ((cache->flag & PTCACHE_DISK_CACHE) == 0) {
link_list_ex(fd, &cache->mem_cache, direct_link_pointcache_cb);
}
else
BLI_listbase_clear(&cache->mem_cache);
cache->flag &= ~PTCACHE_SIMULATION_VALID;
cache->simframe = 0;
cache->edit = NULL;
cache->free_edit = NULL;
cache->cached_frames = NULL;
cache->cached_frames_len = 0;
}
static void direct_link_pointcache_list(FileData *fd, ListBase *ptcaches, PointCache **ocache, int force_disk)
{
if (ptcaches->first) {
PointCache *cache = NULL;
link_list(fd, ptcaches);
for (cache = ptcaches->first; cache; cache = cache->next) {
direct_link_pointcache(fd, cache);
if (force_disk) {
cache->flag |= PTCACHE_DISK_CACHE;
cache->step = 1;
}
}
*ocache = newdataadr(fd, *ocache);
}
else if (*ocache) {
/* old "single" caches need to be linked too */
*ocache = newdataadr(fd, *ocache);
direct_link_pointcache(fd, *ocache);
if (force_disk) {
(*ocache)->flag |= PTCACHE_DISK_CACHE;
(*ocache)->step = 1;
}
ptcaches->first = ptcaches->last = *ocache;
}
}
static void lib_link_partdeflect(FileData *fd, ID *id, PartDeflect *pd)
{
if (pd && pd->tex)
pd->tex = newlibadr_us(fd, id->lib, pd->tex);
if (pd && pd->f_source)
pd->f_source = newlibadr(fd, id->lib, pd->f_source);
}
static void lib_link_particlesettings(FileData *fd, Main *main)
{
for (ParticleSettings *part = main->particles.first; part; part = part->id.next) {
if (part->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(part->id.properties, fd);
lib_link_animdata(fd, &part->id, part->adt);
part->ipo = newlibadr_us(fd, part->id.lib, part->ipo); // XXX deprecated - old animation system
part->instance_object = newlibadr(fd, part->id.lib, part->instance_object);
part->instance_collection = newlibadr_us(fd, part->id.lib, part->instance_collection);
part->eff_group = newlibadr(fd, part->id.lib, part->eff_group);
part->bb_ob = newlibadr(fd, part->id.lib, part->bb_ob);
part->collision_group = newlibadr(fd, part->id.lib, part->collision_group);
lib_link_partdeflect(fd, &part->id, part->pd);
lib_link_partdeflect(fd, &part->id, part->pd2);
if (part->effector_weights) {
part->effector_weights->group = newlibadr(fd, part->id.lib, part->effector_weights->group);
}
else {
part->effector_weights = BKE_effector_add_weights(part->eff_group);
}
if (part->instance_weights.first && part->instance_collection) {
for (ParticleDupliWeight *dw = part->instance_weights.first; dw; dw = dw->next) {
dw->ob = newlibadr(fd, part->id.lib, dw->ob);
}
}
else {
BLI_listbase_clear(&part->instance_weights);
}
if (part->boids) {
BoidState *state = part->boids->states.first;
BoidRule *rule;
for (; state; state = state->next) {
rule = state->rules.first;
for (; rule; rule = rule->next) {
switch (rule->type) {
case eBoidRuleType_Goal:
case eBoidRuleType_Avoid:
{
BoidRuleGoalAvoid *brga = (BoidRuleGoalAvoid *)rule;
brga->ob = newlibadr(fd, part->id.lib, brga->ob);
break;
}
case eBoidRuleType_FollowLeader:
{
BoidRuleFollowLeader *brfl = (BoidRuleFollowLeader *)rule;
brfl->ob = newlibadr(fd, part->id.lib, brfl->ob);
break;
}
}
}
}
}
for (int a = 0; a < MAX_MTEX; a++) {
MTex *mtex = part->mtex[a];
if (mtex) {
mtex->tex = newlibadr_us(fd, part->id.lib, mtex->tex);
mtex->object = newlibadr(fd, part->id.lib, mtex->object);
}
}
part->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_partdeflect(PartDeflect *pd)
{
if (pd) pd->rng = NULL;
}
static void direct_link_particlesettings(FileData *fd, ParticleSettings *part)
{
int a;
part->adt = newdataadr(fd, part->adt);
part->pd = newdataadr(fd, part->pd);
part->pd2 = newdataadr(fd, part->pd2);
direct_link_animdata(fd, part->adt);
direct_link_partdeflect(part->pd);
direct_link_partdeflect(part->pd2);
part->clumpcurve = newdataadr(fd, part->clumpcurve);
if (part->clumpcurve)
direct_link_curvemapping(fd, part->clumpcurve);
part->roughcurve = newdataadr(fd, part->roughcurve);
if (part->roughcurve)
direct_link_curvemapping(fd, part->roughcurve);
part->twistcurve = newdataadr(fd, part->twistcurve);
if (part->twistcurve)
direct_link_curvemapping(fd, part->twistcurve);
part->effector_weights = newdataadr(fd, part->effector_weights);
if (!part->effector_weights)
part->effector_weights = BKE_effector_add_weights(part->eff_group);
link_list(fd, &part->instance_weights);
part->boids = newdataadr(fd, part->boids);
part->fluid = newdataadr(fd, part->fluid);
if (part->boids) {
BoidState *state;
link_list(fd, &part->boids->states);
for (state = part->boids->states.first; state; state = state->next) {
link_list(fd, &state->rules);
link_list(fd, &state->conditions);
link_list(fd, &state->actions);
}
}
for (a = 0; a < MAX_MTEX; a++) {
part->mtex[a] = newdataadr(fd, part->mtex[a]);
}
/* Protect against integer overflow vulnerability. */
CLAMP(part->trail_count, 1, 100000);
}
static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase *particles)
{
ParticleSystem *psys, *psysnext;
for (psys = particles->first; psys; psys = psysnext) {
psysnext = psys->next;
psys->part = newlibadr_us(fd, id->lib, psys->part);
if (psys->part) {
ParticleTarget *pt = psys->targets.first;
for (; pt; pt = pt->next)
pt->ob = newlibadr(fd, id->lib, pt->ob);
psys->parent = newlibadr(fd, id->lib, psys->parent);
psys->target_ob = newlibadr(fd, id->lib, psys->target_ob);
if (psys->clmd) {
/* XXX - from reading existing code this seems correct but intended usage of
* pointcache /w cloth should be added in 'ParticleSystem' - campbell */
psys->clmd->point_cache = psys->pointcache;
psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL;
psys->clmd->coll_parms->group = newlibadr(fd, id->lib, psys->clmd->coll_parms->group);
psys->clmd->modifier.error = NULL;
}
}
else {
/* particle modifier must be removed before particle system */
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
BLI_remlink(&ob->modifiers, psmd);
modifier_free((ModifierData *)psmd);
BLI_remlink(particles, psys);
MEM_freeN(psys);
}
}
}
static void direct_link_particlesystems(FileData *fd, ListBase *particles)
{
ParticleSystem *psys;
ParticleData *pa;
int a;
for (psys = particles->first; psys; psys = psys->next) {
psys->particles = newdataadr(fd, psys->particles);
if (psys->particles && psys->particles->hair) {
for (a = 0, pa = psys->particles; a < psys->totpart; a++, pa++)
pa->hair = newdataadr(fd, pa->hair);
}
if (psys->particles && psys->particles->keys) {
for (a = 0, pa = psys->particles; a < psys->totpart; a++, pa++) {
pa->keys = NULL;
pa->totkey = 0;
}
psys->flag &= ~PSYS_KEYED;
}
if (psys->particles && psys->particles->boid) {
pa = psys->particles;
pa->boid = newdataadr(fd, pa->boid);
pa->boid->ground = NULL; /* This is purely runtime data, but still can be an issue if left dangling. */
for (a = 1, pa++; a < psys->totpart; a++, pa++) {
pa->boid = (pa - 1)->boid + 1;
pa->boid->ground = NULL;
}
}
else if (psys->particles) {
for (a = 0, pa = psys->particles; a < psys->totpart; a++, pa++)
pa->boid = NULL;
}
psys->fluid_springs = newdataadr(fd, psys->fluid_springs);
psys->child = newdataadr(fd, psys->child);
psys->effectors = NULL;
link_list(fd, &psys->targets);
psys->edit = NULL;
psys->free_edit = NULL;
psys->pathcache = NULL;
psys->childcache = NULL;
BLI_listbase_clear(&psys->pathcachebufs);
BLI_listbase_clear(&psys->childcachebufs);
psys->pdd = NULL;
if (psys->clmd) {
psys->clmd = newdataadr(fd, psys->clmd);
psys->clmd->clothObject = NULL;
psys->clmd->hairdata = NULL;
psys->clmd->sim_parms = newdataadr(fd, psys->clmd->sim_parms);
psys->clmd->coll_parms = newdataadr(fd, psys->clmd->coll_parms);
if (psys->clmd->sim_parms) {
psys->clmd->sim_parms->effector_weights = NULL;
if (psys->clmd->sim_parms->presets > 10)
psys->clmd->sim_parms->presets = 0;
}
psys->hair_in_mesh = psys->hair_out_mesh = NULL;
psys->clmd->solver_result = NULL;
}
direct_link_pointcache_list(fd, &psys->ptcaches, &psys->pointcache, 0);
if (psys->clmd) {
psys->clmd->point_cache = psys->pointcache;
}
psys->tree = NULL;
psys->bvhtree = NULL;
psys->orig_psys = NULL;
psys->batch_cache = NULL;
}
return;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Mesh
* \{ */
static void lib_link_mesh(FileData *fd, Main *main)
{
Mesh *me;
for (me = main->meshes.first; me; me = me->id.next) {
if (me->id.tag & LIB_TAG_NEED_LINK) {
int i;
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(me->id.properties, fd);
lib_link_animdata(fd, &me->id, me->adt);
/* this check added for python created meshes */
if (me->mat) {
for (i = 0; i < me->totcol; i++) {
me->mat[i] = newlibadr_us(fd, me->id.lib, me->mat[i]);
}
}
else {
me->totcol = 0;
}
me->ipo = newlibadr_us(fd, me->id.lib, me->ipo); // XXX: deprecated: old anim sys
me->key = newlibadr_us(fd, me->id.lib, me->key);
me->texcomesh = newlibadr_us(fd, me->id.lib, me->texcomesh);
}
}
for (me = main->meshes.first; me; me = me->id.next) {
if (me->id.tag & LIB_TAG_NEED_LINK) {
/*check if we need to convert mfaces to mpolys*/
if (me->totface && !me->totpoly) {
/* temporarily switch main so that reading from
* external CustomData works */
Main *gmain = G_MAIN;
G_MAIN = main;
BKE_mesh_do_versions_convert_mfaces_to_mpolys(me);
G_MAIN = gmain;
}
/*
* Re-tessellate, even if the polys were just created from tessfaces, this
* is important because it:
* - fill the CD_ORIGINDEX layer
* - gives consistency of tessface between loading from a file and
* converting an edited BMesh back into a mesh (i.e. it replaces
* quad tessfaces in a loaded mesh immediately, instead of lazily
* waiting until edit mode has been entered/exited, making it easier
* to recognize problems that would otherwise only show up after edits).
*/
#ifdef USE_TESSFACE_DEFAULT
BKE_mesh_tessface_calc(me);
#else
BKE_mesh_tessface_clear(me);
#endif
me->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_dverts(FileData *fd, int count, MDeformVert *mdverts)
{
int i;
if (mdverts == NULL) {
return;
}
for (i = count; i > 0; i--, mdverts++) {
/*convert to vgroup allocation system*/
MDeformWeight *dw;
if (mdverts->dw && (dw = newdataadr(fd, mdverts->dw))) {
const ssize_t dw_len = mdverts->totweight * sizeof(MDeformWeight);
void *dw_tmp = MEM_mallocN(dw_len, "direct_link_dverts");
memcpy(dw_tmp, dw, dw_len);
mdverts->dw = dw_tmp;
MEM_freeN(dw);
}
else {
mdverts->dw = NULL;
mdverts->totweight = 0;
}
}
}
static void direct_link_mdisps(FileData *fd, int count, MDisps *mdisps, int external)
{
if (mdisps) {
int i;
for (i = 0; i < count; ++i) {
mdisps[i].disps = newdataadr(fd, mdisps[i].disps);
mdisps[i].hidden = newdataadr(fd, mdisps[i].hidden);
if (mdisps[i].totdisp && !mdisps[i].level) {
/* this calculation is only correct for loop mdisps;
* if loading pre-BMesh face mdisps this will be
* overwritten with the correct value in
* bm_corners_to_loops() */
float gridsize = sqrtf(mdisps[i].totdisp);
mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1;
}
if ((fd->flags & FD_FLAGS_SWITCH_ENDIAN) && (mdisps[i].disps)) {
/* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */
/* this does swap for data written at write_mdisps() - readfile.c */
BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3);
}
if (!external && !mdisps[i].disps)
mdisps[i].totdisp = 0;
}
}
}
static void direct_link_grid_paint_mask(FileData *fd, int count, GridPaintMask *grid_paint_mask)
{
if (grid_paint_mask) {
int i;
for (i = 0; i < count; ++i) {
GridPaintMask *gpm = &grid_paint_mask[i];
if (gpm->data)
gpm->data = newdataadr(fd, gpm->data);
}
}
}
/*this isn't really a public api function, so prototyped here*/
static void direct_link_customdata(FileData *fd, CustomData *data, int count)
{
int i = 0;
data->layers = newdataadr(fd, data->layers);
/* annoying workaround for bug [#31079] loading legacy files with
* no polygons _but_ have stale customdata */
if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) {
CustomData_reset(data);
return;
}
data->external = newdataadr(fd, data->external);
while (i < data->totlayer) {
CustomDataLayer *layer = &data->layers[i];
if (layer->flag & CD_FLAG_EXTERNAL)
layer->flag &= ~CD_FLAG_IN_MEMORY;
layer->flag &= ~CD_FLAG_NOFREE;
if (CustomData_verify_versions(data, i)) {
layer->data = newdataadr(fd, layer->data);
if (layer->type == CD_MDISPS)
direct_link_mdisps(fd, count, layer->data, layer->flag & CD_FLAG_EXTERNAL);
else if (layer->type == CD_GRID_PAINT_MASK)
direct_link_grid_paint_mask(fd, count, layer->data);
i++;
}
}
CustomData_update_typemap(data);
}
static void direct_link_mesh(FileData *fd, Mesh *mesh)
{
mesh->mat = newdataadr(fd, mesh->mat);
test_pointer_array(fd, (void **)&mesh->mat);
mesh->mvert = newdataadr(fd, mesh->mvert);
mesh->medge = newdataadr(fd, mesh->medge);
mesh->mface = newdataadr(fd, mesh->mface);
mesh->mloop = newdataadr(fd, mesh->mloop);
mesh->mpoly = newdataadr(fd, mesh->mpoly);
mesh->tface = newdataadr(fd, mesh->tface);
mesh->mtface = newdataadr(fd, mesh->mtface);
mesh->mcol = newdataadr(fd, mesh->mcol);
mesh->dvert = newdataadr(fd, mesh->dvert);
mesh->mloopcol = newdataadr(fd, mesh->mloopcol);
mesh->mloopuv = newdataadr(fd, mesh->mloopuv);
mesh->mselect = newdataadr(fd, mesh->mselect);
/* animdata */
mesh->adt = newdataadr(fd, mesh->adt);
direct_link_animdata(fd, mesh->adt);
/* normally direct_link_dverts should be called in direct_link_customdata,
* but for backwards compat in do_versions to work we do it here */
direct_link_dverts(fd, mesh->totvert, mesh->dvert);
direct_link_customdata(fd, &mesh->vdata, mesh->totvert);
direct_link_customdata(fd, &mesh->edata, mesh->totedge);
direct_link_customdata(fd, &mesh->fdata, mesh->totface);
direct_link_customdata(fd, &mesh->ldata, mesh->totloop);
direct_link_customdata(fd, &mesh->pdata, mesh->totpoly);
mesh->bb = NULL;
mesh->edit_mesh = NULL;
BKE_mesh_runtime_reset(mesh);
/* happens with old files */
if (mesh->mselect == NULL) {
mesh->totselect = 0;
}
/* Multires data */
mesh->mr = newdataadr(fd, mesh->mr);
if (mesh->mr) {
MultiresLevel *lvl;
link_list(fd, &mesh->mr->levels);
lvl = mesh->mr->levels.first;
direct_link_customdata(fd, &mesh->mr->vdata, lvl->totvert);
direct_link_dverts(fd, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT));
direct_link_customdata(fd, &mesh->mr->fdata, lvl->totface);
mesh->mr->edge_flags = newdataadr(fd, mesh->mr->edge_flags);
mesh->mr->edge_creases = newdataadr(fd, mesh->mr->edge_creases);
mesh->mr->verts = newdataadr(fd, mesh->mr->verts);
/* If mesh has the same number of vertices as the
* highest multires level, load the current mesh verts
* into multires and discard the old data. Needed
* because some saved files either do not have a verts
* array, or the verts array contains out-of-date
* data. */
if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) {
if (mesh->mr->verts)
MEM_freeN(mesh->mr->verts);
mesh->mr->verts = MEM_dupallocN(mesh->mvert);
}
for (; lvl; lvl = lvl->next) {
lvl->verts = newdataadr(fd, lvl->verts);
lvl->faces = newdataadr(fd, lvl->faces);
lvl->edges = newdataadr(fd, lvl->edges);
lvl->colfaces = newdataadr(fd, lvl->colfaces);
}
}
/* if multires is present but has no valid vertex data,
* there's no way to recover it; silently remove multires */
if (mesh->mr && !mesh->mr->verts) {
multires_free(mesh->mr);
mesh->mr = NULL;
}
if ((fd->flags & FD_FLAGS_SWITCH_ENDIAN) && mesh->tface) {
TFace *tf = mesh->tface;
int i;
for (i = 0; i < mesh->totface; i++, tf++) {
BLI_endian_switch_uint32_array(tf->col, 4);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Lattice
* \{ */
static void lib_link_latt(FileData *fd, Main *main)
{
for (Lattice *lt = main->lattices.first; lt; lt = lt->id.next) {
if (lt->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(lt->id.properties, fd);
lib_link_animdata(fd, &lt->id, lt->adt);
lt->ipo = newlibadr_us(fd, lt->id.lib, lt->ipo); // XXX deprecated - old animation system
lt->key = newlibadr_us(fd, lt->id.lib, lt->key);
lt->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_latt(FileData *fd, Lattice *lt)
{
lt->def = newdataadr(fd, lt->def);
lt->dvert = newdataadr(fd, lt->dvert);
direct_link_dverts(fd, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert);
lt->editlatt = NULL;
lt->batch_cache = NULL;
lt->adt = newdataadr(fd, lt->adt);
direct_link_animdata(fd, lt->adt);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Object
* \{ */
static void lib_link_modifiers_common(
void *userData, Object *ob, ID **idpoin, int cb_flag)
{
FileData *fd = userData;
*idpoin = newlibadr(fd, ob->id.lib, *idpoin);
if (*idpoin != NULL && (cb_flag & IDWALK_CB_USER) != 0) {
id_us_plus_no_lib(*idpoin);
}
}
static void lib_link_modifiers(FileData *fd, Object *ob)
{
modifiers_foreachIDLink(ob, lib_link_modifiers_common, fd);
/* If linking from a library, clear 'local' static override flag. */
if (ob->id.lib != NULL) {
for (ModifierData *mod = ob->modifiers.first; mod != NULL; mod = mod->next) {
mod->flag &= ~eModifierFlag_StaticOverride_Local;
}
}
}
static void lib_link_gpencil_modifiers(FileData *fd, Object *ob)
{
BKE_gpencil_modifiers_foreachIDLink(ob, lib_link_modifiers_common, fd);
/* If linking from a library, clear 'local' static override flag. */
if (ob->id.lib != NULL) {
for (GpencilModifierData *mod = ob->greasepencil_modifiers.first; mod != NULL; mod = mod->next) {
mod->flag &= ~eGpencilModifierFlag_StaticOverride_Local;
}
}
}
static void lib_link_shaderfxs(FileData *fd, Object *ob)
{
BKE_shaderfx_foreachIDLink(ob, lib_link_modifiers_common, fd);
/* If linking from a library, clear 'local' static override flag. */
if (ob->id.lib != NULL) {
for (ShaderFxData *fx = ob->shader_fx.first; fx != NULL; fx = fx->next) {
fx->flag &= ~eShaderFxFlag_StaticOverride_Local;
}
}
}
static void lib_link_object(FileData *fd, Main *main)
{
bool warn = false;
for (Object *ob = main->objects.first; ob; ob = ob->id.next) {
if (ob->id.tag & LIB_TAG_NEED_LINK) {
int a;
IDP_LibLinkProperty(ob->id.properties, fd);
lib_link_animdata(fd, &ob->id, ob->adt);
// XXX deprecated - old animation system <<<
ob->ipo = newlibadr_us(fd, ob->id.lib, ob->ipo);
ob->action = newlibadr_us(fd, ob->id.lib, ob->action);
// >>> XXX deprecated - old animation system
ob->parent = newlibadr(fd, ob->id.lib, ob->parent);
ob->track = newlibadr(fd, ob->id.lib, ob->track);
ob->poselib = newlibadr_us(fd, ob->id.lib, ob->poselib);
/* 2.8x drops support for non-empty dupli instances. */
if (ob->type == OB_EMPTY) {
ob->instance_collection = newlibadr_us(fd, ob->id.lib, ob->instance_collection);
}
else {
ob->instance_collection = NULL;
ob->transflag &= ~OB_DUPLICOLLECTION;
}
ob->proxy = newlibadr_us(fd, ob->id.lib, ob->proxy);
if (ob->proxy) {
/* paranoia check, actually a proxy_from pointer should never be written... */
if (ob->proxy->id.lib == NULL) {
ob->proxy->proxy_from = NULL;
ob->proxy = NULL;
if (ob->id.lib)
printf("Proxy lost from object %s lib %s\n", ob->id.name + 2, ob->id.lib->name);
else
printf("Proxy lost from object %s lib <NONE>\n", ob->id.name + 2);
}
else {
/* this triggers object_update to always use a copy */
ob->proxy->proxy_from = ob;
}
}
ob->proxy_group = newlibadr(fd, ob->id.lib, ob->proxy_group);
void *poin = ob->data;
ob->data = newlibadr_us(fd, ob->id.lib, ob->data);
if (ob->data == NULL && poin != NULL) {
if (ob->id.lib)
printf("Can't find obdata of %s lib %s\n", ob->id.name + 2, ob->id.lib->name);
else
printf("Object %s lost data.\n", ob->id.name + 2);
ob->type = OB_EMPTY;
warn = true;
if (ob->pose) {
/* we can't call #BKE_pose_free() here because of library linking
* freeing will recurse down into every pose constraints ID pointers
* which are not always valid, so for now free directly and suffer
* some leaked memory rather then crashing immediately
* while bad this _is_ an exceptional case - campbell */
#if 0
BKE_pose_free(ob->pose);
#else
MEM_freeN(ob->pose);
#endif
ob->pose = NULL;
ob->mode &= ~OB_MODE_POSE;
}
}
for (a = 0; a < ob->totcol; a++)
ob->mat[a] = newlibadr_us(fd, ob->id.lib, ob->mat[a]);
/* When the object is local and the data is library its possible
* the material list size gets out of sync. [#22663] */
if (ob->data && ob->id.lib != ((ID *)ob->data)->lib) {
const short *totcol_data = give_totcolp(ob);
/* Only expand so as not to loose any object materials that might be set. */
if (totcol_data && (*totcol_data > ob->totcol)) {
/* printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); */
BKE_material_resize_object(main, ob, *totcol_data, false);
}
}
ob->gpd = newlibadr_us(fd, ob->id.lib, ob->gpd);
ob->id.tag &= ~LIB_TAG_NEED_LINK;
/* if id.us==0 a new base will be created later on */
/* WARNING! Also check expand_object(), should reflect the stuff below. */
lib_link_pose(fd, main, ob, ob->pose);
lib_link_constraints(fd, &ob->id, &ob->constraints);
// XXX deprecated - old animation system <<<
lib_link_constraint_channels(fd, &ob->id, &ob->constraintChannels);
lib_link_nlastrips(fd, &ob->id, &ob->nlastrips);
// >>> XXX deprecated - old animation system
for (PartEff *paf = ob->effect.first; paf; paf = paf->next) {
if (paf->type == EFF_PARTICLE) {
paf->group = newlibadr_us(fd, ob->id.lib, paf->group);
}
}
{
FluidsimModifierData *fluidmd = (FluidsimModifierData *)modifiers_findByType(ob, eModifierType_Fluidsim);
if (fluidmd && fluidmd->fss)
fluidmd->fss->ipo = newlibadr_us(fd, ob->id.lib, fluidmd->fss->ipo); // XXX deprecated - old animation system
}
{
SmokeModifierData *smd = (SmokeModifierData *)modifiers_findByType(ob, eModifierType_Smoke);
if (smd && (smd->type == MOD_SMOKE_TYPE_DOMAIN) && smd->domain) {
smd->domain->flags |= MOD_SMOKE_FILE_LOAD; /* flag for refreshing the simulation after loading */
}
}
/* texture field */
if (ob->pd)
lib_link_partdeflect(fd, &ob->id, ob->pd);
if (ob->soft) {
ob->soft->collision_group = newlibadr(fd, ob->id.lib, ob->soft->collision_group);
ob->soft->effector_weights->group = newlibadr(fd, ob->id.lib, ob->soft->effector_weights->group);
}
lib_link_particlesystems(fd, ob, &ob->id, &ob->particlesystem);
lib_link_modifiers(fd, ob);
lib_link_gpencil_modifiers(fd, ob);
lib_link_shaderfxs(fd, ob);
if (ob->rigidbody_constraint) {
ob->rigidbody_constraint->ob1 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob1);
ob->rigidbody_constraint->ob2 = newlibadr(fd, ob->id.lib, ob->rigidbody_constraint->ob2);
}
{
LodLevel *level;
for (level = ob->lodlevels.first; level; level = level->next) {
level->source = newlibadr(fd, ob->id.lib, level->source);
if (!level->source && level == ob->lodlevels.first)
level->source = ob;
}
}
}
}
if (warn) {
BKE_report(fd->reports, RPT_WARNING, "Warning in console");
}
}
/* direct data for cache */
static void direct_link_motionpath(FileData *fd, bMotionPath *mpath)
{
/* sanity check */
if (mpath == NULL)
return;
/* relink points cache */
mpath->points = newdataadr(fd, mpath->points);
mpath->points_vbo = NULL;
mpath->batch_line = NULL;
mpath->batch_points = NULL;
}
static void direct_link_pose(FileData *fd, bPose *pose)
{
bPoseChannel *pchan;
if (!pose)
return;
link_list(fd, &pose->chanbase);
link_list(fd, &pose->agroups);
pose->chanhash = NULL;
pose->chan_array = NULL;
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
pchan->bone = NULL;
pchan->parent = newdataadr(fd, pchan->parent);
pchan->child = newdataadr(fd, pchan->child);
pchan->custom_tx = newdataadr(fd, pchan->custom_tx);
pchan->bbone_prev = newdataadr(fd, pchan->bbone_prev);
pchan->bbone_next = newdataadr(fd, pchan->bbone_next);
direct_link_constraints(fd, &pchan->constraints);
pchan->prop = newdataadr(fd, pchan->prop);
IDP_DirectLinkGroup_OrFree(&pchan->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
pchan->mpath = newdataadr(fd, pchan->mpath);
if (pchan->mpath)
direct_link_motionpath(fd, pchan->mpath);
BLI_listbase_clear(&pchan->iktree);
BLI_listbase_clear(&pchan->siktree);
/* in case this value changes in future, clamp else we get undefined behavior */
CLAMP(pchan->rotmode, ROT_MODE_MIN, ROT_MODE_MAX);
pchan->draw_data = NULL;
memset(&pchan->runtime, 0, sizeof(pchan->runtime));
}
pose->ikdata = NULL;
if (pose->ikparam != NULL) {
pose->ikparam = newdataadr(fd, pose->ikparam);
}
}
static void direct_link_modifiers(FileData *fd, ListBase *lb)
{
ModifierData *md;
link_list(fd, lb);
for (md = lb->first; md; md = md->next) {
md->error = NULL;
/* if modifiers disappear, or for upward compatibility */
if (NULL == modifierType_getInfo(md->type))
md->type = eModifierType_None;
if (md->type == eModifierType_Subsurf) {
SubsurfModifierData *smd = (SubsurfModifierData *)md;
smd->emCache = smd->mCache = NULL;
smd->subdiv = NULL;
}
else if (md->type == eModifierType_Armature) {
ArmatureModifierData *amd = (ArmatureModifierData *)md;
amd->prevCos = NULL;
}
else if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->clothObject = NULL;
clmd->hairdata = NULL;
clmd->sim_parms = newdataadr(fd, clmd->sim_parms);
clmd->coll_parms = newdataadr(fd, clmd->coll_parms);
direct_link_pointcache_list(fd, &clmd->ptcaches, &clmd->point_cache, 0);
if (clmd->sim_parms) {
if (clmd->sim_parms->presets > 10)
clmd->sim_parms->presets = 0;
clmd->sim_parms->reset = 0;
clmd->sim_parms->effector_weights = newdataadr(fd, clmd->sim_parms->effector_weights);
if (!clmd->sim_parms->effector_weights) {
clmd->sim_parms->effector_weights = BKE_effector_add_weights(NULL);
}
}
clmd->solver_result = NULL;
}
else if (md->type == eModifierType_Fluidsim) {
FluidsimModifierData *fluidmd = (FluidsimModifierData *)md;
fluidmd->fss = newdataadr(fd, fluidmd->fss);
if (fluidmd->fss) {
fluidmd->fss->fmd = fluidmd;
fluidmd->fss->meshVelocities = NULL;
}
}
else if (md->type == eModifierType_Smoke) {
SmokeModifierData *smd = (SmokeModifierData *)md;
if (smd->type == MOD_SMOKE_TYPE_DOMAIN) {
smd->flow = NULL;
smd->coll = NULL;
smd->domain = newdataadr(fd, smd->domain);
smd->domain->smd = smd;
smd->domain->fluid = NULL;
smd->domain->fluid_mutex = BLI_rw_mutex_alloc();
smd->domain->wt = NULL;
smd->domain->shadow = NULL;
smd->domain->tex = NULL;
smd->domain->tex_shadow = NULL;
smd->domain->tex_flame = NULL;
smd->domain->tex_flame_coba = NULL;
smd->domain->tex_coba = NULL;
smd->domain->tex_field = NULL;
smd->domain->tex_velocity_x = NULL;
smd->domain->tex_velocity_y = NULL;
smd->domain->tex_velocity_z = NULL;
smd->domain->tex_wt = NULL;
smd->domain->coba = newdataadr(fd, smd->domain->coba);
smd->domain->effector_weights = newdataadr(fd, smd->domain->effector_weights);
if (!smd->domain->effector_weights)
smd->domain->effector_weights = BKE_effector_add_weights(NULL);
direct_link_pointcache_list(fd, &(smd->domain->ptcaches[0]), &(smd->domain->point_cache[0]), 1);
/* Smoke uses only one cache from now on, so store pointer convert */
if (smd->domain->ptcaches[1].first || smd->domain->point_cache[1]) {
if (smd->domain->point_cache[1]) {
PointCache *cache = newdataadr(fd, smd->domain->point_cache[1]);
if (cache->flag & PTCACHE_FAKE_SMOKE) {
/* Smoke was already saved in "new format" and this cache is a fake one. */
}
else {
printf("High resolution smoke cache not available due to pointcache update. Please reset the simulation.\n");
}
BKE_ptcache_free(cache);
}
BLI_listbase_clear(&smd->domain->ptcaches[1]);
smd->domain->point_cache[1] = NULL;
}
}
else if (smd->type == MOD_SMOKE_TYPE_FLOW) {
smd->domain = NULL;
smd->coll = NULL;
smd->flow = newdataadr(fd, smd->flow);
smd->flow->smd = smd;
smd->flow->mesh = NULL;
smd->flow->verts_old = NULL;
smd->flow->numverts = 0;
smd->flow->psys = newdataadr(fd, smd->flow->psys);
}
else if (smd->type == MOD_SMOKE_TYPE_COLL) {
smd->flow = NULL;
smd->domain = NULL;
smd->coll = newdataadr(fd, smd->coll);
if (smd->coll) {
smd->coll->smd = smd;
smd->coll->verts_old = NULL;
smd->coll->numverts = 0;
smd->coll->mesh = NULL;
}
else {
smd->type = 0;
smd->flow = NULL;
smd->domain = NULL;
smd->coll = NULL;
}
}
}
else if (md->type == eModifierType_DynamicPaint) {
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
if (pmd->canvas) {
pmd->canvas = newdataadr(fd, pmd->canvas);
pmd->canvas->pmd = pmd;
pmd->canvas->mesh = NULL;
pmd->canvas->flags &= ~MOD_DPAINT_BAKING; /* just in case */
if (pmd->canvas->surfaces.first) {
DynamicPaintSurface *surface;
link_list(fd, &pmd->canvas->surfaces);
for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) {
surface->canvas = pmd->canvas;
surface->data = NULL;
direct_link_pointcache_list(fd, &(surface->ptcaches), &(surface->pointcache), 1);
if (!(surface->effector_weights = newdataadr(fd, surface->effector_weights)))
surface->effector_weights = BKE_effector_add_weights(NULL);
}
}
}
if (pmd->brush) {
pmd->brush = newdataadr(fd, pmd->brush);
pmd->brush->pmd = pmd;
pmd->brush->psys = newdataadr(fd, pmd->brush->psys);
pmd->brush->paint_ramp = newdataadr(fd, pmd->brush->paint_ramp);
pmd->brush->vel_ramp = newdataadr(fd, pmd->brush->vel_ramp);
pmd->brush->mesh = NULL;
}
}
else if (md->type == eModifierType_Collision) {
CollisionModifierData *collmd = (CollisionModifierData *)md;
#if 0
// TODO: CollisionModifier should use pointcache
// + have proper reset events before enabling this
collmd->x = newdataadr(fd, collmd->x);
collmd->xnew = newdataadr(fd, collmd->xnew);
collmd->mfaces = newdataadr(fd, collmd->mfaces);
collmd->current_x = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_x");
collmd->current_xnew = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_xnew");
collmd->current_v = MEM_calloc_arrayN(collmd->numverts, sizeof(MVert), "current_v");
#endif
collmd->x = NULL;
collmd->xnew = NULL;
collmd->current_x = NULL;
collmd->current_xnew = NULL;
collmd->current_v = NULL;
collmd->time_x = collmd->time_xnew = -1000;
collmd->mvert_num = 0;
collmd->tri_num = 0;
collmd->is_static = false;
collmd->bvhtree = NULL;
collmd->tri = NULL;
}
else if (md->type == eModifierType_Surface) {
SurfaceModifierData *surmd = (SurfaceModifierData *)md;
surmd->mesh = NULL;
surmd->bvhtree = NULL;
surmd->x = NULL;
surmd->v = NULL;
surmd->numverts = 0;
}
else if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
hmd->indexar = newdataadr(fd, hmd->indexar);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_int32_array(hmd->indexar, hmd->totindex);
}
hmd->curfalloff = newdataadr(fd, hmd->curfalloff);
if (hmd->curfalloff) {
direct_link_curvemapping(fd, hmd->curfalloff);
}
}
else if (md->type == eModifierType_ParticleSystem) {
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
psmd->mesh_final = NULL;
psmd->mesh_original = NULL;
psmd->psys = newdataadr(fd, psmd->psys);
psmd->flag &= ~eParticleSystemFlag_psys_updated;
psmd->flag |= eParticleSystemFlag_file_loaded;
}
else if (md->type == eModifierType_Explode) {
ExplodeModifierData *psmd = (ExplodeModifierData *)md;
psmd->facepa = NULL;
}
else if (md->type == eModifierType_MeshDeform) {
MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
mmd->bindinfluences = newdataadr(fd, mmd->bindinfluences);
mmd->bindoffsets = newdataadr(fd, mmd->bindoffsets);
mmd->bindcagecos = newdataadr(fd, mmd->bindcagecos);
mmd->dyngrid = newdataadr(fd, mmd->dyngrid);
mmd->dyninfluences = newdataadr(fd, mmd->dyninfluences);
mmd->dynverts = newdataadr(fd, mmd->dynverts);
mmd->bindweights = newdataadr(fd, mmd->bindweights);
mmd->bindcos = newdataadr(fd, mmd->bindcos);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
if (mmd->bindoffsets) BLI_endian_switch_int32_array(mmd->bindoffsets, mmd->totvert + 1);
if (mmd->bindcagecos) BLI_endian_switch_float_array(mmd->bindcagecos, mmd->totcagevert * 3);
if (mmd->dynverts) BLI_endian_switch_int32_array(mmd->dynverts, mmd->totvert);
if (mmd->bindweights) BLI_endian_switch_float_array(mmd->bindweights, mmd->totvert);
if (mmd->bindcos) BLI_endian_switch_float_array(mmd->bindcos, mmd->totcagevert * 3);
}
}
else if (md->type == eModifierType_Ocean) {
OceanModifierData *omd = (OceanModifierData *)md;
omd->oceancache = NULL;
omd->ocean = NULL;
}
else if (md->type == eModifierType_Warp) {
WarpModifierData *tmd = (WarpModifierData *)md;
tmd->curfalloff = newdataadr(fd, tmd->curfalloff);
if (tmd->curfalloff)
direct_link_curvemapping(fd, tmd->curfalloff);
}
else if (md->type == eModifierType_WeightVGEdit) {
WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
wmd->cmap_curve = newdataadr(fd, wmd->cmap_curve);
if (wmd->cmap_curve)
direct_link_curvemapping(fd, wmd->cmap_curve);
}
else if (md->type == eModifierType_LaplacianDeform) {
LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
lmd->vertexco = newdataadr(fd, lmd->vertexco);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_float_array(lmd->vertexco, lmd->total_verts * 3);
}
lmd->cache_system = NULL;
}
else if (md->type == eModifierType_CorrectiveSmooth) {
CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
if (csmd->bind_coords) {
csmd->bind_coords = newdataadr(fd, csmd->bind_coords);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_float_array((float *)csmd->bind_coords, csmd->bind_coords_num * 3);
}
}
/* runtime only */
csmd->delta_cache = NULL;
csmd->delta_cache_num = 0;
}
else if (md->type == eModifierType_MeshSequenceCache) {
MeshSeqCacheModifierData *msmcd = (MeshSeqCacheModifierData *)md;
msmcd->reader = NULL;
}
else if (md->type == eModifierType_SurfaceDeform) {
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
smd->verts = newdataadr(fd, smd->verts);
if (smd->verts) {
for (int i = 0; i < smd->numverts; i++) {
smd->verts[i].binds = newdataadr(fd, smd->verts[i].binds);
if (smd->verts[i].binds) {
for (int j = 0; j < smd->verts[i].numbinds; j++) {
smd->verts[i].binds[j].vert_inds = newdataadr(fd, smd->verts[i].binds[j].vert_inds);
smd->verts[i].binds[j].vert_weights = newdataadr(fd, smd->verts[i].binds[j].vert_weights);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
if (smd->verts[i].binds[j].vert_inds)
BLI_endian_switch_uint32_array(
smd->verts[i].binds[j].vert_inds, smd->verts[i].binds[j].numverts);
if (smd->verts[i].binds[j].vert_weights) {
if (smd->verts[i].binds[j].mode == MOD_SDEF_MODE_CENTROID ||
smd->verts[i].binds[j].mode == MOD_SDEF_MODE_LOOPTRI)
{
BLI_endian_switch_float_array(
smd->verts[i].binds[j].vert_weights, 3);
}
else {
BLI_endian_switch_float_array(
smd->verts[i].binds[j].vert_weights, smd->verts[i].binds[j].numverts);
}
}
}
}
}
}
}
}
else if (md->type == eModifierType_Multires) {
MultiresModifierData *mmd = (MultiresModifierData *)md;
mmd->subdiv = NULL;
}
}
}
static void direct_link_gpencil_modifiers(FileData *fd, ListBase *lb)
{
GpencilModifierData *md;
link_list(fd, lb);
for (md = lb->first; md; md = md->next) {
md->error = NULL;
/* if modifiers disappear, or for upward compatibility */
if (NULL == BKE_gpencil_modifierType_getInfo(md->type))
md->type = eModifierType_None;
if (md->type == eGpencilModifierType_Lattice) {
LatticeGpencilModifierData *gpmd = (LatticeGpencilModifierData *)md;
gpmd->cache_data = NULL;
}
else if (md->type == eGpencilModifierType_Hook) {
HookGpencilModifierData *hmd = (HookGpencilModifierData *)md;
hmd->curfalloff = newdataadr(fd, hmd->curfalloff);
if (hmd->curfalloff) {
direct_link_curvemapping(fd, hmd->curfalloff);
}
}
else if (md->type == eGpencilModifierType_Thick) {
ThickGpencilModifierData *gpmd = (ThickGpencilModifierData *)md;
gpmd->curve_thickness = newdataadr(fd, gpmd->curve_thickness);
if (gpmd->curve_thickness) {
direct_link_curvemapping(fd, gpmd->curve_thickness);
/* initialize the curve. Maybe this could be moved to modififer logic */
curvemapping_initialize(gpmd->curve_thickness);
}
}
}
}
static void direct_link_shaderfxs(FileData *fd, ListBase *lb)
{
ShaderFxData *fx;
link_list(fd, lb);
for (fx = lb->first; fx; fx = fx->next) {
fx->error = NULL;
/* if shader disappear, or for upward compatibility */
if (NULL == BKE_shaderfxType_getInfo(fx->type))
fx->type = eShaderFxType_None;
}
}
static void direct_link_object(FileData *fd, Object *ob)
{
PartEff *paf;
/* XXX This should not be needed - but seems like it can happen in some cases, so for now play safe... */
ob->proxy_from = NULL;
/* loading saved files with editmode enabled works, but for undo we like
* to stay in object mode during undo presses so keep editmode disabled.
*
* Also when linking in a file don't allow edit and pose modes.
* See [#34776, #42780] for more information.
*/
if (fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) {
ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT);
if (!fd->memfile) {
ob->mode &= ~OB_MODE_POSE;
}
}
ob->adt = newdataadr(fd, ob->adt);
direct_link_animdata(fd, ob->adt);
ob->pose = newdataadr(fd, ob->pose);
direct_link_pose(fd, ob->pose);
ob->mpath = newdataadr(fd, ob->mpath);
if (ob->mpath)
direct_link_motionpath(fd, ob->mpath);
link_list(fd, &ob->defbase);
link_list(fd, &ob->fmaps);
// XXX deprecated - old animation system <<<
direct_link_nlastrips(fd, &ob->nlastrips);
link_list(fd, &ob->constraintChannels);
// >>> XXX deprecated - old animation system
ob->mat = newdataadr(fd, ob->mat);
test_pointer_array(fd, (void **)&ob->mat);
ob->matbits = newdataadr(fd, ob->matbits);
/* do it here, below old data gets converted */
direct_link_modifiers(fd, &ob->modifiers);
direct_link_gpencil_modifiers(fd, &ob->greasepencil_modifiers);
direct_link_shaderfxs(fd, &ob->shader_fx);
link_list(fd, &ob->effect);
paf = ob->effect.first;
while (paf) {
if (paf->type == EFF_PARTICLE) {
paf->keys = NULL;
}
if (paf->type == EFF_WAVE) {
WaveEff *wav = (WaveEff *)paf;
PartEff *next = paf->next;
WaveModifierData *wmd = (WaveModifierData *)modifier_new(eModifierType_Wave);
wmd->damp = wav->damp;
wmd->flag = wav->flag;
wmd->height = wav->height;
wmd->lifetime = wav->lifetime;
wmd->narrow = wav->narrow;
wmd->speed = wav->speed;
wmd->startx = wav->startx;
wmd->starty = wav->startx;
wmd->timeoffs = wav->timeoffs;
wmd->width = wav->width;
BLI_addtail(&ob->modifiers, wmd);
BLI_remlink(&ob->effect, paf);
MEM_freeN(paf);
paf = next;
continue;
}
if (paf->type == EFF_BUILD) {
BuildEff *baf = (BuildEff *)paf;
PartEff *next = paf->next;
BuildModifierData *bmd = (BuildModifierData *)modifier_new(eModifierType_Build);
bmd->start = baf->sfra;
bmd->length = baf->len;
bmd->randomize = 0;
bmd->seed = 1;
BLI_addtail(&ob->modifiers, bmd);
BLI_remlink(&ob->effect, paf);
MEM_freeN(paf);
paf = next;
continue;
}
paf = paf->next;
}
ob->pd = newdataadr(fd, ob->pd);
direct_link_partdeflect(ob->pd);
ob->soft = newdataadr(fd, ob->soft);
if (ob->soft) {
SoftBody *sb = ob->soft;
sb->bpoint = NULL; // init pointers so it gets rebuilt nicely
sb->bspring = NULL;
sb->scratch = NULL;
/* although not used anymore */
/* still have to be loaded to be compatible with old files */
sb->keys = newdataadr(fd, sb->keys);
test_pointer_array(fd, (void **)&sb->keys);
if (sb->keys) {
int a;
for (a = 0; a < sb->totkey; a++) {
sb->keys[a] = newdataadr(fd, sb->keys[a]);
}
}
sb->effector_weights = newdataadr(fd, sb->effector_weights);
if (!sb->effector_weights)
sb->effector_weights = BKE_effector_add_weights(NULL);
sb->shared = newdataadr(fd, sb->shared);
if (sb->shared == NULL) {
/* Link deprecated caches if they exist, so we can use them for versioning.
* We should only do this when sb->shared == NULL, because those pointers
* are always set (for compatibility with older Blenders). We mustn't link
* the same pointcache twice. */
direct_link_pointcache_list(fd, &sb->ptcaches, &sb->pointcache, false);
}
else {
/* link caches */
direct_link_pointcache_list(fd, &sb->shared->ptcaches, &sb->shared->pointcache, false);
}
}
ob->fluidsimSettings = newdataadr(fd, ob->fluidsimSettings); /* NT */
ob->rigidbody_object = newdataadr(fd, ob->rigidbody_object);
if (ob->rigidbody_object) {
RigidBodyOb *rbo = ob->rigidbody_object;
/* Allocate runtime-only struct */
rbo->shared = MEM_callocN(sizeof(*rbo->shared), "RigidBodyObShared");
}
ob->rigidbody_constraint = newdataadr(fd, ob->rigidbody_constraint);
if (ob->rigidbody_constraint)
ob->rigidbody_constraint->physics_constraint = NULL;
link_list(fd, &ob->particlesystem);
direct_link_particlesystems(fd, &ob->particlesystem);
direct_link_constraints(fd, &ob->constraints);
link_list(fd, &ob->hooks);
while (ob->hooks.first) {
ObHook *hook = ob->hooks.first;
HookModifierData *hmd = (HookModifierData *)modifier_new(eModifierType_Hook);
hook->indexar = newdataadr(fd, hook->indexar);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_int32_array(hook->indexar, hook->totindex);
}
/* Do conversion here because if we have loaded
* a hook we need to make sure it gets converted
* and freed, regardless of version.
*/
copy_v3_v3(hmd->cent, hook->cent);
hmd->falloff = hook->falloff;
hmd->force = hook->force;
hmd->indexar = hook->indexar;
hmd->object = hook->parent;
memcpy(hmd->parentinv, hook->parentinv, sizeof(hmd->parentinv));
hmd->totindex = hook->totindex;
BLI_addhead(&ob->modifiers, hmd);
BLI_remlink(&ob->hooks, hook);
modifier_unique_name(&ob->modifiers, (ModifierData *)hmd);
MEM_freeN(hook);
}
ob->iuser = newdataadr(fd, ob->iuser);
if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE && !ob->iuser) {
BKE_object_empty_draw_type_set(ob, ob->empty_drawtype);
}
ob->derivedDeform = NULL;
ob->derivedFinal = NULL;
BKE_object_runtime_reset(ob);
link_list(fd, &ob->pc_ids);
/* in case this value changes in future, clamp else we get undefined behavior */
CLAMP(ob->rotmode, ROT_MODE_MIN, ROT_MODE_MAX);
if (ob->sculpt) {
ob->sculpt = NULL;
/* Only create data on undo, otherwise rely on editor mode switching. */
if (fd->memfile && (ob->mode & OB_MODE_ALL_SCULPT)) {
BKE_object_sculpt_data_create(ob);
}
}
link_list(fd, &ob->lodlevels);
ob->currentlod = ob->lodlevels.first;
ob->preview = direct_link_preview_image(fd, ob->preview);
}
static void direct_link_view_settings(FileData *fd, ColorManagedViewSettings *view_settings)
{
view_settings->curve_mapping = newdataadr(fd, view_settings->curve_mapping);
if (view_settings->curve_mapping)
direct_link_curvemapping(fd, view_settings->curve_mapping);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read View Layer (Collection Data)
* \{ */
static void direct_link_layer_collections(FileData *fd, ListBase *lb, bool master)
{
link_list(fd, lb);
for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
#ifdef USE_COLLECTION_COMPAT_28
lc->scene_collection = newdataadr(fd, lc->scene_collection);
#endif
/* Master collection is not a real datablock. */
if (master) {
lc->collection = newdataadr(fd, lc->collection);
}
direct_link_layer_collections(fd, &lc->layer_collections, false);
}
}
static void direct_link_view_layer(FileData *fd, ViewLayer *view_layer)
{
view_layer->stats = NULL;
link_list(fd, &view_layer->object_bases);
view_layer->basact = newdataadr(fd, view_layer->basact);
direct_link_layer_collections(fd, &view_layer->layer_collections, true);
view_layer->active_collection = newdataadr(fd, view_layer->active_collection);
view_layer->id_properties = newdataadr(fd, view_layer->id_properties);
IDP_DirectLinkGroup_OrFree(&view_layer->id_properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
link_list(fd, &(view_layer->freestyle_config.modules));
link_list(fd, &(view_layer->freestyle_config.linesets));
BLI_listbase_clear(&view_layer->drawdata);
view_layer->object_bases_array = NULL;
view_layer->object_bases_hash = NULL;
}
static void lib_link_layer_collection(FileData *fd, Library *lib, LayerCollection *layer_collection, bool master)
{
/* Master collection is not a real datablock. */
if (!master) {
layer_collection->collection = newlibadr(fd, lib, layer_collection->collection);
}
for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first;
layer_collection_nested != NULL;
layer_collection_nested = layer_collection_nested->next)
{
lib_link_layer_collection(fd, lib, layer_collection_nested, false);
}
}
static void lib_link_view_layer(FileData *fd, Library *lib, ViewLayer *view_layer)
{
for (FreestyleModuleConfig *fmc = view_layer->freestyle_config.modules.first; fmc; fmc = fmc->next) {
fmc->script = newlibadr(fd, lib, fmc->script);
}
for (FreestyleLineSet *fls = view_layer->freestyle_config.linesets.first; fls; fls = fls->next) {
fls->linestyle = newlibadr_us(fd, lib, fls->linestyle);
fls->group = newlibadr_us(fd, lib, fls->group);
}
for (Base *base = view_layer->object_bases.first, *base_next = NULL; base; base = base_next) {
base_next = base->next;
/* we only bump the use count for the collection objects */
base->object = newlibadr(fd, lib, base->object);
if (base->object == NULL) {
/* Free in case linked object got lost. */
BLI_freelinkN(&view_layer->object_bases, base);
if (view_layer->basact == base) {
view_layer->basact = NULL;
}
}
}
for (LayerCollection *layer_collection = view_layer->layer_collections.first;
layer_collection != NULL;
layer_collection = layer_collection->next)
{
lib_link_layer_collection(fd, lib, layer_collection, true);
}
view_layer->mat_override = newlibadr_us(fd, lib, view_layer->mat_override);
IDP_LibLinkProperty(view_layer->id_properties, fd);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Collection
* \{ */
#ifdef USE_COLLECTION_COMPAT_28
static void direct_link_scene_collection(FileData *fd, SceneCollection *sc)
{
link_list(fd, &sc->objects);
link_list(fd, &sc->scene_collections);
for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
direct_link_scene_collection(fd, nsc);
}
}
static void lib_link_scene_collection(FileData *fd, Library *lib, SceneCollection *sc)
{
for (LinkData *link = sc->objects.first; link; link = link->next) {
link->data = newlibadr_us(fd, lib, link->data);
BLI_assert(link->data);
}
for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
lib_link_scene_collection(fd, lib, nsc);
}
}
#endif
static void direct_link_collection(FileData *fd, Collection *collection)
{
link_list(fd, &collection->gobject);
link_list(fd, &collection->children);
collection->preview = direct_link_preview_image(fd, collection->preview);
collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
BLI_listbase_clear(&collection->object_cache);
BLI_listbase_clear(&collection->parents);
#ifdef USE_COLLECTION_COMPAT_28
/* This runs before the very first doversion. */
collection->collection = newdataadr(fd, collection->collection);
if (collection->collection != NULL) {
direct_link_scene_collection(fd, collection->collection);
}
collection->view_layer = newdataadr(fd, collection->view_layer);
if (collection->view_layer != NULL) {
direct_link_view_layer(fd, collection->view_layer);
}
#endif
}
static void lib_link_collection_data(FileData *fd, Library *lib, Collection *collection)
{
for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
cob_next = cob->next;
cob->ob = newlibadr_us(fd, lib, cob->ob);
if (cob->ob == NULL) {
BLI_freelinkN(&collection->gobject, cob);
}
}
for (CollectionChild *child = collection->children.first, *child_next = NULL; child; child = child_next) {
child_next = child->next;
child->collection = newlibadr_us(fd, lib, child->collection);
if (child->collection == NULL ||
BKE_collection_find_cycle(collection, child->collection))
{
BLI_freelinkN(&collection->children, child);
}
else {
CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), "CollectionParent");
cparent->collection = collection;
BLI_addtail(&child->collection->parents, cparent);
}
}
}
static void lib_link_collection(FileData *fd, Main *main)
{
for (Collection *collection = main->collections.first; collection; collection = collection->id.next) {
if (collection->id.tag & LIB_TAG_NEED_LINK) {
collection->id.tag &= ~LIB_TAG_NEED_LINK;
IDP_LibLinkProperty(collection->id.properties, fd);
#ifdef USE_COLLECTION_COMPAT_28
if (collection->collection) {
lib_link_scene_collection(fd, collection->id.lib, collection->collection);
}
if (collection->view_layer) {
lib_link_view_layer(fd, collection->id.lib, collection->view_layer);
}
#endif
lib_link_collection_data(fd, collection->id.lib, collection);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Scene
* \{ */
/* patch for missing scene IDs, can't be in do-versions */
static void composite_patch(bNodeTree *ntree, Scene *scene)
{
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (node->id == NULL && node->type == CMP_NODE_R_LAYERS)
node->id = &scene->id;
}
}
static void link_paint(FileData *fd, Scene *sce, Paint *p)
{
if (p) {
p->brush = newlibadr_us(fd, sce->id.lib, p->brush);
for (int i = 0; i < p->tool_slots_len; i++) {
if (p->tool_slots[i].brush != NULL) {
p->tool_slots[i].brush = newlibadr_us(fd, sce->id.lib, p->tool_slots[i].brush);
}
}
p->palette = newlibadr_us(fd, sce->id.lib, p->palette);
p->paint_cursor = NULL;
BKE_paint_runtime_init(sce->toolsettings, p);
}
}
static void lib_link_sequence_modifiers(FileData *fd, Scene *scene, ListBase *lb)
{
SequenceModifierData *smd;
for (smd = lb->first; smd; smd = smd->next) {
if (smd->mask_id)
smd->mask_id = newlibadr_us(fd, scene->id.lib, smd->mask_id);
}
}
static void direct_link_lightcache_texture(FileData *fd, LightCacheTexture *lctex)
{
lctex->tex = NULL;
if (lctex->data) {
lctex->data = newdataadr(fd, lctex->data);
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] * lctex->tex_size[2];
if (lctex->data_type == LIGHTCACHETEX_FLOAT) {
BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float));
}
else if (lctex->data_type == LIGHTCACHETEX_UINT) {
BLI_endian_switch_uint32_array((uint *)lctex->data, data_size * sizeof(uint));
}
}
}
}
static void direct_link_lightcache(FileData *fd, LightCache *cache)
{
direct_link_lightcache_texture(fd, &cache->cube_tx);
direct_link_lightcache_texture(fd, &cache->grid_tx);
if (cache->cube_mips) {
cache->cube_mips = newdataadr(fd, cache->cube_mips);
for (int i = 0; i < cache->mips_len; ++i) {
direct_link_lightcache_texture(fd, &cache->cube_mips[i]);
}
}
cache->cube_data = newdataadr(fd, cache->cube_data);
cache->grid_data = newdataadr(fd, cache->grid_data);
}
/* check for cyclic set-scene,
* libs can cause this case which is normally prevented, see (T#####) */
#define USE_SETSCENE_CHECK
#ifdef USE_SETSCENE_CHECK
/**
* A version of #BKE_scene_validate_setscene with special checks for linked libs.
*/
static bool scene_validate_setscene__liblink(Scene *sce, const int totscene)
{
Scene *sce_iter;
int a;
if (sce->set == NULL) return 1;
for (a = 0, sce_iter = sce; sce_iter->set; sce_iter = sce_iter->set, a++) {
if (sce_iter->id.tag & LIB_TAG_NEED_LINK) {
return 1;
}
if (a > totscene) {
sce->set = NULL;
return 0;
}
}
return 1;
}
#endif
static void lib_link_scene(FileData *fd, Main *main)
{
#ifdef USE_SETSCENE_CHECK
bool need_check_set = false;
int totscene = 0;
#endif
for (Scene *sce = main->scenes.first; sce; sce = sce->id.next) {
if (sce->id.tag & LIB_TAG_NEED_LINK) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this.*/
IDP_LibLinkProperty(sce->id.properties, fd);
lib_link_animdata(fd, &sce->id, sce->adt);
lib_link_keyingsets(fd, &sce->id, &sce->keyingsets);
sce->camera = newlibadr(fd, sce->id.lib, sce->camera);
sce->world = newlibadr_us(fd, sce->id.lib, sce->world);
sce->set = newlibadr(fd, sce->id.lib, sce->set);
sce->gpd = newlibadr_us(fd, sce->id.lib, sce->gpd);
link_paint(fd, sce, &sce->toolsettings->sculpt->paint);
link_paint(fd, sce, &sce->toolsettings->vpaint->paint);
link_paint(fd, sce, &sce->toolsettings->wpaint->paint);
link_paint(fd, sce, &sce->toolsettings->imapaint.paint);
link_paint(fd, sce, &sce->toolsettings->uvsculpt->paint);
link_paint(fd, sce, &sce->toolsettings->gp_paint->paint);
if (sce->toolsettings->sculpt)
sce->toolsettings->sculpt->gravity_object =
newlibadr(fd, sce->id.lib, sce->toolsettings->sculpt->gravity_object);
if (sce->toolsettings->imapaint.stencil)
sce->toolsettings->imapaint.stencil =
newlibadr_us(fd, sce->id.lib, sce->toolsettings->imapaint.stencil);
if (sce->toolsettings->imapaint.clone)
sce->toolsettings->imapaint.clone =
newlibadr_us(fd, sce->id.lib, sce->toolsettings->imapaint.clone);
if (sce->toolsettings->imapaint.canvas)
sce->toolsettings->imapaint.canvas =
newlibadr_us(fd, sce->id.lib, sce->toolsettings->imapaint.canvas);
sce->toolsettings->particle.shape_object = newlibadr(fd, sce->id.lib, sce->toolsettings->particle.shape_object);
sce->toolsettings->gp_sculpt.guide.reference_object = newlibadr(fd, sce->id.lib, sce->toolsettings->gp_sculpt.guide.reference_object);
for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy; base_legacy = base_legacy_next) {
base_legacy_next = base_legacy->next;
base_legacy->object = newlibadr_us(fd, sce->id.lib, base_legacy->object);
if (base_legacy->object == NULL) {
blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: object lost from scene: '%s'"),
sce->id.name + 2);
BLI_remlink(&sce->base, base_legacy);
if (base_legacy == sce->basact) sce->basact = NULL;
MEM_freeN(base_legacy);
}
}
Sequence *seq;
SEQ_BEGIN (sce->ed, seq)
{
IDP_LibLinkProperty(seq->prop, fd);
if (seq->ipo) seq->ipo = newlibadr_us(fd, sce->id.lib, seq->ipo); // XXX deprecated - old animation system
seq->scene_sound = NULL;
if (seq->scene) {
seq->scene = newlibadr(fd, sce->id.lib, seq->scene);
if (seq->scene) {
seq->scene_sound = BKE_sound_scene_add_scene_sound_defaults(sce, seq);
}
}
if (seq->clip) {
seq->clip = newlibadr_us(fd, sce->id.lib, seq->clip);
}
if (seq->mask) {
seq->mask = newlibadr_us(fd, sce->id.lib, seq->mask);
}
if (seq->scene_camera) {
seq->scene_camera = newlibadr(fd, sce->id.lib, seq->scene_camera);
}
if (seq->sound) {
seq->scene_sound = NULL;
if (seq->type == SEQ_TYPE_SOUND_HD) {
seq->type = SEQ_TYPE_SOUND_RAM;
}
else {
seq->sound = newlibadr(fd, sce->id.lib, seq->sound);
}
if (seq->sound) {
id_us_plus_no_lib((ID *)seq->sound);
seq->scene_sound = BKE_sound_add_scene_sound_defaults(sce, seq);
}
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
t->text_font = newlibadr_us(fd, sce->id.lib, t->text_font);
}
BLI_listbase_clear(&seq->anims);
lib_link_sequence_modifiers(fd, sce, &seq->modifiers);
} SEQ_END;
for (TimeMarker *marker = sce->markers.first; marker; marker = marker->next) {
if (marker->camera) {
marker->camera = newlibadr(fd, sce->id.lib, marker->camera);
}
}
BKE_sequencer_update_muting(sce->ed);
BKE_sequencer_update_sound_bounds_all(sce);
/* rigidbody world relies on it's linked collections */
if (sce->rigidbody_world) {
RigidBodyWorld *rbw = sce->rigidbody_world;
if (rbw->group)
rbw->group = newlibadr(fd, sce->id.lib, rbw->group);
if (rbw->constraints)
rbw->constraints = newlibadr(fd, sce->id.lib, rbw->constraints);
if (rbw->effector_weights)
rbw->effector_weights->group = newlibadr(fd, sce->id.lib, rbw->effector_weights->group);
}
if (sce->nodetree) {
lib_link_ntree(fd, &sce->id, sce->nodetree);
sce->nodetree->id.lib = sce->id.lib;
composite_patch(sce->nodetree, sce);
}
for (SceneRenderLayer *srl = sce->r.layers.first; srl; srl = srl->next) {
srl->mat_override = newlibadr_us(fd, sce->id.lib, srl->mat_override);
for (FreestyleModuleConfig *fmc = srl->freestyleConfig.modules.first; fmc; fmc = fmc->next) {
fmc->script = newlibadr(fd, sce->id.lib, fmc->script);
}
for (FreestyleLineSet *fls = srl->freestyleConfig.linesets.first; fls; fls = fls->next) {
fls->linestyle = newlibadr_us(fd, sce->id.lib, fls->linestyle);
fls->group = newlibadr_us(fd, sce->id.lib, fls->group);
}
}
/* Motion Tracking */
sce->clip = newlibadr_us(fd, sce->id.lib, sce->clip);
#ifdef USE_COLLECTION_COMPAT_28
if (sce->collection) {
lib_link_scene_collection(fd, sce->id.lib, sce->collection);
}
#endif
if (sce->master_collection) {
lib_link_collection_data(fd, sce->id.lib, sce->master_collection);
}
for (ViewLayer *view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
lib_link_view_layer(fd, sce->id.lib, view_layer);
}
if (sce->r.bake.cage_object) {
sce->r.bake.cage_object = newlibadr(fd, sce->id.lib, sce->r.bake.cage_object);
}
#ifdef USE_SETSCENE_CHECK
if (sce->set != NULL) {
/* link flag for scenes with set would be reset later,
* so this way we only check cyclic for newly linked scenes.
*/
need_check_set = true;
}
else {
/* postpone un-setting the flag until we've checked the set-scene */
sce->id.tag &= ~LIB_TAG_NEED_LINK;
}
#else
sce->id.tag &= ~LIB_TAG_NEED_LINK;
#endif
}
#ifdef USE_SETSCENE_CHECK
totscene++;
#endif
}
#ifdef USE_SETSCENE_CHECK
if (need_check_set) {
for (Scene *sce = main->scenes.first; sce; sce = sce->id.next) {
if (sce->id.tag & LIB_TAG_NEED_LINK) {
sce->id.tag &= ~LIB_TAG_NEED_LINK;
if (!scene_validate_setscene__liblink(sce, totscene)) {
printf("Found cyclic background scene when linking %s\n", sce->id.name + 2);
}
}
}
}
#endif
}
#undef USE_SETSCENE_CHECK
static void link_recurs_seq(FileData *fd, ListBase *lb)
{
Sequence *seq;
link_list(fd, lb);
for (seq = lb->first; seq; seq = seq->next) {
if (seq->seqbase.first)
link_recurs_seq(fd, &seq->seqbase);
}
}
static void direct_link_paint(FileData *fd, const Scene *scene, Paint *p)
{
if (p->num_input_samples < 1)
p->num_input_samples = 1;
p->cavity_curve = newdataadr(fd, p->cavity_curve);
if (p->cavity_curve)
direct_link_curvemapping(fd, p->cavity_curve);
else
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
p->tool_slots = newdataadr(fd, p->tool_slots);
BKE_paint_runtime_init(scene->toolsettings, p);
}
static void direct_link_paint_helper(FileData *fd, const Scene *scene, Paint **paint)
{
/* TODO. is this needed */
(*paint) = newdataadr(fd, (*paint));
if (*paint) {
direct_link_paint(fd, scene, *paint);
}
}
static void direct_link_sequence_modifiers(FileData *fd, ListBase *lb)
{
SequenceModifierData *smd;
link_list(fd, lb);
for (smd = lb->first; smd; smd = smd->next) {
if (smd->mask_sequence)
smd->mask_sequence = newdataadr(fd, smd->mask_sequence);
if (smd->type == seqModifierType_Curves) {
CurvesModifierData *cmd = (CurvesModifierData *)smd;
direct_link_curvemapping(fd, &cmd->curve_mapping);
}
else if (smd->type == seqModifierType_HueCorrect) {
HueCorrectModifierData *hcmd = (HueCorrectModifierData *)smd;
direct_link_curvemapping(fd, &hcmd->curve_mapping);
}
}
}
static void direct_link_scene(FileData *fd, Scene *sce)
{
Editing *ed;
Sequence *seq;
MetaStack *ms;
RigidBodyWorld *rbw;
ViewLayer *view_layer;
SceneRenderLayer *srl;
sce->depsgraph_hash = NULL;
sce->fps_info = NULL;
memset(&sce->customdata_mask, 0, sizeof(sce->customdata_mask));
memset(&sce->customdata_mask_modal, 0, sizeof(sce->customdata_mask_modal));
BKE_sound_create_scene(sce);
/* set users to one by default, not in lib-link, this will increase it for compo nodes */
id_us_ensure_real(&sce->id);
link_list(fd, &(sce->base));
sce->adt = newdataadr(fd, sce->adt);
direct_link_animdata(fd, sce->adt);
link_list(fd, &sce->keyingsets);
direct_link_keyingsets(fd, &sce->keyingsets);
sce->basact = newdataadr(fd, sce->basact);
sce->toolsettings = newdataadr(fd, sce->toolsettings);
if (sce->toolsettings) {
direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->sculpt);
direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->vpaint);
direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->wpaint);
direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->uvsculpt);
direct_link_paint_helper(fd, sce, (Paint **)&sce->toolsettings->gp_paint);
direct_link_paint(fd, sce, &sce->toolsettings->imapaint.paint);
sce->toolsettings->imapaint.paintcursor = NULL;
sce->toolsettings->particle.paintcursor = NULL;
sce->toolsettings->particle.scene = NULL;
sce->toolsettings->particle.object = NULL;
sce->toolsettings->gp_sculpt.paintcursor = NULL;
/* relink grease pencil interpolation curves */
sce->toolsettings->gp_interpolate.custom_ipo = newdataadr(fd, sce->toolsettings->gp_interpolate.custom_ipo);
if (sce->toolsettings->gp_interpolate.custom_ipo) {
direct_link_curvemapping(fd, sce->toolsettings->gp_interpolate.custom_ipo);
}
/* relink grease pencil multiframe falloff curve */
sce->toolsettings->gp_sculpt.cur_falloff = newdataadr(fd, sce->toolsettings->gp_sculpt.cur_falloff);
if (sce->toolsettings->gp_sculpt.cur_falloff) {
direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_falloff);
}
/* relink grease pencil primitive curve */
sce->toolsettings->gp_sculpt.cur_primitive = newdataadr(fd, sce->toolsettings->gp_sculpt.cur_primitive);
if (sce->toolsettings->gp_sculpt.cur_primitive) {
direct_link_curvemapping(fd, sce->toolsettings->gp_sculpt.cur_primitive);
}
}
if (sce->ed) {
ListBase *old_seqbasep = &sce->ed->seqbase;
ed = sce->ed = newdataadr(fd, sce->ed);
ed->act_seq = newdataadr(fd, ed->act_seq);
/* recursive link sequences, lb will be correctly initialized */
link_recurs_seq(fd, &ed->seqbase);
SEQ_BEGIN(ed, seq)
{
seq->seq1 = newdataadr(fd, seq->seq1);
seq->seq2 = newdataadr(fd, seq->seq2);
seq->seq3 = newdataadr(fd, seq->seq3);
/* a patch: after introduction of effects with 3 input strips */
if (seq->seq3 == NULL) seq->seq3 = seq->seq2;
seq->effectdata = newdataadr(fd, seq->effectdata);
seq->stereo3d_format = newdataadr(fd, seq->stereo3d_format);
if (seq->type & SEQ_TYPE_EFFECT)
seq->flag |= SEQ_EFFECT_NOT_LOADED;
if (seq->type == SEQ_TYPE_SPEED) {
SpeedControlVars *s = seq->effectdata;
s->frameMap = NULL;
}
if (seq->type == SEQ_TYPE_TEXT) {
TextVars *t = seq->effectdata;
t->text_blf_id = SEQ_FONT_NOT_LOADED;
}
seq->prop = newdataadr(fd, seq->prop);
IDP_DirectLinkGroup_OrFree(&seq->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
seq->strip = newdataadr(fd, seq->strip);
if (seq->strip && seq->strip->done == 0) {
seq->strip->done = true;
if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) {
seq->strip->stripdata = newdataadr(fd, seq->strip->stripdata);
}
else {
seq->strip->stripdata = NULL;
}
if (seq->flag & SEQ_USE_CROP) {
seq->strip->crop = newdataadr(fd, seq->strip->crop);
}
else {
seq->strip->crop = NULL;
}
if (seq->flag & SEQ_USE_TRANSFORM) {
seq->strip->transform = newdataadr(fd, seq->strip->transform);
}
else {
seq->strip->transform = NULL;
}
if (seq->flag & SEQ_USE_PROXY) {
seq->strip->proxy = newdataadr(fd, seq->strip->proxy);
if (seq->strip->proxy) {
seq->strip->proxy->anim = NULL;
}
else {
BKE_sequencer_proxy_set(seq, true);
}
}
else {
seq->strip->proxy = NULL;
}
/* need to load color balance to it could be converted to modifier */
seq->strip->color_balance = newdataadr(fd, seq->strip->color_balance);
}
direct_link_sequence_modifiers(fd, &seq->modifiers);
} SEQ_END;
/* link metastack, slight abuse of structs here, have to restore pointer to internal part in struct */
{
Sequence temp;
void *poin;
intptr_t offset;
offset = ((intptr_t)&(temp.seqbase)) - ((intptr_t)&temp);
/* root pointer */
if (ed->seqbasep == old_seqbasep) {
ed->seqbasep = &ed->seqbase;
}
else {
poin = POINTER_OFFSET(ed->seqbasep, -offset);
poin = newdataadr(fd, poin);
if (poin)
ed->seqbasep = (ListBase *)POINTER_OFFSET(poin, offset);
else
ed->seqbasep = &ed->seqbase;
}
/* stack */
link_list(fd, &(ed->metastack));
for (ms = ed->metastack.first; ms; ms = ms->next) {
ms->parseq = newdataadr(fd, ms->parseq);
if (ms->oldbasep == old_seqbasep)
ms->oldbasep = &ed->seqbase;
else {
poin = POINTER_OFFSET(ms->oldbasep, -offset);
poin = newdataadr(fd, poin);
if (poin)
ms->oldbasep = (ListBase *)POINTER_OFFSET(poin, offset);
else
ms->oldbasep = &ed->seqbase;
}
}
}
}
sce->r.avicodecdata = newdataadr(fd, sce->r.avicodecdata);
if (sce->r.avicodecdata) {
sce->r.avicodecdata->lpFormat = newdataadr(fd, sce->r.avicodecdata->lpFormat);
sce->r.avicodecdata->lpParms = newdataadr(fd, sce->r.avicodecdata->lpParms);
}
if (sce->r.ffcodecdata.properties) {
sce->r.ffcodecdata.properties = newdataadr(fd, sce->r.ffcodecdata.properties);
IDP_DirectLinkGroup_OrFree(&sce->r.ffcodecdata.properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
link_list(fd, &(sce->markers));
link_list(fd, &(sce->transform_spaces));
link_list(fd, &(sce->r.layers));
link_list(fd, &(sce->r.views));
for (srl = sce->r.layers.first; srl; srl = srl->next) {
srl->prop = newdataadr(fd, srl->prop);
IDP_DirectLinkGroup_OrFree(&srl->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
link_list(fd, &(srl->freestyleConfig.modules));
link_list(fd, &(srl->freestyleConfig.linesets));
}
sce->nodetree = newdataadr(fd, sce->nodetree);
if (sce->nodetree) {
direct_link_id(fd, &sce->nodetree->id);
direct_link_nodetree(fd, sce->nodetree);
}
direct_link_view_settings(fd, &sce->view_settings);
sce->rigidbody_world = newdataadr(fd, sce->rigidbody_world);
rbw = sce->rigidbody_world;
if (rbw) {
rbw->shared = newdataadr(fd, rbw->shared);
if (rbw->shared == NULL) {
/* Link deprecated caches if they exist, so we can use them for versioning.
* We should only do this when rbw->shared == NULL, because those pointers
* are always set (for compatibility with older Blenders). We mustn't link
* the same pointcache twice. */
direct_link_pointcache_list(fd, &rbw->ptcaches, &rbw->pointcache, false);
/* make sure simulation starts from the beginning after loading file */
if (rbw->pointcache) {
rbw->ltime = (float)rbw->pointcache->startframe;
}
}
else {
/* must nullify the reference to physics sim object, since it no-longer exist
* (and will need to be recalculated)
*/
rbw->shared->physics_world = NULL;
/* link caches */
direct_link_pointcache_list(fd, &rbw->shared->ptcaches, &rbw->shared->pointcache, false);
/* make sure simulation starts from the beginning after loading file */
if (rbw->shared->pointcache) {
rbw->ltime = (float)rbw->shared->pointcache->startframe;
}
}
rbw->objects = NULL;
rbw->numbodies = 0;
/* set effector weights */
rbw->effector_weights = newdataadr(fd, rbw->effector_weights);
if (!rbw->effector_weights)
rbw->effector_weights = BKE_effector_add_weights(NULL);
}
sce->preview = direct_link_preview_image(fd, sce->preview);
direct_link_curvemapping(fd, &sce->r.mblur_shutter_curve);
#ifdef USE_COLLECTION_COMPAT_28
/* this runs before the very first doversion */
if (sce->collection) {
sce->collection = newdataadr(fd, sce->collection);
direct_link_scene_collection(fd, sce->collection);
}
#endif
if (sce->master_collection) {
sce->master_collection = newdataadr(fd, sce->master_collection);
/* Needed because this is an ID outside of Main. */
direct_link_id(fd, &sce->master_collection->id);
direct_link_collection(fd, sce->master_collection);
}
/* insert into global old-new map for reading without UI (link_global accesses it again) */
link_glob_list(fd, &sce->view_layers);
for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
direct_link_view_layer(fd, view_layer);
}
if (fd->memfile) {
/* If it's undo try to recover the cache. */
if (fd->scenemap) sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache);
else sce->eevee.light_cache = NULL;
}
else {
/* else try to read the cache from file. */
sce->eevee.light_cache = newdataadr(fd, sce->eevee.light_cache);
if (sce->eevee.light_cache) {
direct_link_lightcache(fd, sce->eevee.light_cache);
}
}
sce->layer_properties = newdataadr(fd, sce->layer_properties);
IDP_DirectLinkGroup_OrFree(&sce->layer_properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Grease Pencil
* \{ */
/* relink's grease pencil data's refs */
static void lib_link_gpencil(FileData *fd, Main *main)
{
/* Relink all datablock linked by GP datablock */
for (bGPdata *gpd = main->gpencils.first; gpd; gpd = gpd->id.next) {
if (gpd->id.tag & LIB_TAG_NEED_LINK) {
/* Layers */
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* Layer -> Parent References */
gpl->parent = newlibadr(fd, gpd->id.lib, gpl->parent);
}
/* Datablock Stuff */
IDP_LibLinkProperty(gpd->id.properties, fd);
lib_link_animdata(fd, &gpd->id, gpd->adt);
/* materials */
for (int a = 0; a < gpd->totcol; a++) {
gpd->mat[a] = newlibadr_us(fd, gpd->id.lib, gpd->mat[a]);
}
gpd->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/* relinks grease-pencil data - used for direct_link and old file linkage */
static void direct_link_gpencil(FileData *fd, bGPdata *gpd)
{
bGPDlayer *gpl;
bGPDframe *gpf;
bGPDstroke *gps;
bGPDpalette *palette;
/* we must firstly have some grease-pencil data to link! */
if (gpd == NULL)
return;
/* relink animdata */
gpd->adt = newdataadr(fd, gpd->adt);
direct_link_animdata(fd, gpd->adt);
/* init stroke buffer */
gpd->runtime.sbuffer = NULL;
gpd->runtime.sbuffer_size = 0;
gpd->runtime.tot_cp_points = 0;
/* relink palettes (old palettes deprecated, only to convert old files) */
link_list(fd, &gpd->palettes);
if (gpd->palettes.first != NULL) {
for (palette = gpd->palettes.first; palette; palette = palette->next) {
link_list(fd, &palette->colors);
}
}
/* materials */
gpd->mat = newdataadr(fd, gpd->mat);
test_pointer_array(fd, (void **)&gpd->mat);
/* relink layers */
link_list(fd, &gpd->layers);
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
/* relink frames */
link_list(fd, &gpl->frames);
gpl->actframe = newdataadr(fd, gpl->actframe);
gpl->runtime.icon_id = 0;
for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
/* relink strokes (and their points) */
link_list(fd, &gpf->strokes);
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* relink stroke points array */
gps->points = newdataadr(fd, gps->points);
/* relink weight data */
if (gps->dvert) {
gps->dvert = newdataadr(fd, gps->dvert);
direct_link_dverts(fd, gps->totpoints, gps->dvert);
}
/* the triangulation is not saved, so need to be recalculated */
gps->triangles = NULL;
gps->tot_triangles = 0;
gps->flag |= GP_STROKE_RECALC_GEOMETRY;
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Screen Area/Region (Screen Data)
* \{ */
static void direct_link_panel_list(FileData *fd, ListBase *lb)
{
link_list(fd, lb);
for (Panel *pa = lb->first; pa; pa = pa->next) {
pa->paneltab = newdataadr(fd, pa->paneltab);
pa->runtime_flag = 0;
pa->activedata = NULL;
pa->type = NULL;
direct_link_panel_list(fd, &pa->children);
}
}
static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
{
uiList *ui_list;
direct_link_panel_list(fd, &ar->panels);
link_list(fd, &ar->panels_category_active);
link_list(fd, &ar->ui_lists);
for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) {
ui_list->type = NULL;
ui_list->dyn_data = NULL;
ui_list->properties = newdataadr(fd, ui_list->properties);
IDP_DirectLinkGroup_OrFree(&ui_list->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
link_list(fd, &ar->ui_previews);
if (spacetype == SPACE_EMPTY) {
/* unkown space type, don't leak regiondata */
ar->regiondata = NULL;
}
else if (ar->flag & RGN_FLAG_TEMP_REGIONDATA) {
/* Runtime data, don't use. */
ar->regiondata = NULL;
}
else {
ar->regiondata = newdataadr(fd, ar->regiondata);
if (ar->regiondata) {
if (spacetype == SPACE_VIEW3D) {
RegionView3D *rv3d = ar->regiondata;
rv3d->localvd = newdataadr(fd, rv3d->localvd);
rv3d->clipbb = newdataadr(fd, rv3d->clipbb);
rv3d->depths = NULL;
rv3d->render_engine = NULL;
rv3d->sms = NULL;
rv3d->smooth_timer = NULL;
}
}
}
ar->v2d.tab_offset = NULL;
ar->v2d.tab_num = 0;
ar->v2d.tab_cur = 0;
ar->v2d.sms = NULL;
ar->v2d.alpha_hor = ar->v2d.alpha_vert = 255; /* visible by default */
BLI_listbase_clear(&ar->panels_category);
BLI_listbase_clear(&ar->handlers);
BLI_listbase_clear(&ar->uiblocks);
ar->headerstr = NULL;
ar->visible = 0;
ar->type = NULL;
ar->do_draw = 0;
ar->gizmo_map = NULL;
ar->regiontimer = NULL;
ar->draw_buffer = NULL;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
}
static void direct_link_area(FileData *fd, ScrArea *area)
{
SpaceLink *sl;
ARegion *ar;
link_list(fd, &(area->spacedata));
link_list(fd, &(area->regionbase));
BLI_listbase_clear(&area->handlers);
area->type = NULL; /* spacetype callbacks */
area->butspacetype = SPACE_EMPTY; /* Should always be unset so that rna_Area_type_get works correctly */
area->region_active_win = -1;
area->flag &= ~AREA_FLAG_ACTIVE_TOOL_UPDATE;
area->global = newdataadr(fd, area->global);
/* if we do not have the spacetype registered we cannot
* free it, so don't allocate any new memory for such spacetypes. */
if (!BKE_spacetype_exists(area->spacetype)) {
area->butspacetype = area->spacetype; /* Hint for versioning code to replace deprecated space types. */
area->spacetype = SPACE_EMPTY;
}
for (ar = area->regionbase.first; ar; ar = ar->next) {
direct_link_region(fd, ar, area->spacetype);
}
/* accident can happen when read/save new file with older version */
/* 2.50: we now always add spacedata for info */
if (area->spacedata.first == NULL) {
SpaceInfo *sinfo = MEM_callocN(sizeof(SpaceInfo), "spaceinfo");
area->spacetype = sinfo->spacetype = SPACE_INFO;
BLI_addtail(&area->spacedata, sinfo);
}
/* add local view3d too */
else if (area->spacetype == SPACE_VIEW3D) {
blo_do_versions_view3d_split_250(area->spacedata.first, &area->regionbase);
}
for (sl = area->spacedata.first; sl; sl = sl->next) {
link_list(fd, &(sl->regionbase));
/* if we do not have the spacetype registered we cannot
* free it, so don't allocate any new memory for such spacetypes. */
if (!BKE_spacetype_exists(sl->spacetype))
sl->spacetype = SPACE_EMPTY;
for (ar = sl->regionbase.first; ar; ar = ar->next)
direct_link_region(fd, ar, sl->spacetype);
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
v3d->flag |= V3D_INVALID_BACKBUF;
if (v3d->gpd) {
v3d->gpd = newdataadr(fd, v3d->gpd);
direct_link_gpencil(fd, v3d->gpd);
}
v3d->localvd = newdataadr(fd, v3d->localvd);
v3d->properties_storage = NULL;
/* render can be quite heavy, set to solid on load */
if (v3d->shading.type == OB_RENDER) {
v3d->shading.type = OB_SOLID;
}
v3d->shading.prev_type = OB_SOLID;
if (v3d->fx_settings.dof)
v3d->fx_settings.dof = newdataadr(fd, v3d->fx_settings.dof);
if (v3d->fx_settings.ssao)
v3d->fx_settings.ssao = newdataadr(fd, v3d->fx_settings.ssao);
blo_do_versions_view3d_split_250(v3d, &sl->regionbase);
}
else if (sl->spacetype == SPACE_GRAPH) {
SpaceGraph *sipo = (SpaceGraph *)sl;
sipo->ads = newdataadr(fd, sipo->ads);
BLI_listbase_clear(&sipo->runtime.ghost_curves);
}
else if (sl->spacetype == SPACE_NLA) {
SpaceNla *snla = (SpaceNla *)sl;
snla->ads = newdataadr(fd, snla->ads);
}
else if (sl->spacetype == SPACE_OUTLINER) {
SpaceOutliner *soops = (SpaceOutliner *)sl;
/* use newdataadr_no_us and do not free old memory avoiding double
* frees and use of freed memory. this could happen because of a
* bug fixed in revision 58959 where the treestore memory address
* was not unique */
TreeStore *ts = newdataadr_no_us(fd, soops->treestore);
soops->treestore = NULL;
if (ts) {
TreeStoreElem *elems = newdataadr_no_us(fd, ts->data);
soops->treestore = BLI_mempool_create(
sizeof(TreeStoreElem), ts->usedelem,
512, BLI_MEMPOOL_ALLOW_ITER);
if (ts->usedelem && elems) {
int i;
for (i = 0; i < ts->usedelem; i++) {
TreeStoreElem *new_elem = BLI_mempool_alloc(soops->treestore);
*new_elem = elems[i];
}
}
/* we only saved what was used */
soops->storeflag |= SO_TREESTORE_CLEANUP; // at first draw
}
soops->treehash = NULL;
soops->tree.first = soops->tree.last = NULL;
}
else if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
sima->iuser.scene = NULL;
sima->iuser.ok = 1;
sima->scopes.waveform_1 = NULL;
sima->scopes.waveform_2 = NULL;
sima->scopes.waveform_3 = NULL;
sima->scopes.vecscope = NULL;
sima->scopes.ok = 0;
/* WARNING: gpencil data is no longer stored directly in sima after 2.5
* so sacrifice a few old files for now to avoid crashes with new files!
* committed: r28002 */
#if 0
sima->gpd = newdataadr(fd, sima->gpd);
if (sima->gpd)
direct_link_gpencil(fd, sima->gpd);
#endif
}
else if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
if (snode->gpd) {
snode->gpd = newdataadr(fd, snode->gpd);
direct_link_gpencil(fd, snode->gpd);
}
link_list(fd, &snode->treepath);
snode->edittree = NULL;
snode->iofsd = NULL;
BLI_listbase_clear(&snode->linkdrag);
}
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
st->drawcache = NULL;
st->scroll_accum[0] = 0.0f;
st->scroll_accum[1] = 0.0f;
}
else if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
/* grease pencil data is not a direct data and can't be linked from direct_link*
* functions, it should be linked from lib_link* functions instead
*
* otherwise it'll lead to lost grease data on open because it'll likely be
* read from file after all other users of grease pencil and newdataadr would
* simple return NULL here (sergey)
*/
#if 0
if (sseq->gpd) {
sseq->gpd = newdataadr(fd, sseq->gpd);
direct_link_gpencil(fd, sseq->gpd);
}
#endif
sseq->scopes.reference_ibuf = NULL;
sseq->scopes.zebra_ibuf = NULL;
sseq->scopes.waveform_ibuf = NULL;
sseq->scopes.sep_waveform_ibuf = NULL;
sseq->scopes.vector_ibuf = NULL;
sseq->scopes.histogram_ibuf = NULL;
sseq->compositor = NULL;
}
else if (sl->spacetype == SPACE_PROPERTIES) {
SpaceProperties *sbuts = (SpaceProperties *)sl;
sbuts->path = NULL;
sbuts->texuser = NULL;
sbuts->mainbo = sbuts->mainb;
sbuts->mainbuser = sbuts->mainb;
}
else if (sl->spacetype == SPACE_CONSOLE) {
SpaceConsole *sconsole = (SpaceConsole *)sl;
ConsoleLine *cl, *cl_next;
link_list(fd, &sconsole->scrollback);
link_list(fd, &sconsole->history);
//for (cl= sconsole->scrollback.first; cl; cl= cl->next)
// cl->line= newdataadr(fd, cl->line);
/* comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression,
* from left to right. the right-most expression sets the result of the comma
* expression as a whole*/
for (cl = sconsole->history.first; cl; cl = cl_next) {
cl_next = cl->next;
cl->line = newdataadr(fd, cl->line);
if (cl->line) {
/* the allocted length is not written, so reset here */
cl->len_alloc = cl->len + 1;
}
else {
BLI_remlink(&sconsole->history, cl);
MEM_freeN(cl);
}
}
}
else if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
/* this sort of info is probably irrelevant for reloading...
* plus, it isn't saved to files yet!
*/
sfile->folders_prev = sfile->folders_next = NULL;
sfile->files = NULL;
sfile->layout = NULL;
sfile->op = NULL;
sfile->previews_timer = NULL;
sfile->params = newdataadr(fd, sfile->params);
}
else if (sl->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)sl;
sclip->scopes.track_search = NULL;
sclip->scopes.track_preview = NULL;
sclip->scopes.ok = 0;
}
}
BLI_listbase_clear(&area->actionzones);
area->v1 = newdataadr(fd, area->v1);
area->v2 = newdataadr(fd, area->v2);
area->v3 = newdataadr(fd, area->v3);
area->v4 = newdataadr(fd, area->v4);
}
static void lib_link_area(FileData *fd, ID *parent_id, ScrArea *area)
{
area->full = newlibadr(fd, parent_id->lib, area->full);
memset(&area->runtime, 0x0, sizeof(area->runtime));
for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
switch (sl->spacetype) {
case SPACE_VIEW3D:
{
View3D *v3d = (View3D *)sl;
v3d->camera = newlibadr(fd, parent_id->lib, v3d->camera);
v3d->ob_centre = newlibadr(fd, parent_id->lib, v3d->ob_centre);
if (v3d->localvd) {
v3d->localvd->camera = newlibadr(fd, parent_id->lib, v3d->localvd->camera);
}
break;
}
case SPACE_GRAPH:
{
SpaceGraph *sipo = (SpaceGraph *)sl;
bDopeSheet *ads = sipo->ads;
if (ads) {
ads->source = newlibadr(fd, parent_id->lib, ads->source);
ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
}
break;
}
case SPACE_PROPERTIES:
{
SpaceProperties *sbuts = (SpaceProperties *)sl;
sbuts->pinid = newlibadr(fd, parent_id->lib, sbuts->pinid);
if (sbuts->pinid == NULL) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
break;
}
case SPACE_FILE:
break;
case SPACE_ACTION:
{
SpaceAction *saction = (SpaceAction *)sl;
bDopeSheet *ads = &saction->ads;
if (ads) {
ads->source = newlibadr(fd, parent_id->lib, ads->source);
ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
}
saction->action = newlibadr(fd, parent_id->lib, saction->action);
break;
}
case SPACE_IMAGE:
{
SpaceImage *sima = (SpaceImage *)sl;
sima->image = newlibadr_real_us(fd, parent_id->lib, sima->image);
sima->mask_info.mask = newlibadr_real_us(fd, parent_id->lib, sima->mask_info.mask);
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so fingers crossed this works fine!
*/
sima->gpd = newlibadr_us(fd, parent_id->lib, sima->gpd);
break;
}
case SPACE_SEQ:
{
SpaceSeq *sseq = (SpaceSeq *)sl;
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so fingers crossed this works fine!
*/
sseq->gpd = newlibadr_us(fd, parent_id->lib, sseq->gpd);
break;
}
case SPACE_NLA:
{
SpaceNla *snla = (SpaceNla *)sl;
bDopeSheet *ads = snla->ads;
if (ads) {
ads->source = newlibadr(fd, parent_id->lib, ads->source);
ads->filter_grp = newlibadr(fd, parent_id->lib, ads->filter_grp);
}
break;
}
case SPACE_TEXT:
{
SpaceText *st = (SpaceText *)sl;
st->text = newlibadr(fd, parent_id->lib, st->text);
break;
}
case SPACE_SCRIPT:
{
SpaceScript *scpt = (SpaceScript *)sl;
/*scpt->script = NULL; - 2.45 set to null, better re-run the script */
if (scpt->script) {
scpt->script = newlibadr(fd, parent_id->lib, scpt->script);
if (scpt->script) {
SCRIPT_SET_NULL(scpt->script);
}
}
break;
}
case SPACE_OUTLINER:
{
SpaceOutliner *so = (SpaceOutliner *)sl;
so->search_tse.id = newlibadr(fd, NULL, so->search_tse.id);
if (so->treestore) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
BLI_mempool_iternew(so->treestore, &iter);
while ((tselem = BLI_mempool_iterstep(&iter))) {
tselem->id = newlibadr(fd, NULL, tselem->id);
}
if (so->treehash) {
/* rebuild hash table, because it depends on ids too */
so->storeflag |= SO_TREESTORE_REBUILD;
}
}
break;
}
case SPACE_NODE:
{
SpaceNode *snode = (SpaceNode *)sl;
bNodeTreePath *path, *path_next;
bNodeTree *ntree;
/* node tree can be stored locally in id too, link this first */
snode->id = newlibadr(fd, parent_id->lib, snode->id);
snode->from = newlibadr(fd, parent_id->lib, snode->from);
ntree = snode->id ? ntreeFromID(snode->id) : NULL;
snode->nodetree = ntree ? ntree : newlibadr_us(fd, parent_id->lib, snode->nodetree);
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
path->nodetree = snode->nodetree;
}
else
path->nodetree = newlibadr_us(fd, parent_id->lib, path->nodetree);
if (!path->nodetree)
break;
}
/* remaining path entries are invalid, remove */
for (; path; path = path_next) {
path_next = path->next;
BLI_remlink(&snode->treepath, path);
MEM_freeN(path);
}
/* edittree is just the last in the path,
* set this directly since the path may have been shortened above */
if (snode->treepath.last) {
path = snode->treepath.last;
snode->edittree = path->nodetree;
}
else {
snode->edittree = NULL;
}
break;
}
case SPACE_CLIP:
{
SpaceClip *sclip = (SpaceClip *)sl;
sclip->clip = newlibadr_real_us(fd, parent_id->lib, sclip->clip);
sclip->mask_info.mask = newlibadr_real_us(fd, parent_id->lib, sclip->mask_info.mask);
break;
}
default:
break;
}
}
}
/**
* \return false on error.
*/
static bool direct_link_area_map(FileData *fd, ScrAreaMap *area_map)
{
link_list(fd, &area_map->vertbase);
link_list(fd, &area_map->edgebase);
link_list(fd, &area_map->areabase);
for (ScrArea *area = area_map->areabase.first; area; area = area->next) {
direct_link_area(fd, area);
}
/* edges */
for (ScrEdge *se = area_map->edgebase.first; se; se = se->next) {
se->v1 = newdataadr(fd, se->v1);
se->v2 = newdataadr(fd, se->v2);
BKE_screen_sort_scrvert(&se->v1, &se->v2);
if (se->v1 == NULL) {
BLI_remlink(&area_map->edgebase, se);
return false;
}
}
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Window Manager
* \{ */
static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
{
wmWindow *win;
id_us_ensure_real(&wm->id);
link_list(fd, &wm->windows);
for (win = wm->windows.first; win; win = win->next) {
win->parent = newdataadr(fd, win->parent);
WorkSpaceInstanceHook *hook = win->workspace_hook;
win->workspace_hook = newdataadr(fd, hook);
/* we need to restore a pointer to this later when reading workspaces, so store in global oldnew-map */
oldnewmap_insert(fd->globmap, hook, win->workspace_hook, 0);
direct_link_area_map(fd, &win->global_areas);
win->ghostwin = NULL;
win->gpuctx = NULL;
win->eventstate = NULL;
win->cursor_keymap_status = NULL;
win->tweak = NULL;
#ifdef WIN32
win->ime_data = NULL;
#endif
BLI_listbase_clear(&win->queue);
BLI_listbase_clear(&win->handlers);
BLI_listbase_clear(&win->modalhandlers);
BLI_listbase_clear(&win->gesture);
win->active = 0;
win->cursor = 0;
win->lastcursor = 0;
win->modalcursor = 0;
win->grabcursor = 0;
win->addmousemove = true;
win->stereo3d_format = newdataadr(fd, win->stereo3d_format);
/* multiview always fallback to anaglyph at file opening
* otherwise quadbuffer saved files can break Blender */
if (win->stereo3d_format) {
win->stereo3d_format->display_mode = S3D_DISPLAY_ANAGLYPH;
}
}
BLI_listbase_clear(&wm->timers);
BLI_listbase_clear(&wm->operators);
BLI_listbase_clear(&wm->paintcursors);
BLI_listbase_clear(&wm->queue);
BKE_reports_init(&wm->reports, RPT_STORE);
BLI_listbase_clear(&wm->keyconfigs);
wm->defaultconf = NULL;
wm->addonconf = NULL;
wm->userconf = NULL;
wm->undo_stack = NULL;
wm->message_bus = NULL;
BLI_listbase_clear(&wm->jobs);
BLI_listbase_clear(&wm->drags);
wm->windrawable = NULL;
wm->winactive = NULL;
wm->initialized = 0;
wm->op_undo_depth = 0;
wm->is_interface_locked = 0;
}
static void lib_link_windowmanager(FileData *fd, Main *main)
{
wmWindowManager *wm;
wmWindow *win;
for (wm = main->wm.first; wm; wm = wm->id.next) {
if (wm->id.tag & LIB_TAG_NEED_LINK) {
/* Note: WM IDProperties are never written to file, hence no need to read/link them here. */
for (win = wm->windows.first; win; win = win->next) {
if (win->workspace_hook) { /* NULL for old files */
lib_link_workspace_instance_hook(fd, win->workspace_hook, &wm->id);
}
win->scene = newlibadr(fd, wm->id.lib, win->scene);
/* deprecated, but needed for versioning (will be NULL'ed then) */
win->screen = newlibadr(fd, NULL, win->screen);
for (ScrArea *area = win->global_areas.areabase.first; area; area = area->next) {
lib_link_area(fd, &wm->id, area);
}
}
wm->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Screen
* \{ */
/* note: file read without screens option G_FILE_NO_UI;
* check lib pointers in call below */
static void lib_link_screen(FileData *fd, Main *main)
{
for (bScreen *sc = main->screens.first; sc; sc = sc->id.next) {
if (sc->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(sc->id.properties, fd);
/* deprecated, but needed for versioning (will be NULL'ed then) */
sc->scene = newlibadr(fd, sc->id.lib, sc->scene);
sc->animtimer = NULL; /* saved in rare cases */
sc->tool_tip = NULL;
sc->scrubbing = false;
for (ScrArea *area = sc->areabase.first; area; area = area->next) {
lib_link_area(fd, &sc->id, area);
}
sc->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/* how to handle user count on pointer restore */
typedef enum ePointerUserMode {
USER_IGNORE = 0, /* ignore user count */
USER_REAL = 1, /* ensure at least one real user (fake user ignored) */
} ePointerUserMode;
static void restore_pointer_user(ID *id, ID *newid, ePointerUserMode user)
{
BLI_assert(STREQ(newid->name + 2, id->name + 2));
BLI_assert(newid->lib == id->lib);
UNUSED_VARS_NDEBUG(id);
if (user == USER_REAL) {
id_us_ensure_real(newid);
}
}
#ifndef USE_GHASH_RESTORE_POINTER
/**
* A version of #restore_pointer_by_name that performs a full search (slow!).
* Use only for limited lookups, when the overhead of
* creating a #IDNameLib_Map for a single lookup isn't worthwhile.
*/
static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode user)
{
if (id) {
ListBase *lb = which_libbase(mainp, GS(id->name));
if (lb) { /* there's still risk of checking corrupt mem (freed Ids in oops) */
ID *idn = lb->first;
for (; idn; idn = idn->next) {
if (STREQ(idn->name + 2, id->name + 2)) {
if (idn->lib == id->lib) {
restore_pointer_user(id, idn, user);
break;
}
}
}
return idn;
}
}
return NULL;
}
#endif
/**
* Only for undo files, or to restore a screen after reading without UI...
*
* \param user:
* - USER_IGNORE: no usercount change
* - USER_REAL: ensure a real user (even if a fake one is set)
* \param id_map: lookup table, use when performing many lookups.
* this could be made an optional argument (falling back to a full lookup),
* however at the moment it's always available.
*/
static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePointerUserMode user)
{
#ifdef USE_GHASH_RESTORE_POINTER
if (id) {
/* use fast lookup when available */
ID *idn = BKE_main_idmap_lookup_id(id_map, id);
if (idn) {
restore_pointer_user(id, idn, user);
}
return idn;
}
return NULL;
#else
Main *mainp = BKE_main_idmap_main_get(id_map);
return restore_pointer_by_name_main(mainp, id, user);
#endif
}
static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_map)
{
if (id) {
/* clipboard must ensure this */
BLI_assert(id->newid != NULL);
id->newid = restore_pointer_by_name(id_map, id->newid, USER_REAL);
}
}
static int lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
{
struct IDNameLib_Map *id_map = arg_pt;
lib_link_seq_clipboard_pt_restore((ID *)seq->scene, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->clip, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->mask, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->sound, id_map);
return 1;
}
static void lib_link_clipboard_restore(struct IDNameLib_Map *id_map)
{
/* update IDs stored in sequencer clipboard */
BKE_sequencer_base_recursive_apply(&seqbase_clipboard, lib_link_seq_clipboard_cb, id_map);
}
static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer)
{
bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
for (ScrArea *area = screen->areabase.first; area; area = area->next) {
for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
if (v3d->camera == NULL || v3d->scenelock) {
v3d->camera = scene->camera;
}
if (v3d->localvd) {
Base *base = NULL;
v3d->localvd->camera = scene->camera;
/* Localview can become invalid during undo/redo steps, so we exit it when no could be found. */
for (base = view_layer->object_bases.first; base; base = base->next) {
if (base->local_view_bits & v3d->local_view_uuid) {
break;
}
}
if (base == NULL) {
MEM_freeN(v3d->localvd);
v3d->localvd = NULL;
v3d->local_view_uuid = 0;
/* Regionbase storage is different depending if the space is active. */
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : &sl->regionbase;
for (ARegion *ar = regionbase->first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = ar->regiondata;
if (rv3d->localvd) {
MEM_freeN(rv3d->localvd);
rv3d->localvd = NULL;
}
}
}
}
}
}
}
}
}
static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map, Main *newmain, WorkSpaceLayout *layout)
{
bScreen *screen = BKE_workspace_layout_screen_get(layout);
/* avoid conflicts with 2.8x branch */
{
for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
ARegion *ar;
v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL);
v3d->ob_centre = restore_pointer_by_name(id_map, (ID *)v3d->ob_centre, USER_REAL);
/* Free render engines for now. */
ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
for (ar = regionbase->first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = ar->regiondata;
if (rv3d && rv3d->render_engine) {
RE_engine_free(rv3d->render_engine);
rv3d->render_engine = NULL;
}
}
}
}
else if (sl->spacetype == SPACE_GRAPH) {
SpaceGraph *sipo = (SpaceGraph *)sl;
bDopeSheet *ads = sipo->ads;
if (ads) {
ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
if (ads->filter_grp)
ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE);
}
/* force recalc of list of channels (i.e. includes calculating F-Curve colors)
* thus preventing the "black curves" problem post-undo
*/
sipo->runtime.flag |= SIPO_RUNTIME_FLAG_NEED_CHAN_SYNC_COLOR;
}
else if (sl->spacetype == SPACE_PROPERTIES) {
SpaceProperties *sbuts = (SpaceProperties *)sl;
sbuts->pinid = restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE);
if (sbuts->pinid == NULL) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
/* TODO: restore path pointers: T40046
* (complicated because this contains data pointers too, not just ID)*/
MEM_SAFE_FREE(sbuts->path);
}
else if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
sfile->op = NULL;
sfile->previews_timer = NULL;
}
else if (sl->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)sl;
saction->action = restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL);
saction->ads.source = restore_pointer_by_name(id_map, (ID *)saction->ads.source, USER_REAL);
if (saction->ads.filter_grp)
saction->ads.filter_grp = restore_pointer_by_name(id_map, (ID *)saction->ads.filter_grp, USER_IGNORE);
/* force recalc of list of channels, potentially updating the active action
* while we're at it (as it can only be updated that way) [#28962]
*/
saction->runtime.flag |= SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC;
}
else if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
sima->image = restore_pointer_by_name(id_map, (ID *)sima->image, USER_REAL);
/* this will be freed, not worth attempting to find same scene,
* since it gets initialized later */
sima->iuser.scene = NULL;
#if 0
/* Those are allocated and freed by space code, no need to handle them here. */
MEM_SAFE_FREE(sima->scopes.waveform_1);
MEM_SAFE_FREE(sima->scopes.waveform_2);
MEM_SAFE_FREE(sima->scopes.waveform_3);
MEM_SAFE_FREE(sima->scopes.vecscope);
#endif
sima->scopes.ok = 0;
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
sima->gpd = restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL);
sima->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sima->mask_info.mask, USER_REAL);
}
else if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
sseq->gpd = restore_pointer_by_name(id_map, (ID *)sseq->gpd, USER_REAL);
}
else if (sl->spacetype == SPACE_NLA) {
SpaceNla *snla = (SpaceNla *)sl;
bDopeSheet *ads = snla->ads;
if (ads) {
ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
if (ads->filter_grp)
ads->filter_grp = restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE);
}
}
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_REAL);
if (st->text == NULL) st->text = newmain->texts.first;
}
else if (sl->spacetype == SPACE_SCRIPT) {
SpaceScript *scpt = (SpaceScript *)sl;
scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL);
/*sc->script = NULL; - 2.45 set to null, better re-run the script */
if (scpt->script) {
SCRIPT_SET_NULL(scpt->script);
}
}
else if (sl->spacetype == SPACE_OUTLINER) {
SpaceOutliner *so = (SpaceOutliner *)sl;
so->search_tse.id = restore_pointer_by_name(id_map, so->search_tse.id, USER_IGNORE);
if (so->treestore) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
BLI_mempool_iternew(so->treestore, &iter);
while ((tselem = BLI_mempool_iterstep(&iter))) {
/* Do not try to restore pointers to drivers/sequence/etc., can crash in undo case! */
if (TSE_IS_REAL_ID(tselem)) {
tselem->id = restore_pointer_by_name(id_map, tselem->id, USER_IGNORE);
}
else {
tselem->id = NULL;
}
}
if (so->treehash) {
/* rebuild hash table, because it depends on ids too */
so->storeflag |= SO_TREESTORE_REBUILD;
}
}
}
else if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
bNodeTreePath *path, *path_next;
bNodeTree *ntree;
/* node tree can be stored locally in id too, link this first */
snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL);
snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE);
ntree = snode->id ? ntreeFromID(snode->id) : NULL;
snode->nodetree = ntree ? ntree : restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL);
for (path = snode->treepath.first; path; path = path->next) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
path->nodetree = snode->nodetree;
}
else
path->nodetree = restore_pointer_by_name(id_map, (ID *)path->nodetree, USER_REAL);
if (!path->nodetree)
break;
}
/* remaining path entries are invalid, remove */
for (; path; path = path_next) {
path_next = path->next;
BLI_remlink(&snode->treepath, path);
MEM_freeN(path);
}
/* edittree is just the last in the path,
* set this directly since the path may have been shortened above */
if (snode->treepath.last) {
path = snode->treepath.last;
snode->edittree = path->nodetree;
}
else
snode->edittree = NULL;
}
else if (sl->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)sl;
sclip->clip = restore_pointer_by_name(id_map, (ID *)sclip->clip, USER_REAL);
sclip->mask_info.mask = restore_pointer_by_name(id_map, (ID *)sclip->mask_info.mask, USER_REAL);
sclip->scopes.ok = 0;
}
}
}
}
}
/**
* Used to link a file (without UI) to the current UI.
* Note that it assumes the old pointers in UI are still valid, so old Main is not freed.
*/
void blo_lib_link_restore(Main *oldmain, Main *newmain, wmWindowManager *curwm, Scene *curscene, ViewLayer *cur_view_layer)
{
struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain, true, oldmain);
for (WorkSpace *workspace = newmain->workspaces.first; workspace; workspace = workspace->id.next) {
ListBase *layouts = BKE_workspace_layouts_get(workspace);
for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) {
lib_link_workspace_layout_restore(id_map, newmain, layout);
}
}
for (wmWindow *win = curwm->windows.first; win; win = win->next) {
WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
ID *workspace_id = (ID *)workspace;
Scene *oldscene = win->scene;
workspace = restore_pointer_by_name(id_map, workspace_id, USER_REAL);
BKE_workspace_active_set(win->workspace_hook, workspace);
win->scene = restore_pointer_by_name(id_map, (ID *)win->scene, USER_REAL);
if (win->scene == NULL) {
win->scene = curscene;
}
if (BKE_view_layer_find(win->scene, win->view_layer_name) == NULL) {
STRNCPY(win->view_layer_name, cur_view_layer->name);
}
BKE_workspace_active_set(win->workspace_hook, workspace);
/* keep cursor location through undo */
memcpy(&win->scene->cursor, &oldscene->cursor, sizeof(win->scene->cursor));
lib_link_window_scene_data_restore(win, win->scene, cur_view_layer);
BLI_assert(win->screen == NULL);
}
/* update IDs stored in all possible clipboards */
lib_link_clipboard_restore(id_map);
BKE_main_idmap_destroy(id_map);
}
/* for the saved 2.50 files without regiondata */
/* and as patch for 2.48 and older */
void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions)
{
ARegion *ar;
for (ar = regions->first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_WINDOW && ar->regiondata == NULL) {
RegionView3D *rv3d;
rv3d = ar->regiondata = MEM_callocN(sizeof(RegionView3D), "region v3d patch");
rv3d->persp = (char)v3d->persp;
rv3d->view = (char)v3d->view;
rv3d->dist = v3d->dist;
copy_v3_v3(rv3d->ofs, v3d->ofs);
copy_qt_qt(rv3d->viewquat, v3d->viewquat);
}
}
/* this was not initialized correct always */
if (v3d->gridsubdiv == 0)
v3d->gridsubdiv = 10;
}
static bool direct_link_screen(FileData *fd, bScreen *sc)
{
bool wrong_id = false;
sc->regionbase.first = sc->regionbase.last = NULL;
sc->context = NULL;
sc->active_region = NULL;
sc->preview = direct_link_preview_image(fd, sc->preview);
if (!direct_link_area_map(fd, AREAMAP_FROM_SCREEN(sc))) {
printf("Error reading Screen %s... removing it.\n", sc->id.name + 2);
wrong_id = true;
}
return wrong_id;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Library
* \{ */
static void direct_link_library(FileData *fd, Library *lib, Main *main)
{
Main *newmain;
/* check if the library was already read */
for (newmain = fd->mainlist->first; newmain; newmain = newmain->next) {
if (newmain->curlib) {
if (BLI_path_cmp(newmain->curlib->filepath, lib->filepath) == 0) {
blo_reportf_wrap(fd->reports, RPT_WARNING,
TIP_("Library '%s', '%s' had multiple instances, save and reload!"),
lib->name, lib->filepath);
change_link_placeholder_to_real_ID_pointer(fd->mainlist, fd, lib, newmain->curlib);
/* change_link_placeholder_to_real_ID_pointer_fd(fd, lib, newmain->curlib); */
BLI_remlink(&main->libraries, lib);
MEM_freeN(lib);
/* Now, since Blender always expect **latest** Main pointer from fd->mainlist to be the active library
* Main pointer, where to add all non-library data-blocks found in file next, we have to switch that
* 'dupli' found Main to latest position in the list!
* Otherwise, you get weird disappearing linked data on a rather unconsistant basis.
* See also T53977 for reproducible case. */
BLI_remlink(fd->mainlist, newmain);
BLI_addtail(fd->mainlist, newmain);
return;
}
}
}
/* make sure we have full path in lib->filepath */
BLI_strncpy(lib->filepath, lib->name, sizeof(lib->name));
BLI_cleanup_path(fd->relabase, lib->filepath);
// printf("direct_link_library: name %s\n", lib->name);
// printf("direct_link_library: filepath %s\n", lib->filepath);
lib->packedfile = direct_link_packedfile(fd, lib->packedfile);
/* new main */
newmain = BKE_main_new();
BLI_addtail(fd->mainlist, newmain);
newmain->curlib = lib;
lib->parent = NULL;
}
static void lib_link_library(FileData *UNUSED(fd), Main *main)
{
Library *lib;
for (lib = main->libraries.first; lib; lib = lib->id.next) {
id_us_ensure_real(&lib->id);
}
}
/* Always call this once you have loaded new library data to set the relative paths correctly in relation to the blend file */
static void fix_relpaths_library(const char *basepath, Main *main)
{
Library *lib;
/* BLO_read_from_memory uses a blank filename */
if (basepath == NULL || basepath[0] == '\0') {
for (lib = main->libraries.first; lib; lib = lib->id.next) {
/* when loading a linked lib into a file which has not been saved,
* there is nothing we can be relative to, so instead we need to make
* it absolute. This can happen when appending an object with a relative
* link into an unsaved blend file. See [#27405].
* The remap relative option will make it relative again on save - campbell */
if (BLI_path_is_rel(lib->name)) {
BLI_strncpy(lib->name, lib->filepath, sizeof(lib->name));
}
}
}
else {
for (lib = main->libraries.first; lib; lib = lib->id.next) {
/* Libraries store both relative and abs paths, recreate relative paths,
* relative to the blend file since indirectly linked libs will be relative to their direct linked library */
if (BLI_path_is_rel(lib->name)) { /* if this is relative to begin with? */
BLI_strncpy(lib->name, lib->filepath, sizeof(lib->name));
BLI_path_rel(lib->name, basepath);
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Light Probe
* \{ */
static void lib_link_lightprobe(FileData *fd, Main *main)
{
for (LightProbe *prb = main->lightprobes.first; prb; prb = prb->id.next) {
if (prb->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(prb->id.properties, fd);
lib_link_animdata(fd, &prb->id, prb->adt);
prb->visibility_grp = newlibadr(fd, prb->id.lib, prb->visibility_grp);
prb->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_lightprobe(FileData *fd, LightProbe *prb)
{
prb->adt = newdataadr(fd, prb->adt);
direct_link_animdata(fd, prb->adt);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Speaker
* \{ */
static void lib_link_speaker(FileData *fd, Main *main)
{
for (Speaker *spk = main->speakers.first; spk; spk = spk->id.next) {
if (spk->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(spk->id.properties, fd);
lib_link_animdata(fd, &spk->id, spk->adt);
spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound);
spk->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_speaker(FileData *fd, Speaker *spk)
{
spk->adt = newdataadr(fd, spk->adt);
direct_link_animdata(fd, spk->adt);
#if 0
spk->sound = newdataadr(fd, spk->sound);
direct_link_sound(fd, spk->sound);
#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Sound
* \{ */
static void direct_link_sound(FileData *fd, bSound *sound)
{
sound->tags = 0;
sound->handle = NULL;
sound->playback_handle = NULL;
/* versioning stuff, if there was a cache, then we enable caching: */
if (sound->cache) {
sound->flags |= SOUND_FLAGS_CACHING;
sound->cache = NULL;
}
if (fd->soundmap) {
sound->waveform = newsoundadr(fd, sound->waveform);
sound->tags |= SOUND_TAGS_WAVEFORM_NO_RELOAD;
}
else {
sound->waveform = NULL;
}
if (sound->spinlock) {
sound->spinlock = MEM_mallocN(sizeof(SpinLock), "sound_spinlock");
BLI_spin_init(sound->spinlock);
}
/* clear waveform loading flag */
sound->tags &= ~SOUND_TAGS_WAVEFORM_LOADING;
sound->packedfile = direct_link_packedfile(fd, sound->packedfile);
sound->newpackedfile = direct_link_packedfile(fd, sound->newpackedfile);
}
static void lib_link_sound(FileData *fd, Main *main)
{
for (bSound *sound = main->sounds.first; sound; sound = sound->id.next) {
if (sound->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(sound->id.properties, fd);
sound->ipo = newlibadr_us(fd, sound->id.lib, sound->ipo); // XXX deprecated - old animation system
BKE_sound_load(main, sound);
sound->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Movie Clip
* \{ */
static void direct_link_movieReconstruction(FileData *fd, MovieTrackingReconstruction *reconstruction)
{
reconstruction->cameras = newdataadr(fd, reconstruction->cameras);
}
static void direct_link_movieTracks(FileData *fd, ListBase *tracksbase)
{
MovieTrackingTrack *track;
link_list(fd, tracksbase);
for (track = tracksbase->first; track; track = track->next) {
track->markers = newdataadr(fd, track->markers);
}
}
static void direct_link_moviePlaneTracks(FileData *fd, ListBase *plane_tracks_base)
{
MovieTrackingPlaneTrack *plane_track;
link_list(fd, plane_tracks_base);
for (plane_track = plane_tracks_base->first;
plane_track;
plane_track = plane_track->next)
{
int i;
plane_track->point_tracks = newdataadr(fd, plane_track->point_tracks);
test_pointer_array(fd, (void **)&plane_track->point_tracks);
for (i = 0; i < plane_track->point_tracksnr; i++) {
plane_track->point_tracks[i] = newdataadr(fd, plane_track->point_tracks[i]);
}
plane_track->markers = newdataadr(fd, plane_track->markers);
}
}
static void direct_link_movieclip(FileData *fd, MovieClip *clip)
{
MovieTracking *tracking = &clip->tracking;
MovieTrackingObject *object;
clip->adt = newdataadr(fd, clip->adt);
if (fd->movieclipmap) clip->cache = newmclipadr(fd, clip->cache);
else clip->cache = NULL;
if (fd->movieclipmap) clip->tracking.camera.intrinsics = newmclipadr(fd, clip->tracking.camera.intrinsics);
else clip->tracking.camera.intrinsics = NULL;
direct_link_movieTracks(fd, &tracking->tracks);
direct_link_moviePlaneTracks(fd, &tracking->plane_tracks);
direct_link_movieReconstruction(fd, &tracking->reconstruction);
clip->tracking.act_track = newdataadr(fd, clip->tracking.act_track);
clip->tracking.act_plane_track = newdataadr(fd, clip->tracking.act_plane_track);
clip->anim = NULL;
clip->tracking_context = NULL;
clip->tracking.stats = NULL;
/* Needed for proper versioning, will be NULL for all newer files anyway. */
clip->tracking.stabilization.rot_track = newdataadr(fd, clip->tracking.stabilization.rot_track);
clip->tracking.dopesheet.ok = 0;
BLI_listbase_clear(&clip->tracking.dopesheet.channels);
BLI_listbase_clear(&clip->tracking.dopesheet.coverage_segments);
link_list(fd, &tracking->objects);
for (object = tracking->objects.first; object; object = object->next) {
direct_link_movieTracks(fd, &object->tracks);
direct_link_moviePlaneTracks(fd, &object->plane_tracks);
direct_link_movieReconstruction(fd, &object->reconstruction);
}
}
static void lib_link_movieTracks(FileData *fd, MovieClip *clip, ListBase *tracksbase)
{
MovieTrackingTrack *track;
for (track = tracksbase->first; track; track = track->next) {
track->gpd = newlibadr_us(fd, clip->id.lib, track->gpd);
}
}
static void lib_link_moviePlaneTracks(FileData *fd, MovieClip *clip, ListBase *tracksbase)
{
MovieTrackingPlaneTrack *plane_track;
for (plane_track = tracksbase->first; plane_track; plane_track = plane_track->next) {
plane_track->image = newlibadr_us(fd, clip->id.lib, plane_track->image);
}
}
static void lib_link_movieclip(FileData *fd, Main *main)
{
for (MovieClip *clip = main->movieclips.first; clip; clip = clip->id.next) {
if (clip->id.tag & LIB_TAG_NEED_LINK) {
MovieTracking *tracking = &clip->tracking;
IDP_LibLinkProperty(clip->id.properties, fd);
lib_link_animdata(fd, &clip->id, clip->adt);
clip->gpd = newlibadr_us(fd, clip->id.lib, clip->gpd);
lib_link_movieTracks(fd, clip, &tracking->tracks);
lib_link_moviePlaneTracks(fd, clip, &tracking->plane_tracks);
for (MovieTrackingObject *object = tracking->objects.first; object; object = object->next) {
lib_link_movieTracks(fd, clip, &object->tracks);
lib_link_moviePlaneTracks(fd, clip, &object->plane_tracks);
}
clip->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Masks
* \{ */
static void direct_link_mask(FileData *fd, Mask *mask)
{
MaskLayer *masklay;
mask->adt = newdataadr(fd, mask->adt);
link_list(fd, &mask->masklayers);
for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
MaskLayerShape *masklay_shape;
/* can't use newdataadr since it's a pointer within an array */
MaskSplinePoint *act_point_search = NULL;
link_list(fd, &masklay->splines);
for (spline = masklay->splines.first; spline; spline = spline->next) {
MaskSplinePoint *points_old = spline->points;
int i;
spline->points = newdataadr(fd, spline->points);
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
if (point->tot_uw)
point->uw = newdataadr(fd, point->uw);
}
/* detect active point */
if ((act_point_search == NULL) &&
(masklay->act_point >= points_old) &&
(masklay->act_point < points_old + spline->tot_point))
{
act_point_search = &spline->points[masklay->act_point - points_old];
}
}
link_list(fd, &masklay->splines_shapes);
for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) {
masklay_shape->data = newdataadr(fd, masklay_shape->data);
if (masklay_shape->tot_vert) {
if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) {
BLI_endian_switch_float_array(
masklay_shape->data,
masklay_shape->tot_vert * sizeof(float) * MASK_OBJECT_SHAPE_ELEM_SIZE);
}
}
}
masklay->act_spline = newdataadr(fd, masklay->act_spline);
masklay->act_point = act_point_search;
}
}
static void lib_link_mask_parent(FileData *fd, Mask *mask, MaskParent *parent)
{
parent->id = newlibadr_us(fd, mask->id.lib, parent->id);
}
static void lib_link_mask(FileData *fd, Main *main)
{
for (Mask *mask = main->masks.first; mask; mask = mask->id.next) {
if (mask->id.tag & LIB_TAG_NEED_LINK) {
IDP_LibLinkProperty(mask->id.properties, fd);
lib_link_animdata(fd, &mask->id, mask->adt);
for (MaskLayer *masklay = mask->masklayers.first; masklay; masklay = masklay->next) {
MaskSpline *spline;
spline = masklay->splines.first;
while (spline) {
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
lib_link_mask_parent(fd, mask, &point->parent);
}
lib_link_mask_parent(fd, mask, &spline->parent);
spline = spline->next;
}
}
mask->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
/* ************ READ LINE STYLE ***************** */
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read ID: Line Style
* \{ */
static void lib_link_linestyle(FileData *fd, Main *main)
{
for (FreestyleLineStyle *linestyle = main->linestyles.first; linestyle; linestyle = linestyle->id.next) {
if (linestyle->id.tag & LIB_TAG_NEED_LINK) {
LineStyleModifier *m;
IDP_LibLinkProperty(linestyle->id.properties, fd);
lib_link_animdata(fd, &linestyle->id, linestyle->adt);
for (m = linestyle->color_modifiers.first; m; m = m->next) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleColorModifier_DistanceFromObject *cm = (LineStyleColorModifier_DistanceFromObject *)m;
cm->target = newlibadr(fd, linestyle->id.lib, cm->target);
break;
}
}
}
for (m = linestyle->alpha_modifiers.first; m; m = m->next) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleAlphaModifier_DistanceFromObject *am = (LineStyleAlphaModifier_DistanceFromObject *)m;
am->target = newlibadr(fd, linestyle->id.lib, am->target);
break;
}
}
}
for (m = linestyle->thickness_modifiers.first; m; m = m->next) {
switch (m->type) {
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleThicknessModifier_DistanceFromObject *tm = (LineStyleThicknessModifier_DistanceFromObject *)m;
tm->target = newlibadr(fd, linestyle->id.lib, tm->target);
break;
}
}
}
for (int a = 0; a < MAX_MTEX; a++) {
MTex *mtex = linestyle->mtex[a];
if (mtex) {
mtex->tex = newlibadr_us(fd, linestyle->id.lib, mtex->tex);
mtex->object = newlibadr(fd, linestyle->id.lib, mtex->object);
}
}
if (linestyle->nodetree) {
lib_link_ntree(fd, &linestyle->id, linestyle->nodetree);
linestyle->nodetree->id.lib = linestyle->id.lib;
}
linestyle->id.tag &= ~LIB_TAG_NEED_LINK;
}
}
}
static void direct_link_linestyle_color_modifier(FileData *fd, LineStyleModifier *modifier)
{
switch (modifier->type) {
case LS_MODIFIER_ALONG_STROKE:
{
LineStyleColorModifier_AlongStroke *m = (LineStyleColorModifier_AlongStroke *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_DISTANCE_FROM_CAMERA:
{
LineStyleColorModifier_DistanceFromCamera *m = (LineStyleColorModifier_DistanceFromCamera *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleColorModifier_DistanceFromObject *m = (LineStyleColorModifier_DistanceFromObject *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_MATERIAL:
{
LineStyleColorModifier_Material *m = (LineStyleColorModifier_Material *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_TANGENT:
{
LineStyleColorModifier_Tangent *m = (LineStyleColorModifier_Tangent *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_NOISE:
{
LineStyleColorModifier_Noise *m = (LineStyleColorModifier_Noise *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_CREASE_ANGLE:
{
LineStyleColorModifier_CreaseAngle *m = (LineStyleColorModifier_CreaseAngle *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
case LS_MODIFIER_CURVATURE_3D:
{
LineStyleColorModifier_Curvature_3D *m = (LineStyleColorModifier_Curvature_3D *)modifier;
m->color_ramp = newdataadr(fd, m->color_ramp);
break;
}
}
}
static void direct_link_linestyle_alpha_modifier(FileData *fd, LineStyleModifier *modifier)
{
switch (modifier->type) {
case LS_MODIFIER_ALONG_STROKE:
{
LineStyleAlphaModifier_AlongStroke *m = (LineStyleAlphaModifier_AlongStroke *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_CAMERA:
{
LineStyleAlphaModifier_DistanceFromCamera *m = (LineStyleAlphaModifier_DistanceFromCamera *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleAlphaModifier_DistanceFromObject *m = (LineStyleAlphaModifier_DistanceFromObject *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_MATERIAL:
{
LineStyleAlphaModifier_Material *m = (LineStyleAlphaModifier_Material *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_TANGENT:
{
LineStyleAlphaModifier_Tangent *m = (LineStyleAlphaModifier_Tangent *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_NOISE:
{
LineStyleAlphaModifier_Noise *m = (LineStyleAlphaModifier_Noise *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_CREASE_ANGLE:
{
LineStyleAlphaModifier_CreaseAngle *m = (LineStyleAlphaModifier_CreaseAngle *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_CURVATURE_3D:
{
LineStyleAlphaModifier_Curvature_3D *m = (LineStyleAlphaModifier_Curvature_3D *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
}
}
static void direct_link_linestyle_thickness_modifier(FileData *fd, LineStyleModifier *modifier)
{
switch (modifier->type) {
case LS_MODIFIER_ALONG_STROKE:
{
LineStyleThicknessModifier_AlongStroke *m = (LineStyleThicknessModifier_AlongStroke *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_CAMERA:
{
LineStyleThicknessModifier_DistanceFromCamera *m = (LineStyleThicknessModifier_DistanceFromCamera *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_DISTANCE_FROM_OBJECT:
{
LineStyleThicknessModifier_DistanceFromObject *m = (LineStyleThicknessModifier_DistanceFromObject *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_MATERIAL:
{
LineStyleThicknessModifier_Material *m = (LineStyleThicknessModifier_Material *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_TANGENT:
{
LineStyleThicknessModifier_Tangent *m = (LineStyleThicknessModifier_Tangent *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_CREASE_ANGLE:
{
LineStyleThicknessModifier_CreaseAngle *m = (LineStyleThicknessModifier_CreaseAngle *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
case LS_MODIFIER_CURVATURE_3D:
{
LineStyleThicknessModifier_Curvature_3D *m = (LineStyleThicknessModifier_Curvature_3D *)modifier;
m->curve = newdataadr(fd, m->curve);
direct_link_curvemapping(fd, m->curve);
break;
}
}
}
static void direct_link_linestyle_geometry_modifier(FileData *UNUSED(fd), LineStyleModifier *UNUSED(modifier))
{
}
static void direct_link_linestyle(FileData *fd, FreestyleLineStyle *linestyle)
{
int a;
LineStyleModifier *modifier;
linestyle->adt = newdataadr(fd, linestyle->adt);
direct_link_animdata(fd, linestyle->adt);
link_list(fd, &linestyle->color_modifiers);
for (modifier = linestyle->color_modifiers.first; modifier; modifier = modifier->next)
direct_link_linestyle_color_modifier(fd, modifier);
link_list(fd, &linestyle->alpha_modifiers);
for (modifier = linestyle->alpha_modifiers.first; modifier; modifier = modifier->next)
direct_link_linestyle_alpha_modifier(fd, modifier);
link_list(fd, &linestyle->thickness_modifiers);
for (modifier = linestyle->thickness_modifiers.first; modifier; modifier = modifier->next)
direct_link_linestyle_thickness_modifier(fd, modifier);
link_list(fd, &linestyle->geometry_modifiers);
for (modifier = linestyle->geometry_modifiers.first; modifier; modifier = modifier->next)
direct_link_linestyle_geometry_modifier(fd, modifier);
for (a = 0; a < MAX_MTEX; a++) {
linestyle->mtex[a] = newdataadr(fd, linestyle->mtex[a]);
}
linestyle->nodetree = newdataadr(fd, linestyle->nodetree);
if (linestyle->nodetree) {
direct_link_id(fd, &linestyle->nodetree->id);
direct_link_nodetree(fd, linestyle->nodetree);
}
}
/* ************** GENERAL & MAIN ******************** */
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Library Data Block
* \{ */
static const char *dataname(short id_code)
{
switch (id_code) {
case ID_OB: return "Data from OB";
case ID_ME: return "Data from ME";
case ID_IP: return "Data from IP";
case ID_SCE: return "Data from SCE";
case ID_MA: return "Data from MA";
case ID_TE: return "Data from TE";
case ID_CU: return "Data from CU";
case ID_GR: return "Data from GR";
case ID_AR: return "Data from AR";
case ID_AC: return "Data from AC";
case ID_LI: return "Data from LI";
case ID_MB: return "Data from MB";
case ID_IM: return "Data from IM";
case ID_LT: return "Data from LT";
case ID_LA: return "Data from LA";
case ID_CA: return "Data from CA";
case ID_KE: return "Data from KE";
case ID_WO: return "Data from WO";
case ID_SCR: return "Data from SCR";
case ID_VF: return "Data from VF";
case ID_TXT: return "Data from TXT";
case ID_SPK: return "Data from SPK";
case ID_LP: return "Data from LP";
case ID_SO: return "Data from SO";
case ID_NT: return "Data from NT";
case ID_BR: return "Data from BR";
case ID_PA: return "Data from PA";
case ID_PAL: return "Data from PAL";
case ID_PC: return "Data from PCRV";
case ID_GD: return "Data from GD";
case ID_WM: return "Data from WM";
case ID_MC: return "Data from MC";
case ID_MSK: return "Data from MSK";
case ID_LS: return "Data from LS";
case ID_CF: return "Data from CF";
case ID_WS: return "Data from WS";
}
return "Data from Lib Block";
}
static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *allocname)
{
bhead = blo_bhead_next(fd, bhead);
while (bhead && bhead->code == DATA) {
void *data;
#if 0
/* XXX DUMB DEBUGGING OPTION TO GIVE NAMES for guarded malloc errors */
short *sp = fd->filesdna->structs[bhead->SDNAnr];
char *tmp = malloc(100);
allocname = fd->filesdna->types[sp[0]];
strcpy(tmp, allocname);
data = read_struct(fd, bhead, tmp);
#else
data = read_struct(fd, bhead, allocname);
#endif
if (data) {
oldnewmap_insert(fd->datamap, bhead->old, data, 0);
}
bhead = blo_bhead_next(fd, bhead);
}
return bhead;
}
static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id)
{
/* this routine reads a libblock and its direct data. Use link functions to connect it all
*/
ID *id;
ListBase *lb;
const char *allocname;
bool wrong_id = false;
/* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile).
* However, some needed by the snapshot being read may have been removed in previous one, and would go missing.
* This leads e.g. to desappearing objects in some undo/redo case, see T34446.
* That means we have to carefully check whether current lib or libdata already exits in old main, if it does
* we merely copy it over into new main area, otherwise we have to do a full read of that bhead... */
if (fd->memfile && ELEM(bhead->code, ID_LI, ID_LINK_PLACEHOLDER)) {
const char *idname = blo_bhead_id_name(fd, bhead);
DEBUG_PRINTF("Checking %s...\n", idname);
if (bhead->code == ID_LI) {
Main *libmain = fd->old_mainlist->first;
/* Skip oldmain itself... */
for (libmain = libmain->next; libmain; libmain = libmain->next) {
DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>");
if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) {
Main *oldmain = fd->old_mainlist->first;
DEBUG_PRINTF("FOUND!\n");
/* In case of a library, we need to re-add its main to fd->mainlist, because if we have later
* a missing ID_LINK_PLACEHOLDER, we need to get the correct lib it is linked to!
* Order is crucial, we cannot bulk-add it in BLO_read_from_memfile() like it used to be... */
BLI_remlink(fd->old_mainlist, libmain);
BLI_remlink_safe(&oldmain->libraries, libmain->curlib);
BLI_addtail(fd->mainlist, libmain);
BLI_addtail(&main->libraries, libmain->curlib);
if (r_id) {
*r_id = NULL; /* Just in case... */
}
return blo_bhead_next(fd, bhead);
}
DEBUG_PRINTF("nothing...\n");
}
}
else {
DEBUG_PRINTF("... in %s (%s): ", main->curlib ? main->curlib->id.name : "<NULL>", main->curlib ? main->curlib->name : "<NULL>");
if ((id = BKE_libblock_find_name(main, GS(idname), idname + 2))) {
DEBUG_PRINTF("FOUND!\n");
/* Even though we found our linked ID, there is no guarantee its address is still the same... */
if (id != bhead->old) {
oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
}
/* No need to do anything else for ID_LINK_PLACEHOLDER, it's assumed already present in its lib's main... */
if (r_id) {
*r_id = NULL; /* Just in case... */
}
return blo_bhead_next(fd, bhead);
}
DEBUG_PRINTF("nothing...\n");
}
}
/* read libblock */
id = read_struct(fd, bhead, "lib block");
if (id) {
const short idcode = GS(id->name);
/* do after read_struct, for dna reconstruct */
lb = which_libbase(main, idcode);
if (lb) {
oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); /* for ID_LINK_PLACEHOLDER check */
BLI_addtail(lb, id);
}
else {
/* unknown ID type */
printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8));
MEM_freeN(id);
id = NULL;
}
}
if (r_id)
*r_id = id;
if (!id)
return blo_bhead_next(fd, bhead);
id->lib = main->curlib;
id->us = ID_FAKE_USERS(id);
id->icon_id = 0;
id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */
id->orig_id = NULL;
id->recalc = 0;
/* this case cannot be direct_linked: it's just the ID part */
if (bhead->code == ID_LINK_PLACEHOLDER) {
/* That way, we know which datablock needs do_versions (required currently for linking). */
id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
return blo_bhead_next(fd, bhead);
}
/* need a name for the mallocN, just for debugging and sane prints on leaks */
allocname = dataname(GS(id->name));
/* read all data into fd->datamap */
bhead = read_data_into_oldnewmap(fd, bhead, allocname);
/* init pointers direct data */
direct_link_id(fd, id);
/* That way, we know which datablock needs do_versions (required currently for linking). */
/* Note: doing this after driect_link_id(), which resets that field. */
id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW;
switch (GS(id->name)) {
case ID_WM:
direct_link_windowmanager(fd, (wmWindowManager *)id);
break;
case ID_SCR:
wrong_id = direct_link_screen(fd, (bScreen *)id);
break;
case ID_SCE:
direct_link_scene(fd, (Scene *)id);
break;
case ID_OB:
direct_link_object(fd, (Object *)id);
break;
case ID_ME:
direct_link_mesh(fd, (Mesh *)id);
break;
case ID_CU:
direct_link_curve(fd, (Curve *)id);
break;
case ID_MB:
direct_link_mball(fd, (MetaBall *)id);
break;
case ID_MA:
direct_link_material(fd, (Material *)id);
break;
case ID_TE:
direct_link_texture(fd, (Tex *)id);
break;
case ID_IM:
direct_link_image(fd, (Image *)id);
break;
case ID_LA:
direct_link_light(fd, (Light *)id);
break;
case ID_VF:
direct_link_vfont(fd, (VFont *)id);
break;
case ID_TXT:
direct_link_text(fd, (Text *)id);
break;
case ID_IP:
direct_link_ipo(fd, (Ipo *)id);
break;
case ID_KE:
direct_link_key(fd, (Key *)id);
break;
case ID_LT:
direct_link_latt(fd, (Lattice *)id);
break;
case ID_WO:
direct_link_world(fd, (World *)id);
break;
case ID_LI:
direct_link_library(fd, (Library *)id, main);
break;
case ID_CA:
direct_link_camera(fd, (Camera *)id);
break;
case ID_SPK:
direct_link_speaker(fd, (Speaker *)id);
break;
case ID_SO:
direct_link_sound(fd, (bSound *)id);
break;
case ID_LP:
direct_link_lightprobe(fd, (LightProbe *)id);
break;
case ID_GR:
direct_link_collection(fd, (Collection *)id);
break;
case ID_AR:
direct_link_armature(fd, (bArmature *)id);
break;
case ID_AC:
direct_link_action(fd, (bAction *)id);
break;
case ID_NT:
direct_link_nodetree(fd, (bNodeTree *)id);
break;
case ID_BR:
direct_link_brush(fd, (Brush *)id);
break;
case ID_PA:
direct_link_particlesettings(fd, (ParticleSettings *)id);
break;
case ID_GD:
direct_link_gpencil(fd, (bGPdata *)id);
break;
case ID_MC:
direct_link_movieclip(fd, (MovieClip *)id);
break;
case ID_MSK:
direct_link_mask(fd, (Mask *)id);
break;
case ID_LS:
direct_link_linestyle(fd, (FreestyleLineStyle *)id);
break;
case ID_PAL:
direct_link_palette(fd, (Palette *)id);
break;
case ID_PC:
direct_link_paint_curve(fd, (PaintCurve *)id);
break;
case ID_CF:
direct_link_cachefile(fd, (CacheFile *)id);
break;
case ID_WS:
direct_link_workspace(fd, (WorkSpace *)id, main);
break;
}
oldnewmap_free_unused(fd->datamap);
oldnewmap_clear(fd->datamap);
if (wrong_id) {
BKE_id_free(main, id);
}
return (bhead);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Global Data
* \{ */
/* note, this has to be kept for reading older files... */
/* also version info is written here */
static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
FileGlobal *fg = read_struct(fd, bhead, "Global");
/* copy to bfd handle */
bfd->main->subversionfile = fg->subversion;
bfd->main->minversionfile = fg->minversion;
bfd->main->minsubversionfile = fg->minsubversion;
bfd->main->build_commit_timestamp = fg->build_commit_timestamp;
BLI_strncpy(bfd->main->build_hash, fg->build_hash, sizeof(bfd->main->build_hash));
bfd->fileflags = fg->fileflags;
bfd->globalf = fg->globalf;
BLI_strncpy(bfd->filename, fg->filename, sizeof(bfd->filename));
/* error in 2.65 and older: main->name was not set if you save from startup (not after loading file) */
if (bfd->filename[0] == 0) {
if (fd->fileversion < 265 || (fd->fileversion == 265 && fg->subversion < 1))
if ((G.fileflags & G_FILE_RECOVER) == 0)
BLI_strncpy(bfd->filename, BKE_main_blendfile_path(bfd->main), sizeof(bfd->filename));
/* early 2.50 version patch - filename not in FileGlobal struct at all */
if (fd->fileversion <= 250)
BLI_strncpy(bfd->filename, BKE_main_blendfile_path(bfd->main), sizeof(bfd->filename));
}
if (G.fileflags & G_FILE_RECOVER)
BLI_strncpy(fd->relabase, fg->filename, sizeof(fd->relabase));
bfd->curscreen = fg->curscreen;
bfd->curscene = fg->curscene;
bfd->cur_view_layer = fg->cur_view_layer;
MEM_freeN(fg);
fd->globalf = bfd->globalf;
fd->fileflags = bfd->fileflags;
return blo_bhead_next(fd, bhead);
}
/* note, this has to be kept for reading older files... */
static void link_global(FileData *fd, BlendFileData *bfd)
{
bfd->cur_view_layer = newglobadr(fd, bfd->cur_view_layer);
bfd->curscreen = newlibadr(fd, NULL, bfd->curscreen);
bfd->curscene = newlibadr(fd, NULL, bfd->curscene);
// this happens in files older than 2.35
if (bfd->curscene == NULL) {
if (bfd->curscreen) bfd->curscene = bfd->curscreen->scene;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Versioning
* \{ */
/* initialize userdef with non-UI dependency stuff */
/* other initializers (such as theme color defaults) go to resources.c */
static void do_versions_userdef(FileData *fd, BlendFileData *bfd)
{
Main *bmain = bfd->main;
UserDef *user = bfd->user;
if (user == NULL) return;
if (MAIN_VERSION_OLDER(bmain, 266, 4)) {
bTheme *btheme;
/* themes for Node and Sequence editor were not using grid color, but back. we copy this over then */
for (btheme = user->themes.first; btheme; btheme = btheme->next) {
copy_v4_v4_char(btheme->space_node.grid, btheme->space_node.back);
copy_v4_v4_char(btheme->space_sequencer.grid, btheme->space_sequencer.back);
}
}
if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "WalkNavigation", "walk_navigation")) {
user->walk_navigation.mouse_speed = 1.0f;
user->walk_navigation.walk_speed = 2.5f; /* m/s */
user->walk_navigation.walk_speed_factor = 5.0f;
user->walk_navigation.view_height = 1.6f; /* m */
user->walk_navigation.jump_height = 0.4f; /* m */
user->walk_navigation.teleport_time = 0.2f; /* s */
}
/* grease pencil multisamples */
if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "short", "gpencil_multisamples")) {
user->gpencil_multisamples = 4;
}
/* tablet pressure threshold */
if (!DNA_struct_elem_find(fd->filesdna, "UserDef", "float", "pressure_threshold_max")) {
user->pressure_threshold_max = 1.0f;
}
}
static void do_versions(FileData *fd, Library *lib, Main *main)
{
/* WATCH IT!!!: pointers from libdata have not been converted */
if (G.debug & G_DEBUG) {
char build_commit_datetime[32];
time_t temp_time = main->build_commit_timestamp;
struct tm *tm = (temp_time) ? gmtime(&temp_time) : NULL;
if (LIKELY(tm)) {
strftime(build_commit_datetime, sizeof(build_commit_datetime), "%Y-%m-%d %H:%M", tm);
}
else {
BLI_strncpy(build_commit_datetime, "unknown", sizeof(build_commit_datetime));
}
printf("read file %s\n Version %d sub %d date %s hash %s\n",
fd->relabase, main->versionfile, main->subversionfile,
build_commit_datetime, main->build_hash);
}
blo_do_versions_pre250(fd, lib, main);
blo_do_versions_250(fd, lib, main);
blo_do_versions_260(fd, lib, main);
blo_do_versions_270(fd, lib, main);
blo_do_versions_280(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
/* WATCH IT 2!: Userdef struct init see do_versions_userdef() above! */
/* don't forget to set version number in BKE_blender_version.h! */
}
static void do_versions_after_linking(Main *main)
{
// printf("%s for %s (%s), %d.%d\n", __func__, main->curlib ? main->curlib->name : main->name,
// main->curlib ? "LIB" : "MAIN", main->versionfile, main->subversionfile);
do_versions_after_linking_270(main);
do_versions_after_linking_280(main);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read Library Data Block (all)
* \{ */
static void lib_link_all(FileData *fd, Main *main)
{
lib_link_id(fd, main);
/* No load UI for undo memfiles */
if (fd->memfile == NULL) {
lib_link_windowmanager(fd, main);
}
/* DO NOT skip screens here, 3Dview may contains pointers to other ID data (like bgpic)! See T41411. */
lib_link_screen(fd, main);
lib_link_scene(fd, main);
lib_link_object(fd, main);
lib_link_mesh(fd, main);
lib_link_curve(fd, main);
lib_link_mball(fd, main);
lib_link_material(fd, main);
lib_link_texture(fd, main);
lib_link_image(fd, main);
lib_link_ipo(fd, main); /* XXX deprecated... still needs to be maintained for version patches still */
lib_link_key(fd, main);
lib_link_world(fd, main);
lib_link_light(fd, main);
lib_link_latt(fd, main);
lib_link_text(fd, main);
lib_link_camera(fd, main);
lib_link_speaker(fd, main);
lib_link_lightprobe(fd, main);
lib_link_sound(fd, main);
lib_link_collection(fd, main);
lib_link_armature(fd, main);
lib_link_action(fd, main);
lib_link_vfont(fd, main);
lib_link_nodetree(fd, main); /* has to be done after scene/materials, this will verify group nodes */
lib_link_palette(fd, main);
lib_link_brush(fd, main);
lib_link_paint_curve(fd, main);
lib_link_particlesettings(fd, main);
lib_link_movieclip(fd, main);
lib_link_mask(fd, main);
lib_link_linestyle(fd, main);
lib_link_gpencil(fd, main);
lib_link_cachefiles(fd, main);
lib_link_workspaces(fd, main);
lib_link_library(fd, main); /* only init users */
/* We could integrate that to mesh/curve/lattice lib_link, but this is really cheap process,
* so simpler to just use it directly in this single call. */
BLO_main_validate_shapekeys(main, NULL);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read User Preferences
* \{ */
static void direct_link_keymapitem(FileData *fd, wmKeyMapItem *kmi)
{
kmi->properties = newdataadr(fd, kmi->properties);
IDP_DirectLinkGroup_OrFree(&kmi->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
kmi->ptr = NULL;
kmi->flag &= ~KMI_UPDATE;
}
static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
UserDef *user;
wmKeyMap *keymap;
wmKeyMapItem *kmi;
wmKeyMapDiffItem *kmdi;
bAddon *addon;
bfd->user = user = read_struct(fd, bhead, "user def");
/* User struct has separate do-version handling */
user->versionfile = bfd->main->versionfile;
user->subversionfile = bfd->main->subversionfile;
/* read all data into fd->datamap */
bhead = read_data_into_oldnewmap(fd, bhead, "user def");
link_list(fd, &user->themes);
link_list(fd, &user->user_keymaps);
link_list(fd, &user->user_keyconfig_prefs);
link_list(fd, &user->user_menus);
link_list(fd, &user->addons);
link_list(fd, &user->autoexec_paths);
for (keymap = user->user_keymaps.first; keymap; keymap = keymap->next) {
keymap->modal_items = NULL;
keymap->poll = NULL;
keymap->flag &= ~KEYMAP_UPDATE;
link_list(fd, &keymap->diff_items);
link_list(fd, &keymap->items);
for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) {
kmdi->remove_item = newdataadr(fd, kmdi->remove_item);
kmdi->add_item = newdataadr(fd, kmdi->add_item);
if (kmdi->remove_item)
direct_link_keymapitem(fd, kmdi->remove_item);
if (kmdi->add_item)
direct_link_keymapitem(fd, kmdi->add_item);
}
for (kmi = keymap->items.first; kmi; kmi = kmi->next)
direct_link_keymapitem(fd, kmi);
}
for (wmKeyConfigPref *kpt = user->user_keyconfig_prefs.first; kpt; kpt = kpt->next) {
kpt->prop = newdataadr(fd, kpt->prop);
IDP_DirectLinkGroup_OrFree(&kpt->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
for (bUserMenu *um = user->user_menus.first; um; um = um->next) {
link_list(fd, &um->items);
for (bUserMenuItem *umi = um->items.first; umi; umi = umi->next) {
if (umi->type == USER_MENU_TYPE_OPERATOR) {
bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
umi_op->prop = newdataadr(fd, umi_op->prop);
IDP_DirectLinkGroup_OrFree(&umi_op->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
}
}
for (addon = user->addons.first; addon; addon = addon->next) {
addon->prop = newdataadr(fd, addon->prop);
IDP_DirectLinkGroup_OrFree(&addon->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
// XXX
user->uifonts.first = user->uifonts.last = NULL;
link_list(fd, &user->uistyles);
/* Don't read the active app template, use the default one. */
user->app_template[0] = '\0';
/* free fd->datamap again */
oldnewmap_free_unused(fd->datamap);
oldnewmap_clear(fd->datamap);
return bhead;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Read File (Internal)
* \{ */
BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
{
BHead *bhead = blo_bhead_first(fd);
BlendFileData *bfd;
ListBase mainlist = {NULL, NULL};
bfd = MEM_callocN(sizeof(BlendFileData), "blendfiledata");
bfd->main = BKE_main_new();
BLI_addtail(&mainlist, bfd->main);
fd->mainlist = &mainlist;
bfd->main->versionfile = fd->fileversion;
bfd->type = BLENFILETYPE_BLEND;
BLI_strncpy(bfd->main->name, filepath, sizeof(bfd->main->name));
if (G.background) {
/* We only read & store .blend thumbnail in background mode
* (because we cannot re-generate it, no OpenGL available).
*/
const int *data = read_file_thumbnail(fd);
if (data) {
const int width = data[0];
const int height = data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
const size_t sz = BLEN_THUMB_MEMSIZE(width, height);
bfd->main->blen_thumb = MEM_mallocN(sz, __func__);
BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) ==
(BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2)));
bfd->main->blen_thumb->width = width;
bfd->main->blen_thumb->height = height;
memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb));
}
}
}
while (bhead) {
switch (bhead->code) {
case DATA:
case DNA1:
case TEST: /* used as preview since 2.5x */
case REND:
bhead = blo_bhead_next(fd, bhead);
break;
case GLOB:
bhead = read_global(bfd, fd, bhead);
break;
case USER:
if (fd->skip_flags & BLO_READ_SKIP_USERDEF) {
bhead = blo_bhead_next(fd, bhead);
}
else {
bhead = read_userdef(bfd, fd, bhead);
}
break;
case ENDB:
bhead = NULL;
break;
case ID_LINK_PLACEHOLDER:
if (fd->skip_flags & BLO_READ_SKIP_DATA) {
bhead = blo_bhead_next(fd, bhead);
}
else {
/* Add link placeholder to the main of the library it belongs to.
* The library is the most recently loaded ID_LI block, according
* to the file format definition. So we can use the entry at the
* end of mainlist, added in direct_link_library. */
Main *libmain = mainlist.last;
bhead = read_libblock(fd, libmain, bhead, LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_EXTERN, NULL);
}
break;
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
case ID_SCRN:
bhead->code = ID_SCR;
/* pass on to default */
ATTR_FALLTHROUGH;
default:
if (fd->skip_flags & BLO_READ_SKIP_DATA) {
bhead = blo_bhead_next(fd, bhead);
}
else {
bhead = read_libblock(fd, bfd->main, bhead, LIB_TAG_LOCAL, NULL);
}
}
}
/* do before read_libraries, but skip undo case */
if (fd->memfile == NULL) {
do_versions(fd, NULL, bfd->main);
do_versions_userdef(fd, bfd);
}
read_libraries(fd, &mainlist);
blo_join_main(&mainlist);
lib_link_all(fd, bfd->main);
/* Skip in undo case. */
if (fd->memfile == NULL) {
/* Yep, second splitting... but this is a very cheap operation, so no big deal. */
blo_split_main(&mainlist, bfd->main);
for (Main *mainvar = mainlist.first; mainvar; mainvar = mainvar->next) {
BLI_assert(mainvar->versionfile != 0);
do_versions_after_linking(mainvar);
}
blo_join_main(&mainlist);
}
BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false);
/* Before static overrides, which needs typeinfo. */
lib_verify_nodetree(bfd->main, true);
/* Now that all our data-blocks are loaded, we can re-generate overrides from their references. */
if (fd->memfile == NULL) {
/* Do not apply in undo case! */
BKE_main_override_static_update(bfd->main);
}
BKE_collections_after_lib_link(bfd->main);
fix_relpaths_library(fd->relabase, bfd->main); /* make all relative paths, relative to the open blend file */
link_global(fd, bfd); /* as last */
fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */
return bfd;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Library Linking
*
* Also used for append.
* \{ */
struct BHeadSort {
BHead *bhead;
const void *old;
};
static int verg_bheadsort(const void *v1, const void *v2)
{
const struct BHeadSort *x1 = v1, *x2 = v2;
if (x1->old > x2->old) return 1;
else if (x1->old < x2->old) return -1;
return 0;
}
static void sort_bhead_old_map(FileData *fd)
{
BHead *bhead;
struct BHeadSort *bhs;
int tot = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead))
tot++;
fd->tot_bheadmap = tot;
if (tot == 0) return;
bhs = fd->bheadmap = MEM_malloc_arrayN(tot, sizeof(struct BHeadSort), "BHeadSort");
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead), bhs++) {
bhs->bhead = bhead;
bhs->old = bhead->old;
}
qsort(fd->bheadmap, tot, sizeof(struct BHeadSort), verg_bheadsort);
}
static BHead *find_previous_lib(FileData *fd, BHead *bhead)
{
/* skip library datablocks in undo, see comment in read_libblock */
if (fd->memfile)
return NULL;
for (; bhead; bhead = blo_bhead_prev(fd, bhead)) {
if (bhead->code == ID_LI)
break;
}
return bhead;
}
static BHead *find_bhead(FileData *fd, void *old)
{
#if 0
BHead *bhead;
#endif
struct BHeadSort *bhs, bhs_s;
if (!old)
return NULL;
if (fd->bheadmap == NULL)
sort_bhead_old_map(fd);
bhs_s.old = old;
bhs = bsearch(&bhs_s, fd->bheadmap, fd->tot_bheadmap, sizeof(struct BHeadSort), verg_bheadsort);
if (bhs)
return bhs->bhead;
#if 0
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->old == old)
return bhead;
}
#endif
return NULL;
}
static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name)
{
#ifdef USE_GHASH_BHEAD
char idname_full[MAX_ID_NAME];
*((short *)idname_full) = idcode;
BLI_strncpy(idname_full + 2, name, sizeof(idname_full) - 2);
return BLI_ghash_lookup(fd->bhead_idname_hash, idname_full);
#else
BHead *bhead;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == idcode) {
const char *idname_test = blo_bhead_id_name(fd, bhead);
if (STREQ(idname_test + 2, name)) {
return bhead;
}
}
else if (bhead->code == ENDB) {
break;
}
}
return NULL;
#endif
}
static BHead *find_bhead_from_idname(FileData *fd, const char *idname)
{
#ifdef USE_GHASH_BHEAD
return BLI_ghash_lookup(fd->bhead_idname_hash, idname);
#else
return find_bhead_from_code_name(fd, GS(idname), idname + 2);
#endif
}
static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
{
const char *idname = blo_bhead_id_name(fd, bhead);
/* which_libbase can be NULL, intentionally not using idname+2 */
return BLI_findstring(which_libbase(mainvar, GS(idname)), idname, offsetof(ID, name));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Library Linking (expand pointers)
* \{ */
static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
{
FileData *fd = fdhandle;
BHead *bhead = find_bhead(fd, old);
if (bhead == NULL) {
return;
}
if (bhead->code == ID_LINK_PLACEHOLDER) {
/* Placeholder link to datablock in another library. */
BHead *bheadlib = find_previous_lib(fd, bhead);
if (bheadlib == NULL) {
return;
}
Library *lib = read_struct(fd, bheadlib, "Library");
Main *libmain = blo_find_main(fd, lib->name, fd->relabase);
if (libmain->curlib == NULL) {
const char *idname = blo_bhead_id_name(fd, bhead);
blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: Data refers to main .blend file: '%s' from %s"),
idname, mainvar->curlib->filepath);
return;
}
ID *id = is_yet_read(fd, libmain, bhead);
if (id == NULL) {
/* ID has not been read yet, add placeholder to the main of the
* library it belongs to, so that it will be read later. */
read_libblock(fd, libmain, bhead, LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_INDIRECT, NULL);
// commented because this can print way too much
// if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->name);
/* for outliner dependency only */
libmain->curlib->parent = mainvar->curlib;
}
else {
/* "id" is either a placeholder or real ID that is already in the
* main of the library (A) it belongs to. However it might have been
* put there by another library (C) which only updated its own
* fd->libmap. In that case we also need to update the fd->libmap
* of the current library (B) so we can find it for lookups.
*
* An example of such a setup is:
* (A) tree.blend: contains Tree object.
* (B) forest.blend: contains Forest collection linking in Tree from tree.blend.
* (C) shot.blend: links in both Tree from tree.blend and Forest from forest.blend.
*/
oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
/* If "id" is a real datablock and not a placeholder, we need to
* update fd->libmap to replace ID_LINK_PLACEHOLDER with the real
* ID_* code.
*
* When the real ID is read this replacement happens for all
* libraries read so far, but not for libraries that have not been
* read yet at that point. */
change_link_placeholder_to_real_ID_pointer_fd(fd, bhead->old, id);
// commented because this can print way too much
// if (G.debug & G_DEBUG) printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->name);
}
MEM_freeN(lib);
}
else {
/* Datablock in same library. */
/* In 2.50+ file identifier for screens is patched, forward compatibility. */
if (bhead->code == ID_SCRN) {
bhead->code = ID_SCR;
}
ID *id = is_yet_read(fd, mainvar, bhead);
if (id == NULL) {
read_libblock(fd, mainvar, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, NULL);
}
else {
/* this is actually only needed on UI call? when ID was already read before, and another append
* happens which invokes same ID... in that case the lookup table needs this entry */
oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
// commented because this can print way too much
// if (G.debug & G_DEBUG) printf("expand: already read %s\n", id->name);
}
}
}
static BLOExpandDoitCallback expand_doit;
// XXX deprecated - old animation system
static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo)
{
IpoCurve *icu;
for (icu = ipo->curve.first; icu; icu = icu->next) {
if (icu->driver)
expand_doit(fd, mainvar, icu->driver->ob);
}
}
// XXX deprecated - old animation system
static void expand_constraint_channels(FileData *fd, Main *mainvar, ListBase *chanbase)
{
bConstraintChannel *chan;
for (chan = chanbase->first; chan; chan = chan->next) {
expand_doit(fd, mainvar, chan->ipo);
}
}
static void expand_id(FileData *fd, Main *mainvar, ID *id)
{
if (id->override_static) {
expand_doit(fd, mainvar, id->override_static->reference);
expand_doit(fd, mainvar, id->override_static->storage);
}
}
static void expand_idprops(FileData *fd, Main *mainvar, IDProperty *prop)
{
if (!prop)
return;
switch (prop->type) {
case IDP_ID:
expand_doit(fd, mainvar, IDP_Id(prop));
break;
case IDP_IDPARRAY:
{
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
expand_idprops(fd, mainvar, &idp_array[i]);
}
break;
}
case IDP_GROUP:
for (IDProperty *loop = prop->data.group.first; loop; loop = loop->next) {
expand_idprops(fd, mainvar, loop);
}
break;
}
}
static void expand_fmodifiers(FileData *fd, Main *mainvar, ListBase *list)
{
FModifier *fcm;
for (fcm = list->first; fcm; fcm = fcm->next) {
/* library data for specific F-Modifier types */
switch (fcm->type) {
case FMODIFIER_TYPE_PYTHON:
{
FMod_Python *data = (FMod_Python *)fcm->data;
expand_doit(fd, mainvar, data->script);
break;
}
}
}
}
static void expand_fcurves(FileData *fd, Main *mainvar, ListBase *list)
{
FCurve *fcu;
for (fcu = list->first; fcu; fcu = fcu->next) {
/* Driver targets if there is a driver */
if (fcu->driver) {
ChannelDriver *driver = fcu->driver;
DriverVar *dvar;
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
DRIVER_TARGETS_LOOPER_BEGIN(dvar)
{
// TODO: only expand those that are going to get used?
expand_doit(fd, mainvar, dtar->id);
}
DRIVER_TARGETS_LOOPER_END;
}
}
/* F-Curve Modifiers */
expand_fmodifiers(fd, mainvar, &fcu->modifiers);
}
}
static void expand_action(FileData *fd, Main *mainvar, bAction *act)
{
bActionChannel *chan;
// XXX deprecated - old animation system --------------
for (chan = act->chanbase.first; chan; chan = chan->next) {
expand_doit(fd, mainvar, chan->ipo);
expand_constraint_channels(fd, mainvar, &chan->constraintChannels);
}
// ---------------------------------------------------
/* F-Curves in Action */
expand_fcurves(fd, mainvar, &act->curves);
for (TimeMarker *marker = act->markers.first; marker; marker = marker->next) {
if (marker->camera) {
expand_doit(fd, mainvar, marker->camera);
}
}
}
static void expand_keyingsets(FileData *fd, Main *mainvar, ListBase *list)
{
KeyingSet *ks;
KS_Path *ksp;
/* expand the ID-pointers in KeyingSets's paths */
for (ks = list->first; ks; ks = ks->next) {
for (ksp = ks->paths.first; ksp; ksp = ksp->next) {
expand_doit(fd, mainvar, ksp->id);
}
}
}
static void expand_animdata_nlastrips(FileData *fd, Main *mainvar, ListBase *list)
{
NlaStrip *strip;
for (strip = list->first; strip; strip = strip->next) {
/* check child strips */
expand_animdata_nlastrips(fd, mainvar, &strip->strips);
/* check F-Curves */
expand_fcurves(fd, mainvar, &strip->fcurves);
/* check F-Modifiers */
expand_fmodifiers(fd, mainvar, &strip->modifiers);
/* relink referenced action */
expand_doit(fd, mainvar, strip->act);
}
}
static void expand_animdata(FileData *fd, Main *mainvar, AnimData *adt)
{
NlaTrack *nlt;
/* own action */
expand_doit(fd, mainvar, adt->action);
expand_doit(fd, mainvar, adt->tmpact);
/* drivers - assume that these F-Curves have driver data to be in this list... */
expand_fcurves(fd, mainvar, &adt->drivers);
/* nla-data - referenced actions */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next)
expand_animdata_nlastrips(fd, mainvar, &nlt->strips);
}
static void expand_particlesettings(FileData *fd, Main *mainvar, ParticleSettings *part)
{
int a;
expand_doit(fd, mainvar, part->instance_object);
expand_doit(fd, mainvar, part->instance_collection);
expand_doit(fd, mainvar, part->eff_group);
expand_doit(fd, mainvar, part->bb_ob);
expand_doit(fd, mainvar, part->collision_group);
if (part->adt)
expand_animdata(fd, mainvar, part->adt);
for (a = 0; a < MAX_MTEX; a++) {
if (part->mtex[a]) {
expand_doit(fd, mainvar, part->mtex[a]->tex);
expand_doit(fd, mainvar, part->mtex[a]->object);
}
}
if (part->effector_weights) {
expand_doit(fd, mainvar, part->effector_weights->group);
}
if (part->pd) {
expand_doit(fd, mainvar, part->pd->tex);
expand_doit(fd, mainvar, part->pd->f_source);
}
if (part->pd2) {
expand_doit(fd, mainvar, part->pd2->tex);
expand_doit(fd, mainvar, part->pd2->f_source);
}
if (part->boids) {
BoidState *state;
BoidRule *rule;
for (state = part->boids->states.first; state; state = state->next) {
for (rule = state->rules.first; rule; rule = rule->next) {
if (rule->type == eBoidRuleType_Avoid) {
BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule;
expand_doit(fd, mainvar, gabr->ob);
}
else if (rule->type == eBoidRuleType_FollowLeader) {
BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule;
expand_doit(fd, mainvar, flbr->ob);
}
}
}
}
for (ParticleDupliWeight *dw = part->instance_weights.first; dw; dw = dw->next) {
expand_doit(fd, mainvar, dw->ob);
}
}
static void expand_collection(FileData *fd, Main *mainvar, Collection *collection)
{
for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
expand_doit(fd, mainvar, cob->ob);
}
for (CollectionChild *child = collection->children.first; child; child = child->next) {
expand_doit(fd, mainvar, child->collection);
}
#ifdef USE_COLLECTION_COMPAT_28
if (collection->collection != NULL) {
expand_scene_collection(fd, mainvar, collection->collection);
}
#endif
}
static void expand_key(FileData *fd, Main *mainvar, Key *key)
{
expand_doit(fd, mainvar, key->ipo); // XXX deprecated - old animation system
if (key->adt)
expand_animdata(fd, mainvar, key->adt);
}
static void expand_nodetree(FileData *fd, Main *mainvar, bNodeTree *ntree)
{
bNode *node;
bNodeSocket *sock;
if (ntree->adt)
expand_animdata(fd, mainvar, ntree->adt);
if (ntree->gpd)
expand_doit(fd, mainvar, ntree->gpd);
for (node = ntree->nodes.first; node; node = node->next) {
if (node->id && node->type != CMP_NODE_R_LAYERS) {
expand_doit(fd, mainvar, node->id);
}
expand_idprops(fd, mainvar, node->prop);
for (sock = node->inputs.first; sock; sock = sock->next)
expand_idprops(fd, mainvar, sock->prop);
for (sock = node->outputs.first; sock; sock = sock->next)
expand_idprops(fd, mainvar, sock->prop);
}
for (sock = ntree->inputs.first; sock; sock = sock->next)
expand_idprops(fd, mainvar, sock->prop);
for (sock = ntree->outputs.first; sock; sock = sock->next)
expand_idprops(fd, mainvar, sock->prop);
}
static void expand_texture(FileData *fd, Main *mainvar, Tex *tex)
{
expand_doit(fd, mainvar, tex->ima);
expand_doit(fd, mainvar, tex->ipo); // XXX deprecated - old animation system
if (tex->adt)
expand_animdata(fd, mainvar, tex->adt);
if (tex->nodetree)
expand_nodetree(fd, mainvar, tex->nodetree);
}
static void expand_brush(FileData *fd, Main *mainvar, Brush *brush)
{
expand_doit(fd, mainvar, brush->mtex.tex);
expand_doit(fd, mainvar, brush->mask_mtex.tex);
expand_doit(fd, mainvar, brush->clone.image);
expand_doit(fd, mainvar, brush->paint_curve);
if (brush->gpencil_settings != NULL) {
expand_doit(fd, mainvar, brush->gpencil_settings->material);
}
}
static void expand_material(FileData *fd, Main *mainvar, Material *ma)
{
expand_doit(fd, mainvar, ma->ipo); // XXX deprecated - old animation system
if (ma->adt)
expand_animdata(fd, mainvar, ma->adt);
if (ma->nodetree)
expand_nodetree(fd, mainvar, ma->nodetree);
if (ma->gp_style) {
MaterialGPencilStyle *gp_style = ma->gp_style;
expand_doit(fd, mainvar, gp_style->sima);
expand_doit(fd, mainvar, gp_style->ima);
}
}
static void expand_light(FileData *fd, Main *mainvar, Light *la)
{
expand_doit(fd, mainvar, la->ipo); // XXX deprecated - old animation system
if (la->adt)
expand_animdata(fd, mainvar, la->adt);
if (la->nodetree)
expand_nodetree(fd, mainvar, la->nodetree);
}
static void expand_lattice(FileData *fd, Main *mainvar, Lattice *lt)
{
expand_doit(fd, mainvar, lt->ipo); // XXX deprecated - old animation system
expand_doit(fd, mainvar, lt->key);
if (lt->adt)
expand_animdata(fd, mainvar, lt->adt);
}
static void expand_world(FileData *fd, Main *mainvar, World *wrld)
{
expand_doit(fd, mainvar, wrld->ipo); // XXX deprecated - old animation system
if (wrld->adt)
expand_animdata(fd, mainvar, wrld->adt);
if (wrld->nodetree)
expand_nodetree(fd, mainvar, wrld->nodetree);
}
static void expand_mball(FileData *fd, Main *mainvar, MetaBall *mb)
{
int a;
for (a = 0; a < mb->totcol; a++) {
expand_doit(fd, mainvar, mb->mat[a]);
}
if (mb->adt)
expand_animdata(fd, mainvar, mb->adt);
}
static void expand_curve(FileData *fd, Main *mainvar, Curve *cu)
{
int a;
for (a = 0; a < cu->totcol; a++) {
expand_doit(fd, mainvar, cu->mat[a]);
}
expand_doit(fd, mainvar, cu->vfont);
expand_doit(fd, mainvar, cu->vfontb);
expand_doit(fd, mainvar, cu->vfonti);
expand_doit(fd, mainvar, cu->vfontbi);
expand_doit(fd, mainvar, cu->key);
expand_doit(fd, mainvar, cu->ipo); // XXX deprecated - old animation system
expand_doit(fd, mainvar, cu->bevobj);
expand_doit(fd, mainvar, cu->taperobj);
expand_doit(fd, mainvar, cu->textoncurve);
if (cu->adt)
expand_animdata(fd, mainvar, cu->adt);
}
static void expand_mesh(FileData *fd, Main *mainvar, Mesh *me)
{
int a;
if (me->adt)
expand_animdata(fd, mainvar, me->adt);
for (a = 0; a < me->totcol; a++) {
expand_doit(fd, mainvar, me->mat[a]);
}
expand_doit(fd, mainvar, me->key);
expand_doit(fd, mainvar, me->texcomesh);
}
/* temp struct used to transport needed info to expand_constraint_cb() */
typedef struct tConstraintExpandData {
FileData *fd;
Main *mainvar;
} tConstraintExpandData;
/* callback function used to expand constraint ID-links */
static void expand_constraint_cb(bConstraint *UNUSED(con), ID **idpoin, bool UNUSED(is_reference), void *userdata)
{
tConstraintExpandData *ced = (tConstraintExpandData *)userdata;
expand_doit(ced->fd, ced->mainvar, *idpoin);
}
static void expand_constraints(FileData *fd, Main *mainvar, ListBase *lb)
{
tConstraintExpandData ced;
bConstraint *curcon;
/* relink all ID-blocks used by the constraints */
ced.fd = fd;
ced.mainvar = mainvar;
BKE_constraints_id_loop(lb, expand_constraint_cb, &ced);
/* deprecated manual expansion stuff */
for (curcon = lb->first; curcon; curcon = curcon->next) {
if (curcon->ipo)
expand_doit(fd, mainvar, curcon->ipo); // XXX deprecated - old animation system
}
}
static void expand_pose(FileData *fd, Main *mainvar, bPose *pose)
{
bPoseChannel *chan;
if (!pose)
return;
for (chan = pose->chanbase.first; chan; chan = chan->next) {
expand_constraints(fd, mainvar, &chan->constraints);
expand_idprops(fd, mainvar, chan->prop);
expand_doit(fd, mainvar, chan->custom);
}
}
static void expand_bones(FileData *fd, Main *mainvar, Bone *bone)
{
expand_idprops(fd, mainvar, bone->prop);
for (Bone *curBone = bone->childbase.first; curBone; curBone = curBone->next) {
expand_bones(fd, mainvar, curBone);
}
}
static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm)
{
if (arm->adt)
expand_animdata(fd, mainvar, arm->adt);
for (Bone *curBone = arm->bonebase.first; curBone; curBone = curBone->next) {
expand_bones(fd, mainvar, curBone);
}
}
static void expand_object_expandModifiers(
void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cb_flag))
{
struct { FileData *fd; Main *mainvar; } *data = userData;
FileData *fd = data->fd;
Main *mainvar = data->mainvar;
expand_doit(fd, mainvar, *idpoin);
}
static void expand_object(FileData *fd, Main *mainvar, Object *ob)
{
ParticleSystem *psys;
bActionStrip *strip;
PartEff *paf;
int a;
expand_doit(fd, mainvar, ob->data);
/* expand_object_expandModifier() */
if (ob->modifiers.first) {
struct { FileData *fd; Main *mainvar; } data;
data.fd = fd;
data.mainvar = mainvar;
modifiers_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data);
}
/* expand_object_expandModifier() */
if (ob->greasepencil_modifiers.first) {
struct { FileData *fd; Main *mainvar; } data;
data.fd = fd;
data.mainvar = mainvar;
BKE_gpencil_modifiers_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data);
}
/* expand_object_expandShaderFx() */
if (ob->shader_fx.first) {
struct { FileData *fd; Main *mainvar; } data;
data.fd = fd;
data.mainvar = mainvar;
BKE_shaderfx_foreachIDLink(ob, expand_object_expandModifiers, (void *)&data);
}
expand_pose(fd, mainvar, ob->pose);
expand_doit(fd, mainvar, ob->poselib);
expand_constraints(fd, mainvar, &ob->constraints);
expand_doit(fd, mainvar, ob->gpd);
// XXX deprecated - old animation system (for version patching only)
expand_doit(fd, mainvar, ob->ipo);
expand_doit(fd, mainvar, ob->action);
expand_constraint_channels(fd, mainvar, &ob->constraintChannels);
for (strip = ob->nlastrips.first; strip; strip = strip->next) {
expand_doit(fd, mainvar, strip->object);
expand_doit(fd, mainvar, strip->act);
expand_doit(fd, mainvar, strip->ipo);
}
// XXX deprecated - old animation system (for version patching only)
if (ob->adt)
expand_animdata(fd, mainvar, ob->adt);
for (a = 0; a < ob->totcol; a++) {
expand_doit(fd, mainvar, ob->mat[a]);
}
paf = blo_do_version_give_parteff_245(ob);
if (paf && paf->group)
expand_doit(fd, mainvar, paf->group);
if (ob->instance_collection)
expand_doit(fd, mainvar, ob->instance_collection);
if (ob->proxy)
expand_doit(fd, mainvar, ob->proxy);
if (ob->proxy_group)
expand_doit(fd, mainvar, ob->proxy_group);
for (psys = ob->particlesystem.first; psys; psys = psys->next)
expand_doit(fd, mainvar, psys->part);
if (ob->pd) {
expand_doit(fd, mainvar, ob->pd->tex);
expand_doit(fd, mainvar, ob->pd->f_source);
}
if (ob->soft) {
expand_doit(fd, mainvar, ob->soft->collision_group);
if (ob->soft->effector_weights) {
expand_doit(fd, mainvar, ob->soft->effector_weights->group);
}
}
if (ob->rigidbody_constraint) {
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob1);
expand_doit(fd, mainvar, ob->rigidbody_constraint->ob2);
}
if (ob->currentlod) {
LodLevel *level;
for (level = ob->lodlevels.first; level; level = level->next) {
expand_doit(fd, mainvar, level->source);
}
}
}
#ifdef USE_COLLECTION_COMPAT_28
static void expand_scene_collection(FileData *fd, Main *mainvar, SceneCollection *sc)
{
for (LinkData *link = sc->objects.first; link; link = link->next) {
expand_doit(fd, mainvar, link->data);
}
for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
expand_scene_collection(fd, mainvar, nsc);
}
}
#endif
static void expand_scene(FileData *fd, Main *mainvar, Scene *sce)
{
SceneRenderLayer *srl;
FreestyleModuleConfig *module;
FreestyleLineSet *lineset;
for (Base *base_legacy = sce->base.first; base_legacy; base_legacy = base_legacy->next) {
expand_doit(fd, mainvar, base_legacy->object);
}
expand_doit(fd, mainvar, sce->camera);
expand_doit(fd, mainvar, sce->world);
if (sce->adt)
expand_animdata(fd, mainvar, sce->adt);
expand_keyingsets(fd, mainvar, &sce->keyingsets);
if (sce->set)
expand_doit(fd, mainvar, sce->set);
if (sce->nodetree)
expand_nodetree(fd, mainvar, sce->nodetree);
for (srl = sce->r.layers.first; srl; srl = srl->next) {
expand_doit(fd, mainvar, srl->mat_override);
for (module = srl->freestyleConfig.modules.first; module; module = module->next) {
if (module->script)
expand_doit(fd, mainvar, module->script);
}
for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) {
if (lineset->group)
expand_doit(fd, mainvar, lineset->group);
expand_doit(fd, mainvar, lineset->linestyle);
}
}
for (ViewLayer *view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) {
expand_idprops(fd, mainvar, view_layer->id_properties);
for (module = view_layer->freestyle_config.modules.first; module; module = module->next) {
if (module->script) {
expand_doit(fd, mainvar, module->script);
}
}
for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) {
if (lineset->group) {
expand_doit(fd, mainvar, lineset->group);
}
expand_doit(fd, mainvar, lineset->linestyle);
}
}
if (sce->gpd)
expand_doit(fd, mainvar, sce->gpd);
if (sce->ed) {
Sequence *seq;
SEQ_BEGIN(sce->ed, seq)
{
expand_idprops(fd, mainvar, seq->prop);
if (seq->scene) expand_doit(fd, mainvar, seq->scene);
if (seq->scene_camera) expand_doit(fd, mainvar, seq->scene_camera);
if (seq->clip) expand_doit(fd, mainvar, seq->clip);
if (seq->mask) expand_doit(fd, mainvar, seq->mask);
if (seq->sound) expand_doit(fd, mainvar, seq->sound);
if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) {
TextVars *data = seq->effectdata;
expand_doit(fd, mainvar, data->text_font);
}
} SEQ_END;
}
if (sce->rigidbody_world) {
expand_doit(fd, mainvar, sce->rigidbody_world->group);
expand_doit(fd, mainvar, sce->rigidbody_world->constraints);
}
for (TimeMarker *marker = sce->markers.first; marker; marker = marker->next) {
if (marker->camera) {
expand_doit(fd, mainvar, marker->camera);
}
}
expand_doit(fd, mainvar, sce->clip);
#ifdef USE_COLLECTION_COMPAT_28
if (sce->collection) {
expand_scene_collection(fd, mainvar, sce->collection);
}
#endif
if (sce->master_collection) {
expand_collection(fd, mainvar, sce->master_collection);
}
if (sce->r.bake.cage_object) {
expand_doit(fd, mainvar, sce->r.bake.cage_object);
}
}
static void expand_camera(FileData *fd, Main *mainvar, Camera *ca)
{
expand_doit(fd, mainvar, ca->ipo); // XXX deprecated - old animation system
if (ca->adt)
expand_animdata(fd, mainvar, ca->adt);
}
static void expand_cachefile(FileData *fd, Main *mainvar, CacheFile *cache_file)
{
if (cache_file->adt) {
expand_animdata(fd, mainvar, cache_file->adt);
}
}
static void expand_speaker(FileData *fd, Main *mainvar, Speaker *spk)
{
expand_doit(fd, mainvar, spk->sound);
if (spk->adt)
expand_animdata(fd, mainvar, spk->adt);
}
static void expand_sound(FileData *fd, Main *mainvar, bSound *snd)
{
expand_doit(fd, mainvar, snd->ipo); // XXX deprecated - old animation system
}
static void expand_lightprobe(FileData *fd, Main *mainvar, LightProbe *prb)
{
if (prb->adt)
expand_animdata(fd, mainvar, prb->adt);
}
static void expand_movieclip(FileData *fd, Main *mainvar, MovieClip *clip)
{
if (clip->adt)
expand_animdata(fd, mainvar, clip->adt);
}
static void expand_mask_parent(FileData *fd, Main *mainvar, MaskParent *parent)
{
if (parent->id) {
expand_doit(fd, mainvar, parent->id);
}
}
static void expand_mask(FileData *fd, Main *mainvar, Mask *mask)
{
MaskLayer *mask_layer;
if (mask->adt)
expand_animdata(fd, mainvar, mask->adt);
for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) {
MaskSpline *spline;
for (spline = mask_layer->splines.first; spline; spline = spline->next) {
int i;
for (i = 0; i < spline->tot_point; i++) {
MaskSplinePoint *point = &spline->points[i];
expand_mask_parent(fd, mainvar, &point->parent);
}
expand_mask_parent(fd, mainvar, &spline->parent);
}
}
}
static void expand_linestyle(FileData *fd, Main *mainvar, FreestyleLineStyle *linestyle)
{
int a;
LineStyleModifier *m;
for (a = 0; a < MAX_MTEX; a++) {
if (linestyle->mtex[a]) {
expand_doit(fd, mainvar, linestyle->mtex[a]->tex);
expand_doit(fd, mainvar, linestyle->mtex[a]->object);
}
}
if (linestyle->nodetree)
expand_nodetree(fd, mainvar, linestyle->nodetree);
if (linestyle->adt)
expand_animdata(fd, mainvar, linestyle->adt);
for (m = linestyle->color_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT)
expand_doit(fd, mainvar, ((LineStyleColorModifier_DistanceFromObject *)m)->target);
}
for (m = linestyle->alpha_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT)
expand_doit(fd, mainvar, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target);
}
for (m = linestyle->thickness_modifiers.first; m; m = m->next) {
if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT)
expand_doit(fd, mainvar, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target);
}
}
static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd)
{
if (gpd->adt) {
expand_animdata(fd, mainvar, gpd->adt);
}
for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
expand_doit(fd, mainvar, gpl->parent);
}
for (int a = 0; a < gpd->totcol; a++) {
expand_doit(fd, mainvar, gpd->mat[a]);
}
}
static void expand_workspace(FileData *fd, Main *mainvar, WorkSpace *workspace)
{
ListBase *layouts = BKE_workspace_layouts_get(workspace);
for (WorkSpaceLayout *layout = layouts->first; layout; layout = layout->next) {
expand_doit(fd, mainvar, BKE_workspace_layout_screen_get(layout));
}
}
/**
* Set the callback func used over all ID data found by \a BLO_expand_main func.
*
* \param expand_doit_func: Called for each ID block it finds.
*/
void BLO_main_expander(BLOExpandDoitCallback expand_doit_func)
{
expand_doit = expand_doit_func;
}
/**
* Loop over all ID data in Main to mark relations.
* Set (id->tag & LIB_TAG_NEED_EXPAND) to mark expanding. Flags get cleared after expanding.
*
* \param fdhandle: usually filedata, or own handle.
* \param mainvar: the Main database to expand.
*/
void BLO_expand_main(void *fdhandle, Main *mainvar)
{
ListBase *lbarray[MAX_LIBARRAY];
FileData *fd = fdhandle;
ID *id;
int a;
bool do_it = true;
while (do_it) {
do_it = false;
a = set_listbasepointers(mainvar, lbarray);
while (a--) {
id = lbarray[a]->first;
while (id) {
if (id->tag & LIB_TAG_NEED_EXPAND) {
expand_id(fd, mainvar, id);
expand_idprops(fd, mainvar, id->properties);
switch (GS(id->name)) {
case ID_OB:
expand_object(fd, mainvar, (Object *)id);
break;
case ID_ME:
expand_mesh(fd, mainvar, (Mesh *)id);
break;
case ID_CU:
expand_curve(fd, mainvar, (Curve *)id);
break;
case ID_MB:
expand_mball(fd, mainvar, (MetaBall *)id);
break;
case ID_SCE:
expand_scene(fd, mainvar, (Scene *)id);
break;
case ID_MA:
expand_material(fd, mainvar, (Material *)id);
break;
case ID_TE:
expand_texture(fd, mainvar, (Tex *)id);
break;
case ID_WO:
expand_world(fd, mainvar, (World *)id);
break;
case ID_LT:
expand_lattice(fd, mainvar, (Lattice *)id);
break;
case ID_LA:
expand_light(fd, mainvar, (Light *)id);
break;
case ID_KE:
expand_key(fd, mainvar, (Key *)id);
break;
case ID_CA:
expand_camera(fd, mainvar, (Camera *)id);
break;
case ID_SPK:
expand_speaker(fd, mainvar, (Speaker *)id);
break;
case ID_SO:
expand_sound(fd, mainvar, (bSound *)id);
break;
case ID_LP:
expand_lightprobe(fd, mainvar, (LightProbe *)id);
break;
case ID_AR:
expand_armature(fd, mainvar, (bArmature *)id);
break;
case ID_AC:
expand_action(fd, mainvar, (bAction *)id); // XXX deprecated - old animation system
break;
case ID_GR:
expand_collection(fd, mainvar, (Collection *)id);
break;
case ID_NT:
expand_nodetree(fd, mainvar, (bNodeTree *)id);
break;
case ID_BR:
expand_brush(fd, mainvar, (Brush *)id);
break;
case ID_IP:
expand_ipo(fd, mainvar, (Ipo *)id); // XXX deprecated - old animation system
break;
case ID_PA:
expand_particlesettings(fd, mainvar, (ParticleSettings *)id);
break;
case ID_MC:
expand_movieclip(fd, mainvar, (MovieClip *)id);
break;
case ID_MSK:
expand_mask(fd, mainvar, (Mask *)id);
break;
case ID_LS:
expand_linestyle(fd, mainvar, (FreestyleLineStyle *)id);
break;
case ID_GD:
expand_gpencil(fd, mainvar, (bGPdata *)id);
break;
case ID_CF:
expand_cachefile(fd, mainvar, (CacheFile *)id);
break;
case ID_WS:
expand_workspace(fd, mainvar, (WorkSpace *)id);
break;
default:
break;
}
do_it = true;
id->tag &= ~LIB_TAG_NEED_EXPAND;
}
id = id->next;
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Library Linking (helper functions)
* \{ */
static bool object_in_any_scene(Main *bmain, Object *ob)
{
Scene *sce;
for (sce = bmain->scenes.first; sce; sce = sce->id.next) {
if (BKE_scene_object_find(sce, ob)) {
return true;
}
}
return false;
}
static void add_loose_objects_to_scene(
Main *mainvar, Main *bmain,
Scene *scene, ViewLayer *view_layer, const View3D *v3d, Library *lib, const short flag)
{
Collection *active_collection = NULL;
const bool is_link = (flag & FILE_LINK) != 0;
BLI_assert(scene);
/* Give all objects which are LIB_TAG_INDIRECT a base, or for a collection when *lib has been set. */
for (Object *ob = mainvar->objects.first; ob; ob = ob->id.next) {
bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0;
if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) {
if (!is_link) {
if (ob->id.us == 0) {
do_it = true;
}
else if ((ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) {
/* When appending, make sure any indirectly loaded objects get a base, else they cant be accessed at all
* (see T27437). */
do_it = true;
}
}
if (do_it) {
/* Find or add collection as needed. */
if (active_collection == NULL) {
if (flag & FILE_ACTIVE_COLLECTION) {
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
active_collection = lc->collection;
}
else {
active_collection = BKE_collection_add(bmain, scene->master_collection, NULL);
}
}
CLAMP_MIN(ob->id.us, 0);
ob->mode = OB_MODE_OBJECT;
BKE_collection_object_add(bmain, active_collection, ob);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (v3d != NULL) {
base->local_view_bits |= v3d->local_view_uuid;
}
BKE_scene_object_base_flag_sync_from_base(base);
if (flag & FILE_AUTOSELECT) {
if (base->flag & BASE_SELECTABLE) {
base->flag |= BASE_SELECTED;
BKE_scene_object_base_flag_sync_from_base(base);
}
/* Do NOT make base active here! screws up GUI stuff, if you want it do it on src/ level. */
}
ob->id.tag &= ~LIB_TAG_INDIRECT;
ob->id.tag |= LIB_TAG_EXTERN;
}
}
}
}
static void add_collections_to_scene(
Main *mainvar, Main *bmain,
Scene *scene, ViewLayer *view_layer, const View3D *v3d, Library *lib, const short flag)
{
const bool do_append = (flag & FILE_LINK) == 0;
Collection *active_collection = scene->master_collection;
if (flag & FILE_ACTIVE_COLLECTION) {
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
active_collection = lc->collection;
}
/* Give all objects which are tagged a base. */
for (Collection *collection = mainvar->collections.first; collection; collection = collection->id.next) {
if ((flag & FILE_GROUP_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) {
/* Any indirect collection should not have been tagged. */
BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0);
/* BKE_object_add(...) messes with the selection. */
Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2);
ob->type = OB_EMPTY;
BKE_collection_object_add(bmain, active_collection, ob);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (v3d != NULL) {
base->local_view_bits |= v3d->local_view_uuid;
}
if (base->flag & BASE_SELECTABLE) {
base->flag |= BASE_SELECTED;
}
BKE_scene_object_base_flag_sync_from_base(base);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
view_layer->basact = base;
/* Assign the collection. */
ob->instance_collection = collection;
id_us_plus(&collection->id);
ob->transflag |= OB_DUPLICOLLECTION;
copy_v3_v3(ob->loc, scene->cursor.location);
}
/* We do not want to force instantiation of indirectly linked collections...
* Except when we are appending (since in that case, we'll end up instantiating all objects,
* it's better to do it via their own collections if possible).
* Reports showing that desired difference in behaviors between link and append: T62570, T61796. */
else if (do_append || (collection->id.tag & LIB_TAG_INDIRECT) == 0) {
bool do_add_collection = (collection->id.tag & LIB_TAG_DOIT) != 0;
if (!do_add_collection) {
/* We need to check that objects in that collections are already instantiated in a scene.
* Otherwise, it's better to add the collection to the scene's active collection, than to
* instantiate its objects in active scene's collection directly. See T61141.
* Note that we only check object directly into that collection, not recursively into its children.
*/
for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; coll_ob = coll_ob->next) {
Object *ob = coll_ob->ob;
if ((ob->id.tag & LIB_TAG_PRE_EXISTING) == 0 &&
(ob->id.tag & LIB_TAG_DOIT) == 0 &&
(do_append || (ob->id.tag & LIB_TAG_INDIRECT) == 0) &&
(ob->id.lib == lib) &&
(object_in_any_scene(bmain, ob) == 0))
{
do_add_collection = true;
break;
}
}
}
if (do_add_collection) {
/* Add collection as child of active collection. */
BKE_collection_child_add(bmain, active_collection, collection);
collection->id.tag &= ~LIB_TAG_INDIRECT;
collection->id.tag |= LIB_TAG_EXTERN;
}
}
}
}
static ID *create_placeholder(Main *mainvar, const short idcode, const char *idname, const int tag)
{
ListBase *lb = which_libbase(mainvar, idcode);
ID *ph_id = BKE_libblock_alloc_notest(idcode);
*((short *)ph_id->name) = idcode;
BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2);
BKE_libblock_init_empty(ph_id);
ph_id->lib = mainvar->curlib;
ph_id->tag = tag | LIB_TAG_MISSING;
ph_id->us = ID_FAKE_USERS(ph_id);
ph_id->icon_id = 0;
BLI_addtail(lb, ph_id);
id_sort_by_name(lb, ph_id);
return ph_id;
}
/* returns true if the item was found
* but it may already have already been appended/linked */
static ID *link_named_part(
Main *mainl, FileData *fd, const short idcode, const char *name, const int flag)
{
BHead *bhead = find_bhead_from_code_name(fd, idcode, name);
ID *id;
const bool use_placeholders = (flag & BLO_LIBLINK_USE_PLACEHOLDERS) != 0;
const bool force_indirect = (flag & BLO_LIBLINK_FORCE_INDIRECT) != 0;
BLI_assert(BKE_idcode_is_linkable(idcode) && BKE_idcode_is_valid(idcode));
if (bhead) {
id = is_yet_read(fd, mainl, bhead);
if (id == NULL) {
/* not read yet */
const int tag = force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN;
read_libblock(fd, mainl, bhead, tag | LIB_TAG_NEED_EXPAND, &id);
if (id) {
/* sort by name in list */
ListBase *lb = which_libbase(mainl, idcode);
id_sort_by_name(lb, id);
}
}
else {
/* already linked */
if (G.debug)
printf("append: already linked\n");
oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
if (!force_indirect && (id->tag & LIB_TAG_INDIRECT)) {
id->tag &= ~LIB_TAG_INDIRECT;
id->tag |= LIB_TAG_EXTERN;
}
}
}
else if (use_placeholders) {
/* XXX flag part is weak! */
id = create_placeholder(mainl, idcode, name, force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN);
}
else {
id = NULL;
}
/* if we found the id but the id is NULL, this is really bad */
BLI_assert(!((bhead != NULL) && (id == NULL)));
return id;
}
/**
* Simple reader for copy/paste buffers.
*/
void BLO_library_link_copypaste(Main *mainl, BlendHandle *bh)
{
FileData *fd = (FileData *)(bh);
BHead *bhead;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
ID *id = NULL;
if (bhead->code == ENDB)
break;
if (ELEM(bhead->code, ID_OB, ID_GR)) {
read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, &id);
}
if (id) {
/* sort by name in list */
ListBase *lb = which_libbase(mainl, GS(id->name));
id_sort_by_name(lb, id);
if (bhead->code == ID_OB) {
/* Instead of instancing Base's directly, postpone until after collections are loaded
* otherwise the base's flag is set incorrectly when collections are used */
Object *ob = (Object *)id;
ob->mode = OB_MODE_OBJECT;
/* ensure add_loose_objects_to_scene runs on this object */
BLI_assert(id->us == 0);
}
}
}
}
static ID *link_named_part_ex(
Main *mainl, FileData *fd, const short idcode, const char *name, const int flag)
{
ID *id = link_named_part(mainl, fd, idcode, name, flag);
if (id && (GS(id->name) == ID_OB)) {
/* Tag as loose object needing to be instantiated somewhere... */
id->tag |= LIB_TAG_DOIT;
}
else if (id && (GS(id->name) == ID_GR)) {
/* tag as needing to be instantiated or linked */
id->tag |= LIB_TAG_DOIT;
}
return id;
}
/**
* Link a named data-block from an external blend file.
*
* \param mainl: The main database to link from (not the active one).
* \param bh: The blender file handle.
* \param idcode: The kind of data-block to link.
* \param name: The name of the data-block (without the 2 char ID prefix).
* \return the linked ID when found.
*/
ID *BLO_library_link_named_part(Main *mainl, BlendHandle **bh, const short idcode, const char *name)
{
FileData *fd = (FileData *)(*bh);
return link_named_part(mainl, fd, idcode, name, 0);
}
/**
* Link a named data-block from an external blend file.
* Optionally instantiate the object/collection in the scene when the flags are set.
*
* \param mainl: The main database to link from (not the active one).
* \param bh: The blender file handle.
* \param idcode: The kind of data-block to link.
* \param name: The name of the data-block (without the 2 char ID prefix).
* \param flag: Options for linking, used for instantiating.
* \param scene: The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
* \param v3d: The active View3D (only to define active layers for instantiated objects & collections, can be NULL).
* \return the linked ID when found.
*/
ID *BLO_library_link_named_part_ex(
Main *mainl, BlendHandle **bh,
const short idcode, const char *name, const int flag)
{
FileData *fd = (FileData *)(*bh);
return link_named_part_ex(mainl, fd, idcode, name, flag);
}
/* common routine to append/link something from a library */
static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath)
{
Main *mainl;
(*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist");
/* clear for collection instantiating tag */
BKE_main_id_tag_listbase(&(mainvar->collections), LIB_TAG_DOIT, false);
/* make mains */
blo_split_main((*fd)->mainlist, mainvar);
/* which one do we need? */
mainl = blo_find_main(*fd, filepath, BKE_main_blendfile_path(mainvar));
/* needed for do_version */
mainl->versionfile = (*fd)->fileversion;
read_file_version(*fd, mainl);
#ifdef USE_GHASH_BHEAD
read_file_bhead_idname_map_create(*fd);
#endif
return mainl;
}
/**
* Initialize the BlendHandle for linking library data.
*
* \param mainvar: The current main database, e.g. G_MAIN or CTX_data_main(C).
* \param bh: A blender file handle as returned by \a BLO_blendhandle_from_file or \a BLO_blendhandle_from_memory.
* \param filepath: Used for relative linking, copied to the \a lib->name.
* \return the library Main, to be passed to \a BLO_library_append_named_part as \a mainl.
*/
Main *BLO_library_link_begin(Main *mainvar, BlendHandle **bh, const char *filepath)
{
FileData *fd = (FileData *)(*bh);
return library_link_begin(mainvar, &fd, filepath);
}
static void split_main_newid(Main *mainptr, Main *main_newid)
{
/* We only copy the necessary subset of data in this temp main. */
main_newid->versionfile = mainptr->versionfile;
main_newid->subversionfile = mainptr->subversionfile;
BLI_strncpy(main_newid->name, mainptr->name, sizeof(main_newid->name));
main_newid->curlib = mainptr->curlib;
ListBase *lbarray[MAX_LIBARRAY];
ListBase *lbarray_newid[MAX_LIBARRAY];
int i = set_listbasepointers(mainptr, lbarray);
set_listbasepointers(main_newid, lbarray_newid);
while (i--) {
BLI_listbase_clear(lbarray_newid[i]);
for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) {
idnext = id->next;
if (id->tag & LIB_TAG_NEW) {
BLI_remlink(lbarray[i], id);
BLI_addtail(lbarray_newid[i], id);
}
}
}
}
/* scene and v3d may be NULL. */
static void library_link_end(
Main *mainl, FileData **fd, const short flag, Main *bmain,
Scene *scene, ViewLayer *view_layer, const View3D *v3d)
{
Main *mainvar;
Library *curlib;
/* expander now is callback function */
BLO_main_expander(expand_doit_library);
/* make main consistent */
BLO_expand_main(*fd, mainl);
/* do this when expand found other libs */
read_libraries(*fd, (*fd)->mainlist);
curlib = mainl->curlib;
/* make the lib path relative if required */
if (flag & FILE_RELPATH) {
/* use the full path, this could have been read by other library even */
BLI_strncpy(curlib->name, curlib->filepath, sizeof(curlib->name));
/* uses current .blend file as reference */
BLI_path_rel(curlib->name, BKE_main_blendfile_path_from_global());
}
blo_join_main((*fd)->mainlist);
mainvar = (*fd)->mainlist->first;
mainl = NULL; /* blo_join_main free's mainl, cant use anymore */
lib_link_all(*fd, mainvar);
BKE_collections_after_lib_link(mainvar);
/* Yep, second splitting... but this is a very cheap operation, so no big deal. */
blo_split_main((*fd)->mainlist, mainvar);
Main *main_newid = BKE_main_new();
for (mainvar = ((Main *)(*fd)->mainlist->first)->next; mainvar; mainvar = mainvar->next) {
BLI_assert(mainvar->versionfile != 0);
/* We need to split out IDs already existing, or they will go again through do_versions - bad, very bad! */
split_main_newid(mainvar, main_newid);
do_versions_after_linking(main_newid);
add_main_to_main(mainvar, main_newid);
}
BKE_main_free(main_newid);
blo_join_main((*fd)->mainlist);
mainvar = (*fd)->mainlist->first;
MEM_freeN((*fd)->mainlist);
BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false);
lib_verify_nodetree(mainvar, false);
fix_relpaths_library(BKE_main_blendfile_path(mainvar), mainvar); /* make all relative paths, relative to the open blend file */
/* Give a base to loose objects and collections.
* Only directly linked objects & collections are instantiated by `BLO_library_link_named_part_ex()` & co,
* here we handle indirect ones and other possible edge-cases. */
if (scene) {
add_collections_to_scene(mainvar, bmain, scene, view_layer, v3d, curlib, flag);
add_loose_objects_to_scene(mainvar, bmain, scene, view_layer, v3d, curlib, flag);
}
else {
/* printf("library_append_end, scene is NULL (objects wont get bases)\n"); */
}
/* Clear objects and collections instantiating tag. */
BKE_main_id_tag_listbase(&(mainvar->objects), LIB_TAG_DOIT, false);
BKE_main_id_tag_listbase(&(mainvar->collections), LIB_TAG_DOIT, false);
/* patch to prevent switch_endian happens twice */
if ((*fd)->flags & FD_FLAGS_SWITCH_ENDIAN) {
blo_filedata_free(*fd);
*fd = NULL;
}
}
/**
* Finalize linking from a given .blend file (library).
* Optionally instance the indirect object/collection in the scene when the flags are set.
* \note Do not use \a bh after calling this function, it may frees it.
*
* \param mainl: The main database to link from (not the active one).
* \param bh: The blender file handle (WARNING! may be freed by this function!).
* \param flag: Options for linking, used for instantiating.
* \param bmain: The main database in which to instantiate objects/collections
* \param scene: The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
* \param view_layer: The scene layer in which to instantiate objects/collections (if NULL, no instantiation is done).
* \param v3d: The active View3D (only to define local-view for instantiated objects & groups, can be NULL).
*/
void BLO_library_link_end(
Main *mainl, BlendHandle **bh, int flag, Main *bmain,
Scene *scene, ViewLayer *view_layer, const View3D *v3d)
{
FileData *fd = (FileData *)(*bh);
library_link_end(mainl, &fd, flag, bmain, scene, view_layer, v3d);
*bh = (BlendHandle *)fd;
}
void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname)
{
return read_struct(fd, bh, blockname);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Library Reading
* \{ */
static int has_linked_ids_to_read(Main *mainvar)
{
ListBase *lbarray[MAX_LIBARRAY];
int a = set_listbasepointers(mainvar, lbarray);
while (a--) {
for (ID *id = lbarray[a]->first; id; id = id->next) {
if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
return true;
}
}
}
return false;
}
static void read_library_linked_id(ReportList *reports, FileData *fd, Main *mainvar, ID *id, ID **r_id)
{
BHead *bhead = NULL;
const bool is_valid = BKE_idcode_is_linkable(GS(id->name)) || ((id->tag & LIB_TAG_EXTERN) == 0);
if (fd) {
bhead = find_bhead_from_idname(fd, id->name);
}
if (!is_valid) {
blo_reportf_wrap(
reports, RPT_ERROR,
TIP_("LIB: %s: '%s' is directly linked from '%s' (parent '%s'), but is a non-linkable data type"),
BKE_idcode_to_name(GS(id->name)),
id->name + 2,
mainvar->curlib->filepath,
library_parent_filepath(mainvar->curlib));
}
id->tag &= ~LIB_TAG_ID_LINK_PLACEHOLDER;
if (bhead) {
id->tag |= LIB_TAG_NEED_EXPAND;
// printf("read lib block %s\n", id->name);
read_libblock(fd, mainvar, bhead, id->tag, r_id);
}
else {
blo_reportf_wrap(
reports, RPT_WARNING,
TIP_("LIB: %s: '%s' missing from '%s', parent '%s'"),
BKE_idcode_to_name(GS(id->name)),
id->name + 2,
mainvar->curlib->filepath,
library_parent_filepath(mainvar->curlib));
/* Generate a placeholder for this ID (simplified version of read_libblock actually...). */
if (r_id) {
*r_id = is_valid ? create_placeholder(mainvar, GS(id->name), id->name + 2, id->tag) : NULL;
}
}
}
static void read_library_linked_ids(FileData *basefd, FileData *fd, ListBase *mainlist, Main *mainvar)
{
GHash *loaded_ids = BLI_ghash_str_new(__func__);
ListBase *lbarray[MAX_LIBARRAY];
int a = set_listbasepointers(mainvar, lbarray);
while (a--) {
ID *id = lbarray[a]->first;
ListBase pending_free_ids = {NULL};
while (id) {
ID *id_next = id->next;
if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) {
BLI_remlink(lbarray[a], id);
/* When playing with lib renaming and such, you may end with cases where
* you have more than one linked ID of the same data-block from same
* library. This is absolutely horrible, hence we use a ghash to ensure
* we go back to a single linked data when loading the file. */
ID **realid = NULL;
if (!BLI_ghash_ensure_p(loaded_ids, id->name, (void ***)&realid)) {
read_library_linked_id(basefd->reports, fd, mainvar, id, realid);
}
/* realid shall never be NULL - unless some source file/lib is broken
* (known case: some directly linked shapekey from a missing lib...). */
/* BLI_assert(*realid != NULL); */
/* Now that we have a real ID, replace all pointers to placeholders in
* fd->libmap with pointers to the real datablocks. We do this for all
* libraries since multiple might be referencing this ID. */
change_link_placeholder_to_real_ID_pointer(mainlist, basefd, id, *realid);
/* We cannot free old lib-ref placeholder ID here anymore, since we use
* its name as key in loaded_ids hash. */
BLI_addtail(&pending_free_ids, id);
}
id = id_next;
}
/* Clear GHash and free link placeholder IDs of the current type. */
BLI_ghash_clear(loaded_ids, NULL, NULL);
BLI_freelistN(&pending_free_ids);
}
BLI_ghash_free(loaded_ids, NULL, NULL);
}
static FileData *read_library_file_data(FileData *basefd, ListBase *mainlist, Main *mainl, Main *mainptr)
{
FileData *fd = mainptr->curlib->filedata;
if (fd != NULL) {
/* File already open. */
return fd;
}
if (mainptr->curlib->packedfile) {
/* Read packed file. */
PackedFile *pf = mainptr->curlib->packedfile;
blo_reportf_wrap(
basefd->reports, RPT_INFO, TIP_("Read packed library: '%s', parent '%s'"),
mainptr->curlib->name,
library_parent_filepath(mainptr->curlib));
fd = blo_filedata_from_memory(pf->data, pf->size, basefd->reports);
/* Needed for library_append and read_libraries. */
BLI_strncpy(fd->relabase, mainptr->curlib->filepath, sizeof(fd->relabase));
}
else {
/* Read file on disk. */
blo_reportf_wrap(
basefd->reports, RPT_INFO, TIP_("Read library: '%s', '%s', parent '%s'"),
mainptr->curlib->filepath,
mainptr->curlib->name,
library_parent_filepath(mainptr->curlib));
fd = blo_filedata_from_file(mainptr->curlib->filepath, basefd->reports);
}
if (fd) {
/* Share the mainlist, so all libraries are added immediately in a
* single list. It used to be that all FileData's had their own list,
* but with indirectly linking this meant we didn't catch duplicate
* libraries properly. */
fd->mainlist = mainlist;
fd->reports = basefd->reports;
if (fd->libmap)
oldnewmap_free(fd->libmap);
fd->libmap = oldnewmap_new();
mainptr->curlib->filedata = fd;
mainptr->versionfile = fd->fileversion;
/* subversion */
read_file_version(fd, mainptr);
#ifdef USE_GHASH_BHEAD
read_file_bhead_idname_map_create(fd);
#endif
}
else {
mainptr->curlib->filedata = NULL;
mainptr->curlib->id.tag |= LIB_TAG_MISSING;
/* Set lib version to current main one... Makes assert later happy. */
mainptr->versionfile = mainptr->curlib->versionfile = mainl->versionfile;
mainptr->subversionfile = mainptr->curlib->subversionfile = mainl->subversionfile;
}
if (fd == NULL) {
blo_reportf_wrap(basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"),
mainptr->curlib->filepath);
}
return fd;
}
static void read_libraries(FileData *basefd, ListBase *mainlist)
{
Main *mainl = mainlist->first;
bool do_it = true;
/* Expander is now callback function. */
BLO_main_expander(expand_doit_library);
/* At this point the base blend file has been read, and each library blend
* encountered so far has a main with placeholders for linked datablocks.
*
* Now we will read the library blend files and replace the placeholders
* with actual datablocks. We loop over library mains multiple times in
* case a library needs to link additional datablocks from another library
* that had been read previously. */
while (do_it) {
do_it = false;
/* Loop over mains of all library blend files encountered so far. Note
* this list gets longer as more indirectly library blends are found. */
for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
/* Does this library have any more linked datablocks we need to read? */
if (has_linked_ids_to_read(mainptr)) {
// printf("Reading linked datablocks from %s (%s)\n", mainptr->curlib->id.name, mainptr->curlib->name);
/* Open file if it has not been done yet. */
FileData *fd = read_library_file_data(basefd, mainlist, mainl, mainptr);
if (fd) {
do_it = true;
}
/* Read linked datablocks for each link placeholder, and replace
* the placeholder with the real datablock. */
read_library_linked_ids(basefd, fd, mainlist, mainptr);
/* Test if linked datablocks need to read further linked datablocks
* and create link placeholders for them. */
BLO_expand_main(fd, mainptr);
}
}
}
Main *main_newid = BKE_main_new();
for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
/* Do versioning for newly added linked datablocks. If no datablocks
* were read from a library versionfile will still be zero and we can
* skip it. */
if (mainptr->versionfile) {
/* Split out already existing IDs to avoid them going through
* do_versions multiple times, which would have bad consequences. */
split_main_newid(mainptr, main_newid);
/* File data can be zero with link/append. */
if (mainptr->curlib->filedata)
do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid);
else
do_versions(basefd, NULL, main_newid);
add_main_to_main(mainptr, main_newid);
}
/* Lib linking. */
if (mainptr->curlib->filedata)
lib_link_all(mainptr->curlib->filedata, mainptr);
/* Free file data we no longer need. */
if (mainptr->curlib->filedata)
blo_filedata_free(mainptr->curlib->filedata);
mainptr->curlib->filedata = NULL;
}
BKE_main_free(main_newid);
}
/** \} */