💿🐜 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.

236 lines
5.4 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. /**
  32. * Loads resources.
  33. */
  34. class resource_manager
  35. {
  36. public:
  37. /**
  38. * Creates a resource manager.
  39. */
  40. resource_manager(debug::logger* logger);
  41. /**
  42. * Destroys a resource manager and frees all of its resources.
  43. */
  44. ~resource_manager();
  45. bool mount(const std::string& path);
  46. /**
  47. * Adds a path to be searched when a resource is requested.
  48. *
  49. * @param path Search path.
  50. */
  51. void include(const std::string& path);
  52. /**
  53. * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented.
  54. *
  55. * @tparam T Resource type.
  56. * @param path Path to the resource, relative to the search paths.
  57. * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded.
  58. */
  59. template <typename T>
  60. T* load(const std::string& name);
  61. /**
  62. * Decrements a resource's reference count and unloads the resource if it's unreferenced.
  63. *
  64. * @param path Path to the resource, relative to the search paths.
  65. */
  66. void unload(const std::string& name);
  67. /**
  68. * Saves the specified resource.
  69. *
  70. * @tparam T Resource type.
  71. * @param resource Pointer to the resource.
  72. * @param path Path to the resource.
  73. */
  74. template <typename T>
  75. void save(const T* resource, const std::string& path);
  76. entt::registry& get_archetype_registry();
  77. private:
  78. std::map<std::string, resource_handle_base*> resource_cache;
  79. std::list<std::string> search_paths;
  80. entt::registry archetype_registry;
  81. debug::logger* logger;
  82. };
  83. template <typename T>
  84. T* resource_manager::load(const std::string& name)
  85. {
  86. // Check if resource is in the cache
  87. auto it = resource_cache.find(name);
  88. if (it != resource_cache.end())
  89. {
  90. /*
  91. if (logger)
  92. {
  93. logger->log("Fetched resource \"" + name + "\"");
  94. }
  95. */
  96. // Resource found
  97. resource_handle<T>* resource = static_cast<resource_handle<T>*>(it->second);
  98. // Increment resource handle reference count
  99. ++resource->reference_count;
  100. // Return resource data
  101. return resource->data;
  102. }
  103. if (logger)
  104. {
  105. logger->push_task("Loading resource \"" + name + "\"");
  106. }
  107. // Resource not cached, look for file in search paths
  108. T* data = nullptr;
  109. bool found = false;
  110. for (const std::string& search_path: search_paths)
  111. {
  112. std::string path = search_path + name;
  113. // Check if file exists
  114. if (!PHYSFS_exists(path.c_str()))
  115. {
  116. continue;
  117. }
  118. // File found
  119. found = true;
  120. // Open file for reading
  121. PHYSFS_File* file = PHYSFS_openRead(path.c_str());
  122. if (!file)
  123. {
  124. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  125. break;
  126. }
  127. // Load opened file
  128. try
  129. {
  130. data = resource_loader<T>::load(this, file);
  131. }
  132. catch (const std::exception& e)
  133. {
  134. logger->error("Failed to load resource: \"" + std::string(e.what()) + "\"");
  135. }
  136. // Close opened file
  137. if (!PHYSFS_close(file))
  138. {
  139. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  140. }
  141. break;
  142. }
  143. if (!data)
  144. {
  145. if (!found)
  146. {
  147. logger->error("File not found");
  148. }
  149. logger->pop_task(EXIT_FAILURE);
  150. return nullptr;
  151. }
  152. // Create a resource handle for the resource data
  153. resource_handle<T>* resource = new resource_handle<T>();
  154. resource->data = data;
  155. resource->reference_count = 1;
  156. // Add resource to the cache
  157. resource_cache[name] = resource;
  158. if (logger)
  159. {
  160. logger->pop_task(EXIT_SUCCESS);
  161. }
  162. return resource->data;
  163. }
  164. template <typename T>
  165. void resource_manager::save(const T* resource, const std::string& path)
  166. {
  167. logger->push_task("Saving resource to \"" + path + "\"");
  168. // Open file for writing
  169. PHYSFS_File* file = PHYSFS_openWrite(path.c_str());
  170. if (!file)
  171. {
  172. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  173. logger->pop_task(EXIT_FAILURE);
  174. return;
  175. }
  176. // Save to opened file
  177. int status = EXIT_SUCCESS;
  178. try
  179. {
  180. resource_loader<T>::save(this, file, resource);
  181. }
  182. catch (const std::exception& e)
  183. {
  184. logger->error("Failed to save resource: \"" + std::string(e.what()) + "\"");
  185. status = EXIT_FAILURE;
  186. }
  187. // Close opened file
  188. if (!PHYSFS_close(file))
  189. {
  190. logger->error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  191. status = EXIT_FAILURE;
  192. }
  193. logger->pop_task(status)
  194. }
  195. inline entt::registry& resource_manager::get_archetype_registry()
  196. {
  197. return archetype_registry;
  198. }
  199. #endif // ANTKEEPER_RESOURCE_MANAGER_HPP