diff options
author | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2019-02-27 21:25:21 +0300 |
---|---|---|
committer | Brecht Van Lommel <brechtvanlommel@gmail.com> | 2019-02-27 21:37:42 +0300 |
commit | 3ad2d6caef7410aec67ebde416f03c9a06014e8a (patch) | |
tree | 3df08217cdf457329221c1f452d21c7f74cebebe /source | |
parent | 8c4b5ac4c1b496e04bc44ed25f0e89335445ba51 (diff) |
Cleanup: better names and comments for library ID linking code.
Differential Revision: https://developer.blender.org/D4415
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/idcode.c | 4 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 522 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 3 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_ID.h | 4 |
4 files changed, 288 insertions, 245 deletions
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c index d34dd2e70df..1c63f184bde 100644 --- a/source/blender/blenkernel/intern/idcode.c +++ b/source/blender/blenkernel/intern/idcode.c @@ -87,10 +87,10 @@ static IDType idtypes[] = { {ID_WS, "WorkSpace", "workspaces", BLT_I18NCONTEXT_ID_WORKSPACE, IDTYPE_FLAGS_ISLINKABLE}, /** Keep last, not an ID exactly, only include for completeness */ - {ID_ID, "ID", "ids", BLT_I18NCONTEXT_ID_ID, 0 }, /* plural is fake */ + {ID_LINK_PLACEHOLDER, "Link Placeholder", "link_placeholders", BLT_I18NCONTEXT_ID_ID, 0}, /* plural is fake */ }; -/* -1 for ID_ID */ +/* -1 for ID_LINK_PLACEHOLDER */ BLI_STATIC_ASSERT((ARRAY_SIZE(idtypes) - 1 == MAX_LIBARRAY), "Missing IDType"); static IDType *idtype_from_name(const char *str) 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); } } } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index fa519ec8011..cd29a3c5bc4 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -397,7 +397,7 @@ typedef enum ID_Type { } ID_Type; /* Only used as 'placeholder' in .blend files for directly linked datablocks. */ -#define ID_ID MAKE_ID2('I', 'D') /* (internal use only) */ +#define ID_LINK_PLACEHOLDER MAKE_ID2('I', 'D') /* (internal use only) */ /* Deprecated. */ #define ID_SCRN MAKE_ID2('S', 'N') @@ -484,7 +484,7 @@ enum { LIB_TAG_NEED_EXPAND = 1 << 3, /* RESET_AFTER_USE Flag used internally in readfile.c to mark ID * placeholders for linked datablocks needing to be read. */ - LIB_TAG_ID_ID = 1 << 4, + LIB_TAG_ID_LINK_PLACEHOLDER = 1 << 4, /* RESET_AFTER_USE */ LIB_TAG_NEED_LINK = 1 << 5, |