#include "core/resources/resource_cache.hpp" #include #include #include #include #include #include "core/audio/jail_audio.hpp" #include "core/resources/resource_helper.hpp" #include "core/resources/resource_list.hpp" // gif.h ja s'inclou des de jdraw8.cpp i text.cpp; el seu codi no és static // ni inline, així que no podem tornar-lo a incloure aquí. Ens fiem de les // declaracions extern dels símbols que ens calen (linkatge C++ normal, // igual que fa text.cpp). extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h); extern unsigned char* LoadPalette(unsigned char* data); namespace Resource { std::unique_ptr Cache::instance; void Cache::init() { instance = std::unique_ptr(new Cache()); } void Cache::destroy() { instance.reset(); } auto Cache::get() -> Cache* { return instance.get(); } namespace { auto basename(const std::string& path) -> std::string { auto pos = path.find_last_of("/\\"); return pos == std::string::npos ? path : path.substr(pos + 1); } } // namespace auto Cache::getMusic(const std::string& name) -> JA_Music_t* { auto it = std::ranges::find_if(musics_, [&](const auto& m) { return m.name == name; }); if (it != musics_.end()) { return it->music.get(); } std::cerr << "Resource::Cache: música no trobada: " << name << '\n'; throw std::runtime_error("Music not found: " + name); } auto Cache::getSound(const std::string& name) -> JA_Sound_t* { auto it = std::ranges::find_if(sounds_, [&](const auto& s) { return s.name == name; }); if (it != sounds_.end()) { return it->sound.get(); } std::cerr << "Resource::Cache: so no trobat: " << name << '\n'; throw std::runtime_error("Sound not found: " + name); } auto Cache::getSurfacePixels(const std::string& name) -> const std::vector& { auto it = std::ranges::find_if(surfaces_, [&](const auto& s) { return s.name == name; }); if (it != surfaces_.end()) { return it->pixels; } std::cerr << "Resource::Cache: surface no trobada: " << name << '\n'; throw std::runtime_error("Surface not found: " + name); } auto Cache::getPaletteBytes(const std::string& name) -> const std::vector& { auto it = std::ranges::find_if(surfaces_, [&](const auto& s) { return s.name == name; }); if (it != surfaces_.end()) { return it->palette; } std::cerr << "Resource::Cache: paleta no trobada: " << name << '\n'; throw std::runtime_error("Palette not found: " + name); } auto Cache::getTextFile(const std::string& name) -> const std::vector& { auto it = std::ranges::find_if(text_files_, [&](const auto& t) { return t.name == name; }); if (it != text_files_.end()) { return it->bytes; } std::cerr << "Resource::Cache: text file no trobat: " << name << '\n'; throw std::runtime_error("TextFile not found: " + name); } void Cache::calculateTotal() { auto* list = List::get(); total_count_ = static_cast( list->getListByType(List::Type::MUSIC).size() + list->getListByType(List::Type::SOUND).size() + list->getListByType(List::Type::BITMAP).size() + list->getListByType(List::Type::DATA).size() + list->getListByType(List::Type::FONT).size()); loaded_count_ = 0; } auto Cache::getProgress() const -> float { if (total_count_ == 0) return 1.0F; return static_cast(loaded_count_) / static_cast(total_count_); } void Cache::beginLoad() { calculateTotal(); stage_ = LoadStage::MUSICS; stage_index_ = 0; std::cout << "Resource::Cache: precarregant " << total_count_ << " assets\n"; } auto Cache::loadStep(int budget_ms) -> bool { if (stage_ == LoadStage::DONE) return true; const Uint64 start_ns = SDL_GetTicksNS(); const Uint64 budget_ns = static_cast(budget_ms) * 1'000'000ULL; auto* list = List::get(); while (stage_ != LoadStage::DONE) { switch (stage_) { case LoadStage::MUSICS: { auto items = list->getListByType(List::Type::MUSIC); if (stage_index_ == 0) musics_.clear(); if (stage_index_ >= items.size()) { stage_ = LoadStage::SOUNDS; stage_index_ = 0; break; } loadOneMusic(stage_index_++); break; } case LoadStage::SOUNDS: { auto items = list->getListByType(List::Type::SOUND); if (stage_index_ == 0) sounds_.clear(); if (stage_index_ >= items.size()) { stage_ = LoadStage::BITMAPS; stage_index_ = 0; break; } loadOneSound(stage_index_++); break; } case LoadStage::BITMAPS: { auto items = list->getListByType(List::Type::BITMAP); if (stage_index_ == 0) surfaces_.clear(); if (stage_index_ >= items.size()) { stage_ = LoadStage::TEXT_FILES; stage_index_ = 0; break; } loadOneBitmap(stage_index_++); break; } case LoadStage::TEXT_FILES: { auto data_items = list->getListByType(List::Type::DATA); auto font_items = list->getListByType(List::Type::FONT); auto items = data_items; items.insert(items.end(), font_items.begin(), font_items.end()); if (stage_index_ == 0) text_files_.clear(); if (stage_index_ >= items.size()) { stage_ = LoadStage::DONE; stage_index_ = 0; std::cout << "Resource::Cache: precarrega completada (" << loaded_count_ << "/" << total_count_ << ")\n"; break; } loadOneTextFile(stage_index_++); break; } case LoadStage::DONE: break; } if ((SDL_GetTicksNS() - start_ns) >= budget_ns) break; } return stage_ == LoadStage::DONE; } void Cache::loadOneMusic(size_t index) { auto items = List::get()->getListByType(List::Type::MUSIC); const auto& path = items[index]; auto name = basename(path); current_loading_name_ = name; auto bytes = ResourceHelper::loadFile(path); if (bytes.empty()) { std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n'; return; } JA_Music_t* music = JA_LoadMusic(bytes.data(), static_cast(bytes.size()), path.c_str()); if (music == nullptr) { std::cerr << "Resource::Cache: JA_LoadMusic ha fallat per " << path << '\n'; return; } musics_.push_back(MusicResource{.name = name, .music = std::unique_ptr(music)}); ++loaded_count_; std::cout << " [music ] " << name << '\n'; } void Cache::loadOneSound(size_t index) { auto items = List::get()->getListByType(List::Type::SOUND); const auto& path = items[index]; auto name = basename(path); current_loading_name_ = name; auto bytes = ResourceHelper::loadFile(path); if (bytes.empty()) { std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n'; return; } JA_Sound_t* sound = JA_LoadSound(bytes.data(), static_cast(bytes.size())); if (sound == nullptr) { std::cerr << "Resource::Cache: JA_LoadSound ha fallat per " << path << '\n'; return; } sounds_.push_back(SoundResource{.name = name, .sound = std::unique_ptr(sound)}); ++loaded_count_; std::cout << " [sound ] " << name << '\n'; } void Cache::loadOneBitmap(size_t index) { auto items = List::get()->getListByType(List::Type::BITMAP); const auto& path = items[index]; auto name = basename(path); current_loading_name_ = name; auto bytes = ResourceHelper::loadFile(path); if (bytes.empty()) { std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n'; return; } // Decodifica píxels. unsigned short w = 0; unsigned short h = 0; unsigned char* pixels = LoadGif(bytes.data(), &w, &h); if (pixels == nullptr) { std::cerr << "Resource::Cache: LoadGif ha fallat per " << path << '\n'; return; } SurfaceResource res; res.name = name; res.pixels.assign(pixels, pixels + 64000); std::free(pixels); // Decodifica paleta des del mateix GIF (necessita una segona passada // perquè LoadGif no exposa la paleta). unsigned char* palette = LoadPalette(bytes.data()); if (palette != nullptr) { res.palette.assign(palette, palette + 768); std::free(palette); } surfaces_.push_back(std::move(res)); ++loaded_count_; std::cout << " [bitmap] " << name << '\n'; } void Cache::loadOneTextFile(size_t index) { auto data_items = List::get()->getListByType(List::Type::DATA); auto font_items = List::get()->getListByType(List::Type::FONT); auto items = data_items; items.insert(items.end(), font_items.begin(), font_items.end()); const auto& path = items[index]; auto name = basename(path); current_loading_name_ = name; auto bytes = ResourceHelper::loadFile(path); if (bytes.empty()) { std::cerr << "Resource::Cache: no s'ha pogut llegir " << path << '\n'; return; } text_files_.push_back(TextFileResource{.name = name, .bytes = std::move(bytes)}); ++loaded_count_; std::cout << " [text ] " << name << '\n'; } } // namespace Resource