Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
DataManager.cpp
Go to the documentation of this file.
1/*
2Olympe Engine V2 2025
3Nicolas Chereau
4nchereau@gmail.com
5
6Purpose:
7- Singleton class that manages game resources (textures, sprites, sounds,
8 animations, levels, navigation/collision maps, game object data, etc.)
9- Loads resources on demand and keeps them in memory until released or
10 the engine shuts down. Provides categorized listings and safe unload.
11- All resource metadata files are expected to be JSON (parsing helpers can
12 be added later). This implementation focuses on texture loading and the
13 generic resource lifetime management with placeholders for extended types.
14
15Notes:
16- This implementation uses SDL for texture loading (BMP via SDL_LoadBMP to
17 avoid additional image dependencies). It stores SDL_Texture* in the
18 Resource struct. Extend loading functions to support PNG/JPEG/OGG/etc
19 using the appropriate libraries when available.
20*/
21
22#include "DataManager.h"
23#include "GameEngine.h"
24#include "system/system_utils.h"
25#include <fstream>
26#include <sstream>
27#include <algorithm>
28#include <cerrno>
29#include "sdl3_image/sdl_image.h"
30#include "ECS_Components.h"
31#include "third_party/nlohmann/json.hpp"
32
33#ifdef _WIN32
34#include <direct.h>
35#include <windows.h>
36#else
37#include <sys/stat.h>
38#include <sys/types.h>
39#include <dirent.h>
40#endif
41
43{
44 name = "DataManager";
45 SYSTEM_LOG << "DataManager created\n";
46}
47//-------------------------------------------------------------
49{
50 SYSTEM_LOG << "DataManager destroyed\n";
51 // Ensure resources are freed if shutdown wasn't explicitly called
52 UnloadAll();
53}
54//-------------------------------------------------------------
60//-------------------------------------------------------------
62{
63 // Placeholder for initialization logic (e.g. preload system icons)
64 SYSTEM_LOG << "DataManager Initialized\n";
65}
66//-------------------------------------------------------------
68{
69 SYSTEM_LOG << "DataManager Shutdown - unloading all resources\n";
70 UnloadAll();
71}
72//-------------------------------------------------------------
73bool DataManager::PreloadTexture(const std::string& id, const std::string& path, ResourceCategory category)
74{
75 std::lock_guard<std::mutex> lock(m_mutex_);
76 if (id.empty() || path.empty()) return false;
77 if (m_resources_.find(id) != m_resources_.end())
78 {
79 // already loaded
80 return true;
81 }
82
83 // try to load BMP surface first (no external deps required)
84 SDL_Surface* surf = IMG_Load(path.c_str()); //SDL_LoadBMP(path.c_str());
85
86 if (!surf)
87 {
88 SYSTEM_LOG << "DataManager::PreloadTexture IMG_Load failed for '" << path << "' : " << SDL_GetError() << "\n";
89 return false;
90 }
91
93 SDL_Texture* tex = nullptr;
94 if (renderer)
95 {
97 if (!tex)
98 {
99 SYSTEM_LOG << "DataManager::PreloadTexture SDL_CreateTextureFromSurface failed for '" << path << "' : " << SDL_GetError() << "\n";
101 return false;
102 }
103 }
104 else
105 {
106 // no global renderer available: keep the surface as raw data pointer for future conversion
107 // store surface pointer in Resource::data
108 }
109
110 auto res = std::make_shared<Resource>();
112 res->category = category;
113 res->id = id;
114 res->path = path;
115 res->sprite_texture = tex;
116 if (!tex)
117 {
118 // store surface pointer for deferred texture creation
119 res->data = surf;
120 }
121 else
122 {
123 // we no longer need the surface
125 }
126
127 m_resources_.emplace(id, res);
128 SYSTEM_LOG << "DataManager: Loaded texture '" << id << "' from '" << path << "'\n";
129 return true;
130}
131//-------------------------------------------------------------
132bool DataManager::PreloadSprite(const std::string& id, const std::string& path, ResourceCategory category)
133{
134 return PreloadTexture(id, path, category);
135}
136//-------------------------------------------------------------
137SDL_Texture* DataManager::GetTexture(const std::string& id) const
138{
139 std::lock_guard<std::mutex> lock(m_mutex_);
140 auto it = m_resources_.find(id);
141 if (it == m_resources_.end()) return nullptr;
142 auto res = it->second;
143 if (res->sprite_texture) return res->sprite_texture;
144
145 // If texture not created yet but we have a surface stored, try to create it now
146 if (res->data)
147 {
148 SDL_Surface* surf = reinterpret_cast<SDL_Surface*>(res->data);
150 if (surf && renderer)
151 {
153 if (tex)
154 {
155 res->sprite_texture = tex;
156 // we can free the surface now
158 res->data = nullptr;
159 return res->sprite_texture;
160 }
161 else
162 {
163 SYSTEM_LOG << "DataManager: Failed to create deferred texture for '" << id << "' : " << SDL_GetError() << "\n";
164 }
165 }
166 }
167
168 return nullptr;
169}
170//-------------------------------------------------------------
171Sprite* DataManager::GetSprite(const std::string& id, const std::string& path, ResourceCategory category)
172{
173 // Optimized: Check existence without locking twice
174 {
175 std::lock_guard<std::mutex> lock(m_mutex_);
176 auto it = m_resources_.find(id);
177 if (it != m_resources_.end() && it->second->sprite_texture) {
178 return it->second->sprite_texture;
179 }
180 }
181
182 // Not found or texture not ready, try to load it
183 if (PreloadSprite(id, path, category))
184 {
185 return GetTexture(id);
186 }
187 return nullptr;
188}
189//-------------------------------------------------------------
190bool DataManager::GetSprite_data(const std::string& id, const std::string& path, VisualSprite_data& outData)
191{
193 // set srcRect based on texture size
194 if (outData.sprite)
195 {
196 int w = ((SDL_Texture*)outData.sprite)->w, h = ((SDL_Texture*)outData.sprite)->h;
197 outData.srcRect = { 0.0f, 0.0f, static_cast<float>(w), static_cast<float>(h) };
198 outData.hotSpot = Vector(w / 2.0f, h / 2.0f, 0.0f);
199 return true;
200 }
201
202 SYSTEM_LOG << "DataManager: GetSprite_data failed for '" << id << "' file/path '" << path << "' does not exists or is incorrect\n";
203 return false;
204}
205//-------------------------------------------------------------
206bool DataManager::GetSpriteEditor_data(const std::string& id, const std::string& path, VisualEditor_data& outData)
207{
209 // set srcRect based on texture size
210 if (outData.sprite)
211 {
212 int w = ((SDL_Texture*)outData.sprite)->w, h = ((SDL_Texture*)outData.sprite)->h;
213 outData.srcRect = { 0.0f, 0.0f, static_cast<float>(w), static_cast<float>(h) };
214 outData.hotSpot = Vector(w / 2.0f, h / 2.0f, 0.0f);
215 return true;
216 }
217 SYSTEM_LOG << "DataManager: GetSpriteEditor_data failed for '" << id << "' file/path '" << path << "' does not exists or is incorrect\n";
218 return false;
219}
220//-------------------------------------------------------------
221bool DataManager::ReleaseResource(const std::string& id)
222{
223 std::lock_guard<std::mutex> lock(m_mutex_);
224 auto it = m_resources_.find(id);
225 if (it == m_resources_.end()) return false;
226 auto res = it->second;
227
228 if (res->sprite_texture)
229 {
230 SDL_DestroyTexture(res->sprite_texture);
231 res->sprite_texture = nullptr;
232 }
233 if (res->data)
234 {
235 // if it was a surface store, free it
236 SDL_Surface* surf = reinterpret_cast<SDL_Surface*>(res->data);
238 res->data = nullptr;
239 }
240
241 m_resources_.erase(it);
242 SYSTEM_LOG << "DataManager: Released resource '" << id << "'\n";
243 return true;
244}
245//-------------------------------------------------------------
247{
248 std::lock_guard<std::mutex> lock(m_mutex_);
249 for (auto& kv : m_resources_)
250 {
251 auto res = kv.second;
252 if (res->sprite_texture)
253 {
254 SDL_DestroyTexture(res->sprite_texture);
255 res->sprite_texture = nullptr;
256 }
257 if (res->data)
258 {
259 SDL_Surface* surf = reinterpret_cast<SDL_Surface*>(res->data);
261 res->data = nullptr;
262 }
263 }
264 m_resources_.clear();
265}
266//-------------------------------------------------------------
267bool DataManager::HasResource(const std::string& id) const
268{
269 std::lock_guard<std::mutex> lock(m_mutex_);
270 return m_resources_.find(id) != m_resources_.end();
271}
272//-------------------------------------------------------------
273std::vector<std::string> DataManager::ListResourcesByType(ResourceType type) const
274{
275 std::vector<std::string> out;
276 std::lock_guard<std::mutex> lock(m_mutex_);
277 // Reserve capacity to avoid reallocations
278 out.reserve(m_resources_.size());
279 for (const auto& kv : m_resources_)
280 {
281 if (kv.second->type == type) out.push_back(kv.first);
282 }
283 return out;
284}
285//-------------------------------------------------------------
286std::vector<std::string> DataManager::ListResourcesByCategory(ResourceCategory category) const
287{
288 std::vector<std::string> out;
289 std::lock_guard<std::mutex> lock(m_mutex_);
290 // Reserve capacity to avoid reallocations
291 out.reserve(m_resources_.size());
292 for (const auto& kv : m_resources_)
293 {
294 if (kv.second->category == category) out.push_back(kv.first);
295 }
296 return out;
297}
298//-------------------------------------------------------------
299// Build standard game data path: ./Gamedata/{videogameName}/{objectName}.json
300std::string DataManager::BuildGameDataPath(const std::string& videogameName, const std::string& objectName)
301{
302 std::string game = videogameName.empty() ? std::string("default") : videogameName;
303 std::string obj = objectName.empty() ? std::string("object") : objectName;
304 std::string path = std::string(".") + "/Gamedata/" + game + "/" + obj + ".json";
305 return path;
306}
307//-------------------------------------------------------------
308bool DataManager::SaveTextFile(const std::string& filepath, const std::string& content) const
309{
310 if (filepath.empty()) return false;
311 // ensure directory exists
312 auto pos = filepath.find_last_of("/\\");
313 if (pos != std::string::npos)
314 {
315 std::string dir = filepath.substr(0, pos);
317 {
318 SYSTEM_LOG << "DataManager: Failed to ensure directory exists for '" << dir << "'\n";
319 // continue attempt to write; fallthrough may fail
320 }
321 }
322
323 std::ofstream ofs(filepath.c_str(), std::ios::binary | std::ios::trunc);
324 if (!ofs) return false;
325 ofs.write(content.data(), static_cast<std::streamsize>(content.size()));
326 return ofs.good();
327}
328//-------------------------------------------------------------
329bool DataManager::LoadTextFile(const std::string& filepath, std::string& outContent) const
330{
331 outContent.clear();
332 if (filepath.empty()) return false;
333 std::ifstream ifs(filepath.c_str(), std::ios::binary);
334 if (!ifs) return false;
335 std::ostringstream ss;
336 ss << ifs.rdbuf();
337 outContent = ss.str();
338 return true;
339}
340//-------------------------------------------------------------
341bool DataManager::SaveJSONForObject(const std::string& videogameName, const std::string& objectName, const std::string& jsonContent) const
342{
343 std::string path = BuildGameDataPath(videogameName, objectName);
344 return SaveTextFile(path, jsonContent);
345}
346//-------------------------------------------------------------
347bool DataManager::LoadJSONForObject(const std::string& videogameName, const std::string& objectName, std::string& outJson) const
348{
349 std::string path = BuildGameDataPath(videogameName, objectName);
350 return LoadTextFile(path, outJson);
351}
352//-------------------------------------------------------------
353bool DataManager::EnsureDirectoryExists(const std::string& dirpath) const
354{
355 if (dirpath.empty()) return false;
356
357 std::string path = dirpath;
358 // normalize separators to '/'
359 std::replace(path.begin(), path.end(), '\\', '/');
360 // remove trailing slash if present
361 if (!path.empty() && path.back() == '/') path.pop_back();
362 if (path.empty()) return true;
363
364 // iterate and create each subpath
365 std::string accum;
366 size_t pos = 0;
367 // if path starts with '/', keep it (unix absolute)
368 if (!path.empty() && path[0] == '/') { accum = "/"; pos = 1; }
369
370 while (true)
371 {
372 size_t next = path.find('/', pos);
373 std::string part = (next == std::string::npos) ? path.substr(pos) : path.substr(pos, next - pos);
374 if (!accum.empty() && accum.back() != '/') accum += '/';
375 accum += part;
376
377 // attempt to create directory
378 #ifdef _WIN32
379 int r = _mkdir(accum.c_str());
380 #else
381 int r = mkdir(accum.c_str(), 0755);
382 #endif
383 if (r != 0)
384 {
385 if (errno == EEXIST)
386 {
387 // already exists - ok
388 }
389 else
390 {
391 // failed for other reason
392 SYSTEM_LOG << "DataManager: mkdir failed for '" << accum << "' (errno=" << errno << ")\n";
393 return false;
394 }
395 }
396
397 if (next == std::string::npos) break;
398 pos = next + 1;
399 }
400
401 return true;
402}
403//-------------------------------------------------------------
405{
406 std::string content;
408 {
409 SYSTEM_LOG << "DataManager: PreloadSystemResources failed to read '" << configFilePath << "'\n";
410 return false;
411 }
412
413 try
414 {
415 nlohmann::json root = nlohmann::json::parse(content);
416 if (!root.contains("system_resources")) return true; // nothing to do
417 const auto& arr = root["system_resources"];
418 if (!arr.is_array()) return false;
419 for (size_t i = 0; i < arr.size(); ++i)
420 {
421 const auto& item = arr[i];
422 if (!item.is_object()) continue;
423 std::string id = item.contains("id") ? item["id"].get<std::string>() : std::string();
424 std::string path = item.contains("path") ? item["path"].get<std::string>() : std::string();
425 std::string type = item.contains("type") ? item["type"].get<std::string>() : std::string();
426 if (id.empty() || path.empty()) continue;
427 if (type == "texture")
428 {
430 }
431 else
432 if (type == "sprite" || type == "animation")
433 {
435 }
436 else
437 {
439 }
440 }
441 }
442 catch (const std::exception& e)
443 {
444 SYSTEM_LOG << "DataManager: JSON parse error in PreloadSystemResources: " << e.what() << "\n";
445 return false;
446 }
447
448 return true;
449}
450
451//=============================================================================
452// PHASE 2: Batch Preloading Implementation
453//=============================================================================
454
456 const std::vector<std::string>& paths,
457 ResourceCategory category,
459{
460 PreloadStats stats;
461 stats.totalRequested = static_cast<int>(paths.size());
462
463 for (const auto& path : paths)
464 {
465 if (path.empty()) continue;
466
467 // Generate ID from path
468 std::string id = path;
469 size_t lastSlash = id.find_last_of("/\\");
470 if (lastSlash != std::string::npos)
471 {
472 id = id.substr(lastSlash + 1);
473 }
474
475 // Try direct load
476 if (PreloadTexture(id, path, category))
477 {
478 stats.successfullyLoaded++;
479 SYSTEM_LOG << " -> Loaded texture: " << path << "\n";
480 }
482 {
483 // Try fallback search
484 std::string filename = id;
486
487 if (!foundPath.empty() && PreloadTexture(id, foundPath, category))
488 {
489 stats.failedWithFallback++;
490 stats.fallbackPaths[path] = foundPath;
491 SYSTEM_LOG << " -> Loaded texture (fallback): " << path << " -> " << foundPath << "\n";
492 }
493 else
494 {
495 stats.completelyFailed++;
496 stats.failedPaths.push_back(path);
497 SYSTEM_LOG << " x Failed to load texture: " << path << "\n";
498 }
499 }
500 else
501 {
502 stats.completelyFailed++;
503 stats.failedPaths.push_back(path);
504 SYSTEM_LOG << " x Failed to load texture: " << path << "\n";
505 }
506 }
507
508 return stats;
509}
510
512 const std::vector<std::string>& paths,
513 ResourceCategory category,
515{
516 PreloadStats stats;
517 stats.totalRequested = static_cast<int>(paths.size());
518
519 for (const auto& path : paths)
520 {
521 if (path.empty()) continue;
522
523 // Generate ID from path
524 std::string id = path;
525 size_t lastSlash = id.find_last_of("/\\");
526 if (lastSlash != std::string::npos)
527 {
528 id = id.substr(lastSlash + 1);
529 }
530
531 // Try direct load
532 if (PreloadSprite(id, path, category))
533 {
534 stats.successfullyLoaded++;
535 SYSTEM_LOG << " -> Loaded sprite: " << path << "\n";
536 }
538 {
539 // Try fallback search
540 std::string filename = id;
542
543 if (!foundPath.empty() && PreloadSprite(id, foundPath, category))
544 {
545 stats.failedWithFallback++;
546 stats.fallbackPaths[path] = foundPath;
547 SYSTEM_LOG << " -> Loaded sprite (fallback): " << path << " -> " << foundPath << "\n";
548 }
549 else
550 {
551 stats.completelyFailed++;
552 stats.failedPaths.push_back(path);
553 SYSTEM_LOG << " x Failed to load sprite: " << path << "\n";
554 }
555 }
556 else
557 {
558 stats.completelyFailed++;
559 stats.failedPaths.push_back(path);
560 SYSTEM_LOG << " x Failed to load sprite: " << path << "\n";
561 }
562 }
563
564 return stats;
565}
566
568 const std::vector<std::string>& paths,
570{
571 PreloadStats stats;
572 stats.totalRequested = static_cast<int>(paths.size());
573
574 // Audio loading not implemented yet - just log
575 for (const auto& path : paths)
576 {
577 if (path.empty()) continue;
578 SYSTEM_LOG << " ⊙ Audio loading not yet implemented: " << path << "\n";
579 stats.completelyFailed++;
580 stats.failedPaths.push_back(path);
581 }
582
583 return stats;
584}
585
587 const std::vector<TilesetInfo>& tilesets,
589{
590 PreloadStats stats;
591 stats.totalRequested = static_cast<int>(tilesets.size());
592
593 for (const auto& tileset : tilesets)
594 {
595 bool success = true;
596
597 // Load main tileset image (if not a collection)
598 if (!tileset.isCollection && !tileset.imageFile.empty())
599 {
600 std::string id = tileset.imageFile;
601 size_t lastSlash = id.find_last_of("/\\");
602 if (lastSlash != std::string::npos)
603 {
604 id = id.substr(lastSlash + 1);
605 }
606
607 if (PreloadTexture(id, tileset.imageFile, ResourceCategory::Level))
608 {
609 SYSTEM_LOG << " -> Loaded tileset image: " << tileset.imageFile << "\n";
610 }
612 {
613 std::string foundPath = FindResourceRecursive(id);
615 {
616 stats.fallbackPaths[tileset.imageFile] = foundPath;
617 SYSTEM_LOG << " -> Loaded tileset image (fallback): " << foundPath << "\n";
618 }
619 else
620 {
621 success = false;
622 SYSTEM_LOG << " x Failed to load tileset image: " << tileset.imageFile << "\n";
623 }
624 }
625 else
626 {
627 success = false;
628 }
629 }
630
631 // Load individual tile images (for collection tilesets)
632 for (const auto& imagePath : tileset.individualImages)
633 {
634 std::string id = imagePath;
635 size_t lastSlash = id.find_last_of("/\\");
636 if (lastSlash != std::string::npos)
637 {
638 id = id.substr(lastSlash + 1);
639 }
640
641 std::string foundPath = FindResourceRecursive(id);
642
643 if (PreloadTexture(id, foundPath/*imagePath*/, ResourceCategory::Level))
644 {
645 SYSTEM_LOG << " -> Loaded tile image: " << imagePath << "\n";
646 }
648 {
649 std::string foundPath = FindResourceRecursive(id);
651 {
652 stats.fallbackPaths[imagePath] = foundPath;
653 SYSTEM_LOG << " -> Loaded tile image (fallback): " << foundPath << "\n";
654 }
655 else
656 {
657 success = false;
658 SYSTEM_LOG << " x Failed to load tile image: " << imagePath << "\n";
659 }
660 }
661 else
662 {
663 success = false;
664 }
665 }
666
667 if (success)
668 {
669 stats.successfullyLoaded++;
670 }
671 else if (!stats.fallbackPaths.empty())
672 {
673 stats.failedWithFallback++;
674 }
675 else
676 {
677 stats.completelyFailed++;
678 if (!tileset.sourceFile.empty())
679 {
680 stats.failedPaths.push_back(tileset.sourceFile);
681 }
682 }
683 }
684
685 return stats;
686}
687
688std::string DataManager::FindResourceRecursive(const std::string& filename, const std::string& rootDir) const
689{
690#ifdef _WIN32
692#else
694#endif
695}
696
697#ifdef _WIN32
698std::string DataManager::FindResourceRecursive_Windows(const std::string& filename, const std::string& rootDir) const
699{
701 std::string searchPath = rootDir + "\\*";
703
705 {
706 return "";
707 }
708
709 do
710 {
711 std::string name = findData.cFileName;
712
713 if (name == "." || name == "..")
714 continue;
715
716 std::string fullPath = rootDir + "\\" + name;
717
718 if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
719 {
720 // Recursively search subdirectory
721 std::string found = FindResourceRecursive_Windows(filename, fullPath);
722 if (!found.empty())
723 {
725 return found;
726 }
727 }
728 else
729 {
730 // Check if filename matches
731 if (name == filename)
732 {
734 return fullPath;
735 }
736 }
737 } while (FindNextFileA(hFind, &findData) != 0);
738
740 return "";
741}
742#else
743std::string DataManager::FindResourceRecursive_Unix(const std::string& filename, const std::string& rootDir) const
744{
745 DIR* dir = opendir(rootDir.c_str());
746 if (!dir)
747 {
748 return "";
749 }
750
751 struct dirent* entry;
752 while ((entry = readdir(dir)) != nullptr)
753 {
754 std::string name = entry->d_name;
755
756 if (name == "." || name == "..")
757 continue;
758
759 std::string fullPath = rootDir + "/" + name;
760
761 struct stat statbuf;
762 if (stat(fullPath.c_str(), &statbuf) == 0)
763 {
764 if (S_ISDIR(statbuf.st_mode))
765 {
766 // Recursively search subdirectory
767 std::string found = FindResourceRecursive_Unix(filename, fullPath);
768 if (!found.empty())
769 {
770 closedir(dir);
771 return found;
772 }
773 }
774 else if (S_ISREG(statbuf.st_mode))
775 {
776 // Check if filename matches
777 if (name == filename)
778 {
779 closedir(dir);
780 return fullPath;
781 }
782 }
783 }
784 }
785
786 closedir(dir);
787 return "";
788}
789#endif
ResourceCategory
Definition DataManager.h:57
ResourceType
Definition DataManager.h:41
SDL_Texture Sprite
Definition DataManager.h:34
Core ECS component definitions.
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Core game engine class.
static SDL_Renderer * renderer
static std::string BuildGameDataPath(const std::string &videogameName, const std::string &objectName)
std::string FindResourceRecursive_Unix(const std::string &filename, const std::string &rootDir) const
bool PreloadSystemResources(const std::string &configFilePath)
Sprite * GetSprite(const std::string &id, const std::string &path, ResourceCategory category=ResourceCategory::GameEntity)
void Initialize()
PreloadStats PreloadSprites(const std::vector< std::string > &paths, ResourceCategory category=ResourceCategory::GameEntity, bool enableFallbackScan=true)
PreloadStats PreloadTextures(const std::vector< std::string > &paths, ResourceCategory category=ResourceCategory::Level, bool enableFallbackScan=true)
virtual ~DataManager()
bool m_enableFallbackScan
Sprite * GetTexture(const std::string &id) const
bool EnsureDirectoryExists(const std::string &dirpath) const
std::string FindResourceRecursive(const std::string &filename, const std::string &rootDir="GameData") const
bool SaveTextFile(const std::string &filepath, const std::string &content) const
static DataManager & GetInstance()
std::vector< std::string > ListResourcesByType(ResourceType type) const
bool HasResource(const std::string &id) const
bool LoadJSONForObject(const std::string &videogameName, const std::string &objectName, std::string &outJson) const
std::unordered_map< std::string, std::shared_ptr< Resource > > m_resources_
std::mutex m_mutex_
bool PreloadTexture(const std::string &id, const std::string &path, ResourceCategory category=ResourceCategory::System)
PreloadStats PreloadTilesets(const std::vector< TilesetInfo > &tilesets, bool enableFallbackScan=true)
bool SaveJSONForObject(const std::string &videogameName, const std::string &objectName, const std::string &jsonContent) const
bool ReleaseResource(const std::string &id)
PreloadStats PreloadAudioFiles(const std::vector< std::string > &paths, bool enableFallbackScan=true)
bool LoadTextFile(const std::string &filepath, std::string &outContent) const
bool PreloadSprite(const std::string &id, const std::string &path, ResourceCategory category=ResourceCategory::GameEntity)
void Shutdown()
std::vector< std::string > ListResourcesByCategory(ResourceCategory category) const
std::string name
bool GetSprite_data(const std::string &id, const std::string &path, VisualSprite_data &outData)
bool GetSpriteEditor_data(const std::string &id, const std::string &path, VisualEditor_data &outData)
static SDL_Renderer * renderer
Main SDL renderer.
Definition GameEngine.h:129
nlohmann::json json
std::vector< std::string > failedPaths
std::map< std::string, std::string > fallbackPaths
#define SYSTEM_LOG