From 2fa1684f0159341ee532c404f920b01760e3cf09 Mon Sep 17 00:00:00 2001 From: Sergio Date: Thu, 23 Oct 2025 09:16:18 +0200 Subject: [PATCH] Refactorizar sistema de recursos: crear ResourceManager centralizado MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Crear ResourceManager singleton para gestión centralizada de recursos - Separar lógica de ResourcePack de la clase Texture - Adaptar TextRenderer para cargar fuentes TTF desde pack - Adaptar LogoScaler para cargar imágenes PNG desde pack - Actualizar main.cpp y engine.cpp para usar ResourceManager - Regenerar resources.pack con fuentes y logos incluidos Fixes: - Resuelve error de carga de fuentes desde disco - Resuelve error de carga de logos (can't fopen) - Implementa fallback automático a disco si no existe pack - Todas las clases ahora pueden cargar recursos desde pack 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- resources.pack | Bin 3865571 -> 3857350 bytes source/engine.cpp | 8 +-- source/external/texture.cpp | 66 +++++++------------------ source/external/texture.h | 5 -- source/logo_scaler.cpp | 17 ++++++- source/main.cpp | 3 +- source/resource_manager.cpp | 91 +++++++++++++++++++++++++++++++++++ source/resource_manager.h | 85 ++++++++++++++++++++++++++++++++ source/text/textrenderer.cpp | 53 ++++++++++++++++++-- 9 files changed, 264 insertions(+), 64 deletions(-) create mode 100644 source/resource_manager.cpp create mode 100644 source/resource_manager.h diff --git a/resources.pack b/resources.pack index 57a7ef94445eba730c338fae74ea9b500e151943..71e247e3f9c600a5b8634268d7bf1ee61a1b2cfd 100644 GIT binary patch delta 410 zcmX}myGw#m0EY2%&S!S>l37;f{nEwqQg$)NAe>qwD9~KQ5DBuOR)?VNJNRFFG7^H%pQ0Qba<* zfCWRaVi+SB#Tdq6gB=byF##7Q;l>oE;lT`MF$XW^;e#Im1Q9|QgC9}E5XS-*v4mwL eu!2=2kwO|7tYIBlY+w^x*hUUJtx_c4?EC>?t9e8K delta 2285 zcmcJQUrZxK9LHz2g|=|O0_D%a0X+)d30(PeAaF)0Z7)cafLBD2yQ|xFDJ#oPv%A1? zKuU5ZCSFWz>YpcHj7Cj-@C_4*r=RTY)1Pkd(~-H*`LmP|A_5NxM53{%$#i*AAjIh-=`-TR3e8YVsJ3AhCoxk_N3$Yb- zRb$Wd0#HJ}WWoO5FT}(uBcU@@oneMzDok6rXkfD^O?h=vTVik&)udI~;}6c*9uE$2HyI~WWR9$mt0YTa zBkzz8$Yw%+ZeZK}Bk+Kbl~xX;f5b2fkGniSYXi z4)+sD@znO?pN_*W@+S}cnR^AKm;Zk9;w{D=TOS4w>)L@H^~PJl4EB}v4_6-If>x#f z?Z#_zR6DF`NMkwlJpV`FH{oqZ8FfaYkq`zc{vsMOd{sX1G~kU_x@#*pdEZ(%)G8X9 zziTa@FCP=k>#nUB#*k)Lsh<&xHjFxH^Cn zlz>v;0&d^|WuP1!0~MeWQ~?oGgBnl^yr2%$g9gwDj)Nx93{HR+& // for cout #include // for string +#include "resource_manager.h" // for ResourceManager + #ifdef _WIN32 #include // for GetModuleFileName #endif @@ -164,9 +166,9 @@ bool Engine::initialize(int width, int height, int zoom, bool fullscreen, AppMod } } } else { - // Fallback: cargar texturas desde pack usando la lista del ResourcePack - if (Texture::isPackLoaded()) { - auto pack_resources = Texture::getPackResourceList(); + // Fallback: cargar texturas desde pack usando la lista del ResourceManager + if (ResourceManager::isPackLoaded()) { + auto pack_resources = ResourceManager::getResourceList(); // Filtrar solo los recursos en balls/ con extensión .png for (const auto& resource : pack_resources) { diff --git a/source/external/texture.cpp b/source/external/texture.cpp index 0d8df3e..bd6ea20 100644 --- a/source/external/texture.cpp +++ b/source/external/texture.cpp @@ -12,38 +12,7 @@ #include // Para operator<<, string #include "stb_image.h" // Para stbi_failure_reason, stbi_image_free -#include "../resource_pack.h" // Sistema de empaquetado de recursos - -// Instancia global de ResourcePack (se inicializa al primer uso) -static ResourcePack* g_resourcePack = nullptr; - -// Inicializar el sistema de recursos (llamar desde main antes de cargar texturas) -void Texture::initResourceSystem(const std::string& packFilePath) { - if (g_resourcePack == nullptr) { - g_resourcePack = new ResourcePack(); - if (!g_resourcePack->loadPack(packFilePath)) { - // Si falla, borrar instancia (usará fallback a disco) - delete g_resourcePack; - g_resourcePack = nullptr; - std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl; - } else { - std::cout << "resources.pack cargado (" << g_resourcePack->getResourceCount() << " recursos)" << std::endl; - } - } -} - -// Obtener lista de recursos disponibles en el pack -std::vector Texture::getPackResourceList() { - if (g_resourcePack != nullptr) { - return g_resourcePack->getResourceList(); - } - return std::vector(); // Vacío si no hay pack -} - -// Verificar si el pack está cargado -bool Texture::isPackLoaded() { - return g_resourcePack != nullptr; -} +#include "../resource_manager.h" // Sistema de empaquetado de recursos centralizado Texture::Texture(SDL_Renderer *renderer) : renderer_(renderer), @@ -70,30 +39,29 @@ bool Texture::loadFromFile(const std::string &file_path) { int width, height, orig_format; unsigned char *data = nullptr; - // 1. Intentar cargar desde pack (si está inicializado) - if (g_resourcePack != nullptr) { - ResourcePack::ResourceData packData = g_resourcePack->loadResource(file_path); - if (packData.data != nullptr) { - // Descodificar imagen desde memoria usando stb_image - data = stbi_load_from_memory(packData.data, static_cast(packData.size), - &width, &height, &orig_format, req_format); - delete[] packData.data; // Liberar buffer temporal del pack + // 1. Intentar cargar desde ResourceManager (pack o disco) + unsigned char* resourceData = nullptr; + size_t resourceSize = 0; - if (data != nullptr) { + if (ResourceManager::loadResource(file_path, resourceData, resourceSize)) { + // Descodificar imagen desde memoria usando stb_image + data = stbi_load_from_memory(resourceData, static_cast(resourceSize), + &width, &height, &orig_format, req_format); + delete[] resourceData; // Liberar buffer temporal + + if (data != nullptr) { + if (ResourceManager::isPackLoaded()) { std::cout << "Imagen cargada desde pack: " << filename.c_str() << std::endl; + } else { + std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl; } } } - // 2. Fallback: cargar desde disco (modo desarrollo) + // 2. Si todo falla, error if (data == nullptr) { - data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format); - if (data == nullptr) { - SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason()); - exit(1); - } else { - std::cout << "Imagen cargada desde disco: " << filename.c_str() << std::endl; - } + SDL_Log("Error al cargar la imagen: %s", stbi_failure_reason()); + exit(1); } int pitch; diff --git a/source/external/texture.h b/source/external/texture.h index dc91634..bbfd014 100644 --- a/source/external/texture.h +++ b/source/external/texture.h @@ -16,11 +16,6 @@ class Texture { int height_; public: - // Sistema de recursos empaquetados (inicializar desde main) - static void initResourceSystem(const std::string& packFilePath); - static std::vector getPackResourceList(); - static bool isPackLoaded(); - // Inicializa las variables explicit Texture(SDL_Renderer *renderer); Texture(SDL_Renderer *renderer, const std::string &file_path); diff --git a/source/logo_scaler.cpp b/source/logo_scaler.cpp index 2601837..4caee9a 100644 --- a/source/logo_scaler.cpp +++ b/source/logo_scaler.cpp @@ -13,6 +13,7 @@ #include "external/stb_image.h" // Para stbi_load, stbi_image_free #include "external/stb_image_resize2.h" // Para stbir_resize_uint8_srgb +#include "resource_manager.h" // Para cargar desde pack // ============================================================================ // Detectar resolución nativa del monitor principal @@ -51,10 +52,22 @@ bool LogoScaler::detectNativeResolution(int& native_width, int& native_height) { unsigned char* LogoScaler::loadAndScale(const std::string& path, int target_width, int target_height, int& out_width, int& out_height) { - // 1. Cargar imagen original con stb_image + // 1. Intentar cargar imagen desde ResourceManager (pack o disco) int orig_width, orig_height, orig_channels; - unsigned char* orig_data = stbi_load(path.c_str(), &orig_width, &orig_height, &orig_channels, STBI_rgb_alpha); + unsigned char* orig_data = nullptr; + // 1a. Cargar desde ResourceManager + unsigned char* resourceData = nullptr; + size_t resourceSize = 0; + + if (ResourceManager::loadResource(path, resourceData, resourceSize)) { + // Descodificar imagen desde memoria usando stb_image + orig_data = stbi_load_from_memory(resourceData, static_cast(resourceSize), + &orig_width, &orig_height, &orig_channels, STBI_rgb_alpha); + delete[] resourceData; // Liberar buffer temporal + } + + // 1b. Si falla todo, error if (orig_data == nullptr) { SDL_Log("Error al cargar imagen %s: %s", path.c_str(), stbi_failure_reason()); return nullptr; diff --git a/source/main.cpp b/source/main.cpp index c67e624..9f8bbb8 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -3,6 +3,7 @@ #include #include "engine.h" #include "defines.h" +#include "resource_manager.h" // getExecutableDirectory() ya está definido en defines.h como inline @@ -108,7 +109,7 @@ int main(int argc, char* argv[]) { // Inicializar sistema de recursos empaquetados (intentar cargar resources.pack) std::string resources_dir = getResourcesDirectory(); std::string pack_path = resources_dir + "/resources.pack"; - Texture::initResourceSystem(pack_path); + ResourceManager::init(pack_path); Engine engine; diff --git a/source/resource_manager.cpp b/source/resource_manager.cpp new file mode 100644 index 0000000..b5efd7d --- /dev/null +++ b/source/resource_manager.cpp @@ -0,0 +1,91 @@ +#include "resource_manager.h" +#include "resource_pack.h" + +#include +#include + +// Inicializar el puntero estático +ResourcePack* ResourceManager::resourcePack_ = nullptr; + +bool ResourceManager::init(const std::string& packFilePath) { + // Si ya estaba inicializado, liberar primero + if (resourcePack_ != nullptr) { + delete resourcePack_; + resourcePack_ = nullptr; + } + + // Intentar cargar el pack + resourcePack_ = new ResourcePack(); + if (!resourcePack_->loadPack(packFilePath)) { + // Si falla, borrar instancia (usará fallback a disco) + delete resourcePack_; + resourcePack_ = nullptr; + std::cout << "resources.pack no encontrado - usando carpeta data/" << std::endl; + return false; + } + + std::cout << "resources.pack cargado (" << resourcePack_->getResourceCount() << " recursos)" << std::endl; + return true; +} + +void ResourceManager::shutdown() { + if (resourcePack_ != nullptr) { + delete resourcePack_; + resourcePack_ = nullptr; + } +} + +bool ResourceManager::loadResource(const std::string& resourcePath, unsigned char*& data, size_t& size) { + data = nullptr; + size = 0; + + // 1. Intentar cargar desde pack (si está disponible) + if (resourcePack_ != nullptr) { + ResourcePack::ResourceData packData = resourcePack_->loadResource(resourcePath); + if (packData.data != nullptr) { + data = packData.data; + size = packData.size; + return true; + } + } + + // 2. Fallback: cargar desde disco + std::ifstream file(resourcePath, std::ios::binary | std::ios::ate); + if (!file) { + // Intentar con "data/" como prefijo si no se encontró + std::string dataPath = "data/" + resourcePath; + file.open(dataPath, std::ios::binary | std::ios::ate); + if (!file) { + return false; + } + } + + // Obtener tamaño del archivo + size = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); + + // Alocar buffer y leer + data = new unsigned char[size]; + file.read(reinterpret_cast(data), size); + file.close(); + + return true; +} + +bool ResourceManager::isPackLoaded() { + return resourcePack_ != nullptr; +} + +std::vector ResourceManager::getResourceList() { + if (resourcePack_ != nullptr) { + return resourcePack_->getResourceList(); + } + return std::vector(); // Vacío si no hay pack +} + +size_t ResourceManager::getResourceCount() { + if (resourcePack_ != nullptr) { + return resourcePack_->getResourceCount(); + } + return 0; +} diff --git a/source/resource_manager.h b/source/resource_manager.h new file mode 100644 index 0000000..4506756 --- /dev/null +++ b/source/resource_manager.h @@ -0,0 +1,85 @@ +#ifndef RESOURCE_MANAGER_H +#define RESOURCE_MANAGER_H + +#include +#include + +class ResourcePack; + +/** + * ResourceManager - Gestor centralizado de recursos empaquetados + * + * Singleton que administra el sistema de recursos empaquetados (resources.pack) + * y proporciona fallback automático a disco cuando el pack no está disponible. + * + * Uso: + * // En main.cpp, antes de inicializar cualquier sistema: + * ResourceManager::init("resources.pack"); + * + * // Desde cualquier clase que necesite recursos: + * unsigned char* data = nullptr; + * size_t size = 0; + * if (ResourceManager::loadResource("textures/ball.png", data, size)) { + * // Usar datos... + * delete[] data; // Liberar cuando termine + * } + */ +class ResourceManager { +public: + /** + * Inicializa el sistema de recursos empaquetados + * Debe llamarse una única vez al inicio del programa + * + * @param packFilePath Ruta al archivo .pack (ej: "resources.pack") + * @return true si el pack se cargó correctamente, false si no existe (fallback a disco) + */ + static bool init(const std::string& packFilePath); + + /** + * Libera el sistema de recursos + * Opcional - se llama automáticamente al cerrar el programa + */ + static void shutdown(); + + /** + * Carga un recurso desde el pack (o disco si no existe pack) + * + * @param resourcePath Ruta relativa del recurso (ej: "textures/ball.png") + * @param data [out] Puntero donde se almacenará el buffer (debe liberar con delete[]) + * @param size [out] Tamaño del buffer en bytes + * @return true si se cargó correctamente, false si falla + */ + static bool loadResource(const std::string& resourcePath, unsigned char*& data, size_t& size); + + /** + * Verifica si el pack está cargado + * @return true si hay un pack cargado, false si se usa disco + */ + static bool isPackLoaded(); + + /** + * Obtiene la lista de recursos disponibles en el pack + * @return Vector con las rutas de todos los recursos, vacío si no hay pack + */ + static std::vector getResourceList(); + + /** + * Obtiene el número de recursos en el pack + * @return Número de recursos, 0 si no hay pack + */ + static size_t getResourceCount(); + +private: + // Constructor privado (singleton) + ResourceManager() = default; + ~ResourceManager() = default; + + // Deshabilitar copia y asignación + ResourceManager(const ResourceManager&) = delete; + ResourceManager& operator=(const ResourceManager&) = delete; + + // Instancia del pack (nullptr si no está cargado) + static ResourcePack* resourcePack_; +}; + +#endif // RESOURCE_MANAGER_H diff --git a/source/text/textrenderer.cpp b/source/text/textrenderer.cpp index ac30b9c..6dffae1 100644 --- a/source/text/textrenderer.cpp +++ b/source/text/textrenderer.cpp @@ -1,6 +1,7 @@ #include "textrenderer.h" #include #include +#include "../resource_manager.h" TextRenderer::TextRenderer() : renderer_(nullptr), font_(nullptr), font_size_(0), use_antialiasing_(true) { } @@ -23,7 +24,30 @@ bool TextRenderer::init(SDL_Renderer* renderer, const char* font_path, int font_ } } - // Cargar la fuente + // Intentar cargar la fuente desde ResourceManager (pack o disco) + unsigned char* fontData = nullptr; + size_t fontDataSize = 0; + + if (ResourceManager::loadResource(font_path, fontData, fontDataSize)) { + // Crear SDL_IOStream desde memoria + SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast(fontDataSize)); + if (fontIO != nullptr) { + // Cargar fuente desde IOStream + font_ = TTF_OpenFontIO(fontIO, true, font_size); // true = cerrar stream automáticamente + delete[] fontData; // Liberar buffer temporal después de crear el stream + + if (font_ == nullptr) { + SDL_Log("Error al cargar fuente desde memoria '%s': %s", font_path, SDL_GetError()); + return false; + } + + return true; + } else { + delete[] fontData; + } + } + + // Fallback final: intentar cargar directamente desde disco (por si falla ResourceManager) font_ = TTF_OpenFont(font_path, font_size); if (font_ == nullptr) { SDL_Log("Error al cargar fuente '%s': %s", font_path, SDL_GetError()); @@ -51,7 +75,30 @@ bool TextRenderer::reinitialize(int new_font_size) { font_ = nullptr; } - // Cargar fuente con nuevo tamaño + // Intentar cargar la fuente desde ResourceManager con el nuevo tamaño + unsigned char* fontData = nullptr; + size_t fontDataSize = 0; + + if (ResourceManager::loadResource(font_path_, fontData, fontDataSize)) { + SDL_IOStream* fontIO = SDL_IOFromConstMem(fontData, static_cast(fontDataSize)); + if (fontIO != nullptr) { + font_ = TTF_OpenFontIO(fontIO, true, new_font_size); + delete[] fontData; + + if (font_ == nullptr) { + SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s", + font_path_.c_str(), new_font_size, SDL_GetError()); + return false; + } + + font_size_ = new_font_size; + return true; + } else { + delete[] fontData; + } + } + + // Fallback: cargar directamente desde disco font_ = TTF_OpenFont(font_path_.c_str(), new_font_size); if (font_ == nullptr) { SDL_Log("Error al recargar fuente '%s' con tamaño %d: %s", @@ -59,9 +106,7 @@ bool TextRenderer::reinitialize(int new_font_size) { return false; } - // Actualizar tamaño almacenado font_size_ = new_font_size; - return true; }