2
0
Эх сурвалжийг харах

resource: load from resource bundles

Fixes: #151
Daniele Bartolini 2 жил өмнө
parent
commit
0b348b2f33

+ 104 - 0
src/core/filesystem/file_memory.inl

@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2012-2023 Daniele Bartolini et al.
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include "core/filesystem/file.h"
+
+namespace crown
+{
+/// A struct to access read-only memory as a File.
+///
+/// @ingroup Filesystem
+struct FileMemory : public File
+{
+	const u8 *_memory;
+	u32 _size;
+	u32 _position;
+
+	explicit FileMemory(const void *mem, u32 size)
+		: _memory((const u8 *)mem)
+		, _size(size)
+		, _position(0)
+	{
+	}
+
+	virtual ~FileMemory()
+	{
+		CE_NOOP();
+	}
+
+	virtual void open(const char *path, FileOpenMode::Enum mode) override
+	{
+		CE_UNUSED(path);
+		CE_UNUSED(mode);
+		CE_NOOP();
+	}
+
+	virtual void close() override
+	{
+		_memory = NULL;
+		_size = 0;
+		_position = 0;
+	}
+
+	virtual bool is_open() override
+	{
+		return _memory != NULL;
+	}
+
+	virtual u32 size() override
+	{
+		return _size;
+	}
+
+	virtual u32 position() override
+	{
+		return _position;
+	}
+
+	virtual bool end_of_file() override
+	{
+		return _position == _size;
+	}
+
+	virtual void seek(u32 position) override
+	{
+		_position = position;
+	}
+
+	virtual void seek_to_end() override
+	{
+		_position = _size;
+	}
+
+	virtual void skip(u32 bytes) override
+	{
+		seek(_position + bytes);
+	}
+
+	virtual u32 read(void *data, u32 size) override
+	{
+		const u32 rest = _size - _position;
+		const u32 num = min(size, rest);
+		memcpy(data, _memory + _position, num);
+		_position += num;
+		return num;
+	}
+
+	virtual u32 write(const void *data, u32 size) override
+	{
+		CE_UNUSED_2(data, size);
+		CE_NOOP();
+		return 0;
+	}
+
+	virtual void flush() override
+	{
+		CE_NOOP();
+	}
+};
+
+} // namespace crown

+ 15 - 7
src/device/device.cpp

@@ -293,13 +293,21 @@ void Device::run()
 	_data_filesystem = CE_NEW(_allocator, FilesystemDisk)(default_allocator());
 	{
 		char cwd[1024];
-		const char *data_dir = !_options._data_dir.value().empty()
-			? _options._data_dir.value().c_str()
-			: os::getcwd(cwd, sizeof(cwd))
-			;
+		const char *data_dir = NULL;
+
+		if (!_options._bundle_dir.value().empty())
+			data_dir = _options._bundle_dir.value().c_str();
+
+		if (data_dir == NULL) {
+			if (!_options._data_dir.value().empty())
+				data_dir = _options._data_dir.value().c_str();
+			else
+				data_dir = os::getcwd(cwd, sizeof(cwd));
+		}
+
 		((FilesystemDisk *)_data_filesystem)->set_prefix(data_dir);
 	}
-#endif
+#endif // if CROWN_PLATFORM_ANDROID
 
 	logi(DEVICE, "Crown %s %s %s", CROWN_VERSION, CROWN_PLATFORM_NAME, CROWN_ARCH_NAME);
 
@@ -324,7 +332,7 @@ void Device::run()
 	namespace txr = texture_resource_internal;
 	namespace utr = unit_resource_internal;
 
