Blender V4.3
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
9#include <ctime>
10#include <fstream>
11#include <iomanip>
12#include <optional>
13
14#include "ED_asset_indexer.hh"
15
16#include "DNA_asset_types.h"
17#include "DNA_userdef_types.h"
18
19#include "BLI_fileops.h"
20#include "BLI_hash.hh"
21#include "BLI_linklist.h"
22#include "BLI_path_utils.hh"
23#include "BLI_serialize.hh"
24#include "BLI_set.hh"
25#include "BLI_string.h"
26#include "BLI_string_ref.hh"
27#include "BLI_string_utf8.h"
28#include "BLI_uuid.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 = {"ed.asset"};
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;
161 result.append_str(ATTRIBUTE_ENTRIES_CATALOG_ID, CatalogID(asset_data.catalog_id).str());
162 result.append_str(ATTRIBUTE_ENTRIES_CATALOG_NAME, asset_data.catalog_simple_name);
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)) {
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(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->get());
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()) {
269 FileIndexerEntry *entry = static_cast<FileIndexerEntry *>(
270 MEM_callocN(sizeof(FileIndexerEntry), __func__));
271 init_indexer_entry_from_value(*entry, *element->as_dictionary_value());
272
273 BLI_linklist_prepend(&indexer_entries.entries, entry);
274 num_entries_read += 1;
275 }
276
277 return num_entries_read;
278}
279
290 bool is_used = false;
291 };
292
301
307 std::string indices_base_path;
308
309 std::string library_path;
310
315
317 {
318 return get_default_hash(this->library_path);
319 }
320
322 {
323 return this->library_path;
324 }
325
332 {
333 char index_path[FILE_MAX];
334 BKE_appdir_folder_caches(index_path, sizeof(index_path));
335
336 BLI_path_append(index_path, sizeof(index_path), "asset-library-indices");
337
338 std::stringstream ss;
339 ss << std::setfill('0') << std::setw(16) << std::hex << hash() << SEP_STR;
340 BLI_path_append(index_path, sizeof(index_path), ss.str().c_str());
341
342 this->indices_base_path = std::string(index_path);
343 }
344
350 std::string index_file_path(const BlendFile &asset_file) const
351 {
352 std::stringstream ss;
353 ss << this->indices_base_path;
354 ss << std::setfill('0') << std::setw(16) << std::hex << asset_file.hash() << "_"
355 << asset_file.get_filename() << ".index.json";
356 return ss.str();
357 }
358
364 {
365 const char *index_path = this->indices_base_path.c_str();
366 if (!BLI_is_dir(index_path)) {
367 return;
368 }
369 direntry *dir_entries = nullptr;
370 const int dir_entries_num = BLI_filelist_dir_contents(index_path, &dir_entries);
371 for (int i = 0; i < dir_entries_num; i++) {
372 direntry *entry = &dir_entries[i];
373 if (BLI_str_endswith(entry->relname, ".index.json")) {
374 this->preexisting_file_indices.add_as(std::string(entry->path));
375 }
376 }
377
378 BLI_filelist_free(dir_entries, dir_entries_num);
379 }
380
381 void mark_as_used(const std::string &filename)
382 {
383 PreexistingFileIndexInfo *preexisting = this->preexisting_file_indices.lookup_ptr(filename);
384 if (preexisting) {
385 preexisting->is_used = true;
386 }
387 }
388
394 bool delete_file_index(const std::string &filename)
395 {
396 if (BLI_delete(filename.c_str(), false, false) == 0) {
397 this->preexisting_file_indices.remove(filename);
398 return true;
399 }
400 return false;
401 }
402
407 /* Implemented further below. */
409
411 {
412 int num_files_deleted = 0;
413
414 Set<StringRef> files_to_remove;
415
416 for (auto preexisting_index : this->preexisting_file_indices.items()) {
417 if (preexisting_index.value.is_used) {
418 continue;
419 }
420
421 const std::string &file_path = preexisting_index.key;
422 CLOG_INFO(&LOG, 2, "Remove unused index file [%s].", file_path.c_str());
423 files_to_remove.add(preexisting_index.key);
424 }
425
426 for (StringRef file_to_remove : files_to_remove) {
427 if (delete_file_index(file_to_remove)) {
428 num_files_deleted++;
429 }
430 }
431
432 return num_files_deleted;
433 }
434};
435
454 static const int CURRENT_VERSION = 1;
455
459 const int UNKNOWN_VERSION = -1;
460
468 std::unique_ptr<Value> contents;
469
474 AssetIndex(const FileIndexerEntries &indexer_entries)
475 {
476 std::unique_ptr<DictionaryValue> root = std::make_unique<DictionaryValue>();
477 root->append_int(ATTRIBUTE_VERSION, CURRENT_VERSION);
478 init_value_from_file_indexer_entries(*root, indexer_entries);
479
480 this->contents = std::move(root);
481 }
482
487 AssetIndex(std::unique_ptr<Value> &value) : contents(std::move(value)) {}
488
489 int get_version() const
490 {
491 const DictionaryValue *root = this->contents->as_dictionary_value();
492 if (root == nullptr) {
493 return UNKNOWN_VERSION;
494 }
495 const std::optional<int64_t> version_value = root->lookup_int(ATTRIBUTE_VERSION);
496 return version_value.value_or(UNKNOWN_VERSION);
497 }
498
499 bool is_latest_version() const
500 {
501 return get_version() == CURRENT_VERSION;
502 }
503
509 int extract_into(FileIndexerEntries &indexer_entries) const
510 {
511 const DictionaryValue *root = this->contents->as_dictionary_value();
512 const int num_entries_read = init_indexer_entries_from_value(indexer_entries, *root);
513 return num_entries_read;
514 }
515};
516
518 public:
524 const size_t MIN_FILE_SIZE_WITH_ENTRIES = 32;
525 std::string filename;
526
528 : library_index(library_index), filename(index_file_path)
529 {
530 }
531
533 : AssetIndexFile(library_index, library_index.index_file_path(asset_filename))
534 {
535 }
536
538 {
539 this->library_index.mark_as_used(this->filename);
540 }
541
542 const char *get_file_path() const override
543 {
544 return filename.c_str();
545 }
546
550 bool is_older_than(const BlendFile &asset_file) const
551 {
552 return BLI_file_older(this->get_file_path(), asset_file.get_file_path());
553 }
554
558 bool constains_entries() const
559 {
560 const size_t file_size = get_file_size();
561 return file_size >= MIN_FILE_SIZE_WITH_ENTRIES;
562 }
563
564 std::unique_ptr<AssetIndex> read_contents() const
565 {
566 JsonFormatter formatter;
567 std::ifstream is;
568 is.open(this->filename);
569 BLI_SCOPED_DEFER([&]() { is.close(); });
570
571 std::unique_ptr<Value> read_data = formatter.deserialize(is);
572 if (!read_data) {
573 return nullptr;
574 }
575
576 return std::make_unique<AssetIndex>(read_data);
577 }
578
580 {
582 }
583
585 {
586 JsonFormatter formatter;
588 CLOG_ERROR(&LOG, "Index not created: couldn't create folder [%s].", this->get_file_path());
589 return;
590 }
591
592 std::ofstream os;
593 os.open(this->filename, std::ios::out | std::ios::trunc);
594 formatter.serialize(os, *content.contents);
595 os.close();
596 }
597};
598
599/* TODO(Julian): remove this after a short while. Just necessary for people who've been using alpha
600 * builds from a certain period. */
602{
603 Set<StringRef> files_to_remove;
604
605 for (const std::string &index_path : this->preexisting_file_indices.keys()) {
606 AssetIndexFile index_file(*this, index_path);
607
608 /* Bug was causing empty index files, so non-empty ones can be skipped. */
609 if (index_file.constains_entries()) {
610 continue;
611 }
612
613 /* Use the file modification time stamp to attempt to remove empty index files from a
614 * certain period (when the bug was in there). Starting from a day before the bug was
615 * introduced until a day after the fix should be enough to mitigate possible local time
616 * zone issues. */
617
618 std::tm tm_from{};
619 tm_from.tm_year = 2022 - 1900; /* 2022 */
620 tm_from.tm_mon = 11 - 1; /* November */
621 tm_from.tm_mday = 8; /* Day before bug was introduced. */
622 std::tm tm_to{};
623 tm_from.tm_year = 2022 - 1900; /* 2022 */
624 tm_from.tm_mon = 12 - 1; /* December */
625 tm_from.tm_mday = 3; /* Day after fix. */
626 std::time_t timestamp_from = std::mktime(&tm_from);
627 std::time_t timestamp_to = std::mktime(&tm_to);
628 BLI_stat_t stat = {};
629 if (BLI_stat(index_file.get_file_path(), &stat) == -1) {
630 continue;
631 }
632 if (IN_RANGE(stat.st_mtime, timestamp_from, timestamp_to)) {
633 CLOG_INFO(&LOG, 2, "Remove potentially broken index file [%s].", index_path.c_str());
634 files_to_remove.add(index_path);
635 }
636 }
637
638 int num_files_deleted = 0;
639 for (StringRef filepath : files_to_remove) {
640 if (delete_file_index(filepath)) {
641 num_files_deleted++;
642 }
643 }
644
645 return num_files_deleted;
646}
647
648static eFileIndexerResult read_index(const char *filename,
649 FileIndexerEntries *entries,
650 int *r_read_entries_len,
651 void *user_data)
652{
653 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
654 BlendFile asset_file(filename);
655 AssetIndexFile asset_index_file(library_index, asset_file);
656
657 if (!asset_index_file.exists()) {
659 }
660
661 /* Mark index as used, even when it will be recreated. When not done it would remove the index
662 * when the indexing has finished (see `AssetLibraryIndex.remove_unused_index_files`), thereby
663 * removing the newly created index.
664 */
665 asset_index_file.mark_as_used();
666
667 if (asset_index_file.is_older_than(asset_file)) {
668 CLOG_INFO(
669 &LOG,
670 3,
671 "Asset index file [%s] needs to be refreshed as it is older than the asset file [%s].",
672 asset_index_file.filename.c_str(),
673 filename);
675 }
676
677 if (!asset_index_file.constains_entries()) {
678 CLOG_INFO(&LOG,
679 3,
680 "Asset file index is to small to contain any entries. [%s]",
681 asset_index_file.filename.c_str());
682 *r_read_entries_len = 0;
684 }
685
686 std::unique_ptr<AssetIndex> contents = asset_index_file.read_contents();
687 if (!contents) {
688 CLOG_INFO(&LOG, 3, "Asset file index is ignored; failed to read contents.");
690 }
691
692 if (!contents->is_latest_version()) {
693 CLOG_INFO(&LOG,
694 3,
695 "Asset file index is ignored; expected version %d but file is version %d [%s].",
697 contents->get_version(),
698 asset_index_file.filename.c_str());
700 }
701
702 const int read_entries_len = contents->extract_into(*entries);
703 CLOG_INFO(&LOG, 1, "Read %d entries from asset index for [%s].", read_entries_len, filename);
704 *r_read_entries_len = read_entries_len;
705
707}
708
709static void update_index(const char *filename, FileIndexerEntries *entries, void *user_data)
710{
711 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
712 BlendFile asset_file(filename);
713 AssetIndexFile asset_index_file(library_index, asset_file);
714 CLOG_INFO(&LOG,
715 1,
716 "Update asset index for [%s] store index in [%s].",
717 asset_file.get_file_path(),
718 asset_index_file.get_file_path());
719
720 AssetIndex content(*entries);
721 asset_index_file.write_contents(content);
722}
723
724static void *init_user_data(const char *root_directory, size_t root_directory_maxncpy)
725{
726 AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>(
727 __func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxncpy)));
728 library_index->collect_preexisting_file_indices();
729 library_index->remove_broken_index_files();
730 return library_index;
731}
732
733static void free_user_data(void *user_data)
734{
735 MEM_delete((AssetLibraryIndex *)user_data);
736}
737
738static void filelist_finished(void *user_data)
739{
740 AssetLibraryIndex &library_index = *static_cast<AssetLibraryIndex *>(user_data);
741 const int num_indices_removed = library_index.remove_unused_index_files();
742 if (num_indices_removed > 0) {
743 CLOG_INFO(&LOG, 1, "Removed %d unused indices.", num_indices_removed);
744 }
745}
746
748{
749 FileIndexerType indexer = {nullptr};
750 indexer.read_index = read_index;
751 indexer.update_index = update_index;
755 return indexer;
756}
757
759
760} // namespace blender::ed::asset::index
bool BKE_appdir_folder_caches(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:226
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:50
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
bool BLI_file_older(const char *file1, const char *file2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:605
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:215
struct stat BLI_stat_t
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:433
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:429
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
#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.c:29
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.c:909
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:182
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
eFileIndexerResult
@ FILE_INDEXER_ENTRIES_LOADED
@ FILE_INDEXER_NEEDS_UPDATE
static CLG_LogRef LOG
bool add(const Key &key)
Definition BLI_set.hh:248
void copy(char *dst, int64_t dst_size) const
constexpr StringRef substr(int64_t start, int64_t size) const
constexpr const char * data() const
constexpr const char * c_str() const
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
std::shared_ptr< ArrayValue > append_array()
Definition serialize.cc:245
void append_str(std::string value)
Definition serialize.cc:228
const std::shared_ptr< Value > * lookup(const StringRef key) const
Definition serialize.cc:261
const ArrayValue * lookup_array(const StringRef key) const
Definition serialize.cc:309
std::optional< int64_t > lookup_int(const StringRef key) const
Definition serialize.cc:281
std::optional< StringRefNull > lookup_str(const StringRef key) const
Definition serialize.cc:271
std::unique_ptr< Value > deserialize(std::istream &is) override
Definition serialize.cc:363
void serialize(std::ostream &os, const Value &value) override
Definition serialize.cc:351
#define GS(x)
Definition iris.cc:202
#define LOG(severity)
Definition log.h:33
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
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)
Definition BLI_hash.hh:219
unsigned __int64 uint64_t
Definition stdint.h:90
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
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
#define SEP_STR
Definition unit.cc:39