summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/pch.hh1
-rw-r--r--core/resource/CMakeLists.txt1
-rw-r--r--core/resource/binfile.cc68
-rw-r--r--core/resource/binfile.hh2
-rw-r--r--core/resource/image.cc96
-rw-r--r--core/resource/image.hh2
-rw-r--r--core/resource/resource.cc129
-rw-r--r--core/resource/resource.hh43
-rw-r--r--core/utils/CMakeLists.txt2
-rw-r--r--core/utils/physfs.cc76
-rw-r--r--core/utils/physfs.hh18
11 files changed, 327 insertions, 111 deletions
diff --git a/core/pch.hh b/core/pch.hh
index 3a013bd..dedd310 100644
--- a/core/pch.hh
+++ b/core/pch.hh
@@ -21,6 +21,7 @@
#include <stdexcept>
#include <thread>
#include <type_traits>
+#include <typeindex>
#include <unordered_map>
#include <unordered_set>
#include <vector>
diff --git a/core/resource/CMakeLists.txt b/core/resource/CMakeLists.txt
index ef2ffae..7df4306 100644
--- a/core/resource/CMakeLists.txt
+++ b/core/resource/CMakeLists.txt
@@ -3,4 +3,5 @@ target_sources(core PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/binfile.hh"
"${CMAKE_CURRENT_LIST_DIR}/image.cc"
"${CMAKE_CURRENT_LIST_DIR}/image.hh"
+ "${CMAKE_CURRENT_LIST_DIR}/resource.cc"
"${CMAKE_CURRENT_LIST_DIR}/resource.hh")
diff --git a/core/resource/binfile.cc b/core/resource/binfile.cc
index 726ef9b..0e6efa8 100644
--- a/core/resource/binfile.cc
+++ b/core/resource/binfile.cc
@@ -4,68 +4,48 @@
#include "core/resource/resource.hh"
-static emhash8::HashMap<std::string, resource_ptr<BinFile>> resource_map;
+#include "core/utils/physfs.hh"
-template<>
-resource_ptr<BinFile> resource::load<BinFile>(std::string_view name, unsigned int flags)
+static const void* binfile_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
+ assert(name);
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
+ auto file = PHYSFS_openRead(name);
+
+ if(file == nullptr) {
+ spdlog::error("binfile: {}: {}", name, utils::physfs_error());
+ return nullptr;
}
- auto file = PHYSFS_openRead(std::string(name).c_str());
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
- if(file == nullptr) {
- spdlog::warn("resource: {}: {}", name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ if(file_size < 0) {
+ spdlog::error("binfile: {}: {}", name, utils::physfs_error());
+ PHYSFS_close(file);
return nullptr;
}
- auto new_resource = std::make_shared<BinFile>();
- new_resource->size = PHYSFS_fileLength(file);
- new_resource->buffer = new std::byte[new_resource->size];
+ auto binfile = new BinFile();
+ binfile->size = static_cast<std::size_t>(file_size);
+ binfile->buffer = new std::byte[binfile->size];
- PHYSFS_readBytes(file, new_resource->buffer, new_resource->size);
+ PHYSFS_readBytes(file, binfile->buffer, file_size);
PHYSFS_close(file);
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
+ return binfile;
}
-template<>
-void resource::hard_cleanup<BinFile>(void)
+static void binfile_free_func(const void* resource)
{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [BinFile] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [BinFile] {}", it.first);
- }
+ assert(resource);
- delete[] it.second->buffer;
- }
+ auto binfile = reinterpret_cast<const BinFile*>(resource);
+ delete[] binfile->buffer;
- resource_map.clear();
+ delete binfile;
}
-template<>
-void resource::soft_cleanup<BinFile>(void)
+void BinFile::register_resource(void)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [BinFile] {}", iter->first);
-
- delete[] iter->second->buffer;
-
- iter = resource_map.erase(iter);
-
- continue;
- }
-
- iter = std::next(iter);
- }
+ resource::register_loader<BinFile>(&binfile_load_func, &binfile_free_func);
}
diff --git a/core/resource/binfile.hh b/core/resource/binfile.hh
index 6bb5e3d..5f24d77 100644
--- a/core/resource/binfile.hh
+++ b/core/resource/binfile.hh
@@ -3,6 +3,8 @@
#pragma once
struct BinFile final {
+ static void register_resource(void);
+
std::byte* buffer;
std::size_t size;
};
diff --git a/core/resource/image.cc b/core/resource/image.cc
index 8b570e4..d5e256d 100644
--- a/core/resource/image.cc
+++ b/core/resource/image.cc
@@ -4,7 +4,7 @@
#include "core/resource/resource.hh"
-static emhash8::HashMap<std::string, resource_ptr<Image>> resource_map;
+#include "core/utils/physfs.hh"
static int stbi_physfs_read(void* context, char* data, int size)
{
@@ -22,94 +22,60 @@ static int stbi_physfs_eof(void* context)
return PHYSFS_eof(reinterpret_cast<PHYSFS_File*>(context));
}
-template<>
-resource_ptr<Image> resource::load<Image>(std::string_view name, unsigned int flags)
+static const void* image_load_func(const char* name, std::uint32_t flags)
{
- auto it = resource_map.find(std::string(name));
+ assert(name);
- if(it != resource_map.cend()) {
- // Return an existing resource
- return it->second;
- }
+ stbi_io_callbacks callbacks;
+ callbacks.read = &stbi_physfs_read;
+ callbacks.skip = &stbi_physfs_skip;
+ callbacks.eof = &stbi_physfs_eof;
+
+ stbi_set_flip_vertically_on_load(bool(flags & IMAGE_LOAD_FLIP));
- auto file = PHYSFS_openRead(std::string(name).c_str());
+ auto file = PHYSFS_openRead(name);
if(file == nullptr) {
- spdlog::warn("resource: {}: {}", name, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ spdlog::error("image: {}: {}", name, utils::physfs_error());
return nullptr;
}
- if(flags & IMAGE_LOAD_FLIP) {
- stbi_set_flip_vertically_on_load(true);
- }
- else {
- stbi_set_flip_vertically_on_load(false);
- }
-
- stbi_io_callbacks callbacks;
- callbacks.read = &stbi_physfs_read;
- callbacks.skip = &stbi_physfs_skip;
- callbacks.eof = &stbi_physfs_eof;
-
- auto new_resource = std::make_shared<Image>();
+ int desired_channels;
if(flags & IMAGE_LOAD_GRAY) {
- new_resource->pixels = stbi_load_from_callbacks(&callbacks, file, &new_resource->size.x, &new_resource->size.y, nullptr, STBI_grey);
+ desired_channels = STBI_grey;
}
else {
- new_resource->pixels = stbi_load_from_callbacks(
- &callbacks, file, &new_resource->size.x, &new_resource->size.y, nullptr, STBI_rgb_alpha);
+ desired_channels = STBI_rgb_alpha;
}
- PHYSFS_close(file);
+ int width, height, channels;
+ auto pixels = stbi_load_from_callbacks(&callbacks, file, &width, &height, &channels, desired_channels);
- if(new_resource->pixels == nullptr) {
- spdlog::warn("resource: {}: {}", name, stbi_failure_reason());
- return nullptr;
- }
+ PHYSFS_close(file);
- if(new_resource->size.x <= 0 || new_resource->size.y <= 0) {
- spdlog::warn("resource {}: invalid dimensions", name);
- stbi_image_free(new_resource->pixels);
+ if(pixels == nullptr) {
+ spdlog::error("image: {}: {}", name, stbi_failure_reason());
return nullptr;
}
- return resource_map.insert_or_assign(std::string(name), new_resource).first->second;
+ auto image = new Image;
+ image->pixels = pixels;
+ image->size = glm::ivec2(width, height);
+ return image;
}
-template<>
-void resource::hard_cleanup<Image>(void)
+static void image_free_func(const void* resource)
{
- for(const auto& it : resource_map) {
- if(it.second.use_count() > 1L) {
- spdlog::warn("resource: zombie resource [Image] {} [use_count={}]", it.first, it.second.use_count());
- }
- else {
- spdlog::debug("resource: releasing [Image] {}", it.first);
- }
-
- stbi_image_free(it.second->pixels);
- }
+ assert(resource);
+
+ auto image = reinterpret_cast<const Image*>(resource);
+ stbi_image_free(image->pixels);
- resource_map.clear();
+ delete image;
}
-template<>
-void resource::soft_cleanup<Image>(void)
+void Image::register_resource(void)
{
- auto iter = resource_map.cbegin();
-
- while(iter != resource_map.cend()) {
- if(iter->second.use_count() == 1L) {
- spdlog::debug("resource: releasing [Image] {}", iter->first);
-
- stbi_image_free(iter->second->pixels);
-
- iter = resource_map.erase(iter);
-
- continue;
- }
-
- iter = std::next(iter);
- }
+ resource::register_loader<Image>(&image_load_func, &image_free_func);
}
diff --git a/core/resource/image.hh b/core/resource/image.hh
index 13e912e..b62fcbb 100644
--- a/core/resource/image.hh
+++ b/core/resource/image.hh
@@ -6,6 +6,8 @@ constexpr static unsigned int IMAGE_LOAD_GRAY = 0x0001U;
constexpr static unsigned int IMAGE_LOAD_FLIP = 0x0002U;
struct Image final {
+ static void register_resource(void);
+
stbi_uc* pixels;
glm::ivec2 size;
};
diff --git a/core/resource/resource.cc b/core/resource/resource.cc
new file mode 100644
index 0000000..600843f
--- /dev/null
+++ b/core/resource/resource.cc
@@ -0,0 +1,129 @@
+#include "core/pch.hh"
+
+#include "core/resource/resource.hh"
+
+struct ResourceLoader final {
+ ResourceLoadFunc load_func;
+ ResourceFreeFunc free_func;
+ emhash8::HashMap<std::string, std::shared_ptr<const void>> resources;
+ std::vector<std::shared_ptr<const void>> cache;
+ std::string class_name;
+};
+
+namespace
+{
+emhash8::HashMap<std::type_index, std::unique_ptr<ResourceLoader>> loaders;
+} // namespace
+
+void resource::detail::register_loader(const std::type_info& type, ResourceLoadFunc load_func, ResourceFreeFunc free_func)
+{
+ assert(load_func);
+ assert(free_func);
+
+ auto type_index = std::type_index(type);
+ auto loader = std::make_unique<ResourceLoader>();
+ loader->class_name = type.name();
+ loader->load_func = load_func;
+ loader->free_func = free_func;
+
+ assert(!loaders.contains(type_index));
+
+ loaders.insert_or_assign(type_index, std::move(loader));
+}
+
+std::shared_ptr<const void> resource::detail::load_resource(const std::type_info& type, std::string_view name, std::uint32_t flags)
+{
+ auto name_str = std::string(name);
+ auto type_index = std::type_index(type);
+ auto loader = loaders.find(type_index);
+
+ if(loader == loaders.cend()) {
+ spdlog::error("resource: no loader registered for type [{}]", type.name());
+ return nullptr;
+ }
+
+ auto resource_it = loader->second->resources.find(name_str);
+
+ if(resource_it == loader->second->resources.cend()) {
+ auto resource_raw = loader->second->load_func(name_str.c_str(), flags);
+
+ if(resource_raw == nullptr) {
+ spdlog::error("resource: {} [{}]: load failed", loader->second->class_name, name);
+ return nullptr;
+ }
+
+ std::shared_ptr<const void> resource_ptr(resource_raw, [](const void* ptr) { /* empty */ });
+ auto loaded_it = loader->second->resources.insert_or_assign(name_str, std::move(resource_ptr));
+
+ if(flags & RESOURCE_CACHE) {
+ loader->second->cache.push_back(loaded_it.first->second);
+ }
+
+ return loaded_it.first->second;
+ }
+
+ return resource_it->second;
+}
+
+std::shared_ptr<const void> resource::detail::find_resource(const std::type_info& type, std::string_view name)
+{
+ auto name_str = std::string(name);
+ auto type_index = std::type_index(type);
+ auto loader = loaders.find(type_index);
+
+ if(loader == loaders.cend()) {
+ spdlog::error("resource: no loader registered for type [{}]", type.name());
+ return nullptr;
+ }
+
+ auto resource_it = loader->second->resources.find(name_str);
+
+ if(resource_it == loader->second->resources.cend()) {
+ spdlog::error("resource: {} [{}]: not found", loader->second->class_name, name);
+ return nullptr;
+ }
+
+ return resource_it->second;
+}
+
+void resource::hard_cleanup(void)
+{
+ for(auto& [type_index, loader] : loaders) {
+ loader->cache.clear();
+
+ for(auto& [name, resource_ptr] : loader->resources) {
+ if(resource_ptr.use_count() > 1) {
+ spdlog::warn("resource: zombie resource: {} [{}] [use_count={}]", name, loader->class_name, resource_ptr.use_count());
+ }
+ else {
+ spdlog::debug("resource: releasing {} [{}]", name, loader->class_name);
+ }
+
+ loader->free_func(resource_ptr.get());
+ }
+
+ loader->resources.clear();
+ }
+
+ loaders.clear();
+}
+
+void resource::soft_cleanup(void)
+{
+ for(auto& [type_index, loader] : loaders) {
+ auto resource_it = loader->resources.begin();
+
+ while(resource_it != loader->resources.end()) {
+ if(resource_it->second.use_count() <= 1) {
+ spdlog::debug("resource: releasing {} [{}]", resource_it->first, loader->class_name);
+
+ loader->free_func(resource_it->second.get());
+ resource_it = loader->resources.erase(resource_it);
+
+ continue;
+ }
+
+ resource_it = std::next(resource_it);
+ }
+ }
+}
diff --git a/core/resource/resource.hh b/core/resource/resource.hh
index 5b6b2ac..ad8d944 100644
--- a/core/resource/resource.hh
+++ b/core/resource/resource.hh
@@ -5,14 +5,53 @@
template<typename T>
using resource_ptr = std::shared_ptr<const T>;
+constexpr std::uint32_t RESOURCE_CACHE = 0x00000001U; ///< Cache the resource after loading
+constexpr std::uint32_t RESOURCE_USER = 0xFFFFFF00U; ///< User-defined flags for custom behavior
+
+using ResourceLoadFunc = const void* (*)(const char* name, std::uint32_t flags);
+using ResourceFreeFunc = void (*)(const void* resource);
+
+namespace resource::detail
+{
+void register_loader(const std::type_info& type, ResourceLoadFunc load_func, ResourceFreeFunc free_func);
+resource_ptr<void> load_resource(const std::type_info& type, std::string_view name, std::uint32_t flags);
+resource_ptr<void> find_resource(const std::type_info& type, std::string_view name);
+} // namespace resource::detail
+
namespace resource
{
template<typename T>
-resource_ptr<T> load(std::string_view name, unsigned int flags = 0U);
+void register_loader(ResourceLoadFunc load_func, ResourceFreeFunc free_func);
template<typename T>
-void hard_cleanup(void);
+resource_ptr<T> load(std::string_view name, std::uint32_t flags = 0U);
template<typename T>
+resource_ptr<T> find(std::string_view name);
+} // namespace resource
+
+namespace resource
+{
+void hard_cleanup(void);
void soft_cleanup(void);
} // namespace resource
+template<typename T>
+void resource::register_loader(ResourceLoadFunc load_func, ResourceFreeFunc free_func)
+{
+ resource::detail::register_loader(typeid(T), load_func, free_func);
+}
+
+template<typename T>
+resource_ptr<T> resource::load(std::string_view name, std::uint32_t flags)
+{
+ auto result = resource::detail::load_resource(typeid(T), name, flags);
+ return std::reinterpret_pointer_cast<const T>(result);
+}
+
+template<typename T>
+resource_ptr<T> resource::find(std::string_view name)
+{
+ auto result = resource::detail::find_resource(typeid(T), name);
+ return std::reinterpret_pointer_cast<const T>(result);
+}
+
#endif // CORE_RESOURCE_RESOURCE_HH
diff --git a/core/utils/CMakeLists.txt b/core/utils/CMakeLists.txt
index 4f96261..5d59e8d 100644
--- a/core/utils/CMakeLists.txt
+++ b/core/utils/CMakeLists.txt
@@ -1,5 +1,7 @@
target_sources(core PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/epoch.cc"
"${CMAKE_CURRENT_LIST_DIR}/epoch.hh"
+ "${CMAKE_CURRENT_LIST_DIR}/physfs.cc"
+ "${CMAKE_CURRENT_LIST_DIR}/physfs.hh"
"${CMAKE_CURRENT_LIST_DIR}/string.cc"
"${CMAKE_CURRENT_LIST_DIR}/string.hh")
diff --git a/core/utils/physfs.cc b/core/utils/physfs.cc
new file mode 100644
index 0000000..cb310df
--- /dev/null
+++ b/core/utils/physfs.cc
@@ -0,0 +1,76 @@
+#include "core/pch.hh"
+
+#include "core/utils/physfs.hh"
+
+bool utils::read_file(std::string_view path, std::vector<std::byte>& buffer)
+{
+ auto file = PHYSFS_openRead(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
+ buffer.resize(static_cast<std::size_t>(file_size));
+
+ PHYSFS_readBytes(file, buffer.data(), file_size);
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::read_file(std::string_view path, std::string& buffer)
+{
+ auto file = PHYSFS_openRead(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
+ buffer.resize(static_cast<std::size_t>(file_size));
+
+ PHYSFS_readBytes(file, buffer.data(), file_size);
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::write_file(std::string_view path, const std::vector<std::byte>& buffer)
+{
+ auto file = PHYSFS_openWrite(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_writeBytes(file, buffer.data(), static_cast<PHYSFS_uint64>(buffer.size()));
+ PHYSFS_close(file);
+
+ return true;
+}
+
+bool utils::write_file(std::string_view path, const std::string& buffer)
+{
+ auto file = PHYSFS_openWrite(std::string(path).c_str());
+
+ if(file == nullptr) {
+ spdlog::error("physfs: {}: {}", path, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
+ return false;
+ }
+
+ PHYSFS_writeBytes(file, buffer.data(), static_cast<PHYSFS_uint64>(buffer.size()));
+ PHYSFS_close(file);
+
+ return true;
+}
+
+std::string_view utils::physfs_error(void)
+{
+ auto error_code = PHYSFS_getLastErrorCode();
+ auto error_string = PHYSFS_getErrorByCode(error_code);
+ return std::string_view(error_string, std::strlen(error_string));
+}
diff --git a/core/utils/physfs.hh b/core/utils/physfs.hh
new file mode 100644
index 0000000..df5e392
--- /dev/null
+++ b/core/utils/physfs.hh
@@ -0,0 +1,18 @@
+#ifndef UTILS_PHYSFS_HH
+#define UTILS_PHYSFS_HH 1
+#pragma once
+
+namespace utils
+{
+bool read_file(std::string_view path, std::vector<std::byte>& buffer);
+bool read_file(std::string_view path, std::string& buffer);
+bool write_file(std::string_view path, const std::vector<std::byte>& buffer);
+bool write_file(std::string_view path, const std::string& buffer);
+} // namespace utils
+
+namespace utils
+{
+std::string_view physfs_error(void);
+} // namespace utils
+
+#endif // UTILS_PHYSFS_HH