diff options
Diffstat (limited to 'source/blender/blenloader/intern/blend_validate.cc')
-rw-r--r-- | source/blender/blenloader/intern/blend_validate.cc | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/source/blender/blenloader/intern/blend_validate.cc b/source/blender/blenloader/intern/blend_validate.cc new file mode 100644 index 00000000000..96314ab4324 --- /dev/null +++ b/source/blender/blenloader/intern/blend_validate.cc @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup blenloader + * + * Utils to check/validate a Main is in sane state, + * only checks relations between data-blocks and libraries for now. + * + * \note Does not *fix* anything, only reports found errors. + */ + +#include <string.h> /* for #strrchr #strncmp #strstr */ + +#include "BLI_utildefines.h" + +#include "BLI_blenlib.h" +#include "BLI_linklist.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_key_types.h" +#include "DNA_sdna_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" + +#include "BLO_blend_validate.h" +#include "BLO_readfile.h" + +#include "readfile.h" + +bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) +{ + ListBase mainlist; + bool is_valid = true; + + BKE_main_lock(bmain); + + blo_split_main(&mainlist, bmain); + + ListBase *lbarray[INDEX_ID_MAX]; + int i = set_listbasepointers(bmain, lbarray); + while (i--) { + for (ID *id = static_cast<ID *>(lbarray[i]->first); id != nullptr; + id = static_cast<ID *>(id->next)) { + if (ID_IS_LINKED(id)) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s is in local database while being linked from library %s!", + id->name, + id->lib->filepath); + } + } + } + + for (Main *curmain = bmain->next; curmain != nullptr; curmain = curmain->next) { + Library *curlib = curmain->curlib; + if (curlib == nullptr) { + BKE_report(reports, RPT_ERROR, "Library database with nullptr library data-block!"); + continue; + } + + BKE_library_filepath_set(bmain, curlib, curlib->filepath); + BlendFileReadReport bf_reports{}; + bf_reports.reports = reports; + BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, &bf_reports); + + if (bh == nullptr) { + BKE_reportf(reports, + RPT_ERROR, + "Library ID %s not found at expected path %s!", + curlib->id.name, + curlib->filepath_abs); + continue; + } + + i = set_listbasepointers(curmain, lbarray); + while (i--) { + ID *id = static_cast<ID *>(lbarray[i]->first); + if (id == nullptr) { + continue; + } + + if (GS(id->name) == ID_LI) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "Library ID %s in library %s, this should not happen!", + id->name, + curlib->filepath); + continue; + } + + int totnames = 0; + LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames); + for (; id != nullptr; id = static_cast<ID *>(id->next)) { + if (!ID_IS_LINKED(id)) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s has nullptr lib pointer while being in library %s!", + id->name, + curlib->filepath); + continue; + } + if (id->lib != curlib) { + is_valid = false; + BKE_reportf(reports, RPT_ERROR, "ID %s has mismatched lib pointer!", id->name); + continue; + } + + LinkNode *name = names; + for (; name; name = name->next) { + char *str_name = (char *)name->link; + if (id->name[2] == str_name[0] && STREQ(str_name, id->name + 2)) { + break; + } + } + + if (name == nullptr) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s not found in library %s anymore!", + id->name, + id->lib->filepath); + continue; + } + } + + BLI_linklist_freeN(names); + } + + BLO_blendhandle_close(bh); + } + + blo_join_main(&mainlist); + + BLI_assert(BLI_listbase_is_single(&mainlist)); + BLI_assert(mainlist.first == (void *)bmain); + + BKE_main_unlock(bmain); + + return is_valid; +} + +bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports) +{ + ListBase *lb; + ID *id; + bool is_valid = true; + + BKE_main_lock(bmain); + + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (!BKE_key_idtype_support(GS(id->name))) { + break; + } + if (!ID_IS_LINKED(id)) { + /* We assume lib data is valid... */ + Key *shapekey = BKE_key_from_id(id); + if (shapekey != nullptr && shapekey->from != id) { + is_valid = false; + BKE_reportf(reports, + RPT_ERROR, + "ID %s uses shapekey %s, but its 'from' pointer is invalid (%p), fixing...", + id->name, + shapekey->id.name, + shapekey->from); + shapekey->from = id; + } + } + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + + BKE_main_unlock(bmain); + + /* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here. + */ + LISTBASE_FOREACH_MUTABLE (Key *, shapekey, &bmain->shapekeys) { + if (shapekey->from != nullptr) { + continue; + } + + BKE_reportf(reports, + RPT_ERROR, + "Shapekey %s has an invalid 'from' pointer (%p), it will be deleted", + shapekey->id.name, + shapekey->from); + BKE_id_delete(bmain, shapekey); + } + + return is_valid; +} |