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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2019-02-27 21:25:21 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2019-02-27 21:37:42 +0300
commit3ad2d6caef7410aec67ebde416f03c9a06014e8a (patch)
tree3df08217cdf457329221c1f452d21c7f74cebebe /source/blender/blenloader
parent8c4b5ac4c1b496e04bc44ed25f0e89335445ba51 (diff)
Cleanup: better names and comments for library ID linking code.
Differential Revision: https://developer.blender.org/D4415
Diffstat (limited to 'source/blender/blenloader')
-rw-r--r--source/blender/blenloader/intern/readfile.c522
-rw-r--r--source/blender/blenloader/intern/writefile.c3
2 files changed, 284 insertions, 241 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 2472e900e80..9e4be469fb7 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1754,19 +1754,19 @@ static void *newlibadr_real_us(FileData *fd, const void *lib, const void *adr)
return id;
}
-static void change_idid_adr_fd(FileData *fd, const void *old, void *new)
+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_ID) {
+ if (old == entry->newp && entry->nr == ID_LINK_PLACEHOLDER) {
entry->newp = new;
if (new) entry->nr = GS( ((ID *)new)->name);
}
}
}
-static void change_idid_adr(ListBase *mainlist, FileData *basefd, void *old, void *new)
+static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, FileData *basefd, void *old, void *new)
{
Main *mainptr;
@@ -1779,7 +1779,7 @@ static void change_idid_adr(ListBase *mainlist, FileData *basefd, void *old, voi
fd = basefd;
if (fd) {
- change_idid_adr_fd(fd, old, new);
+ change_link_placeholder_to_real_ID_pointer_fd(fd, old, new);
}
}
}
@@ -8156,8 +8156,8 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
TIP_("Library '%s', '%s' had multiple instances, save and reload!"),
lib->name, lib->filepath);
- change_idid_adr(fd->mainlist, fd, lib, newmain->curlib);
-/* change_idid_adr_fd(fd, lib, newmain->curlib); */
+ 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->library, lib);
MEM_freeN(lib);
@@ -8927,7 +8927,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
* 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_ID)) {
+ 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);
@@ -8941,7 +8941,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
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_ID, we need to get the correct lib it is linked to!
+ * 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->library, libmain->curlib);
@@ -8965,7 +8965,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
}
- /* No need to do anything else for ID_ID, it's assumed already present in its lib's main... */
+ /* 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... */
}
@@ -8983,7 +8983,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
/* 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_ID check */
+ oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); /* for ID_LINK_PLACEHOLDER check */
BLI_addtail(lb, id);
}
else {
@@ -9007,7 +9007,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int ta
id->recalc = 0;
/* this case cannot be direct_linked: it's just the ID part */
- if (bhead->code == ID_ID) {
+ 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;
@@ -9516,14 +9516,17 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
bhead = NULL;
break;
- case ID_ID:
- /* Always adds to the most recently loaded ID_LI block, see direct_link_library.
- * This is part of the file format definition. */
+ case ID_LINK_PLACEHOLDER:
if (fd->skip_flags & BLO_READ_SKIP_DATA) {
bhead = blo_bhead_next(fd, bhead);
}
else {
- bhead = read_libblock(fd, mainlist.last, bhead, LIB_TAG_ID_ID | LIB_TAG_EXTERN, NULL);
+ /* 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 */
@@ -9727,83 +9730,89 @@ static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
{
- BHead *bhead;
FileData *fd = fdhandle;
- ID *id;
- bhead = find_bhead(fd, old);
- if (bhead) {
- /* from another library? */
- if (bhead->code == ID_ID) {
- BHead *bheadlib = find_previous_lib(fd, bhead);
+ BHead *bhead = find_bhead(fd, old);
+ if (bhead == NULL) {
+ return;
+ }
- if (bheadlib) {
- Library *lib = read_struct(fd, bheadlib, "Library");
- Main *ptr = blo_find_main(fd, lib->name, fd->relabase);
+ if (bhead->code == ID_LINK_PLACEHOLDER) {
+ /* Placeholder link to datablock in another library. */
+ BHead *bheadlib = find_previous_lib(fd, bhead);
+ if (bheadlib == NULL) {
+ return;
+ }
- if (ptr->curlib == NULL) {
- const char *idname = blo_bhead_id_name(fd, bhead);
+ Library *lib = read_struct(fd, bheadlib, "Library");
+ Main *libmain = blo_find_main(fd, lib->name, fd->relabase);
- blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: Data refers to main .blend file: '%s' from %s"),
- idname, mainvar->curlib->filepath);
- return;
- }
- else
- id = is_yet_read(fd, ptr, bhead);
+ if (libmain->curlib == NULL) {
+ const char *idname = blo_bhead_id_name(fd, bhead);
- if (id == NULL) {
- read_libblock(fd, ptr, bhead, LIB_TAG_ID_ID | 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);
+ blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: Data refers to main .blend file: '%s' from %s"),
+ idname, mainvar->curlib->filepath);
+ return;
+ }
- /* for outliner dependency only */
- ptr->curlib->parent = mainvar->curlib;
- }
- else {
- /* The line below was commented by Ton (I assume), when Hos did the merge from the orange branch. rev 6568
- * This line is NEEDED, the case is that you have 3 blend files...
- * user.blend, lib.blend and lib_indirect.blend - if user.blend already references a "tree" from
- * lib_indirect.blend but lib.blend does too, linking in a Scene or Group from lib.blend can result in an
- * empty without the dupli group referenced. Once you save and reload the group would appear. - Campbell */
- /* This crashes files, must look further into it */
-
- /* Update: the issue is that in file reading, the oldnewmap is OK, but for existing data, it has to be
- * inserted in the map to be found! */
-
- /* Update: previously it was checking for id->tag & LIB_TAG_PRE_EXISTING, however that
- * does not affect file reading. For file reading we may need to insert it into the libmap as well,
- * because you might have two files indirectly linking the same datablock, and in that case
- * we need this in the libmap for the fd of both those files.
- *
- * The crash that this check avoided earlier was because bhead->code wasn't properly passed in, making
- * change_idid_adr not detect the mapping was for an ID_ID datablock. */
- oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
- change_idid_adr_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);
- }
+ ID *id = is_yet_read(fd, libmain, bhead);
- MEM_freeN(lib);
- }
+ 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 {
- /* in 2.50+ file identifier for screens is patched, forward compatibility */
- if (bhead->code == ID_SCRN) {
- bhead->code = ID_SCR;
- }
+ /* "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);
- 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);
- }
+ /* 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);
}
}
}
@@ -11072,48 +11081,6 @@ ID *BLO_library_link_named_part_ex(
return link_named_part_ex(mainl, fd, idcode, name, flag);
}
-static void link_id_part(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);
- }
-
- id->tag &= ~LIB_TAG_ID_ID;
-
- 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));
- }
-
- 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;
- }
- }
-}
-
/* common routine to append/link something from a library */
static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath)
@@ -11293,167 +11260,239 @@ void *BLO_library_read_struct(FileData *fd, BHead *bh, const char *blockname)
/** \name Library Reading
* \{ */
-static int mainvar_id_tag_any_check(Main *mainvar, const int tag)
+static int has_linked_ids_to_read(Main *mainvar)
{
ListBase *lbarray[MAX_LIBARRAY];
- int a;
+ int a = set_listbasepointers(mainvar, lbarray);
- a = set_listbasepointers(mainvar, lbarray);
while (a--) {
- ID *id;
-
- for (id = lbarray[a]->first; id; id = id->next) {
- if (id->tag & tag) {
+ 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_libraries(FileData *basefd, ListBase *mainlist)
+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)
{
- Main *mainl = mainlist->first;
- Main *mainptr;
- ListBase *lbarray[MAX_LIBARRAY];
GHash *loaded_ids = BLI_ghash_str_new(__func__);
- int a;
- bool do_it = true;
- /* expander now is callback function */
- BLO_main_expander(expand_doit_library);
+ ListBase *lbarray[MAX_LIBARRAY];
+ int a = set_listbasepointers(mainvar, lbarray);
- while (do_it) {
- do_it = false;
+ 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);
- /* test 1: read libdata */
- mainptr = mainl->next;
- while (mainptr) {
- if (mainvar_id_tag_any_check(mainptr, LIB_TAG_ID_ID)) {
- // printf("found LIB_TAG_ID_ID %s (%s)\n", mainptr->curlib->id.name, mainptr->curlib->name);
+ /* 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); */
- FileData *fd = mainptr->curlib->filedata;
+ /* 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);
- if (fd == NULL) {
- /* printf and reports for now... its important users know this */
+ /* 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;
+ }
- /* if packed file... */
- if (mainptr->curlib->packedfile) {
- PackedFile *pf = mainptr->curlib->packedfile;
+ /* Clear GHash and free link placeholder IDs of the current type. */
+ BLI_ghash_clear(loaded_ids, NULL, NULL);
+ BLI_freelistN(&pending_free_ids);
+ }
- 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);
+ 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;
- /* needed for library_append and read_libraries */
- BLI_strncpy(fd->relabase, mainptr->curlib->filepath, sizeof(fd->relabase));
- }
- else {
- 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 != 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;
+ 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;
+ fd->reports = basefd->reports;
- if (fd->libmap)
- oldnewmap_free(fd->libmap);
+ if (fd->libmap)
+ oldnewmap_free(fd->libmap);
- fd->libmap = oldnewmap_new();
+ fd->libmap = oldnewmap_new();
- mainptr->curlib->filedata = fd;
- mainptr->versionfile = fd->fileversion;
+ mainptr->curlib->filedata = fd;
+ mainptr->versionfile = fd->fileversion;
- /* subversion */
- read_file_version(fd, mainptr);
+ /* subversion */
+ read_file_version(fd, mainptr);
#ifdef USE_GHASH_BHEAD
- read_file_bhead_idname_map_create(fd);
+ 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;
- }
+ }
+ 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);
- }
- }
- if (fd) {
- do_it = true;
- }
- a = set_listbasepointers(mainptr, lbarray);
- while (a--) {
- ID *id = lbarray[a]->first;
- ListBase pending_free_ids = {NULL};
-
- while (id) {
- ID *idn = id->next;
- if (id->tag & LIB_TAG_ID_ID) {
- 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)) {
- link_id_part(basefd->reports, fd, mainptr, id, realid);
- }
+ if (fd == NULL) {
+ blo_reportf_wrap(basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"),
+ mainptr->curlib->filepath);
+ }
+
+ return fd;
+}
- /* 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); */
+static void read_libraries(FileData *basefd, ListBase *mainlist)
+{
+ Main *mainl = mainlist->first;
+ bool do_it = true;
- change_idid_adr(mainlist, basefd, id, *realid);
+ /* Expander is now callback function. */
+ BLO_main_expander(expand_doit_library);
- /* We cannot free old lib-ref placeholder ID here anymore, since we use its name
- * as key in loaded_ids has. */
- BLI_addtail(&pending_free_ids, id);
- }
- id = idn;
- }
+ /* 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);
- /* Clear GHash and free all lib-ref placeholders IDs of that type now. */
- BLI_ghash_clear(loaded_ids, NULL, NULL);
- BLI_freelistN(&pending_free_ids);
+ /* 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);
}
-
- mainptr = mainptr->next;
}
}
- BLI_ghash_free(loaded_ids, NULL, NULL);
- loaded_ids = NULL;
-
- /* do versions, link, and free */
Main *main_newid = BKE_main_new();
- for (mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
- /* some mains still have to be read, then versionfile is still zero! */
+ 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) {
- /* We need to split out IDs already existing, or they will go again through do_versions - bad, very bad! */
+ /* 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);
- if (mainptr->curlib->filedata) // can be zero... with shift+f1 append
+ /* 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);
@@ -11461,10 +11500,13 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
add_main_to_main(mainptr, main_newid);
}
+ /* Lib linking. */
if (mainptr->curlib->filedata)
lib_link_all(mainptr->curlib->filedata, mainptr);
- if (mainptr->curlib->filedata) blo_filedata_free(mainptr->curlib->filedata);
+ /* 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);
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 24784684f41..ef2c73a675f 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -3752,6 +3752,7 @@ static void write_libraries(WriteData *wd, Main *main)
}
}
+ /* Write link placeholders for all direct linked IDs. */
while (a--) {
for (id = lbarray[a]->first; id; id = id->next) {
if (id->us > 0 && (id->tag & LIB_TAG_EXTERN)) {
@@ -3760,7 +3761,7 @@ static void write_libraries(WriteData *wd, Main *main)
"but is flagged as directly linked", id->name, main->curlib->filepath);
BLI_assert(0);
}
- writestruct(wd, ID_ID, ID, 1, id);
+ writestruct(wd, ID_LINK_PLACEHOLDER, ID, 1, id);
}
}
}