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

225 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. if (logger)
  90. {
  91. logger->log("Fetched resource \"" + path + "\"\n");
  92. }
  93. // Resource found
  94. resource_handle<T>* resource = static_cast<resource_handle<T>*>(it->second);
  95. // Increment resource handle reference count
  96. ++resource->reference_count;
  97. // Return resource data
  98. return resource->data;
  99. }
  100. if (logger)
  101. {
  102. logger->push_task("Loading resource \"" + path + "\"");
  103. }
  104. // Resource not found, load resource data
  105. T* data = nullptr;
  106. try
  107. {
  108. // For each directory in search paths
  109. bool opened = false;
  110. for (const std::string& directory: paths)
  111. {
  112. // Attempt to open file
  113. std::string full_path = directory + path;
  114. std::ifstream fs;
  115. fs.open(full_path.c_str(), std::ios::in | std::ios::binary);
  116. // If unable to open file
  117. if (!fs.is_open() || !fs.good())
  118. {
  119. if (fs.is_open())
  120. {
  121. fs.close();
  122. }
  123. // Try again in next search path
  124. continue;
  125. }
  126. // File opened, load it
  127. opened = true;
  128. data = resource_loader<T>::load(this, &fs);
  129. fs.close();
  130. break;
  131. }
  132. if (!opened)
  133. {
  134. if (logger)
  135. {
  136. logger->pop_task(EXIT_FAILURE);
  137. }
  138. throw std::runtime_error("resource_manager::load<T>(): Unable to open file \"" + path + "\"");
  139. }
  140. }
  141. catch (const std::exception& e)
  142. {
  143. if (logger)
  144. {
  145. logger->pop_task(EXIT_FAILURE);
  146. }
  147. std::string error = std::string("resource_manager::load<T>(): Failed to load resource \"") + path + std::string("\": \"") + e.what() + std::string("\"");
  148. throw std::runtime_error(error.c_str());
  149. }
  150. // Create a resource handle for the resource data
  151. resource_handle<T>* resource = new resource_handle<T>();
  152. resource->data = data;
  153. resource->reference_count = 1;
  154. // Add resource to the cache
  155. resource_cache[path] = resource;
  156. if (logger)
  157. {
  158. logger->pop_task(EXIT_SUCCESS);
  159. }
  160. return resource->data;
  161. }
  162. template <typename T>
  163. void resource_manager::save(const T* resource, const std::string& path)
  164. {
  165. // Attempt to open file
  166. std::ofstream fs;
  167. fs.open(path.c_str(), std::ios::out | std::ios::binary);
  168. // If unable to open file
  169. if (!fs.is_open() || !fs.good())
  170. {
  171. if (fs.is_open())
  172. {
  173. fs.close();
  174. }
  175. throw std::runtime_error("resource_manager::save<T>(): Unable to open file \"" + path + "\"");
  176. }
  177. try
  178. {
  179. resource_loader<T>::save(this, &fs, resource);
  180. }
  181. catch (const std::exception& e)
  182. {
  183. std::string error = std::string("resource_manager::load<T>(): Failed to save resource \"") + path + std::string("\": \"") + e.what() + std::string("\"");
  184. throw std::runtime_error(error.c_str());
  185. }
  186. }
  187. inline entt::registry& resource_manager::get_archetype_registry()
  188. {
  189. return archetype_registry;
  190. }
  191. #endif // ANTKEEPER_RESOURCE_MANAGER_HPP