Files
aee/source/core/resources/resource_cache.cpp
2026-04-18 11:41:34 +02:00

273 lines
10 KiB
C++

#include "core/resources/resource_cache.hpp"
#include <SDL3/SDL.h>
#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#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 {
Cache* Cache::instance = nullptr;
void Cache::init() { instance = new Cache(); }
void Cache::destroy() {
delete instance;
instance = nullptr;
}
auto Cache::get() -> Cache* { return instance; }
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<Uint8>& {
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<Uint8>& {
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<uint8_t>& {
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<int>(
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<float>(loaded_count_) / static_cast<float>(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<Uint64>(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<Uint32>(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<JA_Music_t, MusicDeleter>(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<uint32_t>(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<JA_Sound_t, SoundDeleter>(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