// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #pragma once #include "Asset.h" #include "Engine/Core/Types/Pair.h" #include "Storage/FlaxStorageReference.h" // Helper define #define DECLARE_BINARY_ASSET_HEADER(type, serializedVersion) \ DECLARE_ASSET_HEADER(type); \ static const uint32 SerializedVersion = serializedVersion; \ uint32 GetSerializedVersion() const override { return SerializedVersion; } \ static_assert(true, "") #define REGISTER_BINARY_ASSET_ABSTRACT(type, typeName) \ const String type::TypeName = TEXT(typeName) /// /// Base class for all binary assets. /// /// API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API BinaryAsset : public Asset { DECLARE_ASSET_HEADER(BinaryAsset); protected: AssetHeader _header; FlaxStorageReference _storageRef; // Allow asset to have missing storage reference but only before asset is loaded or if it's virtual bool _isSaving; Array _dependantAssets; public: /// /// Finalizes an instance of the class. /// ~BinaryAsset(); public: /// /// The asset storage container. /// FlaxStorage* Storage; #if USE_EDITOR /// /// The asset metadata information. Stored in a Json format. /// BytesContainer Metadata; /// /// Asset dependencies list used by the asset for tracking (eg. material functions used by material asset). The pair of asset ID and cached file edit time (for tracking modification). /// Array> Dependencies; #endif public: /// /// Gets the asset serialized version. /// virtual uint32 GetSerializedVersion() const = 0; /// /// Initializes the specified asset with asset header and storage only. Asset should be initialized later using will AssetInitData. /// /// The storage container. /// The asset header. bool Init(const FlaxStorageReference& storage, AssetHeader& header); /// /// Initializes the specified asset. /// /// The initialize data. /// True if cannot init, otherwise false bool Init(AssetInitData& initData); /// /// Initializes the specified asset (as virtual). /// /// The initialize data. /// True if cannot init, otherwise false bool InitVirtual(AssetInitData& initData); public: #if USE_EDITOR #if COMPILE_WITH_ASSETS_IMPORTER /// /// Reimports asset from the source file. /// API_FUNCTION() void Reimport() const; #endif /// /// Returns imported file metadata. /// /// The import path. /// The import username. void GetImportMetadata(String& path, String& username) const; /// /// Gets the imported file path from the asset metadata (can be empty if not available). /// API_PROPERTY() String GetImportPath() const; /// /// Clears the asset dependencies list and unregisters from tracking their changes. /// void ClearDependencies(); /// /// Adds the dependency to the given asset. /// /// The asset to depend on. void AddDependency(BinaryAsset* asset); /// /// Determines whether any of the dependency assets was modified after last modification time of this asset (last file write time check). /// bool HasDependenciesModified() const; protected: /// /// Called when one of the asset dependencies gets modified (it was saved or reloaded or reimported). /// /// The modified asset that this asset depends on. virtual void OnDependencyModified(BinaryAsset* asset) { } #endif protected: /// /// Initializes the specified asset. /// /// The initialize data. /// True if cannot init, otherwise false virtual bool init(AssetInitData& initData) { return false; } /// /// Gets packed chunks indices to preload before asset loading action /// /// Chunks to load flags virtual AssetChunksFlag getChunksToPreload() const { return 0; } public: /// /// Gets the asset chunk. /// /// The chunk index. /// Flax Chunk object or null if is missing FlaxChunk* GetChunk(int32 index) const { ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS); auto chunk = _header.Chunks[index]; if (chunk) chunk->RegisterUsage(); return chunk; } /// /// Gets or creates the asset chunk. /// /// The chunk index. /// Flax Chunk object (may be null if asset storage doesn't allow to add new chunks). FlaxChunk* GetOrCreateChunk(int32 index); /// /// Determines whether the specified chunk exists. /// /// The chunk index. /// True if the specified chunk exists, otherwise false. bool HasChunk(int32 index) const { ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS); return _header.Chunks[index] != nullptr; } /// /// Gets the chunk size (in bytes). /// /// The chunk index. /// Chunk size (in bytes) uint32 GetChunkSize(int32 index) const { ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS); return _header.Chunks[index] != nullptr ? _header.Chunks[index]->LocationInFile.Size : 0; } /// /// Determines whether the specified chunk exists and is loaded. /// /// The chunk index. /// True if the specified chunk exists and is loaded, otherwise false. bool HasChunkLoaded(int32 index) const { ASSERT(index >= 0 && index < ASSET_FILE_DATA_CHUNKS); return _header.Chunks[index] != nullptr && _header.Chunks[index]->IsLoaded(); } /// /// Sets the chunk data (creates new chunk if is missing). /// /// The chunk index. /// The chunk data to set. void SetChunk(int32 index, const Span& data); /// /// Releases all the chunks data. /// void ReleaseChunks() const; /// /// Releases the chunk data (if loaded). /// /// The chunk index. void ReleaseChunk(int32 index) const; /// /// Requests the chunk data asynchronously (creates task that will gather chunk data or null if already here). /// /// The chunk index. /// Task that will gather chunk data or null if already here. ContentLoadTask* RequestChunkDataAsync(int32 index); /// /// Gets chunk data. May fail if not data requested. See RequestChunkDataAsync. /// /// The chunk index. /// Result data (linked chunk data, not copied). void GetChunkData(int32 index, BytesContainer& data) const; /// /// Loads the chunk (synchronous, blocks current thread). /// /// The chunk index to load. /// True if failed, otherwise false. bool LoadChunk(int32 chunkIndex); /// /// Loads the chunks (synchronous, blocks current thread). /// /// The chunks to load (packed in a flag). /// True if failed, otherwise false. bool LoadChunks(AssetChunksFlag chunks); #if USE_EDITOR /// /// Saves this asset to the storage container. /// /// Asset data. /// In silent mode don't reload opened storage container that is using target file. /// True if failed, otherwise false. bool SaveAsset(AssetInitData& data, bool silentMode = false) const; /// /// Saves this asset to the file. /// /// Asset data. /// Asset path (will be used to override the asset or create a new one). /// In silent mode don't reload opened storage container that is using target file. /// True if failed, otherwise false. bool SaveAsset(const StringView& path, AssetInitData& data, bool silentMode = false) const; /// /// Saves asset data to the storage container. Asset unique ID is handled by auto. /// /// Asset path (will be used to override the asset or create a new one). /// Asset data. /// In silent mode don't reload opened storage container that is using target file. /// True if failed, otherwise false. static bool SaveToAsset(const StringView& path, AssetInitData& data, bool silentMode = false); #endif protected: /// /// Load data from the chunks /// /// Loading result virtual LoadResult load() = 0; private: #if USE_EDITOR void OnStorageReloaded(FlaxStorage* storage, bool failed); #endif public: // [Asset] #if USE_EDITOR void OnDeleteObject() override; #endif const String& GetPath() const final override; uint64 GetMemoryUsage() const override; protected: // [Asset] ContentLoadTask* createLoadingTask() override; LoadResult loadAsset() override; void releaseStorage() final override; #if USE_EDITOR void onRename(const StringView& newPath) override; #endif };