Blender V5.0
asset_indexer.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <ctime>
10#include <fstream>
11#include <iomanip>
12#include <optional>
13
14#include "ED_asset_indexer.hh"
15
16#include "DNA_ID.h"
17#include "DNA_asset_types.h"
18
19#include "BLI_fileops.h"
20#include "BLI_hash.hh"
21#include "BLI_linklist.h"
22#include "BLI_listbase.h"
23#include "BLI_path_utils.hh"
24#include "BLI_serialize.hh"
25#include "BLI_set.hh"
26#include "BLI_string.h"
27#include "BLI_string_ref.hh"
28#include "BLI_string_utf8.h"
29
30#include "AS_asset_catalog.hh"
31#include "BKE_appdir.hh"
32#include "BKE_asset.hh"
33#include "BKE_idprop.hh"
34
35#include "CLG_log.h"
36
37#include <sstream>
38
39static CLG_LogRef LOG = {"asset.index"};
40
42
43using namespace blender::asset_system;
44using namespace blender::io::serialize;
45using namespace blender::bke::idprop;
46
80constexpr StringRef ATTRIBUTE_VERSION("version");
81constexpr StringRef ATTRIBUTE_ENTRIES("entries");
84constexpr StringRef ATTRIBUTE_ENTRIES_CATALOG_NAME("catalog_name");
91
94 public:
95 virtual ~AbstractFile() = default;
96
97 virtual const char *get_file_path() const = 0;
98
99 bool exists() const
100 {
101 return BLI_exists(this->get_file_path());
102 }
103
104 size_t get_file_size() const
105 {
106 return BLI_file_size(this->get_file_path());
107 }
108};
109
113class BlendFile : public AbstractFile {
114 StringRefNull file_path_;
115
116 public:
117 BlendFile(StringRefNull file_path) : file_path_(file_path) {}
118
120 {
122 return hasher(file_path_);
123 }
124
125 std::string get_filename() const
126 {
127 char filename[FILE_MAX];
128 BLI_path_split_file_part(this->get_file_path(), filename, sizeof(filename));
129 return std::string(filename);
130 }
131
132 const char *get_file_path() const override
133 {
134 return file_path_.c_str();
135 }
136};
137
143static void add_id_name(DictionaryValue &result, const short idcode, const StringRefNull name)
144{
145 char idcode_prefix[2];
146 /* Similar to `BKE_libblock_alloc`. */
147 *((short *)idcode_prefix) = idcode;
148 std::string name_with_idcode = std::string(idcode_prefix, sizeof(idcode_prefix)) + name;
149
150 result.append_str(ATTRIBUTE_ENTRIES_NAME, name_with_idcode);
151}
152
154 const FileIndexerEntry *indexer_entry)
155{
156 const BLODataBlockInfo &datablock_info = indexer_entry->datablock_info;
157
158 add_id_name(result, indexer_entry->idcode, datablock_info.name);
159
160 const AssetMetaData &asset_data = *datablock_info.asset_data;
163
164 if (const char *description = asset_data.description) {
165 result.append_str(ATTRIBUTE_ENTRIES_DESCRIPTION, description);
166 }
167 if (const char *author = asset_data.author) {
168 result.append_str(ATTRIBUTE_ENTRIES_AUTHOR, author);
169 }
170 if (const char *copyright = asset_data.copyright) {
171 result.append_str(ATTRIBUTE_ENTRIES_COPYRIGHT, copyright);
172 }
173 if (const char *license = asset_data.license) {
174 result.append_str(ATTRIBUTE_ENTRIES_LICENSE, license);
175 }
176
177 if (!BLI_listbase_is_empty(&asset_data.tags)) {
178 ArrayValue &tags = *result.append_array(ATTRIBUTE_ENTRIES_TAGS);
179 LISTBASE_FOREACH (AssetTag *, tag, &asset_data.tags) {
180 tags.append_str(tag->name);
181 }
182 }
183
184 if (const IDProperty *properties = asset_data.properties) {
185 if (std::unique_ptr<Value> value = convert_to_serialize_values(properties)) {
186 result.append(ATTRIBUTE_ENTRIES_PROPERTIES, std::move(value));
187 }
188 }
189}
190
192 const FileIndexerEntries &indexer_entries)
193{
194 auto entries = std::make_shared<ArrayValue>();
195
196 for (LinkNode *ln = indexer_entries.entries; ln; ln = ln->next) {
197 const FileIndexerEntry *indexer_entry = static_cast<const FileIndexerEntry *>(ln->link);
198 /* We also get non asset types (brushes, work-spaces), when browsing using the asset browser.
199 */
200 if (indexer_entry->datablock_info.asset_data == nullptr) {
201 continue;
202 }
203 init_value_from_file_indexer_entry(*entries->append_dict(), indexer_entry);
204 }
205
206 /* When no entries to index, we should not store the entries attribute as this would make the
207 * size bigger than the #MIN_FILE_SIZE_WITH_ENTRIES. */
208 if (entries->elements().is_empty()) {
209 return;
210 }
211
212 result.append(ATTRIBUTE_ENTRIES, entries);
213}
214
216 const DictionaryValue &entry)
217{
218 const StringRef idcode_name = *entry.lookup_str(ATTRIBUTE_ENTRIES_NAME);
219
220 indexer_entry.idcode = GS(idcode_name.data());
221
222 idcode_name.substr(2).copy_utf8_truncated(indexer_entry.datablock_info.name);
223
225 indexer_entry.datablock_info.asset_data = asset_data;
226 indexer_entry.datablock_info.free_asset_data = true;
227
228 if (const std::optional<StringRef> value = entry.lookup_str(ATTRIBUTE_ENTRIES_DESCRIPTION)) {
229 asset_data->description = BLI_strdupn(value->data(), value->size());
230 }
231 if (const std::optional<StringRef> value = entry.lookup_str(ATTRIBUTE_ENTRIES_AUTHOR)) {
232 asset_data->author = BLI_strdupn(value->data(), value->size());
233 }
234 if (const std::optional<StringRef> value = entry.lookup_str(ATTRIBUTE_ENTRIES_COPYRIGHT)) {
235 asset_data->copyright = BLI_strdupn(value->data(), value->size());
236 }
237 if (const std::optional<StringRef> value = entry.lookup_str(ATTRIBUTE_ENTRIES_LICENSE)) {
238 asset_data->license = BLI_strdupn(value->data(), value->size());
239 }
240
241 const StringRefNull catalog_name = *entry.lookup_str(ATTRIBUTE_ENTRIES_CATALOG_NAME);
242 STRNCPY_UTF8(asset_data->catalog_simple_name, catalog_name.c_str());
243
244 const StringRefNull catalog_id = *entry.lookup_str(ATTRIBUTE_ENTRIES_CATALOG_ID);
245 asset_data->catalog_id = CatalogID(catalog_id);
246
247 if (const ArrayValue *array_value = entry.lookup_array(ATTRIBUTE_ENTRIES_TAGS)) {
248 for (const std::shared_ptr<Value> &item : array_value->elements()) {
249 BKE_asset_metadata_tag_add(asset_data, item->as_string_value()->value().c_str());
250 }
251 }
252
253 if (const std::shared_ptr<Value> *value = entry.lookup(ATTRIBUTE_ENTRIES_PROPERTIES)) {
254 asset_data->properties = convert_from_serialize_value(**value);
255 }
256}
257
259 const DictionaryValue &value)
260{
261 const ArrayValue *entries = value.lookup_array(ATTRIBUTE_ENTRIES);
262 BLI_assert(entries != nullptr);
263 if (entries == nullptr) {
264 return 0;
265 }
266
267 int num_entries_read = 0;
268 for (const std::shared_ptr<Value> &element : entries->elements()) {
270 init_indexer_entry_from_value(*entry, *element->as_dictionary_value());
271
272 BLI_linklist_prepend(&indexer_entries.entries, entry);
273 num_entries_read += 1;
274 }
275
276 return num_entries_read;
277}
278
289 bool is_used = false;
290 };
291
300
306 std::string indices_base_path;
307
308 std::string library_path;
309
314
316 {
317 return get_default_hash(this->library_path);
318 }
319
321 {
322 return this->library_path;
323 }
324
331 {
332 char index_path[FILE_MAX];
333 BKE_appdir_folder_caches(index_path, sizeof(index_path));
334
335 BLI_path_append(index_path, sizeof(index_path), "asset-library-indices");
336
337 std::stringstream ss;
338 ss << std::setfill('0') << std::setw(16) << std::hex << hash() << SEP_STR;
339 BLI_path_append(index_path, sizeof(index_path), ss.str().c_str());
340
341 this->indices_base_path = std::string(index_path);
342 }
343
349 std::string index_file_path(const BlendFile &asset_file) const
350 {
351 std::stringstream ss;
352 ss << this->indices_base_path;
353 ss << std::setfill('0') << std::setw(16) << std::hex << asset_file.hash() << "_"
354 << asset_file.get_filename() << ".index.json";
355 return ss.str();
356 }
357
363 {
364 const char *index_path = this->indices_base_path.c_str();
365 if (!BLI_is_dir(index_path)) {
366 return;
367 }
368 direntry *dir_entries = nullptr;
369 const int dir_entries_num = BLI_filelist_dir_contents(index_path, &dir_entries);
370 for (int i = 0; i < dir_entries_num; i++) {
371 direntry *entry = &dir_entries[i];
372 if (BLI_str_endswith(entry->relname, ".index.json")) {
373 this->preexisting_file_indices.add_as(std::string(entry->path));
374 }
375 }
376
377 BLI_filelist_free(dir_entries, dir_entries_num);
378 }
379
380 void mark_as_used(const std::string &filename)
381 {
382 PreexistingFileIndexInfo *preexisting = this->preexisting_file_indices.lookup_ptr(filename);
383 if (preexisting) {
384 preexisting->is_used = true;
385 }
386 }
387
393 bool delete_file_index(const std::string &filename)
394 {
395 if (BLI_delete(filename.c_str(), false, false) == 0) {
396 this->preexisting_file_indices.remove(filename);
397 return true;
398 }
399 return false;
400 }
401
406 /* Implemented further below. */
408
410 {
411 int num_files_deleted = 0;
412
413 Set<StringRef> files_to_remove;
414
415 for (auto preexisting_index : this->preexisting_file_indices.items()) {
416 if (preexisting_index.value.is_used) {
417 continue;
418 }
419
420 const std::string &file_path = preexisting_index.key;
421 CLOG_DEBUG(&LOG, "Remove unused index file \"%s\".", file_path.c_str());
422 files_to_remove.add(preexisting_index.key);
423 }
424
425 for (StringRef file_to_remove : files_to_remove) {
426 if (delete_file_index(file_to_remove)) {
427 num_files_deleted++;
428 }
429 }
430
431 return num_files_deleted;
432 }
433};
434
453 static const int CURRENT_VERSION = 1;
454
458 const int UNKNOWN_VERSION = -1;
459
467 std::unique_ptr<Value> contents;
468
473 AssetIndex(const FileIndexerEntries &indexer_entries)
474 {
475 std::unique_ptr<DictionaryValue> root = std::make_unique<DictionaryValue>();
476 root->append_int(ATTRIBUTE_VERSION, CURRENT_VERSION);
477 init_value_from_file_indexer_entries(*root, indexer_entries);
478
479 this->contents = std::move(root);
480 }
481
486 AssetIndex(std::unique_ptr<Value> &value) : contents(std::move(value)) {}
487
488 int get_version() const
489 {
490 const DictionaryValue *root = this->contents->as_dictionary_value();
491 if (root == nullptr) {
492 return UNKNOWN_VERSION;
493 }
494 const std::optional<int64_t> version_value = root->lookup_int(ATTRIBUTE_VERSION);
495 return version_value.value_or(UNKNOWN_VERSION);
496 }
497
498 bool is_latest_version() const
499 {
500 return get_version() == CURRENT_VERSION;
501 }
502
508 int extract_into(FileIndexerEntries &indexer_entries) const
509 {
510 const DictionaryValue *root = this->contents->as_dictionary_value();
511 const int num_entries_read = init_indexer_entries_from_value(indexer_entries, *root);
512 return num_entries_read;
513 }
514};
515
517 public:
523 const size_t MIN_FILE_SIZE_WITH_ENTRIES = 32;
524 std::string filename;
525
527 : library_index(library_index), filename(index_file_path)
528 {
529 }
530
532 : AssetIndexFile(library_index, library_index.index_file_path(asset_filename))
533 {
534 }
535
537 {
538 this->library_index.mark_as_used(this->filename);
539 }
540
541 const char *get_file_path() const override
542 {
543 return filename.c_str();
544 }
545
549 bool is_older_than(const BlendFile &asset_file) const
550 {
551 return BLI_file_older(this->get_file_path(), asset_file.get_file_path());
552 }
553
557 bool constains_entries() const
558 {
559 const size_t file_size = get_file_size();
560 return file_size >= MIN_FILE_SIZE_WITH_ENTRIES;
561 }
562
563 std::unique_ptr<AssetIndex> read_contents() const
564 {
565 JsonFormatter formatter;
566 std::ifstream is;
567 is.open(this->filename);
568 BLI_SCOPED_DEFER([&]() { is.close(); });
569
570 std::unique_ptr<Value> read_data = formatter.deserialize(is);
571 if (!read_data) {
572 return nullptr;
573 }
574
575 return std::make_unique<AssetIndex>(read_data);
576 }
577
579 {
581 }
582
584 {
585 JsonFormatter formatter;
587 CLOG_ERROR(&LOG, "Index not created: couldn't create folder \"%s\".", this->get_file_path());
588 return;
589 }
590
591 std::ofstream os;
592 os.open(this->filename, std::ios::out | std::ios::trunc);
593 formatter.serialize(os, *content.contents);
594 os.close();
595 }
596};
597
598/* TODO(Julian): remove this after a short while. Just necessary for people who've been using alpha
599 * builds from a certain period. */
601{
602 Set<StringRef> files_to_remove;
603
604 for (const std::string &index_path : this->preexisting_file_indices.keys()) {
605 AssetIndexFile index_file(*this, index_path);
606
607 /* Bug was causing empty index files, so non-empty ones can be skipped. */
608 if (index_file.constains_entries()) {
609 continue;
610 }
611
612 /* Use the file modification time stamp to attempt to remove empty index files from a
613 * certain period (when the bug was in there). Starting from a day before the bug was
614 * introduced until a day after the fix should be enough to mitigate possible local time
615 * zone issues. */
616
617 std::tm tm_from{};
618 tm_from.tm_year = 2022 - 1900; /* 2022 */
619 tm_from.tm_mon = 11 - 1; /* November */
620 tm_from.tm_mday = 8; /* Day before bug was introduced. */
621 std::tm tm_to{};
622 tm_from.tm_year = 2022 - 1900; /* 2022 */
623 tm_from.tm_mon = 12 - 1; /* December */
624 tm_from.tm_mday = 3; /* Day after fix. */
625 std::time_t timestamp_from = std::mktime(&tm_from);
626 std::time_t timestamp_to = std::mktime(&tm_to);
627 BLI_stat_t stat = {};
628 if (BLI_stat(index_file.get_file_path(), &stat) == -1) {
629 continue;
630 }
631 if (IN_RANGE(stat.st_mtime, timestamp_from, timestamp_to)) {
632 CLOG_DEBUG(&LOG, "Remove potentially broken index file \"%s\".", index_path.c_str());
633 files_to_remove.add(index_path);
634 }
635 }
636
637 int num_files_deleted = 0;
638 for (StringRef filepath : files_to_remove) {
639 if (delete_file_index(filepath)) {
640 num_files_deleted++;
641 }
642 }
643
644 return num_files_deleted;
645}
646
647static eFileIndexerResult read_index(const char *filename,
648 FileIndexerEntries *entries,
649 int *r_read_entries_len,
650 void *user_data)
651{
652 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
653 BlendFile asset_file(filename);
654 AssetIndexFile asset_index_file(library_index, asset_file);
655
656 if (!asset_index_file.exists()) {
658 }
659
660 /* Mark index as used, even when it will be recreated. When not done it would remove the index
661 * when the indexing has finished (see `AssetLibraryIndex.remove_unused_index_files`), thereby
662 * removing the newly created index.
663 */
664 asset_index_file.mark_as_used();
665
666 if (asset_index_file.is_older_than(asset_file)) {
668 &LOG,
669 "Asset index file \"%s\" needs to be refreshed as it is older than the asset file \"%s\".",
670 asset_index_file.filename.c_str(),
671 filename);
673 }
674
675 if (!asset_index_file.constains_entries()) {
677 "Asset file index is to small to contain any entries. \"%s\"",
678 asset_index_file.filename.c_str());
679 *r_read_entries_len = 0;
681 }
682
683 std::unique_ptr<AssetIndex> contents = asset_index_file.read_contents();
684 if (!contents) {
685 CLOG_DEBUG(&LOG, "Asset file index is ignored; failed to read contents.");
687 }
688
689 if (!contents->is_latest_version()) {
691 "Asset file index is ignored; expected version %d but file is version %d \"%s\".",
693 contents->get_version(),
694 asset_index_file.filename.c_str());
696 }
697
698 const int read_entries_len = contents->extract_into(*entries);
699 CLOG_INFO(&LOG, "Read %d entries for \"%s\".", read_entries_len, filename);
700 *r_read_entries_len = read_entries_len;
701
703}
704
705static void update_index(const char *filename, FileIndexerEntries *entries, void *user_data)
706{
707 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
708 BlendFile asset_file(filename);
709 AssetIndexFile asset_index_file(library_index, asset_file);
710 CLOG_INFO(&LOG,
711 "Update for \"%s\" store index in \"%s\".",
712 asset_file.get_file_path(),
713 asset_index_file.get_file_path());
714
715 AssetIndex content(*entries);
716 asset_index_file.write_contents(content);
717}
718
719static void *init_user_data(const char *root_directory, size_t root_directory_maxncpy)
720{
721 AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>(
722 __func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxncpy)));
723 library_index->collect_preexisting_file_indices();
724 library_index->remove_broken_index_files();
725 return library_index;
726}
727
728static void free_user_data(void *user_data)
729{
730 MEM_delete((AssetLibraryIndex *)user_data);
731}
732
733static void filelist_finished(void *user_data)
734{
735 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
736 const int num_indices_removed = library_index.remove_unused_index_files();
737 if (num_indices_removed > 0) {
738 CLOG_INFO(&LOG, "Removed %d unused indices.", num_indices_removed);
739 }
740}
741
743{
744 FileIndexerType indexer = {nullptr};
745 indexer.read_index = read_index;
746 indexer.update_index = update_index;
750 return indexer;
751}
752
754
755} // namespace blender::ed::asset::index
bool BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:203
AssetMetaData * BKE_asset_metadata_create()
Definition asset.cc:32
AssetTag * BKE_asset_metadata_tag_add(AssetMetaData *asset_data, const char *name) ATTR_NONNULL(1
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
bool BLI_file_older(const char *file1, const char *file2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:614
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:226
struct stat BLI_stat_t
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:443
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
bool BLI_file_ensure_parent_dir_exists(const char *filepath) ATTR_NONNULL(1)
Definition fileops_c.cc:452
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
#define BLI_SCOPED_DEFER(function_to_defer)
size_t BLI_path_append(char *__restrict dst, size_t dst_maxncpy, const char *__restrict file) ATTR_NONNULL(1
#define FILE_MAX
void void void BLI_path_split_file_part(const char *filepath, char *file, size_t file_maxncpy) ATTR_NONNULL(1
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:30
int char char int int int int size_t BLI_strnlen(const char *str, size_t maxlen) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition string.cc:913
int bool bool BLI_str_endswith(const char *__restrict str, const char *__restrict end) ATTR_NONNULL(1
#define STRNCPY_UTF8(dst, src)
#define IN_RANGE(a, b, c)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_DEBUG(clg_ref,...)
Definition CLG_log.h:191
#define CLOG_INFO(clg_ref,...)
Definition CLG_log.h:190
ID and Library types, which are fundamental for SDNA.
eFileIndexerResult
@ FILE_INDEXER_ENTRIES_LOADED
@ FILE_INDEXER_NEEDS_UPDATE
ATTR_WARN_UNUSED_RESULT const void * element
unsigned long long int uint64_t
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr StringRef substr(int64_t start, int64_t size) const
void copy_utf8_truncated(char *dst, int64_t dst_size) const
Definition string_ref.cc:28
constexpr const char * data() const
constexpr const char * c_str() const
std::string str() const
Definition uuid.cc:171
virtual const char * get_file_path() const =0
const char * get_file_path() const override
AssetIndexFile(AssetLibraryIndex &library_index, BlendFile &asset_filename)
std::unique_ptr< AssetIndex > read_contents() const
bool is_older_than(const BlendFile &asset_file) const
AssetIndexFile(AssetLibraryIndex &library_index, StringRef index_file_path)
Reference to a blend file that can be indexed.
BlendFile(StringRefNull file_path)
const char * get_file_path() const override
Span< std::shared_ptr< Value > > elements() const
void append_str(std::string value)
Definition serialize.cc:232
const std::shared_ptr< Value > * lookup(StringRef key) const
Definition serialize.cc:265
std::optional< StringRefNull > lookup_str(StringRef key) const
Definition serialize.cc:275
std::optional< int64_t > lookup_int(StringRef key) const
Definition serialize.cc:285
const ArrayValue * lookup_array(StringRef key) const
Definition serialize.cc:313
std::unique_ptr< Value > deserialize(std::istream &is) override
Definition serialize.cc:367
void serialize(std::ostream &os, const Value &value) override
Definition serialize.cc:355
#define GS(x)
#define LOG(level)
Definition log.h:97
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
std::unique_ptr< blender::io::serialize::ArrayValue > convert_to_serialize_values(const IDProperty *properties)
Convert the given properties to Value objects for serialization.
IDProperty * convert_from_serialize_value(const blender::io::serialize::Value &value)
Convert the given value to an IDProperty.
static void free_user_data(void *user_data)
constexpr StringRef ATTRIBUTE_ENTRIES_CATALOG_NAME("catalog_name")
constexpr StringRef ATTRIBUTE_ENTRIES_TAGS("tags")
constexpr StringRef ATTRIBUTE_ENTRIES_NAME("name")
static void * init_user_data(const char *root_directory, size_t root_directory_maxncpy)
constexpr StringRef ATTRIBUTE_ENTRIES_CATALOG_ID("catalog_id")
static int init_indexer_entries_from_value(FileIndexerEntries &indexer_entries, const DictionaryValue &value)
constexpr StringRef ATTRIBUTE_ENTRIES_DESCRIPTION("description")
constexpr StringRef ATTRIBUTE_ENTRIES_PROPERTIES("properties")
const FileIndexerType file_indexer_asset
static void filelist_finished(void *user_data)
static eFileIndexerResult read_index(const char *filename, FileIndexerEntries *entries, int *r_read_entries_len, void *user_data)
constexpr StringRef ATTRIBUTE_ENTRIES_LICENSE("license")
constexpr StringRef ATTRIBUTE_VERSION("version")
Indexer for asset libraries.
static void init_indexer_entry_from_value(FileIndexerEntry &indexer_entry, const DictionaryValue &entry)
static void init_value_from_file_indexer_entry(DictionaryValue &result, const FileIndexerEntry *indexer_entry)
constexpr StringRef ATTRIBUTE_ENTRIES("entries")
static void add_id_name(DictionaryValue &result, const short idcode, const StringRefNull name)
add id + name to the attributes.
static void init_value_from_file_indexer_entries(DictionaryValue &result, const FileIndexerEntries &indexer_entries)
constexpr StringRef ATTRIBUTE_ENTRIES_COPYRIGHT("copyright")
constexpr StringRef ATTRIBUTE_ENTRIES_AUTHOR("author")
constexpr FileIndexerType asset_indexer()
static void update_index(const char *filename, FileIndexerEntries *entries, void *user_data)
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
const char * name
The meta-data of an asset. By creating and giving this for a data-block (ID.asset_data),...
char catalog_simple_name[64]
struct IDProperty * properties
struct bUUID catalog_id
User defined tag. Currently only used by assets, could be used more often at some point....
AssetMetaData * asset_data
BLODataBlockInfo datablock_info
FileIndexerUpdateIndexFunc update_index
FileIndexerFinishedFunc filelist_finished
FileIndexerReadIndexFunc read_index
FileIndexerFreeUserDataFunc free_user_data
FileIndexerInitUserDataFunc init_user_data
struct LinkNode * next
AssetIndex(std::unique_ptr< Value > &value)
int extract_into(FileIndexerEntries &indexer_entries) const
AssetIndex(const FileIndexerEntries &indexer_entries)
static const int CURRENT_VERSION
Version to store in new index files.
References the asset library directory.
void mark_as_used(const std::string &filename)
AssetLibraryIndex(const StringRef library_path)
std::string index_file_path(const BlendFile &asset_file) const
Map< std::string, PreexistingFileIndexInfo > preexisting_file_indices
bool delete_file_index(const std::string &filename)
void init_indices_base_path()
Initializes AssetLibraryIndex.indices_base_path.
std::string indices_base_path
Absolute path where the indices of library are stored.
const char * relname
const char * path
i
Definition text_draw.cc:230
#define SEP_STR
Definition unit.cc:39