diff options
Diffstat (limited to 'source/blender/blenloader/intern')
-rw-r--r-- | source/blender/blenloader/intern/readblenentry.c | 129 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 983 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.h | 5 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_260.c | 4 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_270.c | 20 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_defaults.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/versioning_legacy.c | 2 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 77 |
8 files changed, 840 insertions, 382 deletions
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 20ec27a1f4b..cd6df354ca7 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -72,6 +72,13 @@ void BLO_blendhandle_print_sizes(BlendHandle *, void *); /* Access routines used by filesel. */ +/** + * Open a blendhandle from a file path. + * + * \param filepath The file path to open. + * \param reports Report errors in opening the file (can be NULL). + * \return A handle on success, or NULL on failure. + */ BlendHandle *BLO_blendhandle_from_file(const char *filepath, ReportList *reports) { BlendHandle *bh; @@ -81,6 +88,13 @@ BlendHandle *BLO_blendhandle_from_file(const char *filepath, ReportList *reports return bh; } +/** + * Open a blendhandle from memory. + * + * \param mem The data to load from. + * \param memsize The size of the data. + * \return A handle on success, or NULL on failure. + */ BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize) { BlendHandle *bh; @@ -120,6 +134,14 @@ void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp) fprintf(fp, "]\n"); } +/** + * Gets the names of all the datablocks in a file of a certain type (e.g. all the scene names in a file). + * + * \param bh The blendhandle to access. + * \param ofblocktype The type of names to get. + * \param tot_names The length of the returned list. + * \return A BLI_linklist of strings. The string links should be freed with malloc. + */ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, int *tot_names) { FileData *fd = (FileData *) bh; @@ -142,6 +164,14 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, return names; } +/** + * Gets the previews of all the datablocks in a file of a certain type (e.g. all the scene previews in a file). + * + * \param bh The blendhandle to access. + * \param ofblocktype The type of names to get. + * \param tot_prev The length of the returned list. + * \return A BLI_linklist of PreviewImage. The PreviewImage links should be freed with malloc. + */ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *tot_prev) { FileData *fd = (FileData *) bh; @@ -161,6 +191,9 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to case ID_IM: /* fall through */ case ID_WO: /* fall through */ case ID_LA: /* fall through */ + case ID_OB: /* fall through */ + case ID_GR: /* fall through */ + case ID_SCE: /* fall through */ new_prv = MEM_callocN(sizeof(PreviewImage), "newpreview"); BLI_linklist_prepend(&previews, new_prv); tot++; @@ -229,7 +262,13 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to return previews; } -LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh) +/** + * Gets the names of all the linkable datablock types available in a file. (e.g. "Scene", "Mesh", "Lamp", etc.). + * + * \param bh The blendhandle to access. + * \return A BLI_linklist of strings. The string links should be freed with malloc. + */ +LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh) { FileData *fd = (FileData *) bh; GSet *gathered = BLI_gset_ptr_new("linkable_groups gh"); @@ -257,6 +296,11 @@ LinkNode *BLO_blendhandle_get_linkable_groups(BlendHandle *bh) return names; } +/** + * Close and free a blendhandle. The handle becomes invalid after this call. + * + * \param bh The handle to close. + */ void BLO_blendhandle_close(BlendHandle *bh) { FileData *fd = (FileData *) bh; @@ -266,6 +310,14 @@ void BLO_blendhandle_close(BlendHandle *bh) /**********/ +/** + * Open a blender file from a pathname. The function returns NULL + * and sets a report in the list if it cannot open the file. + * + * \param filepath The path of the file to open. + * \param reports If the return value is NULL, errors indicating the cause of the failure. + * \return The data of the file. + */ BlendFileData *BLO_read_from_file(const char *filepath, ReportList *reports) { BlendFileData *bfd = NULL; @@ -281,6 +333,15 @@ BlendFileData *BLO_read_from_file(const char *filepath, ReportList *reports) return bfd; } +/** + * Open a blender file from memory. The function returns NULL + * and sets a report in the list if it cannot open the file. + * + * \param mem The file data. + * \param memsize The length of \a mem. + * \param reports If the return value is NULL, errors indicating the cause of the failure. + * \return The data of the file. + */ BlendFileData *BLO_read_from_memory(const void *mem, int memsize, ReportList *reports) { BlendFileData *bfd = NULL; @@ -296,11 +357,17 @@ BlendFileData *BLO_read_from_memory(const void *mem, int memsize, ReportList *re return bfd; } +/** + * Used for undo/redo, skips part of libraries reading (assuming their data are already loaded & valid). + * + * \param oldmain old main, from which we will keep libraries and other datablocks that should not have changed. + * \param filename current file, only for retrieving library data. + */ BlendFileData *BLO_read_from_memfile(Main *oldmain, const char *filename, MemFile *memfile, ReportList *reports) { BlendFileData *bfd = NULL; FileData *fd; - ListBase mainlist; + ListBase old_mainlist; fd = blo_openblendermemfile(memfile, reports); if (fd) { @@ -311,9 +378,9 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, const char *filename, MemFil blo_clear_proxy_pointers_from_lib(oldmain); /* separate libraries from old main */ - blo_split_main(&mainlist, oldmain); + blo_split_main(&old_mainlist, oldmain); /* add the library pointers in oldmap lookup */ - blo_add_library_pointer_map(&mainlist, fd); + blo_add_library_pointer_map(&old_mainlist, fd); /* makes lookup of existing images in old main */ blo_make_image_pointer_map(fd, oldmain); @@ -337,25 +404,55 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain, const char *filename, MemFil /* ensures relinked sounds are not freed */ blo_end_sound_pointer_map(fd, oldmain); - /* move libraries from old main to new main */ - if (bfd && mainlist.first != mainlist.last) { - - /* Library structs themselves */ - bfd->main->library = oldmain->library; - BLI_listbase_clear(&oldmain->library); - - /* add the Library mainlist to the new main */ - BLI_remlink(&mainlist, oldmain); - BLI_addhead(&mainlist, bfd->main); + /* Still in-use libraries have already been moved from oldmain to new mainlist, + * but oldmain itself shall *never* be 'transferred' to new mainlist! */ + BLI_assert(old_mainlist.first == oldmain); + + if (bfd && old_mainlist.first != old_mainlist.last) { + /* Even though directly used libs have been already moved to new main, indirect ones have not. + * This is a bit annoying, but we have no choice but to keep them all for now - means some now unused + * data may remain in memory, but think we'll have to live with it. */ + Main *libmain; + Main *newmain = bfd->main; + ListBase new_mainlist = {newmain, newmain}; + + for (libmain = oldmain->next; libmain; libmain = libmain->next) { + /* Note that LIB_INDIRECT does not work with libraries themselves, so we use non-NULL parent + * to detect indirect-linked ones... */ + if (libmain->curlib && (libmain->curlib->parent != NULL)) { + BLI_remlink(&old_mainlist, libmain); + BLI_addtail(&new_mainlist, libmain); + } +#if 0 + else { + printf("Dropped Main for lib: %s\n", libmain->curlib->id.name); + } +#endif + } + /* In any case, we need to move all lib datablocks themselves - those are 'first level data', + * getting rid of them would imply updating spaces & co to prevent invalid pointers access. */ + BLI_movelisttolist(&newmain->library, &oldmain->library); + + blo_join_main(&new_mainlist); } - blo_join_main(&mainlist); - + + /* printf("Remaining mains/libs in oldmain: %d\n", BLI_listbase_count(&fd->old_mainlist) - 1); */ + + /* That way, libs (aka mains) we did not reuse in new undone/redone state + * will be cleared together with oldmain... */ + blo_join_main(&old_mainlist); + blo_freefiledata(fd); } return bfd; } +/** + * Frees a BlendFileData structure and *all* the data associated with it (the userdef data, and the main libblock data). + * + * \param bfd The structure to free. + */ void BLO_blendfiledata_free(BlendFileData *bfd) { if (bfd->main) { diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index cc84a899856..6545a3b0e67 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -109,7 +109,7 @@ #include "BLI_threads.h" #include "BLI_mempool.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -124,6 +124,7 @@ #include "BKE_global.h" // for G #include "BKE_group.h" #include "BKE_library.h" // for which_libbase +#include "BKE_library_query.h" #include "BKE_idcode.h" #include "BKE_material.h" #include "BKE_main.h" // for Main @@ -313,6 +314,56 @@ void blo_do_versions_oldnewmap_insert(OldNewMap *onm, void *oldaddr, void *newad oldnewmap_insert(onm, oldaddr, newaddr, nr); } +/** + * Do a full search (no state). + * + * \param lasthit: Use as a reference position to avoid a full search + * from either end of the array, giving more efficient lookups. + * + * \note This would seem an ideal case for hash or btree lookups. + * However the data is written in-order, using the \a lasthit will normally avoid calling this function. + * Creating a btree/hash structure adds overhead for the common-case to optimize the corner-case + * (since most entries will never be retrieved). + * So just keep full lookups as a fall-back. + */ +static int oldnewmap_lookup_entry_full(const OldNewMap *onm, const void *addr, int lasthit) +{ + const int nentries = onm->nentries; + const OldNew *entries = onm->entries; + int i; + + /* search relative to lasthit where possible */ + if (lasthit >= 0 && lasthit < nentries) { + + /* search forwards */ + i = lasthit; + while (++i != nentries) { + if (entries[i].old == addr) { + return i; + } + } + + /* search backwards */ + i = lasthit + 1; + while (i--) { + if (entries[i].old == addr) { + return i; + } + } + } + else { + /* search backwards (full) */ + i = nentries; + while (i--) { + if (entries[i].old == addr) { + return i; + } + } + } + + return -1; +} + static void *oldnewmap_lookup_and_inc(OldNewMap *onm, void *addr, bool increase_users) { int i; @@ -329,16 +380,14 @@ static void *oldnewmap_lookup_and_inc(OldNewMap *onm, void *addr, bool increase_ } } - for (i = 0; i < onm->nentries; i++) { + i = oldnewmap_lookup_entry_full(onm, addr, onm->lasthit); + if (i != -1) { OldNew *entry = &onm->entries[i]; - - if (entry->old == addr) { - onm->lasthit = i; - - if (increase_users) - entry->nr++; - return entry->newp; - } + BLI_assert(entry->old == addr); + onm->lasthit = i; + if (increase_users) + entry->nr++; + return entry->newp; } return NULL; @@ -368,16 +417,13 @@ static void *oldnewmap_liblookup(OldNewMap *onm, void *addr, void *lib) } else { /* note, this can be a bottle neck when loading some files */ - unsigned int nentries = (unsigned int)onm->nentries; - unsigned int i; - OldNew *entry; - - for (i = 0, entry = onm->entries; i < nentries; i++, entry++) { - if (entry->old == addr) { - ID *id = entry->newp; - if (id && (!lib || id->lib)) { - return id; - } + const int i = oldnewmap_lookup_entry_full(onm, addr, -1); + if (i != -1) { + OldNew *entry = &onm->entries[i]; + ID *id = entry->newp; + BLI_assert(entry->old == addr); + if (id && (!lib || id->lib)) { + return id; } } } @@ -573,7 +619,9 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab m = BKE_main_new(); BLI_addtail(mainlist, m); - lib = BKE_libblock_alloc(m, ID_LI, "lib"); + /* Add library datablock itself to 'main' Main, since libraries are **never** linked data. + * Fixes bug where you could end with all ID_LI datablocks having the same name... */ + lib = BKE_libblock_alloc(mainlist->first, ID_LI, "Lib"); BLI_strncpy(lib->name, filepath, sizeof(lib->name)); BLI_strncpy(lib->filepath, name1, sizeof(lib->filepath)); @@ -806,6 +854,12 @@ BHead *blo_nextbhead(FileData *fd, BHead *thisblock) return(bhead); } +/* Warning! Caller's responsability to ensure given bhead **is** and ID one! */ +const char *bhead_id_name(const FileData *fd, const BHead *bhead) +{ + return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs); +} + static void decode_blender_header(FileData *fd) { char header[SIZEOFBLENDERHEADER], num[4]; @@ -872,6 +926,41 @@ static int read_file_dna(FileData *fd) return 0; } +static int *read_file_thumbnail(FileData *fd) +{ + BHead *bhead; + int *blend_thumb = NULL; + + for (bhead = blo_firstbhead(fd); bhead; bhead = blo_nextbhead(fd, bhead)) { + if (bhead->code == TEST) { + const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0; + int *data = (int *)(bhead + 1); + + if (bhead->len < (2 * sizeof(int))) { + break; + } + + if (do_endian_swap) { + BLI_endian_switch_int32(&data[0]); + BLI_endian_switch_int32(&data[1]); + } + + if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(data[0], data[1])) { + break; + } + + blend_thumb = data; + break; + } + else if (bhead->code != REND) { + /* Thumbnail is stored in TEST immediately after first REND... */ + break; + } + } + + return blend_thumb; +} + static int fd_read_from_file(FileData *filedata, void *buffer, unsigned int size) { int readsize = read(filedata->filedes, buffer, size); @@ -1035,6 +1124,33 @@ FileData *blo_openblenderfile(const char *filepath, ReportList *reports) } } +/** + * Same as blo_openblenderfile(), but does not reads DNA data, only header. Use it for light access + * (e.g. thumbnail reading). + */ +static FileData *blo_openblenderfile_minimal(const char *filepath) +{ + gzFile gzfile; + errno = 0; + gzfile = BLI_gzopen(filepath, "rb"); + + if (gzfile != (gzFile)Z_NULL) { + FileData *fd = filedata_new(); + fd->gzfiledes = gzfile; + fd->read = fd_read_gzip_from_file; + + decode_blender_header(fd); + + if (fd->flags & FD_FLAGS_FILE_OK) { + return fd; + } + + blo_freefiledata(fd); + } + + return NULL; +} + static int fd_read_gzip_from_memory(FileData *filedata, void *buffer, unsigned int size) { int err; @@ -1183,53 +1299,115 @@ void blo_freefiledata(FileData *fd) /* ************ DIV ****************** */ +/** + * Check whether given path ends with a blend file compatible extension (.blend, .ble or .blend.gz). + * + * \param str The path to check. + * \return true is this path ends with a blender file extension. + */ bool BLO_has_bfile_extension(const char *str) { const char *ext_test[4] = {".blend", ".ble", ".blend.gz", NULL}; return BLI_testextensie_array(str, ext_test); } -bool BLO_is_a_library(const char *path, char *dir, char *group) +/** + * Try to explode given path into its 'library components' (i.e. a .blend file, id type/group, and datablock itself). + * + * \param path the full path to explode. + * \param r_dir the string that'll contain path up to blend file itself ('library' path). + * \param r_group the string that'll contain 'group' part of the path, if any. May be NULL. + * \param r_name the string that'll contain data's name part of the path, if any. May be NULL. + * \return true if path contains a blend file. + */ +bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name) { - /* return ok when a blenderfile, in dir is the filename, - * in group the type of libdata - */ - int len; - char *fd; - - /* if path leads to a directory we can be sure we're not in a library */ - if (BLI_is_dir(path)) return 0; + /* We might get some data names with slashes, so we have to go up in path until we find blend file itself, + * then we now next path item is group, and everything else is data name. */ + char *slash = NULL, *prev_slash = NULL, c = '\0'; - strcpy(dir, path); - len = strlen(dir); - if (len < 7) return 0; - if ((dir[len - 1] != '/') && (dir[len - 1] != '\\')) return 0; - - group[0] = '\0'; - dir[len - 1] = '\0'; + r_dir[0] = '\0'; + if (r_group) { + *r_group = NULL; + } + if (r_name) { + *r_name = NULL; + } + + /* if path leads to an existing directory, we can be sure we're not (in) a library */ + if (BLI_is_dir(path)) { + return false; + } - /* Find the last slash */ - fd = (char *)BLI_last_slash(dir); + strcpy(r_dir, path); + + while ((slash = (char *)BLI_last_slash(r_dir))) { + char tc = *slash; + *slash = '\0'; + if (BLO_has_bfile_extension(r_dir)) { + break; + } - if (fd == NULL) return 0; - *fd = 0; - if (BLO_has_bfile_extension(fd+1)) { - /* the last part of the dir is a .blend file, no group follows */ - *fd = '/'; /* put back the removed slash separating the dir and the .blend file name */ + if (prev_slash) { + *prev_slash = c; + } + prev_slash = slash; + c = tc; + } + + if (!slash) { + return false; + } + + if (slash[1] != '\0') { + BLI_assert(strlen(slash + 1) < BLO_GROUP_MAX); + if (r_group) { + *r_group = slash + 1; + } + } + + if (prev_slash && (prev_slash[1] != '\0')) { + BLI_assert(strlen(prev_slash + 1) < MAX_ID_NAME - 2); + if (r_name) { + *r_name = prev_slash + 1; + } + } + + return true; +} + +/** + * Does a very light reading of given .blend file to extract its stored thumbnail. + * + * \param filepath The path of the file to extract thumbnail from. + * \return The raw thumbnail + * (MEM-allocated, as stored in file, use BKE_main_thumbnail_to_imbuf() to convert it to ImBuf image). + */ +BlendThumbnail *BLO_thumbnail_from_file(const char *filepath) +{ + FileData *fd; + BlendThumbnail *data; + int *fd_data; + + fd = blo_openblenderfile_minimal(filepath); + fd_data = fd ? read_file_thumbnail(fd) : NULL; + + if (fd_data) { + const size_t sz = BLEN_THUMB_MEMSIZE(fd_data[0], fd_data[1]); + data = MEM_mallocN(sz, __func__); + + BLI_assert((sz - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(fd_data[0], fd_data[1]) - (sizeof(*fd_data) * 2))); + data->width = fd_data[0]; + data->height = fd_data[1]; + memcpy(data->rect, &fd_data[2], sz - sizeof(*data)); } else { - const char * const gp = fd + 1; // in case we have a .blend file, gp points to the group - - /* Find the last slash */ - fd = (char *)BLI_last_slash(dir); - if (!fd || !BLO_has_bfile_extension(fd+1)) return 0; - - /* now we know that we are in a blend file and it is safe to - * assume that gp actually points to a group */ - if (!STREQ("Screen", gp)) - BLI_strncpy(group, gp, BLO_GROUP_MAX); + data = NULL; } - return 1; + + blo_freefiledata(fd); + + return data; } /* ************** OLD POINTERS ******************* */ @@ -1239,6 +1417,31 @@ static void *newdataadr(FileData *fd, void *adr) /* only direct databocks */ return oldnewmap_lookup_and_inc(fd->datamap, adr, true); } +/* This is a special version of newdataadr() which allows us to keep lasthit of + * map unchanged. In certain cases this makes file loading time significantly + * faster. + * + * Use this function in cases like restoring pointer from one list element to + * another list element, but keep lasthit value so we can continue restoring + * pointers efficiently. + * + * Example of this could be found in direct_link_fcurves() which restores the + * fcurve group pointer and keeps lasthit optimal for linking all further + * fcurves. + */ +static void *newdataadr_ex(FileData *fd, void *adr, bool increase_lasthit) /* only direct databocks */ +{ + if (increase_lasthit) { + return newdataadr(fd, adr); + } + else { + int lasthit = fd->datamap->lasthit; + void *newadr = newdataadr(fd, adr); + fd->datamap->lasthit = lasthit; + return newadr; + } +} + static void *newdataadr_no_us(FileData *fd, void *adr) /* only direct databocks */ { return oldnewmap_lookup_and_inc(fd->datamap, adr, false); @@ -1597,9 +1800,9 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) /* undo file support: add all library pointers in lookup */ -void blo_add_library_pointer_map(ListBase *mainlist, FileData *fd) +void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) { - Main *ptr = mainlist->first; + Main *ptr = old_mainlist->first; ListBase *lbarray[MAX_LIBARRAY]; for (ptr = ptr->next; ptr; ptr = ptr->next) { @@ -1610,6 +1813,8 @@ void blo_add_library_pointer_map(ListBase *mainlist, FileData *fd) oldnewmap_insert(fd->libmap, id, id, GS(id->name)); } } + + fd->old_mainlist = old_mainlist; } @@ -1655,17 +1860,25 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname) return temp; } -static void link_list(FileData *fd, ListBase *lb) /* only direct data */ +typedef void (*link_list_cb)(FileData *fd, void *data); + +static void link_list_ex(FileData *fd, ListBase *lb, link_list_cb callback) /* only direct data */ { Link *ln, *prev; if (BLI_listbase_is_empty(lb)) return; lb->first = newdataadr(fd, lb->first); + if (callback != NULL) { + callback(fd, lb->first); + } ln = lb->first; prev = NULL; while (ln) { ln->next = newdataadr(fd, ln->next); + if (ln->next != NULL && callback != NULL) { + callback(fd, ln->next); + } ln->prev = prev; prev = ln; ln = ln->next; @@ -1673,6 +1886,11 @@ static void link_list(FileData *fd, ListBase *lb) /* only direct data */ lb->last = prev; } +static void link_list(FileData *fd, ListBase *lb) /* only direct data */ +{ + link_list_ex(fd, lb, NULL); +} + static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */ { Link *ln, *prev; @@ -1882,6 +2100,25 @@ static void IDP_LibLinkProperty(IDProperty *UNUSED(prop), int UNUSED(switch_endi { } +/* ************ READ IMAGE PREVIEW *************** */ + +static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_prv) +{ + PreviewImage *prv = newdataadr(fd, old_prv); + + if (prv) { + int i; + for (i = 0; i < NUM_ICON_SIZES; ++i) { + if (prv->rect[i]) { + prv->rect[i] = newdataadr(fd, prv->rect[i]); + } + prv->gputexture[i] = NULL; + } + } + + return prv; +} + /* ************ READ ID *************** */ static void direct_link_id(FileData *fd, ID *id) @@ -2004,25 +2241,6 @@ static PackedFile *direct_link_packedfile(FileData *fd, PackedFile *oldpf) return pf; } -/* ************ READ IMAGE PREVIEW *************** */ - -static PreviewImage *direct_link_preview_image(FileData *fd, PreviewImage *old_prv) -{ - PreviewImage *prv = newdataadr(fd, old_prv); - - if (prv) { - int i; - for (i = 0; i < NUM_ICON_SIZES; ++i) { - if (prv->rect[i]) { - prv->rect[i] = newdataadr(fd, prv->rect[i]); - } - prv->gputexture[i] = NULL; - } - } - - return prv; -} - /* ************ READ ANIMATION STUFF ***************** */ /* Legacy Data Support (for Version Patching) ----------------------------- */ @@ -2206,7 +2424,7 @@ static void direct_link_fcurves(FileData *fd, ListBase *list) fcu->rna_path = newdataadr(fd, fcu->rna_path); /* group */ - fcu->grp = newdataadr(fd, fcu->grp); + fcu->grp = newdataadr_ex(fd, fcu->grp, false); /* clear disabled flag - allows disabled drivers to be tried again ([#32155]), * but also means that another method for "reviving disabled F-Curves" exists @@ -3706,35 +3924,34 @@ static const char *ptcache_data_struct[] = { "", // BPHYS_DATA_TIMES: "BoidData" // case BPHYS_DATA_BOIDS: }; + +static void direct_link_pointcache_cb(FileData *fd, void *data) +{ + PTCacheMem *pm = data; + PTCacheExtra *extra; + int i; + for (i = 0; i < BPHYS_TOT_DATA; i++) { + pm->data[i] = newdataadr(fd, pm->data[i]); + + /* the cache saves non-struct data without DNA */ + if (pm->data[i] && ptcache_data_struct[i][0]=='\0' && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) { + int tot = (BKE_ptcache_data_size (i) * pm->totpoint) / sizeof(int); /* data_size returns bytes */ + int *poin = pm->data[i]; + + BLI_endian_switch_int32_array(poin, tot); + } + } + + link_list(fd, &pm->extradata); + + for (extra=pm->extradata.first; extra; extra=extra->next) + extra->data = newdataadr(fd, extra->data); +} + static void direct_link_pointcache(FileData *fd, PointCache *cache) { if ((cache->flag & PTCACHE_DISK_CACHE)==0) { - PTCacheMem *pm; - PTCacheExtra *extra; - int i; - - link_list(fd, &cache->mem_cache); - - pm = cache->mem_cache.first; - - for (; pm; pm=pm->next) { - for (i=0; i<BPHYS_TOT_DATA; i++) { - pm->data[i] = newdataadr(fd, pm->data[i]); - - /* the cache saves non-struct data without DNA */ - if (pm->data[i] && ptcache_data_struct[i][0]=='\0' && (fd->flags & FD_FLAGS_SWITCH_ENDIAN)) { - int tot = (BKE_ptcache_data_size (i) * pm->totpoint) / sizeof(int); /* data_size returns bytes */ - int *poin = pm->data[i]; - - BLI_endian_switch_int32_array(poin, tot); - } - } - - link_list(fd, &pm->extradata); - - for (extra=pm->extradata.first; extra; extra=extra->next) - extra->data = newdataadr(fd, extra->data); - } + link_list_ex(fd, &cache->mem_cache, direct_link_pointcache_cb); } else BLI_listbase_clear(&cache->mem_cache); @@ -3944,7 +4161,7 @@ static void lib_link_particlesystems(FileData *fd, Object *ob, ID *id, ListBase if (psys->clmd) { /* XXX - from reading existing code this seems correct but intended usage of - * pointcache should /w cloth should be added in 'ParticleSystem' - campbell */ + * pointcache /w cloth should be added in 'ParticleSystem' - campbell */ psys->clmd->point_cache = psys->pointcache; psys->clmd->ptcaches.first = psys->clmd->ptcaches.last= NULL; psys->clmd->coll_parms->group = newlibadr(fd, id->lib, psys->clmd->coll_parms->group); @@ -4012,8 +4229,6 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) psys->pdd = NULL; psys->renderdata = NULL; - direct_link_pointcache_list(fd, &psys->ptcaches, &psys->pointcache, 0); - if (psys->clmd) { psys->clmd = newdataadr(fd, psys->clmd); psys->clmd->clothObject = NULL; @@ -4030,10 +4245,13 @@ static void direct_link_particlesystems(FileData *fd, ListBase *particles) psys->hair_in_dm = psys->hair_out_dm = NULL; psys->clmd->solver_result = NULL; - + } + + direct_link_pointcache_list(fd, &psys->ptcaches, &psys->pointcache, 0); + if (psys->clmd) { psys->clmd->point_cache = psys->pointcache; } - + psys->tree = NULL; psys->bvhtree = NULL; } @@ -4402,15 +4620,15 @@ static void direct_link_latt(FileData *fd, Lattice *lt) /* ************ READ OBJECT ***************** */ -static void lib_link_modifiers__linkModifiers(void *userData, Object *ob, - ID **idpoin) +static void lib_link_modifiers__linkModifiers( + void *userData, Object *ob, ID **idpoin, int cd_flag) { FileData *fd = userData; *idpoin = newlibadr(fd, ob->id.lib, *idpoin); - /* hardcoded bad exception; non-object modifier data gets user count (texture, displace) */ - if (*idpoin && GS((*idpoin)->name)!=ID_OB) + if (*idpoin != NULL && (cd_flag & IDWALK_USER) != 0) { (*idpoin)->us++; + } } static void lib_link_modifiers(FileData *fd, Object *ob) { @@ -5242,6 +5460,8 @@ static void direct_link_object(FileData *fd, Object *ob) link_list(fd, &ob->lodlevels); ob->currentlod = ob->lodlevels.first; + + ob->preview = direct_link_preview_image(fd, ob->preview); } /* ************ READ SCENE ***************** */ @@ -5387,16 +5607,10 @@ static void lib_link_scene(FileData *fd, Main *main) } } if (seq->clip) { - seq->clip = newlibadr(fd, sce->id.lib, seq->clip); - if (seq->clip) { - seq->clip->id.us++; - } + seq->clip = newlibadr_us(fd, sce->id.lib, seq->clip); } if (seq->mask) { - seq->mask = newlibadr(fd, sce->id.lib, seq->mask); - if (seq->mask) { - seq->mask->id.us++; - } + seq->mask = newlibadr_us(fd, sce->id.lib, seq->mask); } if (seq->scene_camera) { seq->scene_camera = newlibadr(fd, sce->id.lib, seq->scene_camera); @@ -5799,6 +6013,8 @@ static void direct_link_scene(FileData *fd, Scene *sce) rbw->ltime = (float)rbw->pointcache->startframe; } } + + sce->preview = direct_link_preview_image(fd, sce->preview); } /* ************ READ WM ***************** */ @@ -6334,6 +6550,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc else if (sl->spacetype == SPACE_FILE) { SpaceFile *sfile = (SpaceFile *)sl; sfile->op = NULL; + sfile->previews_timer = NULL; } else if (sl->spacetype == SPACE_ACTION) { SpaceAction *saction = (SpaceAction *)sl; @@ -6417,7 +6634,13 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc BLI_mempool_iternew(so->treestore, &iter); while ((tselem = BLI_mempool_iterstep(&iter))) { - tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE); + /* Do not try to restore pointers to drivers/sequence/etc., can crash in undo case! */ + if (TSE_IS_REAL_ID(tselem)) { + tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE); + } + else { + tselem->id = NULL; + } } if (so->treehash) { /* rebuild hash table, because it depends on ids too */ @@ -6863,6 +7086,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc) sfile->files = NULL; sfile->layout = NULL; sfile->op = NULL; + sfile->previews_timer = NULL; sfile->params = newdataadr(fd, sfile->params); } else if (sl->spacetype == SPACE_CLIP) { @@ -6975,11 +7199,7 @@ static void lib_link_speaker(FileData *fd, Main *main) if (spk->id.flag & LIB_NEED_LINK) { if (spk->adt) lib_link_animdata(fd, &spk->id, spk->adt); - spk->sound= newlibadr(fd, spk->id.lib, spk->sound); - if (spk->sound) { - spk->sound->id.us++; - } - + spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound); spk->id.flag -= LIB_NEED_LINK; } } @@ -7045,6 +7265,8 @@ static void lib_link_sound(FileData *fd, Main *main) static void direct_link_group(FileData *fd, Group *group) { link_list(fd, &group->gobject); + + group->preview = direct_link_preview_image(fd, group->preview); } static void lib_link_group(FileData *fd, Main *main) @@ -7630,14 +7852,66 @@ static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *a static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID **r_id) { - /* this routine reads a libblock and its direct data. Use link functions - * to connect it all + /* this routine reads a libblock and its direct data. Use link functions to connect it all */ ID *id; ListBase *lb; const char *allocname; 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). + * 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. + * 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)) { + const char *idname = bhead_id_name(fd, bhead); + + /* printf("Checking %s...\n", idname); */ + + if (bhead->code == ID_LI) { + Main *libmain = fd->old_mainlist->first; + /* Skip oldmain itself... */ + for (libmain = libmain->next; libmain; libmain = libmain->next) { + /* printf("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>"); */ + if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) { + Main *oldmain = fd->old_mainlist->first; + /* 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! + * 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); + BLI_addtail(fd->mainlist, libmain); + BLI_addtail(&main->library, libmain->curlib); + + if (r_id) { + *r_id = NULL; /* Just in case... */ + } + return blo_nextbhead(fd, bhead); + } + /* printf("nothing...\n"); */ + } + } + else { + /* printf("... in %s (%s): ", main->curlib ? main->curlib->id.name : "<NULL>", main->curlib ? main->curlib->name : "<NULL>"); */ + if ((id = BKE_libblock_find_name_ex(main, GS(idname), idname + 2))) { + /* printf("FOUND!\n"); */ + /* Even though we found our linked ID, there is no guarantee its address is still the same... */ + if (id != bhead->old) { + 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... */ + if (r_id) { + *r_id = NULL; /* Just in case... */ + } + return blo_nextbhead(fd, bhead); + } + /* printf("nothing...\n"); */ + } + } + /* read libblock */ id = read_struct(fd, bhead, "lib block"); if (r_id) @@ -7663,7 +7937,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID if (id->flag & LIB_FAKEUSER) id->us= 1; else id->us = 0; id->icon_id = 0; - id->flag &= ~(LIB_ID_RECALC|LIB_ID_RECALC_DATA|LIB_DOIT); + id->flag &= ~(LIB_ID_RECALC | LIB_ID_RECALC_DATA | LIB_DOIT | LIB_MISSING); /* this case cannot be direct_linked: it's just the ID part */ if (bhead->code == ID_ID) { @@ -8072,6 +8346,24 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) bfd->type = BLENFILETYPE_BLEND; BLI_strncpy(bfd->main->name, filepath, sizeof(bfd->main->name)); + if (G.background) { + /* We only read & store .blend thumbnail in background mode + * (because we cannot re-generate it, no OpenGL available). + */ + const int *data = read_file_thumbnail(fd); + + if (data) { + const size_t sz = BLEN_THUMB_MEMSIZE(data[0], data[1]); + bfd->main->blen_thumb = MEM_mallocN(sz, __func__); + + BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) == + (BLEN_THUMB_MEMSIZE_FILE(data[0], data[1]) - (sizeof(*data) * 2))); + bfd->main->blen_thumb->width = data[0]; + bfd->main->blen_thumb->height = data[1]; + memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb)); + } + } + while (bhead) { switch (bhead->code) { case DATA: @@ -8090,26 +8382,10 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) bhead = NULL; break; - case ID_LI: - /* skip library datablocks in undo, this works together with - * BLO_read_from_memfile, where the old main->library is restored - * overwriting the libraries from the memory file. previously - * it did not save ID_LI/ID_ID blocks in this case, but they are - * needed to make quit.blend recover them correctly. */ - if (fd->memfile) - bhead = blo_nextbhead(fd, bhead); - else - bhead = read_libblock(fd, bfd->main, bhead, LIB_LOCAL, NULL); - break; case ID_ID: - /* same as above */ - if (fd->memfile) - bhead = blo_nextbhead(fd, bhead); - else - /* always adds to the most recently loaded - * ID_LI block, see direct_link_library. - * this is part of the file format definition. */ - bhead = read_libblock(fd, mainlist.last, bhead, LIB_READ+LIB_EXTERN, NULL); + /* Always adds to the most recently loaded ID_LI block, see direct_link_library. + * This is part of the file format definition. */ + bhead = read_libblock(fd, mainlist.last, bhead, LIB_READ | LIB_EXTERN, NULL); break; /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ @@ -8122,10 +8398,10 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) } /* do before read_libraries, but skip undo case */ - if (fd->memfile==NULL) + if (fd->memfile == NULL) { do_versions(fd, NULL, bfd->main); - - do_versions_userdef(fd, bfd); + do_versions_userdef(fd, bfd); + } read_libraries(fd, &mainlist); @@ -8138,6 +8414,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) link_global(fd, bfd); /* as last */ + fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */ + return bfd; } @@ -8261,11 +8539,6 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname) #endif } -const char *bhead_id_name(const FileData *fd, const BHead *bhead) -{ - return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs); -} - static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead) { const char *idname= bhead_id_name(fd, bhead); @@ -8351,7 +8624,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old) } } -static void (*expand_doit)(void *, Main *, void *); +static BLOExpandDoitCallback expand_doit; // XXX deprecated - old animation system static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo) @@ -8788,8 +9061,8 @@ static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm) #endif } -static void expand_object_expandModifiers(void *userData, Object *UNUSED(ob), - ID **idpoin) +static void expand_object_expandModifiers( + void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cd_flag)) { struct { FileData *fd; Main *mainvar; } *data= userData; @@ -9126,11 +9399,23 @@ static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd) expand_animdata(fd, mainvar, gpd->adt); } -void BLO_main_expander(void (*expand_doit_func)(void *, Main *, void *)) +/** + * Set the callback func used over all ID data found by \a BLO_expand_main func. + * + * \param expand_doit_func Called for each ID block it finds. + */ +void BLO_main_expander(BLOExpandDoitCallback expand_doit_func) { expand_doit = expand_doit_func; } +/** + * Loop over all ID data in Main to mark relations. + * Set (id->flag & LIB_NEED_EXPAND) to mark expanding. Flags get cleared after expanding. + * + * \param fdhandle usually filedata, or own handle. + * \param mainvar the Main database to expand. + */ void BLO_expand_main(void *fdhandle, Main *mainvar) { ListBase *lbarray[MAX_LIBARRAY]; @@ -9242,101 +9527,86 @@ static bool object_in_any_scene(Main *mainvar, Object *ob) { Scene *sce; - for (sce= mainvar->scene.first; sce; sce= sce->id.next) { - if (BKE_scene_base_find(sce, ob)) - return 1; + for (sce = mainvar->scene.first; sce; sce = sce->id.next) { + if (BKE_scene_base_find(sce, ob)) { + return true; + } } - return 0; + return false; } -static void give_base_to_objects(Main *mainvar, Scene *sce, Library *lib, const short idcode, const bool is_link, const short active_lay) +static void give_base_to_objects(Main *mainvar, Scene *scene, View3D *v3d, Library *lib, const short flag) { Object *ob; Base *base; - const bool is_group_append = (is_link == false && idcode == ID_GR); + const unsigned int active_lay = (flag & FILE_ACTIVELAY) ? BKE_screen_view3d_layer_active(v3d, scene) : 0; + const bool is_link = (flag & FILE_LINK) != 0; + + BLI_assert(scene); /* give all objects which are LIB_INDIRECT a base, or for a group when *lib has been set */ for (ob = mainvar->object.first; ob; ob = ob->id.next) { - if (ob->id.flag & LIB_INDIRECT) { - /* IF below is quite confusing! - * if we are appending, but this object wasnt just added along with a group, - * then this is already used indirectly in the scene somewhere else and we didnt just append it. - * - * (ob->id.flag & LIB_PRE_EXISTING)==0 means that this is a newly appended object - Campbell */ - if (is_group_append==0 || (ob->id.flag & LIB_PRE_EXISTING)==0) { - bool do_it = false; - - if (ob->id.us == 0) { - do_it = true; - } - else if (idcode==ID_GR) { - if ((is_link == false) && (ob->id.lib == lib)) { - if ((ob->flag & OB_FROMGROUP) && object_in_any_scene(mainvar, ob)==0) { - do_it = true; - } - } - } - else { - /* when appending, make sure any indirectly loaded objects - * get a base else they cant be accessed at all [#27437] */ - if ((is_link == false) && (ob->id.lib == lib)) { - /* we may be appending from a scene where we already - * have a linked object which is not in any scene [#27616] */ - if ((ob->id.flag & LIB_PRE_EXISTING)==0) { - if (object_in_any_scene(mainvar, ob)==0) { - do_it = true; - } - } - } - } - - if (do_it) { - base = MEM_callocN(sizeof(Base), "add_ext_base"); - BLI_addtail(&sce->base, base); - - if (active_lay) ob->lay = sce->lay; - - base->lay = ob->lay; - base->object = ob; - base->flag = ob->flag; + if ((ob->id.flag & LIB_INDIRECT) && (ob->id.flag & LIB_PRE_EXISTING) == 0) { + bool do_it = false; - CLAMP_MIN(ob->id.us, 0); - ob->id.us += 1; - - ob->id.flag -= LIB_INDIRECT; - ob->id.flag |= LIB_EXTERN; + if (ob->id.us == 0) { + do_it = true; + } + else if (!is_link && (ob->id.lib == lib) && (object_in_any_scene(mainvar, ob) == 0)) { + /* When appending, make sure any indirectly loaded objects get a base, else they cant be accessed at all + * (see T27437). */ + do_it = true; + } + + if (do_it) { + base = MEM_callocN(sizeof(Base), __func__); + BLI_addtail(&scene->base, base); + + if (active_lay) { + ob->lay = active_lay; } + + base->lay = ob->lay; + base->object = ob; + base->flag = ob->flag; + + CLAMP_MIN(ob->id.us, 0); + ob->id.us += 1; + + ob->id.flag &= ~LIB_INDIRECT; + ob->id.flag |= LIB_EXTERN; } } } } -static void give_base_to_groups(Main *mainvar, Scene *scene) +static void give_base_to_groups( + Main *mainvar, Scene *scene, View3D *v3d, Library *UNUSED(lib), const short UNUSED(flag)) { Group *group; - + Base *base; + Object *ob; + const unsigned int active_lay = BKE_screen_view3d_layer_active(v3d, scene); + /* give all objects which are tagged a base */ for (group = mainvar->group.first; group; group = group->id.next) { if (group->id.flag & LIB_DOIT) { - Base *base; - Object *ob; - /* any indirect group should not have been tagged */ - BLI_assert((group->id.flag & LIB_INDIRECT)==0); - + BLI_assert((group->id.flag & LIB_INDIRECT) == 0); + /* BKE_object_add(...) messes with the selection */ ob = BKE_object_add_only_object(mainvar, OB_EMPTY, group->id.name + 2); ob->type = OB_EMPTY; - ob->lay = scene->lay; - + ob->lay = active_lay; + /* assign the base */ base = BKE_scene_base_add(scene, ob); base->flag |= SELECT; - base->object->flag= base->flag; + base->object->flag = base->flag; DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); scene->basact = base; - + /* assign the group */ ob->dup_group = group; ob->transflag |= OB_DUPLIGROUP; @@ -9345,11 +9615,30 @@ static void give_base_to_groups(Main *mainvar, Scene *scene) } } +static ID *create_placeholder(Main *mainvar, const char *idname, const short flag) +{ + const short idcode = GS(idname); + ListBase *lb = which_libbase(mainvar, idcode); + ID *ph_id = BKE_libblock_alloc_notest(idcode); + + memcpy(ph_id->name, idname, sizeof(ph_id->name)); + BKE_libblock_init_empty(ph_id); + ph_id->lib = mainvar->curlib; + ph_id->flag = flag | LIB_MISSING; + ph_id->us = (flag & LIB_FAKEUSER) ? 1 : 0; + ph_id->icon_id = 0; + + BLI_addtail(lb, ph_id); + id_sort_by_name(lb, ph_id); + + return ph_id; +} + /* returns true if the item was found * but it may already have already been appended/linked */ -static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, const short idcode) +static ID *link_named_part(Main *mainl, FileData *fd, const short idcode, const char *name) { - BHead *bhead = find_bhead_from_code_name(fd, idcode, idname); + BHead *bhead = find_bhead_from_code_name(fd, idcode, name); ID *id; if (bhead) { @@ -9385,8 +9674,10 @@ static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, cons return id; } -/* simple reader for copy/paste buffers */ -void BLO_library_append_all(Main *mainl, BlendHandle *bh) +/** + * Simple reader for copy/paste buffers. + */ +void BLO_library_link_all(Main *mainl, BlendHandle *bh) { FileData *fd = (FileData *)(bh); BHead *bhead; @@ -9406,33 +9697,33 @@ void BLO_library_append_all(Main *mainl, BlendHandle *bh) } } - -static ID *append_named_part_ex(const bContext *C, Main *mainl, FileData *fd, const char *idname, const int idcode, const int flag) +static ID *link_named_part_ex( + Main *mainl, FileData *fd, const short idcode, const char *name, const short flag, + Scene *scene, View3D *v3d) { - ID *id= append_named_part(mainl, fd, idname, idcode); + ID *id = link_named_part(mainl, fd, idcode, name); if (id && (GS(id->name) == ID_OB)) { /* loose object: give a base */ - Scene *scene = CTX_data_scene(C); /* can be NULL */ if (scene) { Base *base; Object *ob; - - base= MEM_callocN(sizeof(Base), "app_nam_part"); + + base = MEM_callocN(sizeof(Base), "app_nam_part"); BLI_addtail(&scene->base, base); - + ob = (Object *)id; - + /* link at active layer (view3d if available in context, else scene one */ - if ((flag & FILE_ACTIVELAY)) { - View3D *v3d = CTX_wm_view3d(C); + if (flag & FILE_ACTIVELAY) { ob->lay = BKE_screen_view3d_layer_active(v3d, scene); } - + ob->mode = OB_MODE_OBJECT; base->lay = ob->lay; base->object = ob; + base->flag = ob->flag; ob->id.us++; - + if (flag & FILE_AUTOSELECT) { base->flag |= SELECT; base->object->flag = base->flag; @@ -9441,47 +9732,91 @@ static ID *append_named_part_ex(const bContext *C, Main *mainl, FileData *fd, co } } else if (id && (GS(id->name) == ID_GR)) { - /* tag as needing to be instanced */ + /* tag as needing to be instantiated */ if (flag & FILE_GROUP_INSTANCE) id->flag |= LIB_DOIT; } - + return id; } -ID *BLO_library_append_named_part(Main *mainl, BlendHandle **bh, const char *idname, const int idcode) +/** + * Link a named datablock from an external blend file. + * + * \param mainl The main database to link from (not the active one). + * \param bh The blender file handle. + * \param idcode The kind of datablock to link. + * \param name The name of the datablock (without the 2 char ID prefix). + * \return the linked ID when found. + */ +ID *BLO_library_link_named_part(Main *mainl, BlendHandle **bh, const short idcode, const char *name) { FileData *fd = (FileData*)(*bh); - return append_named_part(mainl, fd, idname, idcode); + return link_named_part(mainl, fd, idcode, name); } -ID *BLO_library_append_named_part_ex(const bContext *C, Main *mainl, BlendHandle **bh, const char *idname, const int idcode, const short flag) +/** + * Link a named datablock from an external blend file. + * Optionally instantiate the object/group in the scene when the flags are set. + * + * \param mainl The main database to link from (not the active one). + * \param bh The blender file handle. + * \param idcode The kind of datablock to link. + * \param name The name of the datablock (without the 2 char ID prefix). + * \param flag Options for linking, used for instantiating. + * \param scene The scene in which to instantiate objects/groups (if NULL, no instantiation is done). + * \param v3d The active View3D (only to define active layers for instantiated objects & groups, can be NULL). + * \return the linked ID when found. + */ +ID *BLO_library_link_named_part_ex( + Main *mainl, BlendHandle **bh, + const short idcode, const char *name, const short flag, + Scene *scene, View3D *v3d) { FileData *fd = (FileData*)(*bh); - return append_named_part_ex(C, mainl, fd, idname, idcode, flag); + return link_named_part_ex(mainl, fd, idcode, name, flag, scene, v3d); } -static void append_id_part(FileData *fd, Main *mainvar, ID *id, ID **r_id) +static void link_id_part(ReportList *reports, FileData *fd, Main *mainvar, ID *id, ID **r_id) { - BHead *bhead = find_bhead_from_idname(fd, id->name); + BHead *bhead = NULL; + + if (fd) { + bhead = find_bhead_from_idname(fd, id->name); + } + + id->flag &= ~LIB_READ; if (bhead) { - id->flag &= ~LIB_READ; id->flag |= LIB_NEED_EXPAND; // printf("read lib block %s\n", id->name); read_libblock(fd, mainvar, bhead, id->flag, r_id); } + else { + blo_reportf_wrap( + reports, RPT_WARNING, + TIP_("LIB ERROR: %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 = create_placeholder(mainvar, id->name, id->flag); + } + } } /* common routine to append/link something from a library */ -static Main *library_append_begin(Main *mainvar, FileData **fd, const char *filepath) +static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath) { Main *mainl; (*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist"); - /* clear for group instancing tag */ + /* clear for group instantiating tag */ BKE_main_id_tag_listbase(&(mainvar->group), false); /* make mains */ @@ -9500,77 +9835,72 @@ static Main *library_append_begin(Main *mainvar, FileData **fd, const char *file return mainl; } -Main *BLO_library_append_begin(Main *mainvar, BlendHandle **bh, const char *filepath) +/** + * Initialize the BlendHandle for linking library data. + * + * \param mainvar The current main database, e.g. G.main or CTX_data_main(C). + * \param bh A blender file handle as returned by \a BLO_blendhandle_from_file or \a BLO_blendhandle_from_memory. + * \param filepath Used for relative linking, copied to the \a lib->name. + * \return the library Main, to be passed to \a BLO_library_append_named_part as \a mainl. + */ +Main *BLO_library_link_begin(Main *mainvar, BlendHandle **bh, const char *filepath) { FileData *fd = (FileData*)(*bh); - return library_append_begin(mainvar, &fd, filepath); + return library_link_begin(mainvar, &fd, filepath); } - -/* Context == NULL signifies not to do any scene manipulation */ -static void library_append_end(const bContext *C, Main *mainl, FileData **fd, int idcode, short flag) +/* scene and v3d may be NULL. */ +static void library_link_end(Main *mainl, FileData **fd, const short flag, Scene *scene, View3D *v3d) { Main *mainvar; Library *curlib; - + /* expander now is callback function */ BLO_main_expander(expand_doit_library); - + /* make main consistent */ BLO_expand_main(*fd, mainl); - + /* do this when expand found other libs */ read_libraries(*fd, (*fd)->mainlist); - + curlib = mainl->curlib; - + /* make the lib path relative if required */ if (flag & FILE_RELPATH) { /* use the full path, this could have been read by other library even */ BLI_strncpy(curlib->name, curlib->filepath, sizeof(curlib->name)); - + /* uses current .blend file as reference */ BLI_path_rel(curlib->name, G.main->name); } - + blo_join_main((*fd)->mainlist); mainvar = (*fd)->mainlist->first; MEM_freeN((*fd)->mainlist); mainl = NULL; /* blo_join_main free's mainl, cant use anymore */ - + lib_link_all(*fd, mainvar); lib_verify_nodetree(mainvar, false); fix_relpaths_library(G.main->name, mainvar); /* make all relative paths, relative to the open blend file */ - - if (C) { - Scene *scene = CTX_data_scene(C); - - /* give a base to loose objects. If group append, do it for objects too */ - if (scene) { - const bool is_link = (flag & FILE_LINK) != 0; - if (idcode == ID_SCE) { - /* don't instance anything when linking in scenes, assume the scene its self instances the data */ - } - else { - give_base_to_objects(mainvar, scene, curlib, idcode, is_link, flag & FILE_ACTIVELAY); - - if (flag & FILE_GROUP_INSTANCE) { - give_base_to_groups(mainvar, scene); - } - } - } - else { - printf("library_append_end, scene is NULL (objects wont get bases)\n"); + + /* Give a base to loose objects. If group append, do it for objects too. + * Only directly linked objects & groups are instantiated by `BLO_library_link_named_part_ex()` & co, + * here we handle indirect ones and other possible edge-cases. */ + if (scene) { + give_base_to_objects(mainvar, scene, v3d, curlib, flag); + + if (flag & FILE_GROUP_INSTANCE) { + give_base_to_groups(mainvar, scene, v3d, curlib, flag); } } + else { + /* printf("library_append_end, scene is NULL (objects wont get bases)\n"); */ + } - /* clear group instancing tag */ + /* clear group instantiating tag */ BKE_main_id_tag_listbase(&(mainvar->group), false); - - /* has been removed... erm, why? s..ton) */ - /* 20040907: looks like they are give base already in append_named_part(); -Nathan L */ - /* 20041208: put back. It only linked direct, not indirect objects (ton) */ - + /* patch to prevent switch_endian happens twice */ if ((*fd)->flags & FD_FLAGS_SWITCH_ENDIAN) { blo_freefiledata(*fd); @@ -9578,10 +9908,21 @@ static void library_append_end(const bContext *C, Main *mainl, FileData **fd, in } } -void BLO_library_append_end(const bContext *C, struct Main *mainl, BlendHandle **bh, int idcode, short flag) +/** + * Finalize linking from a given .blend file (library). + * Optionally instance the indirect object/group in the scene when the flags are set. + * \note Do not use \a bh after calling this function, it may frees it. + * + * \param mainl The main database to link from (not the active one). + * \param bh The blender file handle (WARNING! may be freed by this function!). + * \param flag Options for linking, used for instantiating. + * \param scene The scene in which to instantiate objects/groups (if NULL, no instantiation is done). + * \param v3d The active View3D (only to define active layers for instantiated objects & groups, can be NULL). + */ +void BLO_library_link_end(Main *mainl, BlendHandle **bh, short flag, Scene *scene, View3D *v3d) { FileData *fd = (FileData*)(*bh); - library_append_end(C, mainl, &fd, idcode, flag); + library_link_end(mainl, &fd, flag, scene, v3d); *bh = (BlendHandle*)fd; } @@ -9709,6 +10050,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) } else { mainptr->curlib->filedata = NULL; + mainptr->curlib->id.flag |= LIB_MISSING; } if (fd == NULL) { @@ -9718,37 +10060,29 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) } if (fd) { do_it = true; - a = set_listbasepointers(mainptr, lbarray); - while (a--) { - ID *id = lbarray[a]->first; - - while (id) { - ID *idn = id->next; - if (id->flag & LIB_READ) { - ID *realid = NULL; - BLI_remlink(lbarray[a], id); - - append_id_part(fd, mainptr, id, &realid); - if (!realid) { - blo_reportf_wrap( - fd->reports, RPT_WARNING, - TIP_("LIB ERROR: %s: '%s' missing from '%s', parent '%s'"), - BKE_idcode_to_name(GS(id->name)), - id->name + 2, - mainptr->curlib->filepath, - library_parent_filepath(mainptr->curlib)); - } - - change_idid_adr(mainlist, basefd, id, realid); - - MEM_freeN(id); - } - id = idn; + } + a = set_listbasepointers(mainptr, lbarray); + while (a--) { + ID *id = lbarray[a]->first; + + while (id) { + ID *idn = id->next; + if (id->flag & LIB_READ) { + ID *realid = NULL; + BLI_remlink(lbarray[a], id); + + link_id_part(basefd->reports, fd, mainptr, id, &realid); + + BLI_assert(realid != NULL); + + change_idid_adr(mainlist, basefd, id, realid); + + MEM_freeN(id); } + id = idn; } - - BLO_expand_main(fd, mainptr); } + BLO_expand_main(fd, mainptr); } mainptr = mainptr->next; @@ -9756,6 +10090,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) } /* test if there are unread libblocks */ + /* XXX This code block is kept for 2.77, until we are sure it never gets reached anymore. Can be removed later. */ for (mainptr = mainl->next; mainptr; mainptr = mainptr->next) { a = set_listbasepointers(mainptr, lbarray); while (a--) { @@ -9764,10 +10099,12 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) for (id = lbarray[a]->first; id; id = idn) { idn = id->next; if (id->flag & LIB_READ) { + BLI_assert(0); BLI_remlink(lbarray[a], id); blo_reportf_wrap( basefd->reports, RPT_WARNING, - TIP_("LIB ERROR: %s: '%s' unread lib block missing from '%s', parent '%s'"), + TIP_("LIB ERROR: %s: '%s' unread lib block missing from '%s', parent '%s' - " + "Please file a bug report if you see this message"), BKE_idcode_to_name(GS(id->name)), id->name + 2, mainptr->curlib->filepath, diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index ed22daef9ec..f6c3b69c414 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -96,7 +96,8 @@ typedef struct FileData { struct GHash *bhead_idname_hash; ListBase *mainlist; - + ListBase *old_mainlist; /* Used for undo. */ + /* ick ick, used to return * data through streamglue. */ @@ -139,7 +140,7 @@ void blo_make_sound_pointer_map(FileData *fd, Main *oldmain); void blo_end_sound_pointer_map(FileData *fd, Main *oldmain); void blo_make_packed_pointer_map(FileData *fd, Main *oldmain); void blo_end_packed_pointer_map(FileData *fd, Main *oldmain); -void blo_add_library_pointer_map(ListBase *mainlist, FileData *fd); +void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd); void blo_freefiledata(FileData *fd); diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index c66b9d1fae9..947f945edcc 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -57,7 +57,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" -#include "BLF_translation.h" +#include "BLT_translation.h" #include "BKE_anim.h" #include "BKE_image.h" @@ -343,7 +343,7 @@ static void do_versions_mesh_mloopcol_swap_2_62_1(Mesh *me) if (layer->type == CD_MLOOPCOL) { mloopcol = (MLoopCol *)layer->data; for (i = 0; i < me->totloop; i++, mloopcol++) { - SWAP(char, mloopcol->r, mloopcol->b); + SWAP(unsigned char, mloopcol->r, mloopcol->b); } } } diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 571a8cc49e5..8cd82c617af 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -42,6 +42,7 @@ #include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_screen_types.h" +#include "DNA_object_force.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" @@ -654,7 +655,9 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) ParticleSystem *psys; for (ob = main->object.first; ob; ob = ob->id.next) { for (psys = ob->particlesystem.first; psys; psys = psys->next) { - psys->recalc |= PSYS_RECALC_RESET; + if ((psys->pointcache->flag & PTCACHE_BAKED) == 0) { + psys->recalc |= PSYS_RECALC_RESET; + } } } } @@ -764,7 +767,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) { SpaceImage *sima = (SpaceImage *) sl; sima->iuser.flag |= IMA_SHOW_STEREO; - sima->iuser.passtype = SCE_PASS_COMBINED; break; } } @@ -852,6 +854,20 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) #undef BRUSH_TORUS } + if (!MAIN_VERSION_ATLEAST(main, 276, 2)) { + if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale")) { + Object *ob; + + for (ob = main->object.first; ob; ob = ob->id.next) { + if (ob->pose) { + bPoseChannel *pchan; + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + pchan->custom_scale = 1.0f; + } + } + } + } + } { bScreen *screen; for (screen = main->screen.first; screen; screen = screen->id.next) { diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 17b0f388af4..c32b3ac51cf 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -160,7 +160,7 @@ void BLO_update_defaults_startup_blend(Main *bmain) br = (Brush *)BKE_libblock_find_name_ex(bmain, ID_BR, "Fill"); if (!br) { - br = BKE_brush_add(bmain, "Fill"); + br = BKE_brush_add(bmain, "Fill", OB_MODE_TEXTURE_PAINT); br->imagepaint_tool = PAINT_TOOL_FILL; br->ob_mode = OB_MODE_TEXTURE_PAINT; } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index dcfafc26e4f..12069fd80dd 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -648,7 +648,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) while (ob) { if (ob->transflag & 1) { ob->transflag -= 1; - //ob->ipoflag |= OB_OFFS_OB; } ob = ob->id.next; } @@ -685,7 +684,6 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *main) } ob = main->object.first; while (ob) { - //ob->ipoflag |= OB_OFFS_PARENT; if (ob->dt == 0) ob->dt = OB_SOLID; ob = ob->id.next; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index f5d35e45fc0..d320274ef96 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -291,11 +291,10 @@ static void ww_handle_init(eWriteWrapType ww_type, WriteWrap *r_ww) typedef struct { struct SDNA *sdna; - int file; unsigned char *buf; MemFile *compare, *current; - int tot, count, error, memsize; + int tot, count, error; /* Wrap writing, so we can use zlib or * other compression types later, see: G_FILE_COMPRESS @@ -588,6 +587,33 @@ void IDP_WriteProperty(IDProperty *prop, void *wd) IDP_WriteProperty_OnlyData(prop, wd); } +static void write_previews(WriteData *wd, PreviewImage *prv) +{ + /* Never write previews when doing memsave (i.e. undo/redo)! */ + if (prv && !wd->current) { + short w = prv->w[1]; + short h = prv->h[1]; + unsigned int *rect = prv->rect[1]; + + /* don't write out large previews if not requested */ + if (!(U.flag & USER_SAVE_PREVIEWS)) { + prv->w[1] = 0; + prv->h[1] = 0; + prv->rect[1] = NULL; + } + writestruct(wd, DATA, "PreviewImage", 1, prv); + if (prv->rect[0]) writedata(wd, DATA, prv->w[0] * prv->h[0] * sizeof(unsigned int), prv->rect[0]); + if (prv->rect[1]) writedata(wd, DATA, prv->w[1] * prv->h[1] * sizeof(unsigned int), prv->rect[1]); + + /* restore preview, we still want to keep it in memory even if not saved to file */ + if (!(U.flag & USER_SAVE_PREVIEWS) ) { + prv->w[1] = w; + prv->h[1] = h; + prv->rect[1] = rect; + } + } +} + static void write_fmodifiers(WriteData *wd, ListBase *fmodifiers) { FModifier *fcm; @@ -1698,6 +1724,9 @@ static void write_objects(WriteData *wd, ListBase *idbase) writelist(wd, DATA, "LinkData", &ob->pc_ids); writelist(wd, DATA, "LodLevel", &ob->lodlevels); } + + write_previews(wd, ob->preview); + ob= ob->id.next; } @@ -2049,7 +2078,7 @@ static void write_meshes(WriteData *wd, ListBase *idbase) /* now fill in polys to mfaces */ /* XXX This breaks writing desing, by using temp allocated memory, which will likely generate - * doublons in stored 'old' addresses. + * duplicates in stored 'old' addresses. * This is very bad, but do not see easy way to avoid this, aside from generating those data * outside of save process itself. * Maybe we can live with this, though? @@ -2138,32 +2167,6 @@ static void write_lattices(WriteData *wd, ListBase *idbase) } } -static void write_previews(WriteData *wd, PreviewImage *prv) -{ - /* Never write previews in undo steps! */ - if (prv && !wd->current) { - short w = prv->w[1]; - short h = prv->h[1]; - unsigned int *rect = prv->rect[1]; - /* don't write out large previews if not requested */ - if (!(U.flag & USER_SAVE_PREVIEWS)) { - prv->w[1] = 0; - prv->h[1] = 0; - prv->rect[1] = NULL; - } - writestruct(wd, DATA, "PreviewImage", 1, prv); - if (prv->rect[0]) writedata(wd, DATA, prv->w[0]*prv->h[0]*sizeof(unsigned int), prv->rect[0]); - if (prv->rect[1]) writedata(wd, DATA, prv->w[1]*prv->h[1]*sizeof(unsigned int), prv->rect[1]); - - /* restore preview, we still want to keep it in memory even if not saved to file */ - if (!(U.flag & USER_SAVE_PREVIEWS) ) { - prv->w[1] = w; - prv->h[1] = h; - prv->rect[1] = rect; - } - } -} - static void write_images(WriteData *wd, ListBase *idbase) { Image *ima; @@ -2572,6 +2575,8 @@ static void write_scenes(WriteData *wd, ListBase *scebase) write_pointcaches(wd, &(sce->rigidbody_world->ptcaches)); } + write_previews(wd, sce->preview); + sce= sce->id.next; } /* flush helps the compression for undo-save */ @@ -3068,6 +3073,8 @@ static void write_groups(WriteData *wd, ListBase *idbase) writestruct(wd, ID_GR, "Group", 1, group); if (group->id.properties) IDP_WriteProperty(group->id.properties, wd); + write_previews(wd, group->preview); + go= group->gobject.first; while (go) { writestruct(wd, DATA, "GroupObject", 1, go); @@ -3680,10 +3687,11 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar) * second are an RGBA image (unsigned char) * note, this uses 'TEST' since new types will segfault on file load for older blender versions. */ -static void write_thumb(WriteData *wd, const int *img) +static void write_thumb(WriteData *wd, const BlendThumbnail *thumb) { - if (img) - writedata(wd, TEST, (2 + img[0] * img[1]) * sizeof(int), img); + if (thumb) { + writedata(wd, TEST, BLEN_THUMB_MEMSIZE_FILE(thumb->width, thumb->height), thumb); + } } /* if MemFile * there's filesave to memory */ @@ -3691,7 +3699,7 @@ static int write_file_handle( Main *mainvar, WriteWrap *ww, MemFile *compare, MemFile *current, - int write_user_block, int write_flags, const int *thumb) + int write_user_block, int write_flags, const BlendThumbnail *thumb) { BHead bhead; ListBase mainlist; @@ -3823,7 +3831,8 @@ static bool do_history(const char *name, ReportList *reports) } /* return: success (1) */ -int BLO_write_file(Main *mainvar, const char *filepath, int write_flags, ReportList *reports, const int *thumb) +int BLO_write_file( + Main *mainvar, const char *filepath, int write_flags, ReportList *reports, const BlendThumbnail *thumb) { char tempname[FILE_MAX+1]; int err, write_user_block; |