feat(boot): precàrrega de música, sons i shapes a l'arrencada

This commit is contained in:
2026-05-24 19:31:35 +02:00
parent 73c7e4ea76
commit d4117e3505
5 changed files with 244 additions and 182 deletions
+61 -56
View File
@@ -9,72 +9,77 @@
namespace Resource::Helper { namespace Resource::Helper {
// Inicialitzar el sistema de recursos // Inicialitzar el sistema de recursos
auto initializeResourceSystem(const std::string& pack_file, bool fallback) -> bool { auto initializeResourceSystem(const std::string& pack_file, bool fallback) -> bool {
return Loader::get().initialize(pack_file, fallback); return Loader::get().initialize(pack_file, fallback);
} }
// Carregar un file // Carregar un file
auto loadFile(const std::string& filepath) -> std::vector<uint8_t> { auto loadFile(const std::string& filepath) -> std::vector<uint8_t> {
// Normalitzar la ruta // Normalitzar la ruta
std::string normalized = normalizePath(filepath); std::string normalized = normalizePath(filepath);
// Carregar del sistema de recursos // Carregar del sistema de recursos
return Loader::get().loadResource(normalized); return Loader::get().loadResource(normalized);
} }
// Comprovar si existeix un file // Llistar recursos amb un prefix donat
auto fileExists(const std::string& filepath) -> bool { auto listResources(const std::string& prefix) -> std::vector<std::string> {
std::string normalized = normalizePath(filepath); return Loader::get().listResources(prefix);
return Loader::get().resourceExists(normalized); }
}
// Obtenir ruta normalitzada per al paquet // Comprovar si existeix un file
// Elimina prefixos "data/", rutes absolutes, etc. auto fileExists(const std::string& filepath) -> bool {
auto getPackPath(const std::string& asset_path) -> std::string { std::string normalized = normalizePath(filepath);
std::string path = asset_path; return Loader::get().resourceExists(normalized);
}
// Eliminar rutes absolutes (detectar / o C:\ al principi) // Obtenir ruta normalitzada per al paquet
if (!path.empty() && path[0] == '/') { // Elimina prefixos "data/", rutes absolutes, etc.
// Buscar "data/" i agafar el que ve después auto getPackPath(const std::string& asset_path) -> std::string {
size_t data_pos = path.find("/data/"); std::string path = asset_path;
if (data_pos != std::string::npos) {
path = path.substr(data_pos + 6); // Saltar "/data/" // Eliminar rutes absolutes (detectar / o C:\ al principi)
if (!path.empty() && path[0] == '/') {
// Buscar "data/" i agafar el que ve después
size_t data_pos = path.find("/data/");
if (data_pos != std::string::npos) {
path = path.substr(data_pos + 6); // Saltar "/data/"
}
} }
// Eliminar "./" i "../" del principi
while (path.starts_with("./")) {
path = path.substr(2);
}
while (path.starts_with("../")) {
path = path.substr(3);
}
// Eliminar "data/" del principi
if (path.starts_with("data/")) {
path = path.substr(5);
}
// Eliminar "Resources/" (macOS bundles)
if (path.starts_with("Resources/")) {
path = path.substr(10);
}
// Convertir barres invertides a normals
std::ranges::replace(path, '\\', '/');
return path;
} }
// Eliminar "./" i "../" del principi // Normalitzar ruta (alias de getPackPath)
while (path.starts_with("./")) { auto normalizePath(const std::string& path) -> std::string {
path = path.substr(2); return getPackPath(path);
}
while (path.starts_with("../")) {
path = path.substr(3);
} }
// Eliminar "data/" del principi // Comprovar si hay paquet carregat
if (path.starts_with("data/")) { auto isPackLoaded() -> bool {
path = path.substr(5); return Loader::get().isPackLoaded();
} }
// Eliminar "Resources/" (macOS bundles)
if (path.starts_with("Resources/")) {
path = path.substr(10);
}
// Convertir barres invertides a normals
std::ranges::replace(path, '\\', '/');
return path;
}
// Normalitzar ruta (alias de getPackPath)
auto normalizePath(const std::string& path) -> std::string {
return getPackPath(path);
}
// Comprovar si hay paquet carregat
auto isPackLoaded() -> bool {
return Loader::get().isPackLoaded();
}
} // namespace Resource::Helper } // namespace Resource::Helper
+13 -10
View File
@@ -10,18 +10,21 @@
namespace Resource::Helper { namespace Resource::Helper {
// Inicialización del sistema // Inicialización del sistema
auto initializeResourceSystem(const std::string& pack_file, bool fallback) -> bool; auto initializeResourceSystem(const std::string& pack_file, bool fallback) -> bool;
// Càrrega de archivos // Càrrega de archivos
auto loadFile(const std::string& filepath) -> std::vector<uint8_t>; auto loadFile(const std::string& filepath) -> std::vector<uint8_t>;
auto fileExists(const std::string& filepath) -> bool; auto fileExists(const std::string& filepath) -> bool;
// Normalització de rutes // Llistat de recursos disponibles amb un prefix (ex. "shapes/", "sounds/").
auto getPackPath(const std::string& asset_path) -> std::string; auto listResources(const std::string& prefix) -> std::vector<std::string>;
auto normalizePath(const std::string& path) -> std::string;
// Estat // Normalització de rutes
auto isPackLoaded() -> bool; auto getPackPath(const std::string& asset_path) -> std::string;
auto normalizePath(const std::string& path) -> std::string;
// Estat
auto isPackLoaded() -> bool;
} // namespace Resource::Helper } // namespace Resource::Helper
+145 -108
View File
@@ -3,141 +3,178 @@
#include "resource_loader.hpp" #include "resource_loader.hpp"
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
namespace Resource { namespace Resource {
// Singleton // Singleton
auto Loader::get() -> Loader& { auto Loader::get() -> Loader& {
static Loader instance_; static Loader instance_;
return instance_; return instance_;
}
// Inicialitzar el sistema de recursos
auto Loader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
fallback_enabled_ = enable_fallback;
// Intentar load el paquet
pack_ = std::make_unique<Pack>();
if (!pack_->loadPack(pack_file)) {
if (!fallback_enabled_) {
std::cerr << "[ResourceLoader] ERROR FATAL: No es pot load " << pack_file
<< " y el fallback está desactivat\n";
return false;
}
std::cout << "[ResourceLoader] Paquet no trobat, usant fallback al sistema de archivos\n";
pack_.reset(); // No hay paquet
return true;
} }
std::cout << "[ResourceLoader] Paquet carregat: " << pack_file << "\n"; // Inicialitzar el sistema de recursos
return true; auto Loader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
} fallback_enabled_ = enable_fallback;
// Carregar un recurs // Intentar load el paquet
auto Loader::loadResource(const std::string& filename) -> std::vector<uint8_t> { pack_ = std::make_unique<Pack>();
// Intentar load del paquet primer
if (pack_) { if (!pack_->loadPack(pack_file)) {
if (pack_->hasResource(filename)) { if (!fallback_enabled_) {
auto data = pack_->getResource(filename); std::cerr << "[ResourceLoader] ERROR FATAL: No es pot load " << pack_file
if (!data.empty()) { << " y el fallback está desactivat\n";
return data; return false;
} }
std::cerr << "[ResourceLoader] Advertència: recurs buit al paquet: " << filename
<< "\n"; std::cout << "[ResourceLoader] Paquet no trobat, usant fallback al sistema de archivos\n";
pack_.reset(); // No hay paquet
return true;
} }
// Si no está al paquet y no hay fallback, falla std::cout << "[ResourceLoader] Paquet carregat: " << pack_file << "\n";
if (!fallback_enabled_) {
std::cerr << "[ResourceLoader] ERROR: Recurs no trobat al paquet i fallback desactivat: "
<< filename << "\n";
return {};
}
}
// Fallback al sistema de archivos
if (fallback_enabled_) {
return loadFromFilesystem(filename);
}
return {};
}
// Comprovar si existeix un recurs
auto Loader::resourceExists(const std::string& filename) -> bool {
// Comprovar al paquet
if (pack_ && pack_->hasResource(filename)) {
return true; return true;
} }
// Comprovar al sistema de archivos si está activat el fallback // Carregar un recurs
if (fallback_enabled_) { auto Loader::loadResource(const std::string& filename) -> std::vector<uint8_t> {
std::string fullpath = base_path_.empty() ? "data/" + filename : base_path_ + "/data/" + filename; // Intentar load del paquet primer
return std::filesystem::exists(fullpath); if (pack_) {
if (pack_->hasResource(filename)) {
auto data = pack_->getResource(filename);
if (!data.empty()) {
return data;
}
std::cerr << "[ResourceLoader] Advertència: recurs buit al paquet: " << filename
<< "\n";
}
// Si no está al paquet y no hay fallback, falla
if (!fallback_enabled_) {
std::cerr << "[ResourceLoader] ERROR: Recurs no trobat al paquet i fallback desactivat: "
<< filename << "\n";
return {};
}
}
// Fallback al sistema de archivos
if (fallback_enabled_) {
return loadFromFilesystem(filename);
}
return {};
} }
return false; auto Loader::listResources(const std::string& prefix) -> std::vector<std::string> {
} std::vector<std::string> result;
if (pack_) {
for (const auto& path : pack_->getResourceList()) {
if (path.starts_with(prefix)) {
result.push_back(path);
}
}
return result;
}
if (!fallback_enabled_) {
return result;
}
std::string root = base_path_.empty() ? "data/" + prefix : base_path_ + "/data/" + prefix;
if (!std::filesystem::exists(root)) {
return result;
}
for (const auto& entry : std::filesystem::recursive_directory_iterator(root)) {
if (!entry.is_regular_file()) {
continue;
}
std::string full = entry.path().generic_string();
if (auto pos = full.find("/data/"); pos != std::string::npos) {
result.push_back(full.substr(pos + 6));
} else if (full.starts_with("data/")) {
result.push_back(full.substr(5));
}
}
std::ranges::sort(result);
return result;
}
// Comprovar si existeix un recurs
auto Loader::resourceExists(const std::string& filename) -> bool {
// Comprovar al paquet
if (pack_ && pack_->hasResource(filename)) {
return true;
}
// Comprovar al sistema de archivos si está activat el fallback
if (fallback_enabled_) {
std::string fullpath = base_path_.empty() ? "data/" + filename : base_path_ + "/data/" + filename;
return std::filesystem::exists(fullpath);
}
// Validar el paquet
auto Loader::validatePack() -> bool {
if (!pack_) {
std::cerr << "[ResourceLoader] Advertència: no hay paquet carregat per validar\n";
return false; return false;
} }
return pack_->validatePack(); // Validar el paquet
} auto Loader::validatePack() -> bool {
if (!pack_) {
std::cerr << "[ResourceLoader] Advertència: no hay paquet carregat per validar\n";
return false;
}
// Comprovar si hay paquet carregat return pack_->validatePack();
auto Loader::isPackLoaded() const -> bool {
return pack_ != nullptr;
}
// Establir la ruta base
void Loader::setBasePath(const std::string& path) {
base_path_ = path;
std::cout << "[ResourceLoader] Ruta base establerta: " << base_path_ << "\n";
}
// Obtenir la ruta base
auto Loader::getBasePath() const -> const std::string& {
return base_path_;
}
// Carregar des del sistema de archivos (fallback)
auto Loader::loadFromFilesystem(const std::string& filename) -> std::vector<uint8_t> {
// The filename is already normalized (e.g., "shapes/logo/letra_j.shp")
// We need to prepend base_path + "data/"
std::string fullpath;
if (base_path_.empty()) {
fullpath = "data/" + filename;
} else {
fullpath = base_path_ + "/data/" + filename;
} }
std::ifstream file(fullpath, std::ios::binary | std::ios::ate); // Comprovar si hay paquet carregat
if (!file) { auto Loader::isPackLoaded() const -> bool {
std::cerr << "[ResourceLoader] Error: no es pot obrir " << fullpath << "\n"; return pack_ != nullptr;
return {};
} }
std::streamsize file_size = file.tellg(); // Establir la ruta base
file.seekg(0, std::ios::beg); void Loader::setBasePath(const std::string& path) {
base_path_ = path;
std::vector<uint8_t> data(file_size); std::cout << "[ResourceLoader] Ruta base establerta: " << base_path_ << "\n";
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "[ResourceLoader] Error: no es pot llegir " << fullpath << "\n";
return {};
} }
std::cout << "[ResourceLoader] Carregat des del sistema de archivos: " << fullpath << "\n"; // Obtenir la ruta base
return data; auto Loader::getBasePath() const -> const std::string& {
} return base_path_;
}
// Carregar des del sistema de archivos (fallback)
auto Loader::loadFromFilesystem(const std::string& filename) -> std::vector<uint8_t> {
// The filename is already normalized (e.g., "shapes/logo/letra_j.shp")
// We need to prepend base_path + "data/"
std::string fullpath;
if (base_path_.empty()) {
fullpath = "data/" + filename;
} else {
fullpath = base_path_ + "/data/" + filename;
}
std::ifstream file(fullpath, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "[ResourceLoader] Error: no es pot obrir " << fullpath << "\n";
return {};
}
std::streamsize file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<uint8_t> data(file_size);
if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
std::cerr << "[ResourceLoader] Error: no es pot llegir " << fullpath << "\n";
return {};
}
std::cout << "[ResourceLoader] Carregat des del sistema de archivos: " << fullpath << "\n";
return data;
}
} // namespace Resource } // namespace Resource
+10 -5
View File
@@ -12,9 +12,9 @@
namespace Resource { namespace Resource {
// Singleton per gestionar la càrrega de recursos // Singleton per gestionar la càrrega de recursos
class Loader { class Loader {
public: public:
// Singleton // Singleton
static auto get() -> Loader&; static auto get() -> Loader&;
@@ -25,6 +25,11 @@ class Loader {
auto loadResource(const std::string& filename) -> std::vector<uint8_t>; auto loadResource(const std::string& filename) -> std::vector<uint8_t>;
auto resourceExists(const std::string& filename) -> bool; auto resourceExists(const std::string& filename) -> bool;
// Llistat de recursos amb prefix (ex. "shapes/", "sounds/"). Si hi ha
// pack, retorna els fitxers del pack filtrats; si no, escaneja el
// sistema de fitxers recursivament a `data/<prefix>`.
auto listResources(const std::string& prefix) -> std::vector<std::string>;
// Validació // Validació
auto validatePack() -> bool; auto validatePack() -> bool;
[[nodiscard]] auto isPackLoaded() const -> bool; [[nodiscard]] auto isPackLoaded() const -> bool;
@@ -37,7 +42,7 @@ class Loader {
Loader(const Loader&) = delete; Loader(const Loader&) = delete;
auto operator=(const Loader&) -> Loader& = delete; auto operator=(const Loader&) -> Loader& = delete;
private: private:
Loader() = default; Loader() = default;
~Loader() = default; ~Loader() = default;
@@ -48,6 +53,6 @@ class Loader {
// Funciones auxiliars // Funciones auxiliars
auto loadFromFilesystem(const std::string& filename) -> std::vector<uint8_t>; auto loadFromFilesystem(const std::string& filename) -> std::vector<uint8_t>;
}; };
} // namespace Resource } // namespace Resource
+15 -3
View File
@@ -12,6 +12,7 @@
#include "core/audio/audio.hpp" #include "core/audio/audio.hpp"
#include "core/audio/audio_adapter.hpp" #include "core/audio/audio_adapter.hpp"
#include "core/defaults/window.hpp" #include "core/defaults/window.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/input/input.hpp" #include "core/input/input.hpp"
#include "core/input/mouse.hpp" #include "core/input/mouse.hpp"
#include "core/locale/locale.hpp" #include "core/locale/locale.hpp"
@@ -147,10 +148,21 @@ Director::Director(int argc, char* argv[])
Audio::init(AUDIO_CONFIG); Audio::init(AUDIO_CONFIG);
Audio::get()->applySettings(AUDIO_CONFIG); Audio::get()->applySettings(AUDIO_CONFIG);
AudioResource::getMusic("title.ogg"); // Precàrrega blocant de tots els recursos al boot per evitar hits d'I/O i
AudioResource::getMusic("game.ogg"); // de decodificació en transicions (TITLE → GAME, primera explosió, etc.).
// Mateix patró que aee_arcade: iterem `listResources` i forcem la càrrega
// al cache de cada subsistema.
for (const auto& path : Resource::Helper::listResources("music/")) {
AudioResource::getMusic(path.substr(std::string_view{"music/"}.size()));
}
for (const auto& path : Resource::Helper::listResources("sounds/")) {
AudioResource::getSound(path.substr(std::string_view{"sounds/"}.size()));
}
for (const auto& path : Resource::Helper::listResources("shapes/")) {
Graphics::ShapeLoader::load(path.substr(std::string_view{"shapes/"}.size()));
}
if (cfg_->console) { if (cfg_->console) {
std::cout << "Música precacheada\n"; std::cout << "Recursos precachejats (música, sons, shapes)\n";
} }
context_ = std::make_unique<SceneContext>(); context_ = std::make_unique<SceneContext>();