💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

237 lines
5.7 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #ifndef ANTKEEPER_RESOURCE_MANAGER_HPP
  20. #define ANTKEEPER_RESOURCE_MANAGER_HPP
  21. #include "resource-handle.hpp"
  22. #include "resource-loader.hpp"
  23. #include "debug/logger.hpp"
  24. #include <fstream>
  25. #include <list>
  26. #include <map>
  27. #include <stdexcept>
  28. #include <string>
  29. #include <entt/entt.hpp>
  30. #include <physfs.h>
  31. #include <filesystem>
  32. /**
  33. * Loads resources.
  34. */
  35. class resource_manager
  36. {
  37. public:
  38. /**
  39. * Creates a resource manager.
  40. */
  41. resource_manager(debug::logger* logger);
  42. /**
  43. * Destroys a resource manager and frees all of its resources.
  44. */
  45. ~resource_manager();
  46. bool mount(const std::filesystem::path& path);
  47. /**
  48. * Adds a path to be searched when a resource is requested.
  49. *
  50. * @param path Search path.
  51. */
  52. void include(const std::filesystem::path& path);
  53. /**
  54. * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented.
  55. *
  56. * @tparam T Resource type.
  57. * @param path Path to the resource, relative to the search paths.
  58. * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded.
  59. */
  60. template <typename T>
  61. T* load(const std::filesystem::path& path);
  62. /**
  63. * Decrements a resource's reference count and unloads the resource if it's unreferenced.
  64. *
  65. * @param path Path to the resource, relative to the search paths.
  66. */
  67. void unload(const std::filesystem::path& path);
  68. /**
  69. * Saves the specified resource.
  70. *
  71. * @tparam T Resource type.
  72. * @param resource Pointer to the resource.
  73. * @param path Path to the resource.
  74. */
  75. template <typename T>
  76. void save(const T* resource, const std::filesystem::path& path);
  77. entt::registry& get_archetype_registry();
  78. private:
  79. std::map<std::filesystem::path, resource_handle_base*> resource_cache;
  80. std::list<std::filesystem::path> search_paths;
  81. entt::registry archetype_registry;
  82. debug::logger* logger;
  83. };
  84. template <typename T>
  85. T* resource_manager::load(const std::filesystem::path& path)
  86. {
  87. // Check if resource is in the cache
  88. auto it = resource_cache.find(path);
  89. if (it != resource_cache.end())
  90. {
  91. /*
  92. if (logger)
  93. {
  94. logger->log("Fetched resource \"" + path.string() + "\"");
  95. }
  96. */
  97. // Resource found
  98. resource_handle<T>* resource = static_cast<resource_handle<T>*>(it->second);
  99. // Increment resource handle reference count
  100. ++resource->reference_count;
  101. // Return resource data
  102. return resource->data;
  103. }
  104. if (logger)
  105. {
  106. logger->push_task("Loading resource \"" + path.string() + "\"");
  107. }
  108. // Resource not cached, look for file in search paths
  109. T* data = nullptr;
  110. bool found = false;
  111. for (const std::filesystem::path& search_path: search_paths)
  112. {
  113. std::filesystem::path full_path = search_path / path;
  114. // Check if file exists
  115. if (!PHYSFS_exists(full_path.string().c_str()))
  116. {
  117. continue;
  118. }
  119. // File found
  120. found = true;
  121. // Open file for reading
  122. PHYSFS_File* file = PHYSFS_openRead(full_path.string().c_str());
  123. if (!file)
  124. {
  125. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  126. break;
  127. }
  128. // Load opened file
  129. try
  130. {
  131. data = resource_loader<T>::load(this, file, full_path);
  132. }
  133. catch (const std::exception& e)
  134. {
  135. logger->error("Failed to load resource: \"" + std::string(e.what()) + "\"");
  136. }
  137. // Close opened file
  138. if (!PHYSFS_close(file))
  139. {
  140. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  141. }
  142. break;
  143. }
  144. if (!data)
  145. {
  146. if (!found)
  147. {
  148. logger->error("File not found");
  149. }
  150. logger->pop_task(EXIT_FAILURE);
  151. return nullptr;
  152. }
  153. // Create a resource handle for the resource data
  154. resource_handle<T>* resource = new resource_handle<T>();
  155. resource->data = data;
  156. resource->reference_count = 1;
  157. // Add resource to the cache
  158. resource_cache[path] = resource;
  159. if (logger)
  160. {
  161. logger->pop_task(EXIT_SUCCESS);
  162. }
  163. return resource->data;
  164. }
  165. template <typename T>
  166. void resource_manager::save(const T* resource, const std::filesystem::path& path)
  167. {
  168. logger->push_task("Saving resource to \"" + path.string() + "\"");
  169. // Open file for writing
  170. PHYSFS_File* file = PHYSFS_openWrite(path.string().c_str());
  171. if (!file)
  172. {
  173. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  174. logger->pop_task(EXIT_FAILURE);
  175. return;
  176. }
  177. // Save to opened file
  178. int status = EXIT_SUCCESS;
  179. try
  180. {
  181. resource_loader<T>::save(this, file, path, resource);
  182. }
  183. catch (const std::exception& e)
  184. {
  185. logger->error("Failed to save resource: \"" + std::string(e.what()) + "\"");
  186. status = EXIT_FAILURE;
  187. }
  188. // Close opened file
  189. if (!PHYSFS_close(file))
  190. {
  191. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  192. status = EXIT_FAILURE;
  193. }
  194. logger->pop_task(status)
  195. }
  196. inline entt::registry& resource_manager::get_archetype_registry()
  197. {
  198. return archetype_registry;
  199. }
  200. #endif // ANTKEEPER_RESOURCE_MANAGER_HPP