💿🐜 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) 2020 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();
  41. /**
  42. * Destroys a resource manager and frees all of its resources.
  43. */
  44. ~resource_manager();
  45. /**
  46. * Adds a path to be searched when a resource is requested.
  47. *
  48. * @param path Search path.
  49. */
  50. void include(const std::string& path);
  51. /**
  52. * Loads the requested resource. If the resource has already been loaded it will be retrieved from the resource cache and its reference count incremented.
  53. *
  54. * @tparam T Resource type.
  55. * @param path Path to the resource, relative to the search paths.
  56. * @return Pointer to the requested resource, or nullptr if the resource could not be found nor loaded.
  57. */
  58. template <typename T>
  59. T* load(const std::string& name);
  60. /**
  61. * Decrements a resource's reference count and unloads the resource if it's unreferenced.
  62. *
  63. * @param path Path to the resource, relative to the search paths.
  64. */
  65. void unload(const std::string& name);
  66. /**
  67. * Saves the specified resource.
  68. *
  69. * @tparam T Resource type.
  70. * @param resource Pointer to the resource.
  71. * @param path Path to the resource.
  72. */
  73. template <typename T>
  74. void save(const T* resource, const std::string& path);
  75. entt::registry& get_archetype_registry();
  76. void set_logger(::logger* logger);
  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. ::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