#include "resource.h" #include #include #include #include #include "asset.h" #include "jail_audio.hpp" #include "menu.h" #include "resource_helper.h" #include "text.h" #include "texture.h" Resource *Resource::instance_ = nullptr; static std::string basename(const std::string &path) { return path.substr(path.find_last_of("\\/") + 1); } static std::string stem(const std::string &path) { std::string b = basename(path); size_t dot = b.find_last_of('.'); if (dot == std::string::npos) return b; return b.substr(0, dot); } void Resource::init(SDL_Renderer *renderer, Asset *asset, Input *input) { if (instance_ == nullptr) { instance_ = new Resource(renderer, asset, input); instance_->preloadAll(); } } void Resource::destroy() { delete instance_; instance_ = nullptr; } Resource *Resource::get() { return instance_; } Resource::Resource(SDL_Renderer *renderer, Asset *asset, Input *input) : renderer_(renderer), asset_(asset), input_(input) {} Resource::~Resource() { for (auto &[name, m] : menus_) delete m; menus_.clear(); for (auto &[name, t] : texts_) delete t; texts_.clear(); for (auto &[name, t] : textures_) delete t; textures_.clear(); for (auto &[name, s] : sounds_) JA_DeleteSound(s); sounds_.clear(); for (auto &[name, m] : musics_) JA_DeleteMusic(m); musics_.clear(); } void Resource::preloadAll() { const auto &items = asset_->getAll(); // Pass 1: texturas, sonidos, músicas, animaciones (raw lines), demo, lenguajes for (const auto &it : items) { if (!ResourceHelper::shouldUseResourcePack(it.file) && it.type != t_lang) { // Ficheros absolutos (config.txt, score.bin, systemFolder) — no se precargan continue; } auto bytes = ResourceHelper::loadFile(it.file); if (bytes.empty()) continue; const std::string bname = basename(it.file); switch (it.type) { case t_bitmap: { auto *tex = new Texture(renderer_, bytes); textures_[bname] = tex; break; } case t_sound: { JA_Sound_t *s = JA_LoadSound(bytes.data(), (uint32_t)bytes.size()); if (s) sounds_[bname] = s; break; } case t_music: { JA_Music_t *m = JA_LoadMusic(bytes.data(), (Uint32)bytes.size()); if (m) musics_[bname] = m; break; } case t_data: { if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".ani") { std::string content(reinterpret_cast(bytes.data()), bytes.size()); std::stringstream ss(content); std::vector lines; std::string line; while (std::getline(ss, line)) { lines.push_back(line); } animationLines_[bname] = std::move(lines); } else if (bname == "demo.bin") { demoBytes_ = bytes; } else if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".men") { // Menús: se construyen en pass 2 porque dependen de textos y sonidos } break; } case t_font: // Fonts: se emparejan en pass 2 break; case t_lang: // Lenguaje: lo sigue leyendo la clase Lang a través de ResourceHelper break; default: break; } } // Pass 2: Text (fuentes emparejadas png+txt) y Menus (dependen de Text+sonidos) // Fuentes: construimos un Text por cada par basename.png + basename.txt // Acumulamos los bytes encontrados por stem (basename sin ext.) std::unordered_map> fontPngs; std::unordered_map> fontTxts; for (const auto &it : items) { if (it.type != t_font) continue; auto bytes = ResourceHelper::loadFile(it.file); if (bytes.empty()) continue; const std::string s = stem(it.file); const std::string bname = basename(it.file); if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".png") { fontPngs[s] = std::move(bytes); } else if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".txt") { fontTxts[s] = std::move(bytes); } } for (const auto &[s, png] : fontPngs) { auto itTxt = fontTxts.find(s); if (itTxt == fontTxts.end()) continue; Text *t = new Text(png, itTxt->second, renderer_); texts_[s] = t; } // Menus: usan aún Menu::loadFromBytes que internamente llama a asset->get() y // Text/JA_LoadSound por path. Funciona en modo fallback; en pack estricto // requiere que Menu se adapte a cargar desde ResourceHelper. Por ahora // lo dejamos así y será una migración del paso 7. for (const auto &it : items) { if (it.type != t_data) continue; const std::string bname = basename(it.file); if (bname.size() < 4 || bname.substr(bname.size() - 4) != ".men") continue; auto bytes = ResourceHelper::loadFile(it.file); if (bytes.empty()) continue; Menu *m = new Menu(renderer_, asset_, input_, ""); m->loadFromBytes(bytes, bname); const std::string s = stem(it.file); menus_[s] = m; } } Texture *Resource::getTexture(const std::string &name) { auto it = textures_.find(name); if (it == textures_.end()) { std::cerr << "Resource::getTexture: missing " << name << '\n'; return nullptr; } return it->second; } JA_Sound_t *Resource::getSound(const std::string &name) { auto it = sounds_.find(name); if (it == sounds_.end()) { std::cerr << "Resource::getSound: missing " << name << '\n'; return nullptr; } return it->second; } JA_Music_t *Resource::getMusic(const std::string &name) { auto it = musics_.find(name); if (it == musics_.end()) { std::cerr << "Resource::getMusic: missing " << name << '\n'; return nullptr; } return it->second; } std::vector &Resource::getAnimationLines(const std::string &name) { auto it = animationLines_.find(name); if (it == animationLines_.end()) { static std::vector empty; std::cerr << "Resource::getAnimationLines: missing " << name << '\n'; return empty; } return it->second; } Text *Resource::getText(const std::string &name) { auto it = texts_.find(name); if (it == texts_.end()) { std::cerr << "Resource::getText: missing " << name << '\n'; return nullptr; } return it->second; } Menu *Resource::getMenu(const std::string &name) { auto it = menus_.find(name); if (it == menus_.end()) { std::cerr << "Resource::getMenu: missing " << name << '\n'; return nullptr; } return it->second; }