Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenloader/intern/readfile.c
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
| Show First 20 Lines • Show All 1,748 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| ID *id = newlibadr(fd, lib, adr); | ID *id = newlibadr(fd, lib, adr); | ||||
| id_us_ensure_real(id); | id_us_ensure_real(id); | ||||
| return id; | 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++) { | for (int i = 0; i < fd->libmap->nentries; i++) { | ||||
| OldNew *entry = &fd->libmap->entries[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; | entry->newp = new; | ||||
| if (new) entry->nr = GS( ((ID *)new)->name); | 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; | Main *mainptr; | ||||
| for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) { | for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) { | ||||
| FileData *fd; | FileData *fd; | ||||
| if (mainptr->curlib) | if (mainptr->curlib) | ||||
| fd = mainptr->curlib->filedata; | fd = mainptr->curlib->filedata; | ||||
| else | else | ||||
| fd = basefd; | fd = basefd; | ||||
| if (fd) { | if (fd) { | ||||
| change_idid_adr_fd(fd, old, new); | change_link_placeholder_to_real_ID_pointer_fd(fd, old, new); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* lib linked proxy objects point to our local data, we need | /* lib linked proxy objects point to our local data, we need | ||||
| * to clear that pointer before reading the undo memfile since | * to clear that pointer before reading the undo memfile since | ||||
| * the object might be removed, it is set again in reading | * the object might be removed, it is set again in reading | ||||
| * if the local object still exists. | * if the local object still exists. | ||||
| ▲ Show 20 Lines • Show All 6,360 Lines • ▼ Show 20 Lines | static void direct_link_library(FileData *fd, Library *lib, Main *main) | ||||
| /* check if the library was already read */ | /* check if the library was already read */ | ||||
| for (newmain = fd->mainlist->first; newmain; newmain = newmain->next) { | for (newmain = fd->mainlist->first; newmain; newmain = newmain->next) { | ||||
| if (newmain->curlib) { | if (newmain->curlib) { | ||||
| if (BLI_path_cmp(newmain->curlib->filepath, lib->filepath) == 0) { | if (BLI_path_cmp(newmain->curlib->filepath, lib->filepath) == 0) { | ||||
| blo_reportf_wrap(fd->reports, RPT_WARNING, | blo_reportf_wrap(fd->reports, RPT_WARNING, | ||||
| TIP_("Library '%s', '%s' had multiple instances, save and reload!"), | TIP_("Library '%s', '%s' had multiple instances, save and reload!"), | ||||
| lib->name, lib->filepath); | lib->name, lib->filepath); | ||||
| change_idid_adr(fd->mainlist, fd, lib, newmain->curlib); | change_link_placeholder_to_real_ID_pointer(fd->mainlist, fd, lib, newmain->curlib); | ||||
| /* change_idid_adr_fd(fd, lib, newmain->curlib); */ | /* change_link_placeholder_to_real_ID_pointer_fd(fd, lib, newmain->curlib); */ | ||||
| BLI_remlink(&main->library, lib); | BLI_remlink(&main->library, lib); | ||||
| MEM_freeN(lib); | MEM_freeN(lib); | ||||
| /* Now, since Blender always expect **latest** Main pointer from fd->mainlist to be the active library | /* Now, since Blender always expect **latest** Main pointer from fd->mainlist to be the active library | ||||
| * Main pointer, where to add all non-library data-blocks found in file next, we have to switch that | * Main pointer, where to add all non-library data-blocks found in file next, we have to switch that | ||||
| * 'dupli' found Main to latest position in the list! | * 'dupli' found Main to latest position in the list! | ||||
| * Otherwise, you get weird disappearing linked data on a rather unconsistant basis. | * Otherwise, you get weird disappearing linked data on a rather unconsistant basis. | ||||
| ▲ Show 20 Lines • Show All 753 Lines • ▼ Show 20 Lines | static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id) | ||||
| const char *allocname; | const char *allocname; | ||||
| bool wrong_id = false; | bool wrong_id = false; | ||||
| /* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile). | /* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile). | ||||
| * However, some needed by the snapshot being read may have been removed in previous one, and would go missing. | * However, some needed by the snapshot being read may have been removed in previous one, and would go missing. | ||||
| * This leads e.g. to desappearing objects in some undo/redo case, see T34446. | * 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 | * 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... */ | * 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); | const char *idname = blo_bhead_id_name(fd, bhead); | ||||
| DEBUG_PRINTF("Checking %s...\n", idname); | DEBUG_PRINTF("Checking %s...\n", idname); | ||||
| if (bhead->code == ID_LI) { | if (bhead->code == ID_LI) { | ||||
| Main *libmain = fd->old_mainlist->first; | Main *libmain = fd->old_mainlist->first; | ||||
| /* Skip oldmain itself... */ | /* Skip oldmain itself... */ | ||||
| for (libmain = libmain->next; libmain; libmain = libmain->next) { | for (libmain = libmain->next; libmain; libmain = libmain->next) { | ||||
| DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>"); | DEBUG_PRINTF("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>"); | ||||
| if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) { | if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) { | ||||
| Main *oldmain = fd->old_mainlist->first; | Main *oldmain = fd->old_mainlist->first; | ||||
| DEBUG_PRINTF("FOUND!\n"); | DEBUG_PRINTF("FOUND!\n"); | ||||
| /* In case of a library, we need to re-add its main to fd->mainlist, because if we have later | /* 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... */ | * 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(fd->old_mainlist, libmain); | ||||
| BLI_remlink_safe(&oldmain->library, libmain->curlib); | BLI_remlink_safe(&oldmain->library, libmain->curlib); | ||||
| BLI_addtail(fd->mainlist, libmain); | BLI_addtail(fd->mainlist, libmain); | ||||
| BLI_addtail(&main->library, libmain->curlib); | BLI_addtail(&main->library, libmain->curlib); | ||||
| if (r_id) { | if (r_id) { | ||||
| *r_id = NULL; /* Just in case... */ | *r_id = NULL; /* Just in case... */ | ||||
| } | } | ||||
| return blo_bhead_next(fd, bhead); | return blo_bhead_next(fd, bhead); | ||||
| } | } | ||||
| DEBUG_PRINTF("nothing...\n"); | DEBUG_PRINTF("nothing...\n"); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| DEBUG_PRINTF("... in %s (%s): ", main->curlib ? main->curlib->id.name : "<NULL>", main->curlib ? main->curlib->name : "<NULL>"); | DEBUG_PRINTF("... in %s (%s): ", main->curlib ? main->curlib->id.name : "<NULL>", main->curlib ? main->curlib->name : "<NULL>"); | ||||
| if ((id = BKE_libblock_find_name(main, GS(idname), idname + 2))) { | if ((id = BKE_libblock_find_name(main, GS(idname), idname + 2))) { | ||||
| DEBUG_PRINTF("FOUND!\n"); | DEBUG_PRINTF("FOUND!\n"); | ||||
| /* Even though we found our linked ID, there is no guarantee its address is still the same... */ | /* Even though we found our linked ID, there is no guarantee its address is still the same... */ | ||||
| if (id != bhead->old) { | if (id != bhead->old) { | ||||
| oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name)); | 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) { | if (r_id) { | ||||
| *r_id = NULL; /* Just in case... */ | *r_id = NULL; /* Just in case... */ | ||||
| } | } | ||||
| return blo_bhead_next(fd, bhead); | return blo_bhead_next(fd, bhead); | ||||
| } | } | ||||
| DEBUG_PRINTF("nothing...\n"); | DEBUG_PRINTF("nothing...\n"); | ||||
| } | } | ||||
| } | } | ||||
| /* read libblock */ | /* read libblock */ | ||||
| id = read_struct(fd, bhead, "lib block"); | id = read_struct(fd, bhead, "lib block"); | ||||
| if (id) { | if (id) { | ||||
| const short idcode = GS(id->name); | const short idcode = GS(id->name); | ||||
| /* do after read_struct, for dna reconstruct */ | /* do after read_struct, for dna reconstruct */ | ||||
| lb = which_libbase(main, idcode); | lb = which_libbase(main, idcode); | ||||
| if (lb) { | 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); | BLI_addtail(lb, id); | ||||
| } | } | ||||
| else { | else { | ||||
| /* unknown ID type */ | /* unknown ID type */ | ||||
| printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8)); | printf("%s: unknown id code '%c%c'\n", __func__, (idcode & 0xff), (idcode >> 8)); | ||||
| MEM_freeN(id); | MEM_freeN(id); | ||||
| id = NULL; | id = NULL; | ||||
| } | } | ||||
| } | } | ||||
| if (r_id) | if (r_id) | ||||
| *r_id = id; | *r_id = id; | ||||
| if (!id) | if (!id) | ||||
| return blo_bhead_next(fd, bhead); | return blo_bhead_next(fd, bhead); | ||||
| id->lib = main->curlib; | id->lib = main->curlib; | ||||
| id->us = ID_FAKE_USERS(id); | id->us = ID_FAKE_USERS(id); | ||||
| id->icon_id = 0; | id->icon_id = 0; | ||||
| id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */ | id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */ | ||||
| id->orig_id = NULL; | id->orig_id = NULL; | ||||
| id->recalc = 0; | id->recalc = 0; | ||||
| /* this case cannot be direct_linked: it's just the ID part */ | /* 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). */ | /* That way, we know which datablock needs do_versions (required currently for linking). */ | ||||
| id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW; | id->tag = tag | LIB_TAG_NEED_LINK | LIB_TAG_NEW; | ||||
| return blo_bhead_next(fd, bhead); | return blo_bhead_next(fd, bhead); | ||||
| } | } | ||||
| /* need a name for the mallocN, just for debugging and sane prints on leaks */ | /* need a name for the mallocN, just for debugging and sane prints on leaks */ | ||||
| allocname = dataname(GS(id->name)); | allocname = dataname(GS(id->name)); | ||||
| ▲ Show 20 Lines • Show All 492 Lines • ▼ Show 20 Lines | switch (bhead->code) { | ||||
| else { | else { | ||||
| bhead = read_userdef(bfd, fd, bhead); | bhead = read_userdef(bfd, fd, bhead); | ||||
| } | } | ||||
| break; | break; | ||||
| case ENDB: | case ENDB: | ||||
| bhead = NULL; | bhead = NULL; | ||||
| break; | break; | ||||
| case ID_ID: | case ID_LINK_PLACEHOLDER: | ||||
| /* Always adds to the most recently loaded ID_LI block, see direct_link_library. | /* Always adds to the most recently loaded ID_LI block, see direct_link_library. | ||||
| * This is part of the file format definition. */ | * This is part of the file format definition. */ | ||||
| if (fd->skip_flags & BLO_READ_SKIP_DATA) { | if (fd->skip_flags & BLO_READ_SKIP_DATA) { | ||||
| bhead = blo_bhead_next(fd, bhead); | bhead = blo_bhead_next(fd, bhead); | ||||
| } | } | ||||
| else { | 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, which is the last entry in mainlist. */ | |||||
mont29: That’s kind of doublon/extending previous comment (the one just after the case: ), they could… | |||||
| bhead = read_libblock(fd, mainlist.last, bhead, LIB_TAG_ID_LINK_PLACEHOLDER | LIB_TAG_EXTERN, NULL); | |||||
| } | } | ||||
| break; | break; | ||||
| /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ | /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ | ||||
| case ID_SCRN: | case ID_SCRN: | ||||
| bhead->code = ID_SCR; | bhead->code = ID_SCR; | ||||
| /* pass on to default */ | /* pass on to default */ | ||||
| ATTR_FALLTHROUGH; | ATTR_FALLTHROUGH; | ||||
| default: | default: | ||||
| ▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | |||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Library Linking (expand pointers) | /** \name Library Linking (expand pointers) | ||||
| * \{ */ | * \{ */ | ||||
| static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) | static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) | ||||
| { | { | ||||
| BHead *bhead; | |||||
| FileData *fd = fdhandle; | FileData *fd = fdhandle; | ||||
| ID *id; | |||||
| bhead = find_bhead(fd, old); | BHead *bhead = find_bhead(fd, old); | ||||
| if (bhead) { | if (bhead == NULL) { | ||||
| /* from another library? */ | return; | ||||
| if (bhead->code == ID_ID) { | } | ||||
| if (bhead->code == ID_LINK_PLACEHOLDER) { | |||||
| /* Placeholder link to datablock in another library. */ | |||||
| BHead *bheadlib = find_previous_lib(fd, bhead); | BHead *bheadlib = find_previous_lib(fd, bhead); | ||||
| if (bheadlib == NULL) { | |||||
| return; | |||||
| } | |||||
| if (bheadlib) { | |||||
| Library *lib = read_struct(fd, bheadlib, "Library"); | Library *lib = read_struct(fd, bheadlib, "Library"); | ||||
| Main *ptr = blo_find_main(fd, lib->name, fd->relabase); | Main *libmain = blo_find_main(fd, lib->name, fd->relabase); | ||||
| if (ptr->curlib == NULL) { | if (libmain->curlib == NULL) { | ||||
| const char *idname = blo_bhead_id_name(fd, bhead); | const char *idname = blo_bhead_id_name(fd, bhead); | ||||
| blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: Data refers to main .blend file: '%s' from %s"), | blo_reportf_wrap(fd->reports, RPT_WARNING, TIP_("LIB: Data refers to main .blend file: '%s' from %s"), | ||||
| idname, mainvar->curlib->filepath); | idname, mainvar->curlib->filepath); | ||||
| return; | return; | ||||
| } | } | ||||
| else | |||||
| id = is_yet_read(fd, ptr, bhead); | ID *id = is_yet_read(fd, libmain, bhead); | ||||
| if (id == NULL) { | if (id == NULL) { | ||||
| read_libblock(fd, ptr, bhead, LIB_TAG_ID_ID | LIB_TAG_INDIRECT, 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 | // commented because this can print way too much | ||||
| // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->name); | // if (G.debug & G_DEBUG) printf("expand_doit: other lib %s\n", lib->name); | ||||
| /* for outliner dependency only */ | /* for outliner dependency only */ | ||||
| ptr->curlib->parent = mainvar->curlib; | libmain->curlib->parent = mainvar->curlib; | ||||
| } | } | ||||
| else { | else { | ||||
| /* The line below was commented by Ton (I assume), when Hos did the merge from the orange branch. rev 6568 | /* "id" is either a placeholder or real ID that is already in the | ||||
| * This line is NEEDED, the case is that you have 3 blend files... | * main of the library (A) it belongs to. However it might have been | ||||
| * user.blend, lib.blend and lib_indirect.blend - if user.blend already references a "tree" from | * put there by another library (C) which only updated its own | ||||
| * lib_indirect.blend but lib.blend does too, linking in a Scene or Group from lib.blend can result in an | * fd->libmap. In that case we also need to update the fd->libmap | ||||
| * empty without the dupli group referenced. Once you save and reload the group would appear. - Campbell */ | * of the current library (B) so we can find it for lookups. | ||||
| /* 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 | * An example of such a setup is: | ||||
| * change_idid_adr not detect the mapping was for an ID_ID datablock. */ | * (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); | oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); | ||||
| change_idid_adr_fd(fd, bhead->old, id); | |||||
| /* 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 | // 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); | // if (G.debug & G_DEBUG) printf("expand_doit: already linked: %s lib: %s\n", id->name, lib->name); | ||||
| } | } | ||||
| MEM_freeN(lib); | MEM_freeN(lib); | ||||
| } | } | ||||
| } | |||||
| else { | else { | ||||
| /* in 2.50+ file identifier for screens is patched, forward compatibility */ | /* Datablock in same library. */ | ||||
| /* In 2.50+ file identifier for screens is patched, forward compatibility. */ | |||||
| if (bhead->code == ID_SCRN) { | if (bhead->code == ID_SCRN) { | ||||
| bhead->code = ID_SCR; | bhead->code = ID_SCR; | ||||
| } | } | ||||
| id = is_yet_read(fd, mainvar, bhead); | ID *id = is_yet_read(fd, mainvar, bhead); | ||||
| if (id == NULL) { | if (id == NULL) { | ||||
| read_libblock(fd, mainvar, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, NULL); | read_libblock(fd, mainvar, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, NULL); | ||||
| } | } | ||||
| else { | else { | ||||
| /* this is actually only needed on UI call? when ID was already read before, and another append | /* 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 */ | * happens which invokes same ID... in that case the lookup table needs this entry */ | ||||
| oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); | oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code); | ||||
| // commented because this can print way too much | // commented because this can print way too much | ||||
| // if (G.debug & G_DEBUG) printf("expand: already read %s\n", id->name); | // if (G.debug & G_DEBUG) printf("expand: already read %s\n", id->name); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| static BLOExpandDoitCallback expand_doit; | static BLOExpandDoitCallback expand_doit; | ||||
| // XXX deprecated - old animation system | // XXX deprecated - old animation system | ||||
| static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo) | static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo) | ||||
| { | { | ||||
| IpoCurve *icu; | IpoCurve *icu; | ||||
| for (icu = ipo->curve.first; icu; icu = icu->next) { | for (icu = ipo->curve.first; icu; icu = icu->next) { | ||||
| ▲ Show 20 Lines • Show All 1,249 Lines • ▼ Show 20 Lines | |||||
| ID *BLO_library_link_named_part_ex( | ID *BLO_library_link_named_part_ex( | ||||
| Main *mainl, BlendHandle **bh, | Main *mainl, BlendHandle **bh, | ||||
| const short idcode, const char *name, const int flag) | const short idcode, const char *name, const int flag) | ||||
| { | { | ||||
| FileData *fd = (FileData *)(*bh); | FileData *fd = (FileData *)(*bh); | ||||
| return link_named_part_ex(mainl, fd, idcode, name, flag); | 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 */ | /* common routine to append/link something from a library */ | ||||
| static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath) | static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath) | ||||
| { | { | ||||
| Main *mainl; | Main *mainl; | ||||
| (*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist"); | (*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist"); | ||||
| ▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Library Reading | /** \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]; | ListBase *lbarray[MAX_LIBARRAY]; | ||||
| int a; | int a = set_listbasepointers(mainvar, lbarray); | ||||
| a = set_listbasepointers(mainvar, lbarray); | |||||
| while (a--) { | while (a--) { | ||||
| ID *id; | for (ID *id = lbarray[a]->first; id; id = id->next) { | ||||
| if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) { | |||||
| for (id = lbarray[a]->first; id; id = id->next) { | |||||
| if (id->tag & tag) { | |||||
| return true; | return true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| return false; | 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__); | GHash *loaded_ids = BLI_ghash_str_new(__func__); | ||||
| int a; | |||||
| bool do_it = true; | |||||
| /* expander now is callback function */ | ListBase *lbarray[MAX_LIBARRAY]; | ||||
| BLO_main_expander(expand_doit_library); | int a = set_listbasepointers(mainvar, lbarray); | ||||
| while (do_it) { | while (a--) { | ||||
| do_it = false; | ID *id = lbarray[a]->first; | ||||
| ListBase pending_free_ids = {NULL}; | |||||
| /* test 1: read libdata */ | while (id) { | ||||
| mainptr = mainl->next; | ID *id_next = id->next; | ||||
| while (mainptr) { | if (id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) { | ||||
| if (mainvar_id_tag_any_check(mainptr, LIB_TAG_ID_ID)) { | BLI_remlink(lbarray[a], 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); */ | |||||
| /* 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; | FileData *fd = mainptr->curlib->filedata; | ||||
| if (fd == NULL) { | if (fd != NULL) { | ||||
| /* printf and reports for now... its important users know this */ | /* File already open. */ | ||||
| return fd; | |||||
| } | |||||
| /* if packed file... */ | |||||
| if (mainptr->curlib->packedfile) { | if (mainptr->curlib->packedfile) { | ||||
| /* Read packed file. */ | |||||
| PackedFile *pf = mainptr->curlib->packedfile; | PackedFile *pf = mainptr->curlib->packedfile; | ||||
| blo_reportf_wrap( | blo_reportf_wrap( | ||||
| basefd->reports, RPT_INFO, TIP_("Read packed library: '%s', parent '%s'"), | basefd->reports, RPT_INFO, TIP_("Read packed library: '%s', parent '%s'"), | ||||
| mainptr->curlib->name, | mainptr->curlib->name, | ||||
| library_parent_filepath(mainptr->curlib)); | library_parent_filepath(mainptr->curlib)); | ||||
| fd = blo_filedata_from_memory(pf->data, pf->size, basefd->reports); | fd = blo_filedata_from_memory(pf->data, pf->size, basefd->reports); | ||||
| /* Needed for library_append and read_libraries. */ | |||||
| /* needed for library_append and read_libraries */ | |||||
| BLI_strncpy(fd->relabase, mainptr->curlib->filepath, sizeof(fd->relabase)); | BLI_strncpy(fd->relabase, mainptr->curlib->filepath, sizeof(fd->relabase)); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Read file on disk. */ | |||||
| blo_reportf_wrap( | blo_reportf_wrap( | ||||
| basefd->reports, RPT_INFO, TIP_("Read library: '%s', '%s', parent '%s'"), | basefd->reports, RPT_INFO, TIP_("Read library: '%s', '%s', parent '%s'"), | ||||
| mainptr->curlib->filepath, | mainptr->curlib->filepath, | ||||
| mainptr->curlib->name, | mainptr->curlib->name, | ||||
| library_parent_filepath(mainptr->curlib)); | library_parent_filepath(mainptr->curlib)); | ||||
| fd = blo_filedata_from_file(mainptr->curlib->filepath, basefd->reports); | fd = blo_filedata_from_file(mainptr->curlib->filepath, basefd->reports); | ||||
| } | } | ||||
| if (fd) { | if (fd) { | ||||
| /* share the mainlist, so all libraries are added immediately in a | /* 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, | * 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 | * but with indirectly linking this meant we didn't catch duplicate | ||||
| * libraries properly */ | * libraries properly. */ | ||||
| fd->mainlist = mainlist; | fd->mainlist = mainlist; | ||||
| fd->reports = basefd->reports; | fd->reports = basefd->reports; | ||||
| if (fd->libmap) | if (fd->libmap) | ||||
| oldnewmap_free(fd->libmap); | oldnewmap_free(fd->libmap); | ||||
| fd->libmap = oldnewmap_new(); | fd->libmap = oldnewmap_new(); | ||||
| mainptr->curlib->filedata = fd; | mainptr->curlib->filedata = fd; | ||||
| mainptr->versionfile = fd->fileversion; | mainptr->versionfile = fd->fileversion; | ||||
| /* subversion */ | /* subversion */ | ||||
| read_file_version(fd, mainptr); | read_file_version(fd, mainptr); | ||||
| #ifdef USE_GHASH_BHEAD | #ifdef USE_GHASH_BHEAD | ||||
| read_file_bhead_idname_map_create(fd); | read_file_bhead_idname_map_create(fd); | ||||
| #endif | #endif | ||||
| } | } | ||||
| else { | else { | ||||
| mainptr->curlib->filedata = NULL; | mainptr->curlib->filedata = NULL; | ||||
| mainptr->curlib->id.tag |= LIB_TAG_MISSING; | mainptr->curlib->id.tag |= LIB_TAG_MISSING; | ||||
| /* Set lib version to current main one... Makes assert later happy. */ | /* Set lib version to current main one... Makes assert later happy. */ | ||||
| mainptr->versionfile = mainptr->curlib->versionfile = mainl->versionfile; | mainptr->versionfile = mainptr->curlib->versionfile = mainl->versionfile; | ||||
| mainptr->subversionfile = mainptr->curlib->subversionfile = mainl->subversionfile; | mainptr->subversionfile = mainptr->curlib->subversionfile = mainl->subversionfile; | ||||
| } | } | ||||
| if (fd == NULL) { | if (fd == NULL) { | ||||
| blo_reportf_wrap(basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"), | blo_reportf_wrap(basefd->reports, RPT_WARNING, TIP_("Cannot find lib '%s'"), | ||||
| mainptr->curlib->filepath); | mainptr->curlib->filepath); | ||||
| } | } | ||||
| return fd; | |||||
| } | } | ||||
| if (fd) { | |||||
| do_it = true; | |||||
| } | |||||
| a = set_listbasepointers(mainptr, lbarray); | |||||
| while (a--) { | |||||
| ID *id = lbarray[a]->first; | |||||
| ListBase pending_free_ids = {NULL}; | |||||
| while (id) { | static void read_libraries(FileData *basefd, ListBase *mainlist) | ||||
| ID *idn = id->next; | { | ||||
| if (id->tag & LIB_TAG_ID_ID) { | Main *mainl = mainlist->first; | ||||
| BLI_remlink(lbarray[a], id); | bool do_it = true; | ||||
| /* When playing with lib renaming and such, you may end with cases where you have | /* Expander is now callback function. */ | ||||
| * more than one linked ID of the same data-block from same library. | BLO_main_expander(expand_doit_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 | /* At this point the base blend file has been read, and each library blend | ||||
| * (known case: some directly linked shapekey from a missing lib...). */ | * encountered so far has a main with placeholders for linked datablocks. | ||||
| /* BLI_assert(*realid != NULL); */ | * | ||||
| * 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; | |||||
| change_idid_adr(mainlist, basefd, id, *realid); | /* 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); | |||||
| /* We cannot free old lib-ref placeholder ID here anymore, since we use its name | /* Open file if it has not been done yet. */ | ||||
| * as key in loaded_ids has. */ | FileData *fd = read_library_file_data(basefd, mainlist, mainl, mainptr); | ||||
| BLI_addtail(&pending_free_ids, id); | |||||
| } | |||||
| id = idn; | |||||
| } | |||||
| /* Clear GHash and free all lib-ref placeholders IDs of that type now. */ | if (fd) { | ||||
| BLI_ghash_clear(loaded_ids, NULL, NULL); | do_it = true; | ||||
| BLI_freelistN(&pending_free_ids); | |||||
| } | } | ||||
| /* 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); | 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(); | Main *main_newid = BKE_main_new(); | ||||
| for (mainptr = mainl->next; mainptr; mainptr = mainptr->next) { | for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) { | ||||
| /* some mains still have to be read, then versionfile is still zero! */ | /* 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) { | 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); | split_main_newid(mainptr, main_newid); | ||||
| if (mainptr->curlib->filedata) // can be zero... with shift+f1 append | /* File data can be zero with shift+f1 append. */ | ||||
mont29Unsubmitted Not Done Inline ActionsShift-F1 does not exist anymore by default, link/append have no more shortcuts... mont29: Shift-F1 does not exist anymore by default, link/append have no more shortcuts... | |||||
| if (mainptr->curlib->filedata) | |||||
| do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid); | do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid); | ||||
| else | else | ||||
| do_versions(basefd, NULL, main_newid); | do_versions(basefd, NULL, main_newid); | ||||
| add_main_to_main(mainptr, main_newid); | add_main_to_main(mainptr, main_newid); | ||||
| } | } | ||||
| /* Lib linking. */ | |||||
| if (mainptr->curlib->filedata) | if (mainptr->curlib->filedata) | ||||
| lib_link_all(mainptr->curlib->filedata, mainptr); | 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; | mainptr->curlib->filedata = NULL; | ||||
| } | } | ||||
| BKE_main_free(main_newid); | BKE_main_free(main_newid); | ||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
That’s kind of doublon/extending previous comment (the one just after the case: ), they could both be merged?