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

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