Refactorizar sistema de recursos: crear ResourceManager centralizado

- 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 <noreply@anthropic.com>
This commit is contained in:
2025-10-23 09:16:18 +02:00
parent 41c76316ef
commit 2fa1684f01
9 changed files with 264 additions and 64 deletions

Binary file not shown.

View File

@@ -17,6 +17,8 @@
#include <iostream> // for cout
#include <string> // for string
#include "resource_manager.h" // for ResourceManager
#ifdef _WIN32
#include <windows.h> // 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) {

View File

@@ -12,38 +12,7 @@
#include <string> // 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<std::string> Texture::getPackResourceList() {
if (g_resourcePack != nullptr) {
return g_resourcePack->getResourceList();
}
return std::vector<std::string>(); // 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<int>(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<int>(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;

View File

@@ -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<std::string> getPackResourceList();
static bool isPackLoaded();
// Inicializa las variables
explicit Texture(SDL_Renderer *renderer);
Texture(SDL_Renderer *renderer, const std::string &file_path);

View File

@@ -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<int>(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;

View File

@@ -3,6 +3,7 @@
#include <string>
#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;

View File

@@ -0,0 +1,91 @@
#include "resource_manager.h"
#include "resource_pack.h"
#include <iostream>
#include <fstream>
// 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<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
// Alocar buffer y leer
data = new unsigned char[size];
file.read(reinterpret_cast<char*>(data), size);
file.close();
return true;
}
bool ResourceManager::isPackLoaded() {
return resourcePack_ != nullptr;
}
std::vector<std::string> ResourceManager::getResourceList() {
if (resourcePack_ != nullptr) {
return resourcePack_->getResourceList();
}
return std::vector<std::string>(); // Vacío si no hay pack
}
size_t ResourceManager::getResourceCount() {
if (resourcePack_ != nullptr) {
return resourcePack_->getResourceCount();
}
return 0;
}

85
source/resource_manager.h Normal file
View File

@@ -0,0 +1,85 @@
#ifndef RESOURCE_MANAGER_H
#define RESOURCE_MANAGER_H
#include <string>
#include <vector>
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<std::string> 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

View File

@@ -1,6 +1,7 @@
#include "textrenderer.h"
#include <SDL3/SDL.h>
#include <SDL3_ttf/SDL_ttf.h>
#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<size_t>(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<size_t>(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;
}