-	_resource_loader  = CE_NEW(_allocator, ResourceLoader)(*_data_filesystem);
+	_resource_loader  = CE_NEW(_allocator, ResourceLoader)(*_data_filesystem, !_options._bundle_dir.value().empty());
 	_resource_loader->register_fallback(RESOURCE_TYPE_TEXTURE,  STRING_ID_64("core/fallback/fallback", 0xd09058ae71962248));
 	_resource_loader->register_fallback(RESOURCE_TYPE_MATERIAL, STRING_ID_64("core/fallback/fallback", 0xd09058ae71962248));
 	_resource_loader->register_fallback(RESOURCE_TYPE_UNIT,     STRING_ID_64("core/fallback/fallback", 0xd09058ae71962248));
@@ -357,7 +365,7 @@ void Device::run()
 		boot_dir += CROWN_BOOT_CONFIG;
 
 		const StringId64 config_name(boot_dir.c_str());
-		_resource_manager->load(RESOURCE_TYPE_CONFIG, config_name);
+		_resource_manager->load(PACKAGE_RESOURCE_NONE, RESOURCE_TYPE_CONFIG, config_name);
 		_resource_manager->flush();
 		_boot_config.parse((const char *)_resource_manager->get(RESOURCE_TYPE_CONFIG, config_name));
 		_resource_manager->unload(RESOURCE_TYPE_CONFIG, config_name);

+ 2 - 1
src/resource/package_resource.cpp

@@ -252,7 +252,8 @@ namespace package_resource
 
 	const u8 *data(const PackageResource *pr)
 	{
-		return (u8 *)resource_offset(pr, pr->num_resources);
+		const u8 *data_offset = (u8 *)resource_offset(pr, pr->num_resources);
+		return (u8 *)memory::align_top(data_offset, 16);
 	}
 
 } // namespace package_resource

+ 70 - 24
src/resource/resource_loader.cpp

@@ -7,6 +7,7 @@
 #include "core/containers/hash_map.inl"
 #include "core/containers/queue.inl"
 #include "core/filesystem/file.h"
+#include "core/filesystem/file_memory.inl"
 #include "core/filesystem/filesystem.h"
 #include "core/filesystem/path.h"
 #include "core/memory/globals.h"
@@ -16,16 +17,19 @@
 #include "core/strings/string_id.inl"
 #include "core/thread/scoped_mutex.inl"
 #include "device/log.h"
+#include "resource/package_resource.h"
 #include "resource/resource_id.inl"
 #include "resource/resource_loader.h"
