ST_engine  0.3-ALPHA
assets_manager.cpp
1 /* This file is part of the "ST" project.
2  * You may use, distribute or modify this code under the terms
3  * of the GNU General Public License version 2.
4  * See LICENCE.txt in the root directory of the project.
5  *
6  * Author: Maxim Atanasov
7  * E-mail: maxim.atanasov@protonmail.com
8  */
9 
10 #include <ST_loaders/loaders.hpp>
11 #include <assets_manager.hpp>
12 
13 static bool singleton_initialized = false;
14 
22 assets_manager::assets_manager(message_bus &gMessageBus, task_manager &gTask_manager) : gMessage_bus(gMessageBus),
23  gTask_manager(gTask_manager) {
24 
25  if (singleton_initialized) {
26  throw std::runtime_error("The assets manager cannot be initialized more than once!");
27  } else {
28  singleton_initialized = true;
29  }
30 
31  //subscribe to messages
32  gMessage_bus.subscribe(LOAD_LIST, &msg_sub);
33  gMessage_bus.subscribe(UNLOAD_LIST, &msg_sub);
34  gMessage_bus.subscribe(LOAD_ASSET, &msg_sub);
35  gMessage_bus.subscribe(UNLOAD_ASSET, &msg_sub);
36  gMessage_bus.subscribe(LOAD_BINARY, &msg_sub);
37 
38  //load the internal engine assets and the global game assets
39  load_assets_from_list("assets_internal/assets_internal.list");
40  load_assets_from_list("game/levels/assets_global.list");
41 }
42 
48 void assets_manager::update_task(void *arg) {
49  auto self = static_cast<assets_manager *>(arg);
50  self->handle_messages();
51 }
52 
57 void assets_manager::handle_messages() {
58  message *temp = msg_sub.get_next_message();
59  while (temp != nullptr) {
60  auto path = *static_cast<std::string *>(temp->get_data());
61  switch (temp->msg_name) {
62  case LOAD_LIST:
63  load_assets_from_list(path);
64  break;
65  case UNLOAD_LIST:
66  unload_assets_from_list(path);
67  break;
68  case LOAD_ASSET: {
69  if (load_asset(path) == 0) {
70  send_assets();
71  }
72  break;
73  }
74  case UNLOAD_ASSET: {
75  if (unload_asset(path) == 0) {
76  send_assets();
77  }
78  break;
79  }
80  case LOAD_BINARY:
81  load_assets_from_binary(path);
82  break;
83  }
84  delete temp;
85  temp = msg_sub.get_next_message();
86  }
87 }
88 
94 int8_t assets_manager::load_assets_from_binary(const std::string &path) {
95  ST::assets_named *assets1 = ST::unpack_binary(path);
96  if (assets1 != nullptr) {
97  for (const auto &surface: assets1->surfaces) {
98  if (count[surface.first] == 0) {
99  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Unpacking " + surface.first)));
100  uint16_t hashed = ST::hash_string(surface.first);
101  all_assets.surfaces[hashed] = surface.second;
102  }
103  ++count[surface.first];
104  }
105  for (const auto &chunk: assets1->chunks) {
106  if (count[chunk.first] == 0) {
107  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Unpacking " + chunk.first)));
108  uint16_t hashed = ST::hash_string(chunk.first);
109  all_assets.chunks[hashed] = chunk.second;
110  }
111  ++count[chunk.first];
112  }
113  for (const auto &music: assets1->music) {
114  if (count[music.first] == 0) {
115  gMessage_bus.send_msg(new message(LOG_SUCCESS, make_data<std::string>("Unpacking " + music.first)));
116  count[music.first]++;
117  uint16_t hashed = ST::hash_string(music.first);
118  all_assets.music[hashed] = music.second;
119  }
120  ++count[music.first];
121  }
122  delete assets1;
123  } else {
124  return -1;
125  }
126  return 0;
127 }
128 
134 int8_t assets_manager::unload_assets_from_binary(const std::string &path) {
135  ST::assets_named *assets1 = ST::unpack_binary(path);
136  if (assets1 != nullptr) {
137  for (const auto &surface: assets1->surfaces) {
138  unload_asset(surface.first);
139  SDL_FreeSurface(surface.second);
140  }
141  for (const auto &chunk: assets1->chunks) {
142  unload_asset(chunk.first);
143  Mix_FreeChunk(chunk.second);
144  }
145  for (const auto &music: assets1->music) {
146  unload_asset(music.first);
147  Mix_FreeMusic(music.second);
148  }
149  delete assets1;
150  } else {
151  return -1;
152  }
153  return 0;
154 }
155 
156 void assets_manager::send_assets() {
157  gMessage_bus.send_msg(new message(SURFACES_ASSETS, make_data(&all_assets.surfaces)));
158  gMessage_bus.send_msg(new message(FONTS_ASSETS, make_data(&all_assets.fonts)));
159  gMessage_bus.send_msg(new message(CHUNKS_ASSETS, make_data(&all_assets.chunks)));
160  gMessage_bus.send_msg(new message(MUSIC_ASSETS, make_data(&all_assets.music)));
161 }
162 
169 int8_t assets_manager::load_asset(std::string path) {
170  //ignore a comment
171  if (path.at(0) == '#') {
172  return 0;
173  }
174 
175  //check if the file is already loaded and increase the reference count
176  auto _asset_count = count.find(ST::trim_path(path));
177  if (_asset_count != count.end() && _asset_count->second > 0) {
178  ++_asset_count->second;
179  return 0;
180  } else if (_asset_count == count.end()) {
181  count.emplace(ST::trim_path(path), 0);
182  }
183 
184  ST::asset_file_type extension = ST::get_file_extension(path);
185  //Handle the different extensions - currently png, webp, wav, mp3, ttf
186 
187  if (extension == ST::asset_file_type::BIN) {
188  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>("Loading from binary " + path)));
189  } else {
190  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>("Loading " + path)));
191  }
192 
193  if (extension == ST::asset_file_type::PNG || extension == ST::asset_file_type::WEBP) {
194  SDL_Surface *temp1 = IMG_Load(path.c_str());
195  if (temp1 != nullptr) {
196  path = ST::trim_path(path);
197  uint16_t string_hash = ST::hash_string(path);
198  all_assets.surfaces[string_hash] = temp1;
199  ++count.at(path);
200  } else {
201  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + path + " not found")));
202  return -1;
203  }
204  } else if (extension == ST::asset_file_type::WAV) {
205  Mix_Chunk *temp1 = Mix_LoadWAV(path.c_str());
206  if (temp1 != nullptr) {
207  path = ST::trim_path(path);
208  uint16_t string_hash = ST::hash_string(path);
209  all_assets.chunks[string_hash] = temp1;
210  ++count.at(path);
211  } else {
212  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + path + " not found")));
213  return -1;
214  }
215  } else if (extension == ST::asset_file_type::OGG) {
216  Mix_Music *temp1 = Mix_LoadMUS(path.c_str());
217  if (temp1 != nullptr) {
218  path = ST::trim_path(path);
219  uint16_t string_hash = ST::hash_string(path);
220  all_assets.music[string_hash] = temp1;
221  ++count.at(path);
222  } else {
223  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + path + " not found")));
224  return -1;
225  }
226  } else if (extension == ST::asset_file_type::BIN) {
227  load_assets_from_binary(path);
228  } else { //if file is a font
229  std::vector<std::string> result;
230  std::istringstream iss(path);
231  for (std::string path_; iss >> path;) {
232  result.push_back(path);
233  }
234  uint32_t size;
235  try {
236  std::stringstream convert(result.at(1));
237  convert >> size;
238  } catch (const std::out_of_range &e) {
239  (void) e;
240  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("Font size not specified!")));
241  return -1;
242  }
243  std::string font = result.at(0);
244  //font sizes can only be positive
245  TTF_Font *tempFont = TTF_OpenFont(font.c_str(), size); // NOLINT(cppcoreguidelines-narrowing-conversions)
246  if (tempFont != nullptr) {
247  font = ST::trim_path(font);
248  std::string font_and_size = font + " " + result.at(1);
249  all_assets.fonts[ST::hash_string(font_and_size)] = tempFont;
250  ++count.at(font_and_size);
251  } else {
252  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + font + " not found!")));
253  return -1;
254  }
255  }
256  return 0;
257 }
258 
264 int8_t assets_manager::load_assets_from_list(const std::string &path) {
265  std::ifstream file;
266  file.open(path.c_str());
267  if (file.is_open()) {
268  std::string temp;
269  while (!file.eof()) {
270  getline(file, temp);
271  if (!temp.empty()) {
272  load_asset(temp);
273  }
274  }
275  file.close();
276  } else {
277  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + path + " not found")));
278  return -1;
279  }
280  send_assets();
281  return 0;
282 }
283 
289 int8_t assets_manager::unload_assets_from_list(const std::string &path) {
290  std::ifstream file;
291  file.open(path.c_str());
292  if (file.is_open()) {
293  std::string temp;
294  while (!file.eof()) {
295  getline(file, temp);
296  if (!temp.empty()) {
297  //Ignore comments
298  if (path.at(0) != '#') {
299  unload_asset(temp);
300  }
301  }
302  }
303  file.close();
304  } else {
305  gMessage_bus.send_msg(new message(LOG_ERROR, make_data<std::string>("File " + path + " not found")));
306  return -1;
307  }
308  send_assets();
309  return 0;
310 }
311 
317 int8_t assets_manager::unload_asset(std::string path) {
318  auto _asset_count = count.find(ST::trim_path(path));
319  if (_asset_count != count.end() && _asset_count->second > 1) {
320  --_asset_count->second;
321  return 0;
322  } else if ((_asset_count == count.end() || _asset_count->second == 0) &&
323  ST::get_file_extension(path) != ST::asset_file_type::BIN) {
324  return -1;
325  }
326 
327  ST::asset_file_type extension = ST::get_file_extension(path);
328  gMessage_bus.send_msg(new message(LOG_INFO, make_data<std::string>("Unloading " + path)));
329 
330  if (extension == ST::asset_file_type::PNG || extension == ST::asset_file_type::WEBP) {
331  path = ST::trim_path(path);
332  uint16_t string_hash = ST::hash_string(path);
333  SDL_FreeSurface(all_assets.surfaces[string_hash]);
334  all_assets.surfaces[string_hash] = nullptr;
335  --count.at(path);
336  } else if (extension == ST::asset_file_type::WAV) {
337  path = ST::trim_path(path);
338  uint16_t string_hash = ST::hash_string(path);
339  Mix_FreeChunk(all_assets.chunks[string_hash]);
340  all_assets.chunks[string_hash] = nullptr;
341  --count.at(path);
342  } else if (extension == ST::asset_file_type::OGG) {
343  path = ST::trim_path(path);
344  uint16_t string_hash = ST::hash_string(path);
345  Mix_FreeMusic(all_assets.music[string_hash]);
346  all_assets.music[string_hash] = nullptr;
347  --count.at(path);
348  } else if (extension == ST::asset_file_type::BIN) {
349  return unload_assets_from_binary(path);
350  } else { //if file is a font
351  path = ST::trim_path(path);
352  TTF_CloseFont(all_assets.fonts[ST::hash_string(path)]);
353  all_assets.fonts[ST::hash_string(path)] = nullptr;
354  --count.at(path);
355  }
356  return 0;
357 }
358 
363  handle_messages();
364  for (auto &i: count) {
365  if (i.second > 0) {
366  i.second = 1;
367  unload_asset(i.first);
368  }
369  }
370  singleton_initialized = false;
371 }
372 
377  gTask_manager.start_task_lockfree(new ST::task(update_task, this, nullptr));
378 }
An object representing a task to be run by the task manager.
Definition: task.hpp:24
This object is responsible for loading/unloading assets.
assets_manager(message_bus &gMessageBus, task_manager &tsk_mngr)
static void update_task(void *arg)
The central messaging system of the engine. All subsystem make extensive use of it.
Definition: message_bus.hpp:29
void subscribe(uint8_t msg, subscriber *sub)
Definition: message_bus.cpp:71
void send_msg(message *msg)
Definition: message_bus.cpp:19
A message object passed around in the message bus. Holds anything created with make_data<>().
Definition: message.hpp:21
void * get_data() const
Definition: message.hpp:80
message * get_next_message()
Definition: subscriber.hpp:39
The Task Manager handles all things multi-threaded in the engine.
void start_task_lockfree(ST::task *arg)