a8c0386355
This reverts commit ebfcad6f22.
266 lines
7.9 KiB
C++
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;
|
|
}
|