+#include "resource/resource_manager.h"
 #include "resource/types.h"
 
 LOG_SYSTEM(RESOURCE_LOADER, "resource_loader")
 
 namespace crown
 {
-ResourceLoader::ResourceLoader(Filesystem &data_filesystem)
+ResourceLoader::ResourceLoader(Filesystem &data_filesystem, bool is_bundle)
 	: _data_filesystem(data_filesystem)
+	, _is_bundle(is_bundle)
 	, _requests(default_allocator())
 	, _loaded(default_allocator())
 	, _fallback(default_allocator())
@@ -102,32 +106,74 @@ s32 ResourceLoader::run()
 		DynamicString path(ta);
 		destination_path(path, res_id);
 
-		File *file = _data_filesystem.open(path.c_str(), FileOpenMode::READ);
-		if (!file->is_open()) {
-			logw(RESOURCE_LOADER, "Can't load resource: " RESOURCE_ID_FMT ". Falling back...", res_id._id);
-
-			StringId64 fallback_name;
-			fallback_name = hash_map::get(_fallback, rr.type, fallback_name);
-			CE_ENSURE(fallback_name._id != 0);
-
-			res_id = resource_id(rr.type, fallback_name);
-			destination_path(path, res_id);
+		if (_is_bundle) {
+			if (rr.type == RESOURCE_TYPE_PACKAGE || rr.type == RESOURCE_TYPE_CONFIG) {
+				File *file = _data_filesystem.open(path.c_str(), FileOpenMode::READ);
+				CE_ASSERT(file->is_open(), "Cannot load " RESOURCE_ID_FMT, res_id);
+
+				// Load the resource.
+				if (rr.load_function) {
+					rr.data = rr.load_function(*file, *rr.allocator);
+				} else {
+					const u32 file_size = file->size();
+					rr.data = rr.allocator->allocate(file_size, 16);
+					file->read(rr.data, file_size);
+					CE_ASSERT(*(u32 *)rr.data == RESOURCE_HEADER(rr.version), "Wrong version");
+				}
+
+				_data_filesystem.close(*file);
+			} else {
+				// Get the package containing the resource.
+				const PackageResource *pkg = (PackageResource *)rr.resource_manager->get(RESOURCE_TYPE_PACKAGE, rr.package_name);
+
+				// Find the resource inside the package.
+				for (u32 ii = 0; ii < pkg->num_resources; ++ii) {
+					const ResourceOffset *offt = package_resource::resource_offset(pkg, ii);
+					if (offt->type == rr.type && offt->name == rr.name) {
+						const void *resource_data = package_resource::data(pkg) + offt->offset;
+
+						// Load the resource.
+						if (rr.load_function) {
+							FileMemory fm(resource_data, offt->size);
+							rr.data = rr.load_function(fm, *rr.allocator);
+						} else {
+							rr.allocator = NULL;
+							rr.data = (void *)resource_data;
+							CE_ASSERT(*(u32 *)rr.data == RESOURCE_HEADER(rr.version), "Wrong version");
+						}
+
+						break;
+					}
+				}
+			}
+		} else {
+			File *file = _data_filesystem.open(path.c_str(), FileOpenMode::READ);
+			if (!file->is_open()) {
+				logw(RESOURCE_LOADER, "Cannot load resource: " RESOURCE_ID_FMT ". Falling back...", res_id._id);
+
+				StringId64 fallback_name;
+				fallback_name = hash_map::get(_fallback, rr.type, fallback_name);
+				CE_ENSURE(fallback_name._id != 0);
+
+				res_id = resource_id(rr.type, fallback_name);
+				destination_path(path, res_id);
+
+				_data_filesystem.close(*file);
+				file = _data_filesystem.open(path.c_str(), FileOpenMode::READ);
+			}
+			CE_ASSERT(file->is_open(), "Cannot load fallback resource: " RESOURCE_ID_FMT, res_id._id);
+
+			if (rr.load_function) {
+				rr.data = rr.load_function(*file, *rr.allocator);
+			} else {
+				const u32 file_size = file->size();
+				rr.data = rr.allocator->allocate(file_size, 16);
+				file->read(rr.data, file_size);
+				CE_ASSERT(*(u32 *)rr.data == RESOURCE_HEADER(rr.version), "Wrong version");
+			}
 
 			_data_filesystem.close(*file);
-			file = _data_filesystem.open(path.c_str(), FileOpenMode::READ);
 		}
-		CE_ASSERT(file->is_open(), "Can't load fallback resource: " RESOURCE_ID_FMT, res_id._id);
-
-		if (rr.load_function) {
-			rr.data = rr.load_function(*file, *rr.allocator);
-		} else {
-			const u32 size = file->size();
-			rr.data = rr.allocator->allocate(size, 16);
-			file->read(rr.data, size);
-			CE_ASSERT(*(u32 *)rr.data == RESOURCE_HEADER(rr.version), "Wrong version");
-		}
-
-		_data_filesystem.close(*file);
 
 		add_loaded(rr);
 		_mutex.lock();

+ 10 - 2
src/resource/resource_loader.h

@@ -12,6 +12,7 @@
 #include "core/thread/mutex.h"
 #include "core/thread/thread.h"
 #include "core/types.h"
+#include "resource/types.h"
 
 namespace crown
 {
@@ -19,6 +20,8 @@ struct ResourceRequest
 {
 	typedef void * (*LoadFunction)(File &file, Allocator &a);
 
+	ResourceManager *resource_manager;
+	StringId64 package_name;
 	StringId64 type;
 	StringId64 name;
 	u32 version;
@@ -33,6 +36,7 @@ struct ResourceRequest
 struct ResourceLoader
 {
 	Filesystem &_data_filesystem;
+	bool _is_bundle;
 
 	Queue<ResourceRequest> _requests;
 	Queue<ResourceRequest> _loaded;
@@ -44,14 +48,18 @@ struct ResourceLoader
 	Mutex _loaded_mutex;
 	bool _exit;
 
+	///
 	u32 num_requests();
+
+	///
 	void add_loaded(ResourceRequest rr);
 
 	/// Do not call explicitly.
 	s32 run();
 
-	/// Read resources from @a data_filesystem.
-	explicit ResourceLoader(Filesystem &data_filesystem);
+	/// Read resources from @a data_filesystem. Is bundle specifies whether
+	/// the filesystem contains bundled data.
+	explicit ResourceLoader(Filesystem &data_filesystem, bool is_bundle);
 
 	///
 	~ResourceLoader();

+ 23 - 21
src/resource/resource_manager.cpp

@@ -35,7 +35,7 @@ bool operator==(const ResourceManager::ResourceEntry &a, const ResourceManager::
 		;
 }
 
-const ResourceManager::ResourceEntry ResourceManager::ResourceEntry::NOT_FOUND = { 0xffffffffu, NULL };
+const ResourceManager::ResourceEntry ResourceManager::ResourceEntry::NOT_FOUND = { 0xffffffffu, NULL, NULL };
 
 template<>
 struct hash<ResourceManager::ResourcePair>
@@ -65,11 +65,11 @@ ResourceManager::~ResourceManager()
 		const StringId64 type = cur->first.type;
 		const StringId64 name = cur->first.name;
 		on_offline(type, name);
-		on_unload(type, cur->second.data);
+		on_unload(type, cur->second.allocator, cur->second.data);
 	}
 }
 
-void ResourceManager::load(StringId64 type, StringId64 name)
+void ResourceManager::load(StringId64 package_name, StringId64 type, StringId64 name)
 {
 	ResourcePair id = { type, name };
 	ResourceEntry &entry = hash_map::get(_rm, id, ResourceEntry::NOT_FOUND);
@@ -84,6 +84,8 @@ void ResourceManager::load(StringId64 type, StringId64 name)
 		rtd = hash_map::get(_type_data, type, rtd);
 
 		ResourceRequest rr;
+		rr.resource_manager = this;
+		rr.package_name = package_name;
 		rr.type = type;
 		rr.name = name;
 		rr.version = rtd.version;
@@ -107,7 +109,7 @@ void ResourceManager::unload(StringId64 type, StringId64 name)
 
 	if (--entry.references == 0) {
 		on_offline(type, name);
-		on_unload(type, entry.data);
+		on_unload(type, entry.allocator, entry.data);
 
 		hash_map::remove(_rm, id);
 	}
@@ -123,7 +125,7 @@ void ResourceManager::reload(StringId64 type, StringId64 name)
 		return;
 
 	unload(type, name);
-	load(type, name);
+	load(PACKAGE_RESOURCE_NONE, type, name);
 	flush();
 
 	ResourceEntry &new_entry = hash_map::get(_rm, id, ResourceEntry::NOT_FOUND);
@@ -145,7 +147,7 @@ const void *ResourceManager::get(StringId64 type, StringId64 name)
 	CE_UNUSED(res_id);
 
 	if (_autoload && !hash_map::has(_rm, id)) {
-		load(type, name);
+		load(PACKAGE_RESOURCE_NONE, type, name);
 		flush();
 	}
 
@@ -170,21 +172,18 @@ void ResourceManager::complete_requests()
 	Array<ResourceRequest> loaded(ta);
 	_loader->get_loaded(loaded);
 
-	for (u32 i = 0; i < array::size(loaded); ++i)
-		complete_request(loaded[i].type, loaded[i].name, loaded[i].data);
-}
-
-void ResourceManager::complete_request(StringId64 type, StringId64 name, void *data)
-{
-	ResourceEntry entry;
-	entry.references = 1;
-	entry.data = data;
+	for (u32 ii = 0; ii < array::size(loaded); ++ii) {
+		ResourceEntry entry;
+		entry.references = 1;
+		entry.data = loaded[ii].data;
+		entry.allocator = loaded[ii].allocator;
 
-	ResourcePair id = { type, name };
+		ResourcePair id = { loaded[ii].type, loaded[ii].name };
 
-	hash_map::set(_rm, id, entry);
+		hash_map::set(_rm, id, entry);
 
-	on_online(type, name);
+		on_online(loaded[ii].type, loaded[ii].name);
+	}
 }
 
 void ResourceManager::register_type(StringId64 type, u32 version, LoadFunction load, UnloadFunction unload, OnlineFunction online, OfflineFunction offline)
@@ -215,14 +214,17 @@ void ResourceManager::on_offline(StringId64 type, StringId64 name)
 		func(name, *this);
 }
 
-void ResourceManager::on_unload(StringId64 type, void *data)
+void ResourceManager::on_unload(StringId64 type, Allocator *allocator, void *data)
 {
+	if (allocator == NULL)
+		return;
+
 	UnloadFunction func = hash_map::get(_type_data, type, ResourceTypeData()).unload;
 
 	if (func)
-		func(_resource_heap, data);
+		func(*allocator, data);
 	else
-		_resource_heap.deallocate(data);
+		allocator->deallocate(data);
 }
 
 } // namespace crown

+ 7 - 6
src/resource/resource_manager.h

@@ -7,12 +7,13 @@
 
 #include "core/containers/types.h"
 #include "core/filesystem/types.h"
+#include "core/json/types.h"
 #include "core/memory/proxy_allocator.h"
 #include "core/strings/string_id.h"
 #include "core/types.h"
-#include "resource/types.h"
-#include "core/json/types.h"
 #include "device/console_server.h"
+#include "resource/resource_id.h"
+#include "resource/types.h"
 
 namespace crown
 {
@@ -35,6 +36,7 @@ struct ResourceManager
 	struct ResourceEntry
 	{
 		u32 references;
+		Allocator *allocator;
 		void *data;
 
 		static const ResourceEntry NOT_FOUND;
@@ -60,8 +62,7 @@ struct ResourceManager
 
 	void on_online(StringId64 type, StringId64 name);
 	void on_offline(StringId64 type, StringId64 name);
-	void on_unload(StringId64 type, void *data);
-	void complete_request(StringId64 type, StringId64 name, void *data);
+	void on_unload(StringId64 type, Allocator *allocator, void *data);
 
 	/// Uses @a rl to load resources.
 	explicit ResourceManager(ResourceLoader &rl);
@@ -69,9 +70,9 @@ struct ResourceManager
 	///
 	~ResourceManager();
 
-	/// Loads the resource (@a type, @a name).
+	/// Loads the resource (@a type, @a name) from the @a package.
 	/// You can check whether the resource is available with can_get().
-	void load(StringId64 type, StringId64 name);
+	void load(StringId64 package_name, StringId64 type, StringId64 name);
 
 	/// Unloads the resource @a type @a name.
 	void unload(StringId64 type, StringId64 name);

+ 13 - 12
src/resource/resource_package.cpp

@@ -5,6 +5,7 @@
 
 #include "core/containers/array.inl"
 #include "resource/package_resource.h"
+#include "resource/resource_id.inl"
 #include "resource/resource_manager.h"
 #include "resource/resource_package.h"
 #include "world/types.h"
@@ -14,33 +15,33 @@ namespace crown
 ResourcePackage::ResourcePackage(StringId64 id, ResourceManager &resman)
 	: _marker(RESOURCE_PACKAGE_MARKER)
 	, _resource_manager(&resman)
-	, _package_id(id)
-	, _package(NULL)
+	, _package_resource_name(id)
+	, _package_resource(NULL)
 {
 }
 
 ResourcePackage::~ResourcePackage()
 {
-	_resource_manager->unload(RESOURCE_TYPE_PACKAGE, _package_id);
+	_resource_manager->unload(RESOURCE_TYPE_PACKAGE, _package_resource_name);
 	_marker = 0;
 }
 
 void ResourcePackage::load()
 {
-	_resource_manager->load(RESOURCE_TYPE_PACKAGE, _package_id);
+	_resource_manager->load(PACKAGE_RESOURCE_NONE, RESOURCE_TYPE_PACKAGE, _package_resource_name);
 	_resource_manager->flush();
-	_package = (const PackageResource *)_resource_manager->get(RESOURCE_TYPE_PACKAGE, _package_id);
+	_package_resource = (const PackageResource *)_resource_manager->get(RESOURCE_TYPE_PACKAGE, _package_resource_name);
 
-	for (u32 ii = 0; ii < _package->num_resources; ++ii) {
-		const ResourceOffset *ro = package_resource::resource_offset(_package, ii);
-		_resource_manager->load(ro->type, ro->name);
+	for (u32 ii = 0; ii < _package_resource->num_resources; ++ii) {
+		const ResourceOffset *ro = package_resource::resource_offset(_package_resource, ii);
+		_resource_manager->load(_package_resource_name, ro->type, ro->name);
 	}
 }
 
 void ResourcePackage::unload()
 {
-	for (u32 ii = 0; ii < _package->num_resources; ++ii) {
-		const ResourceOffset *ro = package_resource::resource_offset(_package, ii);
+	for (u32 ii = 0; ii < _package_resource->num_resources; ++ii) {
+		const ResourceOffset *ro = package_resource::resource_offset(_package_resource, ii);
 		_resource_manager->unload(ro->type, ro->name);
 	}
 }
@@ -52,8 +53,8 @@ void ResourcePackage::flush()
 
 bool ResourcePackage::has_loaded() const
 {
-	for (u32 ii = 0; ii < _package->num_resources; ++ii) {
-		const ResourceOffset *ro = package_resource::resource_offset(_package, ii);
+	for (u32 ii = 0; ii < _package_resource->num_resources; ++ii) {
+		const ResourceOffset *ro = package_resource::resource_offset(_package_resource, ii);
 		if (!_resource_manager->can_get(ro->type, ro->name))
 			return false;
 	}

+ 2 - 2
src/resource/resource_package.h

@@ -16,8 +16,8 @@ struct ResourcePackage
 {
 	u32 _marker;
 	ResourceManager *_resource_manager;
-	StringId64 _package_id;
-	const PackageResource *_package;
+	StringId64 _package_resource_name;
+	const PackageResource *_package_resource;
 
 	///
 	ResourcePackage(StringId64 id, ResourceManager &resman);

+ 3 - 0
src/resource/types.h

@@ -55,6 +55,9 @@ struct UnitResource;
 #define RESOURCE_TYPE_SPRITE           STRING_ID_64("sprite",           UINT64_C(0x8d5871f9ebdb651c))
 #define RESOURCE_TYPE_TEXTURE          STRING_ID_64("texture",          UINT64_C(0xcd4238c6a0c69e32))
 
+#define RESOURCE_NAME_INVALID StringId64(u64(0))
+#define PACKAGE_RESOURCE_NONE RESOURCE_NAME_INVALID
+
 #define RESOURCE_FULL_REBUILD_COUNT       u32(0) //!< How many times we required a full asset rebuild?
 #define RESOURCE_VERSION(ver)             (RESOURCE_FULL_REBUILD_COUNT + ver)
 #define RESOURCE_VERSION_STATE_MACHINE    RESOURCE_VERSION(4)