Files
test2/source/blender/blenkernel/intern/library.cc
2025-09-26 22:00:27 +10:00

716 lines
25 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Contains code specific to the `Library` ID type.
*/
#include <optional>
#include "CLG_log.h"
/* all types are needed here, in order to do memory operations */
#include "DNA_ID.h"
#include "DNA_collection_types.h"
#include "DNA_scene_types.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
#include "BLI_listbase.h"
#include "BLI_path_utils.hh"
#include "BLI_set.hh"
#include "BLI_string.h"
#include "BLI_vector_set.hh"
#include "BLT_translation.hh"
#include "BLO_read_write.hh"
#include "BKE_bpath.hh"
#include "BKE_id_hash.hh"
#include "BKE_idtype.hh"
#include "BKE_key.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
#include "BKE_lib_remap.hh"
#include "BKE_library.hh"
#include "BKE_main.hh"
#include "BKE_main_invariants.hh"
#include "BKE_main_namemap.hh"
#include "BKE_node.hh"
#include "BKE_packedFile.hh"
#include "BKE_report.hh"
struct BlendDataReader;
static CLG_LogRef LOG = {"lib.library"};
using namespace blender::bke;
using namespace blender::bke::library;
static void library_runtime_reset(Library *lib)
{
BKE_main_namemap_destroy(&lib->runtime->name_map);
}
static void library_init_data(ID *id)
{
Library *library = reinterpret_cast<Library *>(id);
library->runtime = MEM_new<LibraryRuntime>(__func__);
}
static void library_free_data(ID *id)
{
Library *library = (Library *)id;
library_runtime_reset(library);
MEM_delete(library->runtime);
if (library->packedfile) {
BKE_packedfile_free(library->packedfile);
}
}
static void library_copy_data(Main *bmain,
std::optional<Library *> owner_library,
ID *id_dst,
const ID *id_src,
int /*flag*/)
{
/* Libraries are always local IDs. */
BLI_assert(!owner_library || *owner_library == nullptr);
UNUSED_VARS_NDEBUG(bmain, owner_library);
const Library *library_src = reinterpret_cast<const Library *>(id_src);
/* Libraries are copyable now, but there should still be only one library ID for each linked
* blendfile (based on absolute filepath). */
BLI_assert(!bmain ||
!search_filepath_abs(&bmain->libraries, library_src->runtime->filepath_abs));
Library *library_dst = reinterpret_cast<Library *>(id_dst);
if (library_src->packedfile) {
library_dst->packedfile = BKE_packedfile_duplicate(library_src->packedfile);
}
library_dst->runtime = MEM_new<LibraryRuntime>(__func__, *library_src->runtime);
library_dst->runtime->filedata = nullptr;
library_dst->runtime->is_filedata_owner = false;
library_dst->runtime->name_map = nullptr;
}
static void library_foreach_id(ID *id, LibraryForeachIDData *data)
{
Library *lib = (Library *)id;
const LibraryForeachIDFlag foreach_flag = BKE_lib_query_foreachid_process_flags_get(data);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->runtime->parent, IDWALK_CB_NEVER_SELF);
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
/* Archive library must have a parent, this can't be nullptr. */
if (lib->archive_parent_library) {
BKE_LIB_FOREACHID_PROCESS_ID(
data, lib->archive_parent_library, IDWALK_CB_NEVER_SELF | IDWALK_CB_NEVER_NULL);
}
/* Archive libraries should never 'own' other archives. */
BLI_assert(lib->runtime->archived_libraries.is_empty());
if (foreach_flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) {
for (Library *&lib_p : lib->runtime->archived_libraries) {
BKE_LIB_FOREACHID_PROCESS_ID(
data, lib_p, IDWALK_CB_NEVER_SELF | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK);
}
}
}
else {
/* Regular libraries should never have an archive parent. */
BLI_assert(!lib->archive_parent_library);
BKE_LIB_FOREACHID_PROCESS_ID(data, lib->archive_parent_library, IDWALK_CB_NEVER_SELF);
if (foreach_flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) {
for (Library *&lib_p : lib->runtime->archived_libraries) {
BKE_LIB_FOREACHID_PROCESS_ID(
data, lib_p, IDWALK_CB_NEVER_SELF | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK);
}
}
}
}
static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data)
{
Library *lib = (Library *)id;
/* FIXME: Find if we should respect #BKE_BPATH_FOREACH_PATH_SKIP_PACKED here, and if not, explain
* why. */
if (lib->packedfile !=
nullptr /*&& (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0 */)
{
return;
}
if (BKE_bpath_foreach_path_fixed_process(bpath_data, lib->filepath, sizeof(lib->filepath))) {
BKE_library_filepath_set(bpath_data->bmain, lib, lib->filepath);
}
}
static void library_blend_write_data(BlendWriter *writer, ID *id, const void *id_address)
{
Library *library = reinterpret_cast<Library *>(id);
const bool is_undo = BLO_write_is_undo(writer);
/* Clear runtime data. */
library->runtime = nullptr;
BLO_write_id_struct(writer, Library, id_address, id);
BKE_id_blend_write(writer, id);
/* Write packed file if necessary. */
if (library->packedfile) {
BKE_packedfile_blend_write(writer, library->packedfile);
if (!is_undo) {
CLOG_DEBUG(&LOG, "Write packed .blend: %s", library->filepath);
}
}
}
static void library_blend_read_data(BlendDataReader * /*reader*/, ID *id)
{
Library *lib = reinterpret_cast<Library *>(id);
lib->runtime = MEM_new<LibraryRuntime>(__func__);
}
static void library_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id)
{
Library *lib = reinterpret_cast<Library *>(id);
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
BLI_assert(lib->archive_parent_library);
lib->archive_parent_library->runtime->archived_libraries.append(lib);
}
}
IDTypeInfo IDType_ID_LI = {
/*id_code*/ Library::id_type,
/*id_filter*/ FILTER_ID_LI,
/*dependencies_id_types*/ FILTER_ID_LI,
/*main_listbase_index*/ INDEX_ID_LI,
/*struct_size*/ sizeof(Library),
/*name*/ "Library",
/*name_plural*/ N_("libraries"),
/*translation_context*/ BLT_I18NCONTEXT_ID_LIBRARY,
/*flags*/ IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NEVER_UNUSED,
/*asset_type_info*/ nullptr,
/*init_data*/ library_init_data,
/*copy_data*/ library_copy_data,
/*free_data*/ library_free_data,
/*make_local*/ nullptr,
/*foreach_id*/ library_foreach_id,
/*foreach_cache*/ nullptr,
/*foreach_path*/ library_foreach_path,
/*foreach_working_space_color*/ nullptr,
/*owner_pointer_get*/ nullptr,
/*blend_write*/ library_blend_write_data,
/*blend_read_data*/ library_blend_read_data,
/*blend_read_after_liblink*/ library_blend_read_after_liblink,
/*blend_read_undo_preserve*/ nullptr,
/*lib_override_apply_post*/ nullptr,
};
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
{
/* in some cases this is used to update the absolute path from the
* relative */
if (lib->filepath != filepath) {
STRNCPY(lib->filepath, filepath);
}
STRNCPY(lib->runtime->filepath_abs, filepath);
/* Not essential but set `filepath_abs` is an absolute copy of value which
* is more useful if its kept in sync. */
if (BLI_path_is_rel(lib->runtime->filepath_abs)) {
/* NOTE(@ideasman42): the file may be unsaved, in this case, setting the
* `filepath_abs` on an indirectly linked path is not allowed from the
* outliner, and its not really supported but allow from here for now
* since making local could cause this to be directly linked.
*/
/* Never make paths relative to parent lib - reading code (blenloader) always set *all*
* `lib->filepath` relative to current main, not to their parent for indirectly linked ones. */
const char *blendfile_path = BKE_main_blendfile_path(bmain);
BLI_path_abs(lib->runtime->filepath_abs, blendfile_path);
}
}
static void rebuild_hierarchy_best_parent_find(Main *bmain,
blender::Set<Library *> &directly_used_libs,
Library *lib)
{
BLI_assert(!directly_used_libs.contains(lib));
Library *best_parent_lib = nullptr;
bool do_break = false;
ListBase *lb;
ID *id_iter;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
if (!ID_IS_LINKED(id_iter) || id_iter->lib != lib) {
continue;
}
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
ID *from_id = item->id_pointer.from;
if (!ID_IS_LINKED(from_id)) {
BLI_assert_unreachable();
continue;
}
Library *from_id_lib = from_id->lib;
if (from_id_lib == lib) {
continue;
}
if (directly_used_libs.contains(from_id_lib)) {
/* Found the first best possible candidate, no need to search further. */
BLI_assert(best_parent_lib == nullptr || best_parent_lib->runtime->temp_index > 0);
best_parent_lib = from_id_lib;
do_break = true;
break;
}
if (!from_id_lib->runtime->parent) {
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, from_id_lib);
}
if (!best_parent_lib ||
best_parent_lib->runtime->temp_index > from_id_lib->runtime->temp_index)
{
best_parent_lib = from_id_lib;
if (best_parent_lib->runtime->temp_index == 0) {
/* Found the first best possible candidate, no need to search further. */
BLI_assert(directly_used_libs.contains(best_parent_lib));
do_break = true;
break;
}
}
}
if (do_break) {
break;
}
}
FOREACH_MAIN_LISTBASE_ID_END;
if (do_break) {
break;
}
}
FOREACH_MAIN_LISTBASE_END;
/* NOTE: It may happen that no parent library is found, e.g. if after deleting a directly used
* library, its indirect dependency is still around, but none of its linked IDs are used by local
* data. */
if (best_parent_lib) {
lib->runtime->parent = best_parent_lib;
lib->runtime->temp_index = best_parent_lib->runtime->temp_index + 1;
}
else {
lib->runtime->parent = nullptr;
lib->runtime->temp_index = 0;
directly_used_libs.add(lib);
}
}
void BKE_library_main_rebuild_hierarchy(Main *bmain)
{
BKE_main_relations_create(bmain, 0);
/* Reset all values, they may have been set to irrelevant values by other processes (like the
* liboverride handling e.g., see #lib_override_libraries_index_define). */
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
lib_iter->runtime->temp_index = 0;
}
/* Find all libraries with directly linked IDs (i.e. IDs used by local data). */
blender::Set<Library *> directly_used_libs;
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (!ID_IS_LINKED(id_iter)) {
continue;
}
id_iter->lib->runtime->temp_index = 0;
if (directly_used_libs.contains(id_iter->lib)) {
continue;
}
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
if (!ID_IS_LINKED(item->id_pointer.from)) {
directly_used_libs.add(id_iter->lib);
id_iter->lib->runtime->parent = nullptr;
break;
}
}
}
FOREACH_MAIN_ID_END;
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
/* A directly used library. */
if (directly_used_libs.contains(lib_iter)) {
BLI_assert(lib_iter->runtime->temp_index == 0);
continue;
}
/* Assume existing parent is still valid, since it was not cleared in previous loop above.
* Just compute 'hierarchy value' in temp index, if needed. */
if (lib_iter->runtime->parent) {
if (lib_iter->runtime->temp_index > 0) {
continue;
}
blender::Vector<Library *> parent_libraries;
for (Library *parent_lib_iter = lib_iter;
parent_lib_iter && parent_lib_iter->runtime->temp_index == 0;
parent_lib_iter = parent_lib_iter->runtime->parent)
{
parent_libraries.append(parent_lib_iter);
}
int parent_temp_index = parent_libraries.last()->runtime->temp_index +
int(parent_libraries.size()) - 1;
for (Library *parent_lib_iter : parent_libraries) {
BLI_assert(parent_lib_iter != parent_libraries.last() ||
parent_lib_iter->runtime->temp_index == parent_temp_index);
parent_lib_iter->runtime->temp_index = parent_temp_index--;
}
continue;
}
/* Otherwise, it's an indirectly used library with no known parent, another loop is needed to
* ensure all known hierarchy has valid indices when trying to find the best valid parent
* library. */
}
/* For all libraries known to be indirect, but without a known parent, find a best valid parent
* (i.e. a 'most directly used' library). */
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
/* A directly used library. */
if (directly_used_libs.contains(lib_iter)) {
BLI_assert(lib_iter->runtime->temp_index == 0);
continue;
}
if (lib_iter->runtime->parent) {
BLI_assert(lib_iter->runtime->temp_index > 0);
}
else {
BLI_assert(lib_iter->runtime->temp_index == 0);
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, lib_iter);
}
}
BKE_main_relations_free(bmain);
}
Library *blender::bke::library::search_filepath_abs(ListBase *libraries,
blender::StringRef filepath_abs)
{
LISTBASE_FOREACH (Library *, lib_iter, libraries) {
if (filepath_abs == lib_iter->runtime->filepath_abs) {
return lib_iter;
}
}
return nullptr;
}
/**
* Add a new 'archive' copy of the given reference library. It is used to store linked packed IDs.
*/
static Library *add_archive_library(Main &bmain, Library &reference_library)
{
BLI_assert((reference_library.flag & LIBRARY_FLAG_IS_ARCHIVE) == 0);
/* Cannot copy libraries using generic ID copying functions, so create the copy manually. */
Library *archive_library = static_cast<Library *>(
BKE_id_new(&bmain, ID_LI, BKE_id_name(reference_library.id)));
/* Like in #direct_link_library. */
id_us_ensure_real(&archive_library->id);
archive_library->archive_parent_library = &reference_library;
constexpr uint16_t copy_flag = ~LIBRARY_FLAG_IS_ARCHIVE;
archive_library->flag = (reference_library.flag & copy_flag) | LIBRARY_FLAG_IS_ARCHIVE;
BKE_library_filepath_set(&bmain, archive_library, reference_library.filepath);
archive_library->runtime->parent = reference_library.runtime->parent;
/* Only copy a subset of the reference library tags. E.g. an archive library should never be
* considered as writable, so never copy #LIBRARY_ASSET_FILE_WRITABLE. This may need further
* tweaking still. */
constexpr uint16_t copy_tag = (LIBRARY_TAG_RESYNC_REQUIRED | LIBRARY_ASSET_EDITABLE |
LIBRARY_IS_ASSET_EDIT_FILE);
archive_library->runtime->tag = reference_library.runtime->tag & copy_tag;
/* By definition, the file version of an archive library containing only packed linked data is
* the same as the one of its Main container. */
archive_library->runtime->versionfile = bmain.versionfile;
archive_library->runtime->subversionfile = bmain.subversionfile;
reference_library.runtime->archived_libraries.append(archive_library);
return archive_library;
}
static Library *get_archive_library(Main &bmain, ID *for_id, const IDHash &for_id_deep_hash)
{
Library *reference_library = for_id->lib;
BLI_assert(reference_library && (reference_library->flag & LIBRARY_FLAG_IS_ARCHIVE) == 0);
Library *archive_library = nullptr;
for (Library *lib_iter : reference_library->runtime->archived_libraries) {
BLI_assert((lib_iter->flag & LIBRARY_FLAG_IS_ARCHIVE) != 0);
BLI_assert(lib_iter->archive_parent_library != nullptr);
BLI_assert(lib_iter->archive_parent_library == reference_library);
/* Check if current archive library already contains an ID of same type and name. */
if (BKE_main_namemap_contain_name(bmain, lib_iter, GS(for_id->name), BKE_id_name(*for_id))) {
#ifndef NDEBUG
ID *packed_id = BKE_libblock_find_name_and_library(
&bmain, GS(for_id->name), BKE_id_name(*for_id), BKE_id_name(lib_iter->id));
BLI_assert_msg(
packed_id && packed_id->deep_hash != for_id_deep_hash,
"An already packed ID with same deep hash as the one to be packed, should have already "
"be found and used (deduplication) before reaching this code-path");
#endif
UNUSED_VARS_NDEBUG(for_id_deep_hash);
continue;
}
archive_library = lib_iter;
break;
}
if (!archive_library) {
archive_library = add_archive_library(bmain, *reference_library);
}
BLI_assert(reference_library->runtime->archived_libraries.contains(archive_library));
return archive_library;
}
static void pack_linked_id(Main &bmain,
ID *linked_id,
const id_hash::ValidDeepHashes &deep_hashes,
blender::Map<IDHash, ID *> &already_packed_ids,
blender::VectorSet<ID *> &ids_to_remap,
blender::bke::id::IDRemapper &id_remapper)
{
BLI_assert(linked_id->newid == nullptr);
const IDHash linked_id_deep_hash = deep_hashes.hashes.lookup(linked_id);
ID *packed_id = already_packed_ids.lookup_default(linked_id_deep_hash, nullptr);
if (packed_id) {
/* Exact same ID (and all of its dependencies) have already been linked and packed before,
* re-use these packed data. */
auto existing_id_process = [&deep_hashes, &id_remapper](ID *linked_id, ID *packed_id) {
BLI_assert(packed_id);
BLI_assert(ID_IS_PACKED(packed_id));
/* Note: linked_id and packed_id may have the same deep hash while still coming from
* different original libraries. This easily happens copying an asset file such that each
* asset exists twice. */
BLI_assert(packed_id->deep_hash == deep_hashes.hashes.lookup(linked_id));
UNUSED_VARS_NDEBUG(deep_hashes);
id_remapper.add(linked_id, packed_id);
linked_id->newid = packed_id;
/* No need to remap this packed ID - otherwise there would be something very wrong in
* packed IDs state. */
};
existing_id_process(linked_id, packed_id);
/* Handle 'fake-embedded' ShapeKeys IDs. */
Key *linked_key = BKE_key_from_id(linked_id);
if (linked_key) {
Key *packed_key = BKE_key_from_id(packed_id);
BLI_assert(packed_key);
existing_id_process(&linked_key->id, &packed_key->id);
}
}
else {
/* This exact version of the ID and its dependencies have not been packed before, creates a
* new copy of it and pack it. */
/* Find an existing archive Library not containing a 'version' of this ID yet (to prevent names
* collisions). */
Library *archive_lib = get_archive_library(bmain, linked_id, linked_id_deep_hash);
auto copied_id_process =
[&archive_lib, &deep_hashes, &ids_to_remap, &id_remapper, &already_packed_ids](
ID *linked_id, ID *packed_id) {
BLI_assert(packed_id);
BLI_assert(ID_IS_PACKED(packed_id));
BLI_assert(packed_id->lib == archive_lib);
UNUSED_VARS_NDEBUG(archive_lib);
packed_id->deep_hash = deep_hashes.hashes.lookup(linked_id);
id_remapper.add(linked_id, packed_id);
ids_to_remap.add(packed_id);
already_packed_ids.add(packed_id->deep_hash, packed_id);
};
packed_id = BKE_id_copy_in_lib(&bmain,
archive_lib,
linked_id,
std::nullopt,
nullptr,
LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ID_NEW_SET |
LIB_ID_COPY_NO_ANIMDATA);
id_us_min(packed_id);
copied_id_process(linked_id, packed_id);
/* Handle 'fake-embedded' ShapeKeys IDs. */
Key *linked_key = BKE_key_from_id(linked_id);
if (linked_key) {
Key *embedded_key = BKE_key_from_id(packed_id);
BLI_assert(embedded_key);
copied_id_process(&linked_key->id, &embedded_key->id);
}
}
}
/**
* Pack given linked IDs. Low-level code, assumes all given IDs are valid and safe to pack.
*
* Will set final packed ID into each ID::newid pointers.
*/
static void pack_linked_ids(Main &bmain, const blender::Set<ID *> &ids_to_pack)
{
blender::VectorSet<ID *> final_ids_to_pack;
blender::VectorSet<ID *> ids_to_remap;
blender::bke::id::IDRemapper id_remapper;
for (ID *id : ids_to_pack) {
BLI_assert(ID_IS_LINKED(id));
if (ID_IS_PACKED(id)) {
/* Should not happen, but also not critical issue. */
CLOG_ERROR(&LOG,
"Trying to pack an already packed ID '%s' (from '%s')",
id->name,
id->lib->runtime->filepath_abs);
/* Already packed. */
continue;
}
final_ids_to_pack.add(id);
}
const id_hash::IDHashResult hash_result = id_hash::compute_linked_id_deep_hashes(
bmain, final_ids_to_pack.as_span());
if (const auto *errors = std::get_if<id_hash::DeepHashErrors>(&hash_result)) {
if (!errors->missing_files.is_empty()) {
CLOG_ERROR(&LOG,
"Trying to pack IDs that depend on missing linked libraries: %s",
errors->missing_files[0].c_str());
}
if (!errors->updated_files.is_empty()) {
CLOG_ERROR(&LOG,
"Trying to pack linked ID that has been modified on disk: %s",
errors->updated_files[0].c_str());
}
return;
}
const auto &deep_hashes = std::get<id_hash::ValidDeepHashes>(hash_result);
blender::Map<IDHash, ID *> already_packed_ids;
{
ID *id;
FOREACH_MAIN_ID_BEGIN (&bmain, id) {
if (ID_IS_PACKED(id)) {
already_packed_ids.add(id->deep_hash, id);
}
}
FOREACH_MAIN_ID_END;
}
for (ID *linked_id : final_ids_to_pack) {
pack_linked_id(bmain, linked_id, deep_hashes, already_packed_ids, ids_to_remap, id_remapper);
}
BKE_libblock_relink_multiple(
&bmain, ids_to_remap.as_span(), ID_REMAP_TYPE_REMAP, id_remapper, 0);
BKE_main_ensure_invariants(bmain);
}
void blender::bke::library::pack_linked_id_hierarchy(Main &bmain, ID &root_id)
{
BLI_assert(ID_IS_LINKED(&root_id));
BLI_assert(!ID_IS_PACKED(&root_id));
blender::Set<ID *> ids_to_pack;
ids_to_pack.add(&root_id);
BKE_library_foreach_ID_link(
&bmain,
&root_id,
[&ids_to_pack](LibraryIDLinkCallbackData *cb_data) -> int {
if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
return IDWALK_RET_NOP;
}
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
return IDWALK_RET_NOP;
}
ID *self_id = cb_data->self_id;
ID *referenced_id = *cb_data->id_pointer;
if (!referenced_id) {
return IDWALK_RET_NOP;
}
if (!ID_IS_LINKED(referenced_id)) {
CLOG_ERROR(&LOG, "Linked data-block references non-linked data-block");
return IDWALK_RET_NOP;
}
if (ID_IS_PACKED(referenced_id)) {
/* A linked ID can use another packed linked ID, as long as it is not from the same
* library. */
BLI_assert(referenced_id->lib && referenced_id->lib->archive_parent_library);
if (referenced_id->lib->archive_parent_library == self_id->lib) {
CLOG_ERROR(&LOG,
"Non-packed data-block references packed data-block from the same library, "
"which is not allowed");
}
return IDWALK_RET_NOP;
}
if (referenced_id->newid && ID_IS_PACKED(referenced_id->newid)) {
return IDWALK_RET_NOP;
}
if (GS(referenced_id->name) == ID_KE) {
/* Shape keys cannot be directly linked, from linking code PoV they behave as embedded
* data (i.e. their owning data is responsible to handle them). */
return IDWALK_RET_NOP;
}
ids_to_pack.add(referenced_id);
return IDWALK_RET_NOP;
},
nullptr,
IDWALK_READONLY | IDWALK_RECURSE);
pack_linked_ids(bmain, ids_to_pack);
}
void blender::bke::library::main_cleanup_parent_archives(Main &bmain)
{
LISTBASE_FOREACH (Library *, lib, &bmain.libraries) {
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
BLI_assert(!lib->runtime || lib->runtime->archived_libraries.is_empty());
}
else {
int i_read_curr = 0;
int i_insert_curr = 0;
for (; i_read_curr < lib->runtime->archived_libraries.size(); i_read_curr++) {
if (!lib->runtime->archived_libraries[i_read_curr]) {
continue;
}
if (i_insert_curr < i_read_curr) {
lib->runtime->archived_libraries[i_insert_curr] =
lib->runtime->archived_libraries[i_read_curr];
}
i_insert_curr++;
}
BLI_assert(i_insert_curr <= i_read_curr);
if (i_insert_curr < i_read_curr) {
lib->runtime->archived_libraries.resize(i_insert_curr);
}
}
}
}