Cleanup: better names and comments for library ID linking code.
Differential Revision: https://developer.blender.org/D4415
This commit is contained in:
parent
8c4b5ac4c1
commit
3ad2d6caef
Notes:
blender-bot
2023-06-07 10:31:13 +02:00
Referenced by issue #62018, Bevel tool crashes
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
||||
|
|
Loading…
Reference in New Issue