Files
coffee-crisis/source/core/resources/resource.cpp
T

266 lines
7.9 KiB
C++

#include "core/resources/resource.h"
#include <iostream>
#include <sstream>
#include "core/audio/jail_audio.hpp"
#include "core/rendering/text.h"
#include "core/rendering/texture.h"
#include "core/resources/asset.h"
#include "core/resources/resource_helper.h"
#include "game/ui/menu.h"
// Nota: Asset::get() e Input::get() se consultan en preloadAll y al construir
// los menús; no se guardan punteros en el objeto Resource.
Resource *Resource::instance = nullptr;
static auto basename(const std::string &path) -> std::string {
return path.substr(path.find_last_of("\\/") + 1);
}
static auto stem(const std::string &path) -> std::string {
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) {
if (instance == nullptr) {
instance = new Resource(renderer);
instance->preloadAll();
}
}
void Resource::destroy() {
delete instance;
instance = nullptr;
}
auto Resource::get() -> Resource * {
return instance;
}
Resource::Resource(SDL_Renderer *renderer)
: renderer_(renderer) {}
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() {
preloadResources();
preloadFonts();
preloadMenus();
}
// Pass 1: texturas, sonidos, músicas y datos (animaciones / demo / menús)
void Resource::preloadResources() {
const auto &items = Asset::get()->getAll();
for (const auto &it : items) {
if (!ResourceHelper::shouldUseResourcePack(it.file) && it.type != Asset::Type::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 BASE_NAME = basename(it.file);
switch (it.type) {
case Asset::Type::BITMAP: {
auto *tex = new Texture(renderer_, bytes);
textures_[BASE_NAME] = tex;
break;
}
case Asset::Type::SOUND: {
Ja::Sound *s = Ja::loadSound(bytes.data(), (uint32_t)bytes.size());
if (s != nullptr) {
sounds_[BASE_NAME] = s;
}
break;
}
case Asset::Type::MUSIC: {
Ja::Music *m = Ja::loadMusic(bytes.data(), (Uint32)bytes.size());
if (m != nullptr) {
musics_[BASE_NAME] = m;
}
break;
}
case Asset::Type::DATA:
loadDataAsset(BASE_NAME, bytes);
break;
case Asset::Type::FONT: // Fonts: se emparejan en pass 2
case Asset::Type::LANG: // Lenguaje: lo sigue leyendo la clase Lang via ResourceHelper
default:
break;
}
}
}
// Despacha un asset Asset::Type::DATA en función de la extensión / nombre
void Resource::loadDataAsset(const std::string &bname, const std::vector<uint8_t> &bytes) {
if (bname.size() >= 4 && bname.substr(bname.size() - 4) == ".ani") {
std::string content(reinterpret_cast<const char *>(bytes.data()), bytes.size());
std::stringstream ss(content);
std::vector<std::string> lines;
std::string line;
while (std::getline(ss, line)) {
// Normalitza CRLF perquè loadFromVector compari línies amb literals
// ("[animation]", "[/animation]") sense \r residual.
if (!line.empty() && line.back() == '\r') {
line.pop_back();
}
lines.push_back(line);
}
animation_lines_[bname] = std::move(lines);
} else if (bname == "demo.bin") {
demo_bytes_ = bytes;
}
// Menús (.men): se construyen en pass 2 porque dependen de textos y sonidos
}
// Pass 2a: construye Text por cada par basename.png + basename.txt
void Resource::preloadFonts() {
const auto &items = Asset::get()->getAll();
std::unordered_map<std::string, std::vector<uint8_t>> font_pngs;
std::unordered_map<std::string, std::vector<uint8_t>> font_txts;
for (const auto &it : items) {
if (it.type != Asset::Type::FONT) {
continue;
}
auto bytes = ResourceHelper::loadFile(it.file);
if (bytes.empty()) {
continue;
}
const std::string S = stem(it.file);
const std::string BASE_NAME = basename(it.file);
if (BASE_NAME.size() >= 4 && BASE_NAME.substr(BASE_NAME.size() - 4) == ".png") {
font_pngs[S] = std::move(bytes);
} else if (BASE_NAME.size() >= 4 && BASE_NAME.substr(BASE_NAME.size() - 4) == ".txt") {
font_txts[S] = std::move(bytes);
}
}
for (const auto &[s, png] : font_pngs) {
auto it_txt = font_txts.find(s);
if (it_txt == font_txts.end()) {
continue;
}
Text *t = new Text(png, it_txt->second, renderer_);
texts_[s] = t;
}
}
// Pass 2b: construye los Menu (dependen de Text+sonidos cargados antes)
//
// NOTA: Menu::loadFromBytes aún llama internamente 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. Migración pendiente.
void Resource::preloadMenus() {
const auto &items = Asset::get()->getAll();
for (const auto &it : items) {
if (it.type != Asset::Type::DATA) {
continue;
}
const std::string BASE_NAME = basename(it.file);
if (BASE_NAME.size() < 4 || BASE_NAME.substr(BASE_NAME.size() - 4) != ".men") {
continue;
}
auto bytes = ResourceHelper::loadFile(it.file);
if (bytes.empty()) {
continue;
}
Menu *m = new Menu(renderer_, "");
m->loadFromBytes(bytes, BASE_NAME);
const std::string S = stem(it.file);
menus_[S] = m;
}
}
auto Resource::getTexture(const std::string &name) -> Texture * {
auto it = textures_.find(name);
if (it == textures_.end()) {
std::cerr << "Resource::getTexture: missing " << name << '\n';
return nullptr;
}
return it->second;
}
auto Resource::getSound(const std::string &name) -> Ja::Sound * {
auto it = sounds_.find(name);
if (it == sounds_.end()) {
std::cerr << "Resource::getSound: missing " << name << '\n';
return nullptr;
}
return it->second;
}
auto Resource::getMusic(const std::string &name) -> Ja::Music * {
auto it = musics_.find(name);
if (it == musics_.end()) {
std::cerr << "Resource::getMusic: missing " << name << '\n';
return nullptr;
}
return it->second;
}
auto Resource::getAnimationLines(const std::string &name) -> std::vector<std::string> & {
auto it = animation_lines_.find(name);
if (it == animation_lines_.end()) {
static std::vector<std::string> empty_;
std::cerr << "Resource::getAnimationLines: missing " << name << '\n';
return empty_;
}
return it->second;
}
auto Resource::getText(const std::string &name) -> Text * {
auto it = texts_.find(name);
if (it == texts_.end()) {
std::cerr << "Resource::getText: missing " << name << '\n';
return nullptr;
}
return it->second;
}
auto Resource::getMenu(const std::string &name) -> Menu * {
auto it = menus_.find(name);
if (it == menus_.end()) {
std::cerr << "Resource::getMenu: missing " << name << '\n';
return nullptr;
}
return it->second;
}