Cleanup: better names and comments for library ID linking code.

Differential Revision: https://developer.blender.org/D4415
This commit is contained in:
Brecht Van Lommel 2019-02-27 19:25:21 +01:00
parent 8c4b5ac4c1
commit 3ad2d6caef
Notes: blender-bot 2023-06-07 10:31:13 +02:00
Referenced by issue #62018, Bevel tool crashes
4 changed files with 295 additions and 252 deletions

View File

@ -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)

View File

@ -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 */
ID *id = is_yet_read(fd, libmain, bhead);
/* 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! */
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);
/* 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);
}
MEM_freeN(lib);
}
/* 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_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;
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 */
/* 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;
/* 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);
/* 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);
FileData *fd = mainptr->curlib->filedata;
/* Open file if it has not been done yet. */
FileData *fd = read_library_file_data(basefd, mainlist, mainl, mainptr);
if (fd == NULL) {
/* printf and reports for now... its important users know this */
/* if packed file... */
if (mainptr->curlib->packedfile) {
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 {
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);
}
}
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);
/* Read linked datablocks for each link placeholder, and replace
* the placeholder with the real datablock. */
read_library_linked_ids(basefd, fd, mainlist, mainptr);
/* 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);
}
/* 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); */
change_idid_adr(mainlist, basefd, id, *realid);
/* 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;
}
/* 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);
}
/* 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);

View File

@ -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);
}
}
}

View File

@ -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,