reestructuració
This commit is contained in:
318
source/core/resources/asset.cpp
Normal file
318
source/core/resources/asset.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "asset.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError
|
||||
|
||||
#include <algorithm> // Para sort
|
||||
#include <cstddef> // Para size_t
|
||||
#include <exception> // Para exception
|
||||
#include <filesystem> // Para exists, path
|
||||
#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream, operator<<, ifstream, istringstream, endl
|
||||
#include <iostream> // Para cout
|
||||
#include <sstream> // Para basic_istringstream
|
||||
#include <stdexcept> // Para runtime_error
|
||||
|
||||
#include "resource_helper.hpp" // Para loadFile
|
||||
#include "utils.hpp" // Para getFileName
|
||||
|
||||
// Singleton
|
||||
Asset* Asset::instance = nullptr;
|
||||
|
||||
void Asset::init(const std::string& executable_path) {
|
||||
Asset::instance = new Asset(executable_path);
|
||||
}
|
||||
|
||||
void Asset::destroy() {
|
||||
delete Asset::instance;
|
||||
}
|
||||
|
||||
auto Asset::get() -> Asset* {
|
||||
return Asset::instance;
|
||||
}
|
||||
|
||||
// Añade un elemento al mapa (función auxiliar)
|
||||
void Asset::addToMap(const std::string& file_path, Type type, bool required, bool absolute) {
|
||||
std::string full_path = absolute ? file_path : executable_path_ + file_path;
|
||||
std::string filename = getFileName(full_path);
|
||||
|
||||
// Verificar si ya existe el archivo
|
||||
if (file_list_.contains(filename)) {
|
||||
std::cout << "Warning: Asset '" << filename << "' already exists, overwriting" << '\n';
|
||||
}
|
||||
|
||||
file_list_.emplace(filename, Item{std::move(full_path), type, required});
|
||||
}
|
||||
|
||||
// Añade un elemento a la lista
|
||||
void Asset::add(const std::string& file_path, Type type, bool required, bool absolute) {
|
||||
addToMap(file_path, type, required, absolute);
|
||||
}
|
||||
|
||||
// Carga recursos desde un archivo de configuración con soporte para variables
|
||||
void Asset::loadFromFile(const std::string& config_file_path, const std::string& prefix, const std::string& system_folder) {
|
||||
std::ifstream file(config_file_path);
|
||||
if (!file.is_open()) {
|
||||
std::cout << "Error: Cannot open config file: " << config_file_path << '\n';
|
||||
return;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
int line_number = 0;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
++line_number;
|
||||
|
||||
// Limpiar espacios en blanco al principio y final
|
||||
line.erase(0, line.find_first_not_of(" \t\r"));
|
||||
line.erase(line.find_last_not_of(" \t\r") + 1);
|
||||
|
||||
// DEBUG: mostrar línea leída (opcional, puedes comentar esta línea)
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Line %d: '%s'", line_number, line.c_str());
|
||||
|
||||
// Ignorar líneas vacías y comentarios
|
||||
if (line.empty() || line[0] == '#' || line[0] == ';') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dividir la línea por el separador '|'
|
||||
std::vector<std::string> parts;
|
||||
std::istringstream iss(line);
|
||||
std::string part;
|
||||
|
||||
while (std::getline(iss, part, '|')) {
|
||||
parts.push_back(part);
|
||||
}
|
||||
|
||||
// Verificar que tenemos al menos tipo y ruta
|
||||
if (parts.size() < 2) {
|
||||
std::cout << "Warning: Malformed line " << line_number << " in config file (insufficient fields)" << '\n';
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const std::string& type_str = parts[0];
|
||||
std::string path = parts[1];
|
||||
|
||||
// Valores por defecto
|
||||
bool required = true;
|
||||
bool absolute = false;
|
||||
|
||||
// Si hay opciones en el tercer campo, parsearlas
|
||||
if (parts.size() >= 3) {
|
||||
parseOptions(parts[2], required, absolute);
|
||||
}
|
||||
|
||||
// Reemplazar variables en la ruta
|
||||
path = replaceVariables(path, prefix, system_folder);
|
||||
|
||||
// Parsear el tipo de asset
|
||||
Type type = parseAssetType(type_str);
|
||||
|
||||
// Añadir al mapa
|
||||
addToMap(path, type, required, absolute);
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
std::cout << "Error parsing line " << line_number << " in config file: " << e.what() << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
// Devuelve la ruta completa a un fichero (búsqueda O(1))
|
||||
auto Asset::getPath(const std::string& filename) const -> std::string {
|
||||
auto it = file_list_.find(filename);
|
||||
if (it != file_list_.end()) {
|
||||
return it->second.file;
|
||||
}
|
||||
|
||||
std::cout << "Warning: file " << filename << " not found" << '\n';
|
||||
return "";
|
||||
}
|
||||
|
||||
// Carga datos del archivo usando ResourceHelper
|
||||
auto Asset::loadData(const std::string& filename) const -> std::vector<uint8_t> {
|
||||
auto it = file_list_.find(filename);
|
||||
if (it != file_list_.end()) {
|
||||
return ResourceHelper::loadFile(it->second.file);
|
||||
}
|
||||
|
||||
std::cout << "Warning: file " << filename << " not found for data loading" << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
// Verifica si un recurso existe
|
||||
auto Asset::exists(const std::string& filename) const -> bool {
|
||||
return file_list_.contains(filename);
|
||||
}
|
||||
|
||||
// Comprueba que existen todos los elementos
|
||||
auto Asset::check() const -> bool {
|
||||
bool success = true;
|
||||
|
||||
// Agrupar por tipo para mostrar organizado
|
||||
std::unordered_map<Type, std::vector<const Item*>> by_type;
|
||||
|
||||
for (const auto& [filename, item] : file_list_) {
|
||||
if (item.required) {
|
||||
by_type[item.type].push_back(&item);
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar por tipo
|
||||
for (int type = 0; type < static_cast<int>(Type::SIZE); ++type) {
|
||||
Type asset_type = static_cast<Type>(type);
|
||||
|
||||
if (by_type.contains(asset_type)) {
|
||||
for (const auto* item : by_type[asset_type]) {
|
||||
if (!checkFile(item->file)) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Comprueba que existe un fichero
|
||||
auto Asset::checkFile(const std::string& path) const -> bool {
|
||||
// Construir ruta del pack usando executable_path_ (misma lógica que Director::init)
|
||||
#ifdef MACOS_BUNDLE
|
||||
std::string pack_path = executable_path_ + "../Resources/resources.pack";
|
||||
#else
|
||||
std::string pack_path = executable_path_ + "resources.pack";
|
||||
#endif
|
||||
bool pack_exists = std::filesystem::exists(pack_path);
|
||||
|
||||
if (pack_exists) {
|
||||
// MODO PACK: Usar ResourceHelper (igual que la carga real)
|
||||
auto data = ResourceHelper::loadFile(path);
|
||||
return !data.empty();
|
||||
} // MODO FILESYSTEM: Verificación directa (modo desarrollo)
|
||||
std::ifstream file(path);
|
||||
bool success = file.good();
|
||||
file.close();
|
||||
|
||||
if (!success) {
|
||||
std::cout << "Error: Could not open file: " << path << '\n';
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// Parsea string a Type
|
||||
auto Asset::parseAssetType(const std::string& type_str) -> Type {
|
||||
if (type_str == "BITMAP") {
|
||||
return Type::BITMAP;
|
||||
}
|
||||
if (type_str == "MUSIC") {
|
||||
return Type::MUSIC;
|
||||
}
|
||||
if (type_str == "SOUND") {
|
||||
return Type::SOUND;
|
||||
}
|
||||
if (type_str == "FONT") {
|
||||
return Type::FONT;
|
||||
}
|
||||
if (type_str == "LANG") {
|
||||
return Type::LANG;
|
||||
}
|
||||
if (type_str == "DATA") {
|
||||
return Type::DATA;
|
||||
}
|
||||
if (type_str == "DEMODATA") {
|
||||
return Type::DEMODATA;
|
||||
}
|
||||
if (type_str == "ANIMATION") {
|
||||
return Type::ANIMATION;
|
||||
}
|
||||
if (type_str == "PALETTE") {
|
||||
return Type::PALETTE;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unknown asset type: " + type_str);
|
||||
}
|
||||
|
||||
// Devuelve el nombre del tipo de recurso
|
||||
auto Asset::getTypeName(Type type) -> std::string {
|
||||
switch (type) {
|
||||
case Type::BITMAP:
|
||||
return "BITMAP";
|
||||
case Type::MUSIC:
|
||||
return "MUSIC";
|
||||
case Type::SOUND:
|
||||
return "SOUND";
|
||||
case Type::FONT:
|
||||
return "FONT";
|
||||
case Type::LANG:
|
||||
return "LANG";
|
||||
case Type::DATA:
|
||||
return "DATA";
|
||||
case Type::DEMODATA:
|
||||
return "DEMODATA";
|
||||
case Type::ANIMATION:
|
||||
return "ANIMATION";
|
||||
case Type::PALETTE:
|
||||
return "PALETTE";
|
||||
default:
|
||||
return "ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
// Devuelve la lista de recursos de un tipo
|
||||
auto Asset::getListByType(Type type) const -> std::vector<std::string> {
|
||||
std::vector<std::string> list;
|
||||
|
||||
for (const auto& [filename, item] : file_list_) {
|
||||
if (item.type == type) {
|
||||
list.push_back(item.file);
|
||||
}
|
||||
}
|
||||
|
||||
// Ordenar alfabéticamente para garantizar orden consistente
|
||||
std::ranges::sort(list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// Reemplaza variables en las rutas
|
||||
auto Asset::replaceVariables(const std::string& path, const std::string& prefix, const std::string& system_folder) -> std::string {
|
||||
std::string result = path;
|
||||
|
||||
// Reemplazar ${PREFIX}
|
||||
size_t pos = 0;
|
||||
while ((pos = result.find("${PREFIX}", pos)) != std::string::npos) {
|
||||
result.replace(pos, 9, prefix); // 9 = longitud de "${PREFIX}"
|
||||
pos += prefix.length();
|
||||
}
|
||||
|
||||
// Reemplazar ${SYSTEM_FOLDER}
|
||||
pos = 0;
|
||||
while ((pos = result.find("${SYSTEM_FOLDER}", pos)) != std::string::npos) {
|
||||
result.replace(pos, 16, system_folder); // 16 = longitud de "${SYSTEM_FOLDER}"
|
||||
pos += system_folder.length();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parsea las opciones de una línea de configuración
|
||||
auto Asset::parseOptions(const std::string& options, bool& required, bool& absolute) -> void {
|
||||
if (options.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream iss(options);
|
||||
std::string option;
|
||||
|
||||
while (std::getline(iss, option, ',')) {
|
||||
// Eliminar espacios
|
||||
option.erase(0, option.find_first_not_of(" \t"));
|
||||
option.erase(option.find_last_not_of(" \t") + 1);
|
||||
|
||||
if (option == "optional") {
|
||||
required = false;
|
||||
} else if (option == "absolute") {
|
||||
absolute = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
74
source/core/resources/asset.hpp
Normal file
74
source/core/resources/asset.hpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // Para uint8_t
|
||||
#include <string> // Para string
|
||||
#include <unordered_map> // Para unordered_map
|
||||
#include <utility> // Para move
|
||||
#include <vector> // Para vector
|
||||
|
||||
// --- Clase Asset: gestor optimizado de recursos (singleton) ---
|
||||
class Asset {
|
||||
public:
|
||||
// --- Enums ---
|
||||
enum class Type : int {
|
||||
BITMAP, // Imágenes
|
||||
MUSIC, // Música
|
||||
SOUND, // Sonidos
|
||||
FONT, // Fuentes
|
||||
LANG, // Idiomas
|
||||
DATA, // Datos
|
||||
DEMODATA, // Datos de demo
|
||||
ANIMATION, // Animaciones
|
||||
PALETTE, // Paletas
|
||||
SIZE, // Tamaño
|
||||
};
|
||||
|
||||
// --- Métodos de singleton ---
|
||||
static void init(const std::string& executable_path);
|
||||
static void destroy();
|
||||
static auto get() -> Asset*;
|
||||
Asset(const Asset&) = delete;
|
||||
auto operator=(const Asset&) -> Asset& = delete;
|
||||
|
||||
// --- Métodos para la gestión de recursos ---
|
||||
void add(const std::string& file_path, Type type, bool required = true, bool absolute = false);
|
||||
void loadFromFile(const std::string& config_file_path, const std::string& prefix = "", const std::string& system_folder = ""); // Con soporte para variables
|
||||
[[nodiscard]] auto getPath(const std::string& filename) const -> std::string;
|
||||
[[nodiscard]] auto loadData(const std::string& filename) const -> std::vector<uint8_t>; // Carga datos del archivo
|
||||
[[nodiscard]] auto check() const -> bool;
|
||||
[[nodiscard]] auto getListByType(Type type) const -> std::vector<std::string>;
|
||||
[[nodiscard]] auto exists(const std::string& filename) const -> bool; // Nueva función para verificar existencia
|
||||
|
||||
private:
|
||||
// --- Estructuras privadas ---
|
||||
struct Item {
|
||||
std::string file; // Ruta completa del archivo
|
||||
Type type; // Tipo de recurso
|
||||
bool required; // Indica si el archivo es obligatorio
|
||||
|
||||
Item(std::string path, Type asset_type, bool is_required)
|
||||
: file(std::move(path)),
|
||||
type(asset_type),
|
||||
required(is_required) {}
|
||||
};
|
||||
|
||||
// --- Variables internas ---
|
||||
std::unordered_map<std::string, Item> file_list_; // Mapa para búsqueda O(1)
|
||||
std::string executable_path_; // Ruta del ejecutable
|
||||
|
||||
// --- Métodos internos ---
|
||||
[[nodiscard]] auto checkFile(const std::string& path) const -> bool; // Verifica si un archivo existe
|
||||
[[nodiscard]] static auto getTypeName(Type type) -> std::string; // Obtiene el nombre del tipo
|
||||
[[nodiscard]] static auto parseAssetType(const std::string& type_str) -> Type; // Convierte string a tipo
|
||||
void addToMap(const std::string& file_path, Type type, bool required, bool absolute); // Añade archivo al mapa
|
||||
[[nodiscard]] static auto replaceVariables(const std::string& path, const std::string& prefix, const std::string& system_folder) -> std::string; // Reemplaza variables en la ruta
|
||||
static auto parseOptions(const std::string& options, bool& required, bool& absolute) -> void; // Parsea opciones
|
||||
|
||||
// --- Constructores y destructor privados (singleton) ---
|
||||
explicit Asset(std::string executable_path) // Constructor privado
|
||||
: executable_path_(std::move(executable_path)) {}
|
||||
~Asset() = default; // Destructor privado
|
||||
|
||||
// --- Instancia singleton ---
|
||||
static Asset* instance; // Instancia única de Asset
|
||||
};
|
||||
105
source/core/resources/asset_integrated.cpp
Normal file
105
source/core/resources/asset_integrated.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "asset_integrated.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
bool AssetIntegrated::resource_pack_enabled = false;
|
||||
|
||||
void AssetIntegrated::initWithResourcePack(const std::string& executable_path,
|
||||
const std::string& resource_pack_path) {
|
||||
// Inicializar Asset normal
|
||||
Asset::init(executable_path);
|
||||
|
||||
// Inicializar ResourceLoader
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
resource_pack_enabled = loader.initialize(resource_pack_path, true);
|
||||
if (resource_pack_enabled) {
|
||||
std::cout << "Asset system initialized with resource pack: " << resource_pack_path << '\n';
|
||||
} else {
|
||||
std::cout << "Asset system initialized in fallback mode (filesystem)" << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
auto AssetIntegrated::loadFile(const std::string& filename) -> std::vector<uint8_t> {
|
||||
if (shouldUseResourcePack(filename) && resource_pack_enabled) {
|
||||
// Intentar cargar del pack de recursos
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
|
||||
// Convertir ruta completa a ruta relativa para el pack
|
||||
std::string relative_path = filename;
|
||||
|
||||
// Si la ruta contiene "data/", extraer la parte relativa
|
||||
size_t data_pos = filename.find("data/");
|
||||
if (data_pos != std::string::npos) {
|
||||
relative_path = filename.substr(data_pos + 5); // +5 para saltar "data/"
|
||||
}
|
||||
|
||||
auto data = loader.loadResource(relative_path);
|
||||
if (!data.empty()) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: cargar del filesystem
|
||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file: " << filename << '\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 << "Error: Could not read file: " << filename << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto AssetIntegrated::fileExists(const std::string& filename) -> bool {
|
||||
if (shouldUseResourcePack(filename) && resource_pack_enabled) {
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
|
||||
// Convertir ruta completa a ruta relativa para el pack
|
||||
std::string relative_path = filename;
|
||||
size_t data_pos = filename.find("data/");
|
||||
if (data_pos != std::string::npos) {
|
||||
relative_path = filename.substr(data_pos + 5);
|
||||
}
|
||||
|
||||
if (loader.resourceExists(relative_path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Verificar en filesystem
|
||||
return std::filesystem::exists(filename);
|
||||
}
|
||||
|
||||
auto AssetIntegrated::getSystemPath(const std::string& filename) -> std::string {
|
||||
// Los archivos de sistema/config siempre van al filesystem
|
||||
return filename;
|
||||
}
|
||||
|
||||
auto AssetIntegrated::shouldUseResourcePack(const std::string& filepath) -> bool {
|
||||
// Los archivos que NO van al pack:
|
||||
// - Archivos de config/ (ahora están fuera de data/)
|
||||
// - Archivos con absolute=true en assets.txt
|
||||
// - Archivos de sistema (${SYSTEM_FOLDER})
|
||||
|
||||
if (filepath.find("/config/") != std::string::npos ||
|
||||
filepath.starts_with("config/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filepath.find("/data/") != std::string::npos ||
|
||||
filepath.starts_with("data/")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
29
source/core/resources/asset_integrated.hpp
Normal file
29
source/core/resources/asset_integrated.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "asset.hpp"
|
||||
#include "resource_loader.hpp"
|
||||
|
||||
// Extensión de Asset que integra ResourceLoader
|
||||
class AssetIntegrated : public Asset {
|
||||
public:
|
||||
// Inicializa Asset con ResourceLoader
|
||||
static void initWithResourcePack(const std::string& executable_path,
|
||||
const std::string& resource_pack_path = "resources.pack");
|
||||
|
||||
// Carga un archivo usando ResourceLoader como primera opción
|
||||
static auto loadFile(const std::string& filename) -> std::vector<uint8_t>;
|
||||
|
||||
// Verifica si un archivo existe (pack o filesystem)
|
||||
static auto fileExists(const std::string& filename) -> bool;
|
||||
|
||||
// Obtiene la ruta completa para archivos del sistema/config
|
||||
static auto getSystemPath(const std::string& filename) -> std::string;
|
||||
|
||||
private:
|
||||
static bool resource_pack_enabled;
|
||||
|
||||
// Determina si un archivo debe cargarse del pack o del filesystem
|
||||
static auto shouldUseResourcePack(const std::string& filepath) -> bool;
|
||||
};
|
||||
849
source/core/resources/resource.cpp
Normal file
849
source/core/resources/resource.cpp
Normal file
@@ -0,0 +1,849 @@
|
||||
#include "resource.hpp"
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError, SDL_SetRenderDrawColor, SDL_EventType, SDL_PollEvent, SDL_RenderFillRect, SDL_RenderRect, SDLK_ESCAPE, SDL_Event
|
||||
|
||||
#include <array> // Para array
|
||||
#include <cstdlib> // Para exit
|
||||
#include <exception> // Para exception
|
||||
#include <filesystem> // Para exists, path, remove
|
||||
#include <fstream> // Para basic_ofstream, basic_ios, basic_ostream::write, ios, ofstream
|
||||
#include <iostream> // Para std::cout
|
||||
#include <ranges> // Para __find_if_fn, find_if, __find_fn, find
|
||||
#include <stdexcept> // Para runtime_error
|
||||
#include <utility> // Para move
|
||||
|
||||
#include "asset.hpp" // Para Asset
|
||||
#include "color.hpp" // Para Color, NO_COLOR_MOD
|
||||
#include "external/jail_audio.hpp" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound
|
||||
#include "lang.hpp" // Para getText
|
||||
#include "param.hpp" // Para Param, param, ParamPlayer, ParamResource, ParamGame
|
||||
#include "resource_helper.hpp" // Para loadFile
|
||||
#include "screen.hpp" // Para Screen
|
||||
#include "text.hpp" // Para Text
|
||||
#include "utils.hpp" // Para getFileName
|
||||
#include "version.h" // Para APP_NAME, GIT_HASH
|
||||
|
||||
struct JA_Music_t; // lines 11-11
|
||||
struct JA_Sound_t; // lines 12-12
|
||||
|
||||
// Helper para cargar archivos de audio desde pack o filesystem en memoria
|
||||
namespace {
|
||||
struct AudioData {
|
||||
std::vector<uint8_t> data;
|
||||
std::string filepath;
|
||||
};
|
||||
|
||||
auto loadAudioData(const std::string& file_path) -> AudioData {
|
||||
auto resource_data = ResourceHelper::loadFile(file_path);
|
||||
return AudioData{.data = std::move(resource_data), .filepath = file_path};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Declaraciones de funciones que necesitas implementar en otros archivos
|
||||
|
||||
// Singleton
|
||||
Resource* Resource::instance = nullptr;
|
||||
|
||||
// Inicializa la instancia única del singleton con modo de carga
|
||||
void Resource::init(LoadingMode mode) {
|
||||
Resource::instance = new Resource(mode);
|
||||
}
|
||||
|
||||
// Libera la instancia
|
||||
void Resource::destroy() {
|
||||
delete Resource::instance;
|
||||
Resource::instance = nullptr;
|
||||
}
|
||||
|
||||
// Obtiene la instancia
|
||||
auto Resource::get() -> Resource* { return Resource::instance; }
|
||||
|
||||
// Constructor con modo de carga
|
||||
Resource::Resource(LoadingMode mode)
|
||||
: loading_mode_(mode),
|
||||
loading_text_(nullptr) {
|
||||
Screen::get()->show();
|
||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||
loading_text_ = Screen::get()->getText();
|
||||
load();
|
||||
} else {
|
||||
// En modo lazy, cargamos lo mínimo indispensable
|
||||
initResourceLists();
|
||||
loadEssentialResources();
|
||||
}
|
||||
}
|
||||
|
||||
// Destructor
|
||||
Resource::~Resource() {
|
||||
clear();
|
||||
}
|
||||
|
||||
// Carga los recursos esenciales que siempre se necesitan (modo lazy)
|
||||
void Resource::loadEssentialResources() {
|
||||
// Cargar recursos de texto básicos que se usan para crear texturas
|
||||
loadTextFilesQuiet(); // <- VERSIÓN SILENCIOSA
|
||||
loadEssentialTextures(); // Ya es silenciosa
|
||||
createText(); // Crear objetos de texto
|
||||
createTextTextures(); // Crear texturas generadas (game_text_xxx)
|
||||
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
||||
}
|
||||
|
||||
// Carga los ficheros de texto del juego (versión silenciosa)
|
||||
void Resource::loadTextFilesQuiet() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
// Buscar en nuestra lista y cargar directamente
|
||||
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||
if (it != text_files_.end()) {
|
||||
it->text_file = Text::loadFile(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Carga solo las texturas esenciales (fuentes)
|
||||
void Resource::loadEssentialTextures() {
|
||||
const std::vector<std::string> ESSENTIAL_TEXTURES = {
|
||||
"04b_25.png",
|
||||
"04b_25_2x.png",
|
||||
"04b_25_metal.png",
|
||||
"04b_25_grey.png",
|
||||
"04b_25_flat.png",
|
||||
"04b_25_reversed.png",
|
||||
"04b_25_flat_2x.png",
|
||||
"04b_25_reversed_2x.png",
|
||||
"8bithud.png",
|
||||
"aseprite.png",
|
||||
"smb2.png",
|
||||
"smb2_grad.png"};
|
||||
|
||||
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||
|
||||
for (const auto& file : texture_list) {
|
||||
auto name = getFileName(file);
|
||||
// Solo cargar texturas esenciales
|
||||
if (std::ranges::find(ESSENTIAL_TEXTURES, name) != ESSENTIAL_TEXTURES.end()) {
|
||||
// Buscar en nuestra lista y cargar
|
||||
auto it = std::ranges::find_if(textures_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||
if (it != textures_.end()) {
|
||||
it->texture = std::make_shared<Texture>(Screen::get()->getRenderer(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa las listas de recursos sin cargar el contenido (modo lazy)
|
||||
void Resource::initResourceLists() {
|
||||
// Inicializa lista de sonidos
|
||||
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||
sounds_.clear();
|
||||
for (const auto& file : sound_list) {
|
||||
sounds_.emplace_back(getFileName(file));
|
||||
}
|
||||
|
||||
// Inicializa lista de músicas
|
||||
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||
musics_.clear();
|
||||
for (const auto& file : music_list) {
|
||||
musics_.emplace_back(getFileName(file));
|
||||
}
|
||||
|
||||
// Inicializa lista de texturas
|
||||
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||
textures_.clear();
|
||||
for (const auto& file : texture_list) {
|
||||
textures_.emplace_back(getFileName(file));
|
||||
}
|
||||
|
||||
// Inicializa lista de ficheros de texto
|
||||
auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||
text_files_.clear();
|
||||
for (const auto& file : text_file_list) {
|
||||
text_files_.emplace_back(getFileName(file));
|
||||
}
|
||||
|
||||
// Inicializa lista de animaciones
|
||||
auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||
animations_.clear();
|
||||
for (const auto& file : animation_list) {
|
||||
animations_.emplace_back(getFileName(file));
|
||||
}
|
||||
|
||||
// Los demos se cargan directamente sin mostrar progreso (son pocos y pequeños)
|
||||
loadDemoDataQuiet();
|
||||
|
||||
// Inicializa lista de objetos de texto (sin cargar el contenido)
|
||||
const std::vector<std::string> TEXT_OBJECTS = {
|
||||
"04b_25",
|
||||
"04b_25_2x",
|
||||
"04b_25_metal",
|
||||
"04b_25_grey",
|
||||
"04b_25_flat",
|
||||
"04b_25_reversed",
|
||||
"04b_25_flat_2x",
|
||||
"04b_25_reversed_2x",
|
||||
"8bithud",
|
||||
"aseprite",
|
||||
"smb2",
|
||||
"smb2_grad"};
|
||||
|
||||
texts_.clear();
|
||||
for (const auto& text_name : TEXT_OBJECTS) {
|
||||
texts_.emplace_back(text_name); // Constructor con nullptr por defecto
|
||||
}
|
||||
}
|
||||
|
||||
// Obtiene el sonido a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getSound(const std::string& name) -> JA_Sound_t* {
|
||||
auto it = std::ranges::find_if(sounds_, [&name](const auto& s) -> auto { return s.name == name; });
|
||||
|
||||
if (it != sounds_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún, lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->sound == nullptr) {
|
||||
it->sound = loadSoundLazy(name);
|
||||
}
|
||||
return it->sound;
|
||||
}
|
||||
|
||||
std::cout << "Error: Sonido no encontrado " << name << '\n';
|
||||
throw std::runtime_error("Sonido no encontrado: " + name);
|
||||
}
|
||||
|
||||
// Obtiene la música a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getMusic(const std::string& name) -> JA_Music_t* {
|
||||
auto it = std::ranges::find_if(musics_, [&name](const auto& m) -> auto { return m.name == name; });
|
||||
|
||||
if (it != musics_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún, lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->music == nullptr) {
|
||||
it->music = loadMusicLazy(name);
|
||||
}
|
||||
return it->music;
|
||||
}
|
||||
|
||||
std::cout << "Error: Música no encontrada " << name << '\n';
|
||||
throw std::runtime_error("Música no encontrada: " + name);
|
||||
}
|
||||
|
||||
// Obtiene la textura a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getTexture(const std::string& name) -> std::shared_ptr<Texture> {
|
||||
auto it = std::ranges::find_if(textures_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||
|
||||
if (it != textures_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún, lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->texture == nullptr) {
|
||||
it->texture = loadTextureLazy(name);
|
||||
}
|
||||
return it->texture;
|
||||
}
|
||||
|
||||
std::cout << "Error: Imagen no encontrada " << name << '\n';
|
||||
throw std::runtime_error("Imagen no encontrada: " + name);
|
||||
}
|
||||
|
||||
// Obtiene el fichero de texto a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getTextFile(const std::string& name) -> std::shared_ptr<Text::File> {
|
||||
auto it = std::ranges::find_if(text_files_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||
|
||||
if (it != text_files_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún, lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->text_file == nullptr) {
|
||||
it->text_file = loadTextFileLazy(name);
|
||||
}
|
||||
return it->text_file;
|
||||
}
|
||||
|
||||
std::cout << "Error: TextFile no encontrado " << name << '\n';
|
||||
throw std::runtime_error("TextFile no encontrado: " + name);
|
||||
}
|
||||
|
||||
// Obtiene el objeto de texto a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getText(const std::string& name) -> std::shared_ptr<Text> {
|
||||
auto it = std::ranges::find_if(texts_, [&name](const auto& t) -> auto { return t.name == name; });
|
||||
|
||||
if (it != texts_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún, lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->text == nullptr) {
|
||||
it->text = loadTextLazy(name);
|
||||
}
|
||||
return it->text;
|
||||
}
|
||||
|
||||
std::cout << "Error: Text no encontrado " << name << '\n';
|
||||
throw std::runtime_error("Text no encontrado: " + name);
|
||||
}
|
||||
|
||||
// Obtiene la animación a partir de un nombre (con carga perezosa)
|
||||
auto Resource::getAnimation(const std::string& name) -> AnimationsFileBuffer& {
|
||||
auto it = std::ranges::find_if(animations_, [&name](const auto& a) -> auto { return a.name == name; });
|
||||
|
||||
if (it != animations_.end()) {
|
||||
// Si está en modo lazy y no se ha cargado aún (vector vacío), lo carga ahora
|
||||
if (loading_mode_ == LoadingMode::LAZY_LOAD && it->animation.empty()) {
|
||||
it->animation = loadAnimationLazy(name);
|
||||
}
|
||||
return it->animation;
|
||||
}
|
||||
|
||||
std::cout << "Error: Animación no encontrada " << name << '\n';
|
||||
throw std::runtime_error("Animación no encontrada: " + name);
|
||||
}
|
||||
|
||||
// Obtiene el fichero con los datos para el modo demostración a partir de un índice
|
||||
auto Resource::getDemoData(int index) -> DemoData& {
|
||||
if (index < 0 || std::cmp_greater_equal(index, demos_.size())) {
|
||||
std::cout << "Index " << index << " out of range for demo data (size: " << static_cast<int>(demos_.size()) << ")" << '\n';
|
||||
static DemoData empty_demo_;
|
||||
return empty_demo_;
|
||||
}
|
||||
return demos_.at(index);
|
||||
}
|
||||
|
||||
// --- Métodos de carga perezosa ---
|
||||
|
||||
auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* {
|
||||
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||
for (const auto& file : sound_list) {
|
||||
if (getFileName(file) == name) {
|
||||
auto audio_data = loadAudioData(file);
|
||||
if (!audio_data.data.empty()) {
|
||||
return JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
||||
}
|
||||
// Fallback a cargar desde disco si no está en pack
|
||||
return JA_LoadSound(file.c_str());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* {
|
||||
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||
for (const auto& file : music_list) {
|
||||
if (getFileName(file) == name) {
|
||||
auto audio_data = loadAudioData(file);
|
||||
if (!audio_data.data.empty()) {
|
||||
return JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
||||
}
|
||||
// Fallback a cargar desde disco si no está en pack
|
||||
return JA_LoadMusic(file.c_str());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture> {
|
||||
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||
for (const auto& file : texture_list) {
|
||||
if (getFileName(file) == name) {
|
||||
return std::make_shared<Texture>(Screen::get()->getRenderer(), file);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File> {
|
||||
auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||
for (const auto& file : text_file_list) {
|
||||
if (getFileName(file) == name) {
|
||||
return Text::loadFile(file);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
|
||||
// Mapeo de objetos de texto a sus recursos
|
||||
struct TextMapping {
|
||||
std::string key;
|
||||
std::string texture_file;
|
||||
std::string text_file;
|
||||
};
|
||||
|
||||
const std::vector<TextMapping> TEXT_MAPPINGS = {
|
||||
{.key = "04b_25", .texture_file = "04b_25.png", .text_file = "04b_25.txt"},
|
||||
{.key = "04b_25_2x", .texture_file = "04b_25_2x.png", .text_file = "04b_25_2x.txt"},
|
||||
{.key = "04b_25_metal", .texture_file = "04b_25_metal.png", .text_file = "04b_25.txt"},
|
||||
{.key = "04b_25_grey", .texture_file = "04b_25_grey.png", .text_file = "04b_25.txt"},
|
||||
{.key = "04b_25_flat", .texture_file = "04b_25_flat.png", .text_file = "04b_25.txt"},
|
||||
{.key = "04b_25_reversed", .texture_file = "04b_25_reversed.png", .text_file = "04b_25.txt"},
|
||||
{.key = "04b_25_flat_2x", .texture_file = "04b_25_flat_2x.png", .text_file = "04b_25_2x.txt"},
|
||||
{.key = "04b_25_reversed_2x", .texture_file = "04b_25_reversed_2x.png", .text_file = "04b_25_2x.txt"},
|
||||
{.key = "8bithud", .texture_file = "8bithud.png", .text_file = "8bithud.txt"},
|
||||
{.key = "aseprite", .texture_file = "aseprite.png", .text_file = "aseprite.txt"},
|
||||
{.key = "smb2", .texture_file = "smb2.png", .text_file = "smb2.txt"},
|
||||
{.key = "smb2_grad", .texture_file = "smb2_grad.png", .text_file = "smb2.txt"}};
|
||||
|
||||
for (const auto& mapping : TEXT_MAPPINGS) {
|
||||
if (mapping.key == name) {
|
||||
// Cargar las dependencias automáticamente
|
||||
auto texture = getTexture(mapping.texture_file); // Esto cargará la textura si no está cargada
|
||||
auto text_file = getTextFile(mapping.text_file); // Esto cargará el archivo de texto si no está cargado
|
||||
|
||||
if (texture && text_file) {
|
||||
return std::make_shared<Text>(texture, text_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto Resource::loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer {
|
||||
auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||
for (const auto& file : animation_list) {
|
||||
if (getFileName(file) == name) {
|
||||
return loadAnimationsFromFile(file);
|
||||
}
|
||||
}
|
||||
// Si no se encuentra, retorna vector vacío
|
||||
return AnimationsFileBuffer{};
|
||||
}
|
||||
|
||||
// Vacia todos los vectores de recursos
|
||||
void Resource::clear() {
|
||||
clearSounds();
|
||||
clearMusics();
|
||||
textures_.clear();
|
||||
text_files_.clear();
|
||||
texts_.clear();
|
||||
animations_.clear();
|
||||
demos_.clear();
|
||||
}
|
||||
|
||||
// Carga todos los recursos del juego y muestra el progreso de carga
|
||||
void Resource::load() {
|
||||
// Prepara la gestión del progreso de carga
|
||||
calculateTotalResources();
|
||||
initProgressBar();
|
||||
|
||||
// Muerstra la ventana y desactiva el sincronismo vertical
|
||||
auto* screen = Screen::get();
|
||||
auto vsync = Screen::getVSync();
|
||||
screen->setVSync(false);
|
||||
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES");
|
||||
loadSounds(); // Carga sonidos
|
||||
loadMusics(); // Carga músicas
|
||||
loadTextures(); // Carga texturas
|
||||
loadTextFiles(); // Carga ficheros de texto
|
||||
loadAnimations(); // Carga animaciones
|
||||
loadDemoData(); // Carga datos de demo
|
||||
createText(); // Crea objetos de texto
|
||||
createTextTextures(); // Crea texturas a partir de texto
|
||||
createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
||||
// SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED");
|
||||
|
||||
// Restablece el sincronismo vertical a su valor original
|
||||
screen->setVSync(vsync);
|
||||
}
|
||||
|
||||
// Recarga todos los recursos (limpia y vuelve a cargar)
|
||||
void Resource::reload() {
|
||||
clear();
|
||||
if (loading_mode_ == LoadingMode::PRELOAD) {
|
||||
load();
|
||||
} else {
|
||||
initResourceLists();
|
||||
}
|
||||
}
|
||||
|
||||
// Carga los sonidos del juego
|
||||
void Resource::loadSounds() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
|
||||
sounds_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
auto audio_data = loadAudioData(l);
|
||||
JA_Sound_t* sound = nullptr;
|
||||
if (!audio_data.data.empty()) {
|
||||
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size());
|
||||
} else {
|
||||
sound = JA_LoadSound(l.c_str());
|
||||
}
|
||||
if (sound == nullptr) {
|
||||
std::cout << "Sound load failed: " << name << '\n';
|
||||
}
|
||||
sounds_.emplace_back(name, sound);
|
||||
}
|
||||
}
|
||||
|
||||
// Carga las músicas del juego
|
||||
void Resource::loadMusics() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
|
||||
musics_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
auto audio_data = loadAudioData(l);
|
||||
JA_Music_t* music = nullptr;
|
||||
if (!audio_data.data.empty()) {
|
||||
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size());
|
||||
} else {
|
||||
music = JA_LoadMusic(l.c_str());
|
||||
}
|
||||
if (music == nullptr) {
|
||||
std::cout << "Music load failed: " << name << '\n';
|
||||
}
|
||||
musics_.emplace_back(name, music);
|
||||
}
|
||||
}
|
||||
|
||||
// Carga las texturas del juego
|
||||
void Resource::loadTextures() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||
textures_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
textures_.emplace_back(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l));
|
||||
}
|
||||
}
|
||||
|
||||
// Carga los ficheros de texto del juego
|
||||
void Resource::loadTextFiles() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::FONT);
|
||||
text_files_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
text_files_.emplace_back(name, Text::loadFile(l));
|
||||
}
|
||||
}
|
||||
|
||||
// Carga las animaciones del juego
|
||||
void Resource::loadAnimations() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
|
||||
animations_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
animations_.emplace_back(name, loadAnimationsFromFile(l));
|
||||
}
|
||||
}
|
||||
|
||||
// Carga los datos para el modo demostración
|
||||
void Resource::loadDemoData() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||
demos_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
auto name = getFileName(l);
|
||||
updateLoadingProgress(name);
|
||||
demos_.emplace_back(loadDemoDataFromFile(l));
|
||||
}
|
||||
}
|
||||
|
||||
// Crea las texturas de jugadores con todas sus variantes de paleta
|
||||
void Resource::createPlayerTextures() {
|
||||
// Configuración de jugadores y sus paletas
|
||||
struct PlayerConfig {
|
||||
std::string base_texture;
|
||||
std::vector<std::string> palette_files;
|
||||
std::string name_prefix;
|
||||
};
|
||||
|
||||
std::vector<PlayerConfig> players = {
|
||||
{.base_texture = "player1.gif", .palette_files = {"player1_coffee1.pal", "player1_coffee2.pal", "player1_invencible.pal"}, .name_prefix = "player1"},
|
||||
{.base_texture = "player2.gif", .palette_files = {"player2_coffee1.pal", "player2_coffee2.pal", "player2_invencible.pal"}, .name_prefix = "player2"}};
|
||||
|
||||
// Bucle principal
|
||||
for (size_t player_idx = 0; player_idx < players.size(); ++player_idx) {
|
||||
const auto& player = players[player_idx]; // Obtenemos el jugador actual
|
||||
|
||||
// Encontrar el archivo original de la textura
|
||||
std::string texture_file_path;
|
||||
auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP);
|
||||
for (const auto& file : texture_list) {
|
||||
if (getFileName(file) == player.base_texture) {
|
||||
texture_file_path = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Crear las 4 texturas con sus respectivas paletas
|
||||
for (int palette_idx = 0; palette_idx < 4; ++palette_idx) {
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
if (palette_idx == 0) {
|
||||
// Textura 0 - usar la ya cargada y modificar solo paleta 0 (default_shirt)
|
||||
texture = getTexture(player.base_texture);
|
||||
texture->setPaletteColor(0, 16, param.player.default_shirt[player_idx].darkest.TO_UINT32());
|
||||
texture->setPaletteColor(0, 17, param.player.default_shirt[player_idx].dark.TO_UINT32());
|
||||
texture->setPaletteColor(0, 18, param.player.default_shirt[player_idx].base.TO_UINT32());
|
||||
texture->setPaletteColor(0, 19, param.player.default_shirt[player_idx].light.TO_UINT32());
|
||||
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||
} else {
|
||||
// Crear textura nueva desde archivo usando ResourceHelper
|
||||
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path);
|
||||
|
||||
// Añadir todas las paletas
|
||||
texture->addPaletteFromPalFile(Asset::get()->getPath(player.palette_files[0]));
|
||||
texture->addPaletteFromPalFile(Asset::get()->getPath(player.palette_files[1]));
|
||||
texture->addPaletteFromPalFile(Asset::get()->getPath(player.palette_files[2]));
|
||||
|
||||
if (palette_idx == 1) {
|
||||
// Textura 1 - modificar solo paleta 1 (one_coffee_shirt)
|
||||
texture->setPaletteColor(1, 16, param.player.one_coffee_shirt[player_idx].darkest.TO_UINT32());
|
||||
texture->setPaletteColor(1, 17, param.player.one_coffee_shirt[player_idx].dark.TO_UINT32());
|
||||
texture->setPaletteColor(1, 18, param.player.one_coffee_shirt[player_idx].base.TO_UINT32());
|
||||
texture->setPaletteColor(1, 19, param.player.one_coffee_shirt[player_idx].light.TO_UINT32());
|
||||
texture->setPaletteColor(1, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||
} else if (palette_idx == 2) {
|
||||
// Textura 2 - modificar solo paleta 2 (two_coffee_shirt)
|
||||
texture->setPaletteColor(2, 16, param.player.two_coffee_shirt[player_idx].darkest.TO_UINT32());
|
||||
texture->setPaletteColor(2, 17, param.player.two_coffee_shirt[player_idx].dark.TO_UINT32());
|
||||
texture->setPaletteColor(2, 18, param.player.two_coffee_shirt[player_idx].base.TO_UINT32());
|
||||
texture->setPaletteColor(2, 19, param.player.two_coffee_shirt[player_idx].light.TO_UINT32());
|
||||
texture->setPaletteColor(2, 56, param.player.outline_color[player_idx].TO_UINT32());
|
||||
}
|
||||
// Textura 3 (palette_idx == 3) - no modificar nada, usar colores originales
|
||||
}
|
||||
|
||||
// Asignar la paleta correspondiente
|
||||
texture->setPalette(palette_idx);
|
||||
|
||||
// Guardar con nombre específico
|
||||
std::string texture_name = player.name_prefix + "_pal" + std::to_string(palette_idx);
|
||||
textures_.emplace_back(texture_name, texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crea texturas a partir de textos para mostrar puntuaciones y mensajes
|
||||
void Resource::createTextTextures() {
|
||||
struct NameAndText {
|
||||
std::string name;
|
||||
std::string text;
|
||||
|
||||
NameAndText(std::string name_init, std::string text_init)
|
||||
: name(std::move(name_init)),
|
||||
text(std::move(text_init)) {}
|
||||
};
|
||||
|
||||
// Texturas de tamaño normal con outline
|
||||
std::vector<NameAndText> strings1 = {
|
||||
{"game_text_1000_points", "1.000"},
|
||||
{"game_text_2500_points", "2.500"},
|
||||
{"game_text_5000_points", "5.000"},
|
||||
{"game_text_powerup", Lang::getText("[GAME_TEXT] 4")},
|
||||
{"game_text_one_hit", Lang::getText("[GAME_TEXT] 5")},
|
||||
{"game_text_stop", Lang::getText("[GAME_TEXT] 6")},
|
||||
{"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}};
|
||||
|
||||
auto text1 = getText("04b_25_enhanced");
|
||||
for (const auto& s : strings1) {
|
||||
textures_.emplace_back(s.name, text1->writeDXToTexture(Text::STROKE, s.text, -2, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||
}
|
||||
|
||||
// Texturas de tamaño doble
|
||||
std::vector<NameAndText> strings2 = {
|
||||
{"game_text_100000_points", "100.000"},
|
||||
{"game_text_get_ready", Lang::getText("[GAME_TEXT] 7")},
|
||||
{"game_text_last_stage", Lang::getText("[GAME_TEXT] 3")},
|
||||
{"game_text_congratulations", Lang::getText("[GAME_TEXT] 1")},
|
||||
{"game_text_new_record", Lang::getText("[GAME_TEXT] NEW_RECORD")},
|
||||
{"game_text_game_over", "Game Over"}};
|
||||
|
||||
auto text2 = getText("04b_25_2x_enhanced");
|
||||
for (const auto& s : strings2) {
|
||||
textures_.emplace_back(s.name, text2->writeDXToTexture(Text::STROKE, s.text, -4, Colors::NO_COLOR_MOD, 1, param.game.item_text_outline_color));
|
||||
}
|
||||
}
|
||||
|
||||
// Crea los objetos de texto a partir de los archivos de textura y texto
|
||||
void Resource::createText() {
|
||||
struct ResourceInfo {
|
||||
std::string key;
|
||||
std::string texture_file;
|
||||
std::string text_file;
|
||||
std::string white_texture_file; // Textura blanca opcional
|
||||
|
||||
ResourceInfo(std::string k, std::string t_file, std::string txt_file, std::string w_file = "")
|
||||
: key(std::move(k)),
|
||||
texture_file(std::move(t_file)),
|
||||
text_file(std::move(txt_file)),
|
||||
white_texture_file(std::move(w_file)) {}
|
||||
};
|
||||
|
||||
std::vector<ResourceInfo> resources = {
|
||||
{"04b_25", "04b_25.png", "04b_25.txt"},
|
||||
{"04b_25_enhanced", "04b_25.png", "04b_25.txt", "04b_25_white.png"}, // Nueva fuente con textura blanca
|
||||
{"04b_25_white", "04b_25_white.png", "04b_25.txt"},
|
||||
{"04b_25_2x", "04b_25_2x.png", "04b_25_2x.txt"},
|
||||
{"04b_25_2x_enhanced", "04b_25_2x.png", "04b_25_2x.txt", "04b_25_2x_white.png"}, // Nueva fuente con textura blanca
|
||||
{"04b_25_metal", "04b_25_metal.png", "04b_25.txt"},
|
||||
{"04b_25_grey", "04b_25_grey.png", "04b_25.txt"},
|
||||
{"04b_25_flat", "04b_25_flat.png", "04b_25.txt"},
|
||||
{"04b_25_reversed", "04b_25_reversed.png", "04b_25.txt"},
|
||||
{"04b_25_flat_2x", "04b_25_flat_2x.png", "04b_25_2x.txt"},
|
||||
{"04b_25_reversed_2x", "04b_25_reversed_2x.png", "04b_25_2x.txt"},
|
||||
{"8bithud", "8bithud.png", "8bithud.txt"},
|
||||
{"aseprite", "aseprite.png", "aseprite.txt"},
|
||||
{"smb2", "smb2.png", "smb2.txt"},
|
||||
{"smb2_grad", "smb2_grad.png", "smb2.txt"}};
|
||||
|
||||
for (const auto& resource : resources) {
|
||||
if (!resource.white_texture_file.empty()) {
|
||||
// Crear texto con textura blanca
|
||||
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTexture(resource.white_texture_file), getTextFile(resource.text_file)));
|
||||
} else {
|
||||
// Crear texto normal
|
||||
texts_.emplace_back(resource.key, std::make_shared<Text>(getTexture(resource.texture_file), getTextFile(resource.text_file)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Vacía el vector de sonidos y libera la memoria asociada
|
||||
void Resource::clearSounds() {
|
||||
for (auto& sound : sounds_) {
|
||||
if (sound.sound != nullptr) {
|
||||
JA_DeleteSound(sound.sound);
|
||||
sound.sound = nullptr;
|
||||
}
|
||||
}
|
||||
sounds_.clear();
|
||||
}
|
||||
|
||||
// Vacía el vector de músicas y libera la memoria asociada
|
||||
void Resource::clearMusics() {
|
||||
for (auto& music : musics_) {
|
||||
if (music.music != nullptr) {
|
||||
JA_DeleteMusic(music.music);
|
||||
music.music = nullptr;
|
||||
}
|
||||
}
|
||||
musics_.clear();
|
||||
}
|
||||
|
||||
// Calcula el número total de recursos a cargar y reinicia el contador de carga
|
||||
void Resource::calculateTotalResources() {
|
||||
const std::array<Asset::Type, 6> ASSET_TYPES = {
|
||||
Asset::Type::SOUND,
|
||||
Asset::Type::MUSIC,
|
||||
Asset::Type::BITMAP,
|
||||
Asset::Type::FONT,
|
||||
Asset::Type::ANIMATION,
|
||||
Asset::Type::DEMODATA};
|
||||
|
||||
size_t total = 0;
|
||||
for (const auto& asset_type : ASSET_TYPES) {
|
||||
auto list = Asset::get()->getListByType(asset_type);
|
||||
total += list.size();
|
||||
}
|
||||
|
||||
loading_count_ = ResourceCount(total);
|
||||
}
|
||||
|
||||
// Muestra el progreso de carga en pantalla (barra y texto)
|
||||
void Resource::renderProgress() {
|
||||
// Obtiene la pantalla y el renderer
|
||||
auto* screen = Screen::get();
|
||||
auto* renderer = screen->getRenderer();
|
||||
|
||||
// Actualiza la lógica principal de la pantalla (input, etc.)
|
||||
screen->coreUpdate();
|
||||
|
||||
// Inicia el frame y limpia la pantalla
|
||||
screen->start();
|
||||
screen->clean();
|
||||
|
||||
auto text_color = param.resource.color;
|
||||
auto bar_color = param.resource.color.DARKEN(100);
|
||||
const auto TEXT_HEIGHT = loading_text_->getCharacterSize();
|
||||
|
||||
// Dibuja el interior de la barra de progreso
|
||||
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
||||
SDL_RenderFillRect(renderer, &loading_full_rect_);
|
||||
|
||||
// Dibuja el marco de la barra de progreso
|
||||
SDL_SetRenderDrawColor(renderer, bar_color.r, bar_color.g, bar_color.b, bar_color.a);
|
||||
SDL_RenderRect(renderer, &loading_wired_rect_);
|
||||
|
||||
// Escribe el texto de carga encima de la barra
|
||||
/*
|
||||
loading_text_->writeColored(
|
||||
loading_wired_rect_.x,
|
||||
loading_wired_rect_.y - 9,
|
||||
Lang::getText("[RESOURCE] LOADING") + " : " + loading_resource_name_,
|
||||
text_color);
|
||||
*/
|
||||
|
||||
// Muestra nombre de la aplicación
|
||||
loading_text_->writeDX(
|
||||
Text::CENTER | Text::COLOR,
|
||||
param.game.game_area.center_x,
|
||||
param.game.game_area.center_y - TEXT_HEIGHT,
|
||||
spaceBetweenLetters(std::string(Version::APP_NAME)),
|
||||
1,
|
||||
text_color);
|
||||
|
||||
// Muestra la versión
|
||||
loading_text_->writeDX(
|
||||
Text::CENTER | Text::COLOR,
|
||||
param.game.game_area.center_x,
|
||||
param.game.game_area.center_y + TEXT_HEIGHT,
|
||||
"(" + std::string(Version::GIT_HASH) + ")",
|
||||
1,
|
||||
text_color);
|
||||
|
||||
// Muestra información del monitor desplazada hacia abajo
|
||||
/*loading_text_->writeColored(
|
||||
X_PADDING,
|
||||
Y_PADDING + 18,
|
||||
screen->getDisplayMonitorName(),
|
||||
text_color);
|
||||
loading_text_->writeColored(
|
||||
X_PADDING,
|
||||
Y_PADDING + 27,
|
||||
std::to_string(screen->getDisplayMonitorWidth()) + "x" + std::to_string(screen->getDisplayMonitorHeight()),
|
||||
text_color);
|
||||
loading_text_->writeColored(
|
||||
X_PADDING,
|
||||
Y_PADDING + 36,
|
||||
std::to_string(screen->getDisplayMonitorRefreshRate()) + "Hz",
|
||||
text_color);*/
|
||||
|
||||
// Renderiza el frame en pantalla
|
||||
screen->coreRender();
|
||||
}
|
||||
|
||||
// Carga los datos para el modo demostración (sin mostrar progreso)
|
||||
void Resource::loadDemoDataQuiet() {
|
||||
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
|
||||
demos_.clear();
|
||||
|
||||
for (const auto& l : list) {
|
||||
demos_.emplace_back(loadDemoDataFromFile(l));
|
||||
}
|
||||
}
|
||||
|
||||
// Inicializa los rectangulos que definen la barra de progreso
|
||||
void Resource::initProgressBar() {
|
||||
const float BAR_Y_POSITION = param.game.height - BAR_HEIGHT - Y_PADDING;
|
||||
|
||||
const float WIRED_BAR_WIDTH = param.game.width - (X_PADDING * 2);
|
||||
loading_wired_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = WIRED_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||
|
||||
const float FULL_BAR_WIDTH = WIRED_BAR_WIDTH * loading_count_.getPercentage();
|
||||
loading_full_rect_ = {.x = X_PADDING, .y = BAR_Y_POSITION, .w = FULL_BAR_WIDTH, .h = BAR_HEIGHT};
|
||||
}
|
||||
|
||||
// Actualiza el progreso de carga y muestra la barra
|
||||
void Resource::updateLoadingProgress(std::string name) {
|
||||
loading_resource_name_ = std::move(name);
|
||||
loading_count_.increase();
|
||||
updateProgressBar();
|
||||
renderProgress();
|
||||
}
|
||||
|
||||
// Actualiza la barra de estado
|
||||
void Resource::updateProgressBar() {
|
||||
loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage();
|
||||
}
|
||||
187
source/core/resources/resource.hpp
Normal file
187
source/core/resources/resource.hpp
Normal file
@@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h> // Para SDL_FRect
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <memory> // Para shared_ptr
|
||||
#include <string> // Para string
|
||||
#include <utility> // Para move
|
||||
#include <vector> // Para vector
|
||||
|
||||
#include "animated_sprite.hpp" // Para AnimationsFileBuffer
|
||||
#include "demo.hpp" // Para DemoData
|
||||
#include "text.hpp" // Para Text, TextFile
|
||||
#include "texture.hpp" // Para Texture
|
||||
|
||||
struct JA_Music_t;
|
||||
struct JA_Sound_t;
|
||||
|
||||
// --- Clase Resource: gestiona todos los recursos del juego (singleton) ---
|
||||
class Resource {
|
||||
public:
|
||||
// --- Enum para el modo de carga ---
|
||||
enum class LoadingMode {
|
||||
PRELOAD, // Carga todos los recursos al inicio
|
||||
LAZY_LOAD // Carga los recursos bajo demanda
|
||||
};
|
||||
|
||||
// --- Métodos de singleton ---
|
||||
static void init(LoadingMode mode = LoadingMode::PRELOAD); // Inicializa el objeto Resource con modo de carga
|
||||
static void destroy(); // Libera el objeto Resource
|
||||
static auto get() -> Resource*; // Obtiene el puntero al objeto Resource
|
||||
|
||||
// --- Métodos de acceso a recursos ---
|
||||
auto getSound(const std::string& name) -> JA_Sound_t*; // Obtiene el sonido por nombre
|
||||
auto getMusic(const std::string& name) -> JA_Music_t*; // Obtiene la música por nombre
|
||||
auto getTexture(const std::string& name) -> std::shared_ptr<Texture>; // Obtiene la textura por nombre
|
||||
auto getTextFile(const std::string& name) -> std::shared_ptr<Text::File>; // Obtiene el fichero de texto por nombre
|
||||
auto getText(const std::string& name) -> std::shared_ptr<Text>; // Obtiene el objeto de texto por nombre
|
||||
auto getAnimation(const std::string& name) -> AnimationsFileBuffer&; // Obtiene la animación por nombre
|
||||
auto getDemoData(int index) -> DemoData&; // Obtiene los datos de demo por índice
|
||||
|
||||
// --- Métodos de recarga de recursos ---
|
||||
void reload(); // Recarga todos los recursos
|
||||
|
||||
// --- Método para obtener el modo de carga actual ---
|
||||
[[nodiscard]] auto getLoadingMode() const -> LoadingMode { return loading_mode_; }
|
||||
|
||||
private:
|
||||
// --- Estructuras para recursos individuales ---
|
||||
struct ResourceSound {
|
||||
std::string name; // Nombre del sonido
|
||||
JA_Sound_t* sound; // Objeto con el sonido
|
||||
|
||||
ResourceSound(std::string name, JA_Sound_t* sound = nullptr)
|
||||
: name(std::move(name)),
|
||||
sound(sound) {}
|
||||
};
|
||||
|
||||
struct ResourceMusic {
|
||||
std::string name; // Nombre de la música
|
||||
JA_Music_t* music; // Objeto con la música
|
||||
|
||||
ResourceMusic(std::string name, JA_Music_t* music = nullptr)
|
||||
: name(std::move(name)),
|
||||
music(music) {}
|
||||
};
|
||||
|
||||
struct ResourceTexture {
|
||||
std::string name; // Nombre de la textura
|
||||
std::shared_ptr<Texture> texture; // Objeto con la textura
|
||||
|
||||
ResourceTexture(std::string name, std::shared_ptr<Texture> texture = nullptr)
|
||||
: name(std::move(name)),
|
||||
texture(std::move(texture)) {}
|
||||
};
|
||||
|
||||
struct ResourceTextFile {
|
||||
std::string name; // Nombre del fichero
|
||||
std::shared_ptr<Text::File> text_file; // Objeto con los descriptores de la fuente de texto
|
||||
|
||||
ResourceTextFile(std::string name, std::shared_ptr<Text::File> text_file = nullptr)
|
||||
: name(std::move(name)),
|
||||
text_file(std::move(text_file)) {}
|
||||
};
|
||||
|
||||
struct ResourceText {
|
||||
std::string name; // Nombre del objeto
|
||||
std::shared_ptr<Text> text; // Objeto de texto
|
||||
|
||||
ResourceText(std::string name, std::shared_ptr<Text> text = nullptr)
|
||||
: name(std::move(name)),
|
||||
text(std::move(text)) {}
|
||||
};
|
||||
|
||||
struct ResourceAnimation {
|
||||
std::string name; // Nombre de la animación
|
||||
AnimationsFileBuffer animation; // Objeto con las animaciones
|
||||
|
||||
ResourceAnimation(std::string name, AnimationsFileBuffer animation = {})
|
||||
: name(std::move(name)),
|
||||
animation(std::move(animation)) {}
|
||||
};
|
||||
|
||||
// --- Estructura para el progreso de carga ---
|
||||
struct ResourceCount {
|
||||
size_t total{0}; // Número total de recursos
|
||||
size_t loaded{0}; // Número de recursos cargados
|
||||
|
||||
ResourceCount() = default;
|
||||
ResourceCount(size_t total)
|
||||
: total(total) {}
|
||||
|
||||
void add(size_t amount) { loaded += amount; }
|
||||
void increase() { loaded++; }
|
||||
[[nodiscard]] auto getPercentage() const -> float {
|
||||
return total > 0 ? static_cast<float>(loaded) / static_cast<float>(total) : 0.0F;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Constantes para la pantalla de carga ---
|
||||
static constexpr float X_PADDING = 60.0F;
|
||||
static constexpr float Y_PADDING = 20.0F;
|
||||
static constexpr float BAR_HEIGHT = 5.0F;
|
||||
static constexpr Color BAR_COLOR = Color(128, 128, 128);
|
||||
static constexpr Color TEXT_COLOR = Color(255, 255, 255);
|
||||
|
||||
// --- Modo de carga ---
|
||||
LoadingMode loading_mode_;
|
||||
|
||||
// --- Vectores de recursos ---
|
||||
std::vector<ResourceSound> sounds_; // Vector con los sonidos
|
||||
std::vector<ResourceMusic> musics_; // Vector con las músicas
|
||||
std::vector<ResourceTexture> textures_; // Vector con las texturas
|
||||
std::vector<ResourceTextFile> text_files_; // Vector con los ficheros de texto
|
||||
std::vector<ResourceText> texts_; // Vector con los objetos de texto
|
||||
std::vector<ResourceAnimation> animations_; // Vector con las animaciones
|
||||
std::vector<DemoData> demos_; // Vector con los ficheros de datos para el modo demostración
|
||||
|
||||
// --- Progreso de carga ---
|
||||
ResourceCount loading_count_; // Contador de recursos cargados
|
||||
std::shared_ptr<Text> loading_text_; // Texto para escribir en pantalla
|
||||
std::string loading_resource_name_; // Nombre del recurso que se está cargando
|
||||
SDL_FRect loading_wired_rect_;
|
||||
SDL_FRect loading_full_rect_;
|
||||
|
||||
// --- Métodos internos de carga y gestión ---
|
||||
void loadSounds(); // Carga los sonidos
|
||||
void loadMusics(); // Carga las músicas
|
||||
void loadTextures(); // Carga las texturas
|
||||
void loadTextFiles(); // Carga los ficheros de texto
|
||||
void loadAnimations(); // Carga las animaciones
|
||||
void loadDemoData(); // Carga los datos para el modo demostración
|
||||
void loadDemoDataQuiet(); // Carga los datos de demo sin mostrar progreso (para modo lazy)
|
||||
void loadEssentialResources(); // Carga recursos esenciales en modo lazy
|
||||
void loadEssentialTextures(); // Carga solo las texturas esenciales (fuentes)
|
||||
void loadTextFilesQuiet(); // Carga ficheros de texto sin mostrar progreso (para modo lazy)
|
||||
void createPlayerTextures(); // Crea las texturas de jugadores con todas sus variantes de paleta
|
||||
void createTextTextures(); // Crea las texturas a partir de los datos cargados
|
||||
void createText(); // Crea los objetos de texto
|
||||
void clear(); // Vacía todos los vectores de recursos
|
||||
void load(); // Carga todos los recursos
|
||||
void clearSounds(); // Vacía el vector de sonidos
|
||||
void clearMusics(); // Vacía el vector de músicas
|
||||
|
||||
// --- Métodos para carga perezosa ---
|
||||
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido
|
||||
static auto loadSoundLazy(const std::string& name) -> JA_Sound_t*; // Carga un sonido específico bajo demanda
|
||||
static auto loadMusicLazy(const std::string& name) -> JA_Music_t*; // Carga una música específica bajo demanda
|
||||
static auto loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture>; // Carga una textura específica bajo demanda
|
||||
static auto loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File>; // Carga un fichero de texto específico bajo demanda
|
||||
auto loadTextLazy(const std::string& name) -> std::shared_ptr<Text>; // Carga un objeto de texto específico bajo demanda
|
||||
static auto loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer; // Carga una animación específica bajo demanda
|
||||
|
||||
// --- Métodos internos para gestionar el progreso ---
|
||||
void calculateTotalResources(); // Calcula el número de recursos para cargar
|
||||
void renderProgress(); // Muestra el progreso de carga
|
||||
void updateLoadingProgress(std::string name); // Actualiza el progreso de carga
|
||||
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
|
||||
void updateProgressBar(); // Actualiza la barra de estado
|
||||
|
||||
// --- Constructores y destructor privados (singleton) ---
|
||||
explicit Resource(LoadingMode mode); // Constructor privado con modo de carga
|
||||
~Resource(); // Destructor privado
|
||||
|
||||
// --- Instancia singleton ---
|
||||
static Resource* instance; // Instancia única de Resource
|
||||
};
|
||||
98
source/core/resources/resource_helper.cpp
Normal file
98
source/core/resources/resource_helper.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "resource_helper.hpp"
|
||||
|
||||
#include <algorithm> // Para replace
|
||||
#include <cstddef> // Para size_t
|
||||
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ios, operator<<, ios, basic_istream, endl, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
||||
#include <iostream> // Para cout
|
||||
|
||||
#include "resource_loader.hpp" // Para ResourceLoader
|
||||
|
||||
namespace ResourceHelper {
|
||||
static bool resource_system_initialized = false;
|
||||
|
||||
auto initializeResourceSystem(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
resource_system_initialized = loader.initialize(pack_file, enable_fallback);
|
||||
|
||||
if (resource_system_initialized) {
|
||||
std::cout << "Resource system initialized with pack: " << pack_file << '\n';
|
||||
} else {
|
||||
std::cout << "Resource system using fallback mode (filesystem only)" << '\n';
|
||||
}
|
||||
|
||||
return true; // Always return true as fallback is acceptable
|
||||
}
|
||||
|
||||
void shutdownResourceSystem() {
|
||||
if (resource_system_initialized) {
|
||||
ResourceLoader::getInstance().shutdown();
|
||||
resource_system_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
auto loadFile(const std::string& filepath) -> std::vector<uint8_t> {
|
||||
if (resource_system_initialized && shouldUseResourcePack(filepath)) {
|
||||
auto& loader = ResourceLoader::getInstance();
|
||||
std::string pack_path = getPackPath(filepath);
|
||||
|
||||
auto data = loader.loadResource(pack_path);
|
||||
if (!data.empty()) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback a filesystem
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
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)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto shouldUseResourcePack(const std::string& filepath) -> bool {
|
||||
// Archivos que NO van al pack:
|
||||
// - config/ (ahora está fuera de data/)
|
||||
// - archivos absolutos del sistema
|
||||
|
||||
if (filepath.find("config/") != std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Si contiene "data/" es candidato para el pack
|
||||
if (filepath.find("data/") != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto getPackPath(const std::string& asset_path) -> std::string {
|
||||
std::string pack_path = asset_path;
|
||||
|
||||
// Normalizar separadores de path a '/'
|
||||
std::ranges::replace(pack_path, '\\', '/');
|
||||
|
||||
// Remover prefijo "data/" si existe
|
||||
size_t data_pos = pack_path.find("data/");
|
||||
if (data_pos != std::string::npos) {
|
||||
pack_path = pack_path.substr(data_pos + 5); // +5 para saltar "data/"
|
||||
}
|
||||
|
||||
// Remover cualquier prefijo de path absoluto
|
||||
size_t last_data = pack_path.rfind("data/");
|
||||
if (last_data != std::string::npos) {
|
||||
pack_path = pack_path.substr(last_data + 5);
|
||||
}
|
||||
|
||||
return pack_path;
|
||||
}
|
||||
} // namespace ResourceHelper
|
||||
45
source/core/resources/resource_helper.hpp
Normal file
45
source/core/resources/resource_helper.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint> // Para uint8_t
|
||||
#include <filesystem> // Para remove, path
|
||||
#include <fstream> // Para basic_ofstream, basic_ios, basic_ostream::write, ios, ofstream
|
||||
#include <string> // Para string, basic_string, hash, operator+, to_string, __str_hash_base
|
||||
#include <vector> // Para vector
|
||||
|
||||
// Helper functions para integrar ResourceLoader con el sistema existente
|
||||
namespace ResourceHelper {
|
||||
// Inicializa ResourceLoader (llamar al inicio del programa)
|
||||
auto initializeResourceSystem(const std::string& pack_file = "resources.pack", bool enable_fallback = true) -> bool;
|
||||
|
||||
// Cierra ResourceLoader
|
||||
void shutdownResourceSystem();
|
||||
|
||||
// Carga un archivo usando ResourceLoader o fallback a filesystem
|
||||
auto loadFile(const std::string& filepath) -> std::vector<uint8_t>;
|
||||
|
||||
// Verifica si un archivo debería cargarse del pack vs filesystem
|
||||
auto shouldUseResourcePack(const std::string& filepath) -> bool;
|
||||
|
||||
// Convierte ruta Asset a ruta relativa para ResourceLoader
|
||||
auto getPackPath(const std::string& asset_path) -> std::string;
|
||||
|
||||
// Wrappea la carga de archivos para mantener compatibilidad
|
||||
template <typename T>
|
||||
auto loadResourceFile(const std::string& asset_path, T* (*loader_func)(const char*)) -> T* {
|
||||
auto data = loadFile(asset_path);
|
||||
if (data.empty()) {
|
||||
return loader_func(asset_path.c_str());
|
||||
}
|
||||
|
||||
// Crear archivo temporal para funciones que esperan path
|
||||
std::string temp_path = "/tmp/ccae_" + std::to_string(std::hash<std::string>{}(asset_path));
|
||||
std::ofstream temp_file(temp_path, std::ios::binary);
|
||||
temp_file.write(reinterpret_cast<const char*>(data.data()), data.size());
|
||||
temp_file.close();
|
||||
|
||||
T* result = loader_func(temp_path.c_str());
|
||||
std::filesystem::remove(temp_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace ResourceHelper
|
||||
133
source/core/resources/resource_loader.cpp
Normal file
133
source/core/resources/resource_loader.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
#include "resource_loader.hpp"
|
||||
|
||||
#include <algorithm> // Para replace
|
||||
#include <filesystem> // Para exists, path, recursive_directory_iterator, directory_entry, relative
|
||||
#include <fstream> // Para basic_ostream, basic_ifstream, operator<<, basic_ios, endl, ios, basic_istream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
|
||||
#include <iostream> // Para cerr, cout
|
||||
|
||||
#include "resource_pack.hpp" // Para ResourcePack
|
||||
|
||||
std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;
|
||||
|
||||
ResourceLoader::ResourceLoader()
|
||||
: resource_pack_(nullptr),
|
||||
fallback_to_files_(true) {}
|
||||
|
||||
auto ResourceLoader::getInstance() -> ResourceLoader& {
|
||||
if (!instance) {
|
||||
instance = std::unique_ptr<ResourceLoader>(new ResourceLoader());
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
ResourceLoader::~ResourceLoader() {
|
||||
shutdown();
|
||||
}
|
||||
|
||||
auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
|
||||
shutdown();
|
||||
|
||||
fallback_to_files_ = enable_fallback;
|
||||
pack_path_ = pack_file;
|
||||
|
||||
if (std::filesystem::exists(pack_file)) {
|
||||
resource_pack_ = new ResourcePack();
|
||||
if (resource_pack_->loadPack(pack_file)) {
|
||||
return true;
|
||||
}
|
||||
delete resource_pack_;
|
||||
resource_pack_ = nullptr;
|
||||
std::cerr << "Failed to load resource pack: " << pack_file << '\n';
|
||||
}
|
||||
|
||||
if (fallback_to_files_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::cerr << "Resource pack not found and fallback disabled: " << pack_file << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
void ResourceLoader::shutdown() {
|
||||
if (resource_pack_ != nullptr) {
|
||||
delete resource_pack_;
|
||||
resource_pack_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto ResourceLoader::loadResource(const std::string& filename) -> std::vector<uint8_t> {
|
||||
if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
|
||||
return resource_pack_->getResource(filename);
|
||||
}
|
||||
|
||||
if (fallback_to_files_) {
|
||||
return loadFromFile(filename);
|
||||
}
|
||||
|
||||
std::cerr << "Resource not found: " << filename << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
auto ResourceLoader::resourceExists(const std::string& filename) -> bool {
|
||||
if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fallback_to_files_) {
|
||||
std::string full_path = getDataPath(filename);
|
||||
return std::filesystem::exists(full_path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ResourceLoader::loadFromFile(const std::string& filename) -> std::vector<uint8_t> {
|
||||
std::string full_path = getDataPath(filename);
|
||||
|
||||
std::ifstream file(full_path, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file: " << full_path << '\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 << "Error: Could not read file: " << full_path << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
auto ResourceLoader::getDataPath(const std::string& filename) -> std::string {
|
||||
return "data/" + filename;
|
||||
}
|
||||
|
||||
auto ResourceLoader::getLoadedResourceCount() const -> size_t {
|
||||
if (resource_pack_ != nullptr) {
|
||||
return resource_pack_->getResourceCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ResourceLoader::getAvailableResources() const -> std::vector<std::string> {
|
||||
if (resource_pack_ != nullptr) {
|
||||
return resource_pack_->getResourceList();
|
||||
}
|
||||
|
||||
std::vector<std::string> result;
|
||||
if (fallback_to_files_ && std::filesystem::exists("data")) {
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string filename = std::filesystem::relative(entry.path(), "data").string();
|
||||
std::ranges::replace(filename, '\\', '/');
|
||||
result.push_back(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
39
source/core/resources/resource_loader.hpp
Normal file
39
source/core/resources/resource_loader.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <cstdint> // Para uint8_t
|
||||
#include <memory> // Para unique_ptr
|
||||
#include <string> // Para string, basic_string
|
||||
#include <vector> // Para vector
|
||||
|
||||
class ResourcePack;
|
||||
|
||||
class ResourceLoader {
|
||||
private:
|
||||
static std::unique_ptr<ResourceLoader> instance;
|
||||
ResourcePack* resource_pack_;
|
||||
std::string pack_path_;
|
||||
bool fallback_to_files_;
|
||||
|
||||
ResourceLoader();
|
||||
|
||||
public:
|
||||
static auto getInstance() -> ResourceLoader&;
|
||||
~ResourceLoader();
|
||||
|
||||
auto initialize(const std::string& pack_file, bool enable_fallback = true) -> bool;
|
||||
void shutdown();
|
||||
|
||||
auto loadResource(const std::string& filename) -> std::vector<uint8_t>;
|
||||
auto resourceExists(const std::string& filename) -> bool;
|
||||
|
||||
void setFallbackToFiles(bool enable) { fallback_to_files_ = enable; }
|
||||
[[nodiscard]] auto getFallbackToFiles() const -> bool { return fallback_to_files_; }
|
||||
|
||||
[[nodiscard]] auto getLoadedResourceCount() const -> size_t;
|
||||
[[nodiscard]] auto getAvailableResources() const -> std::vector<std::string>;
|
||||
|
||||
private:
|
||||
static auto loadFromFile(const std::string& filename) -> std::vector<uint8_t>;
|
||||
static auto getDataPath(const std::string& filename) -> std::string;
|
||||
};
|
||||
228
source/core/resources/resource_pack.cpp
Normal file
228
source/core/resources/resource_pack.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
#include "resource_pack.hpp"
|
||||
|
||||
#include <algorithm> // Para replace
|
||||
#include <array> // Para array
|
||||
#include <filesystem> // Para path, recursive_directory_iterator, directory_entry, exists, relative
|
||||
#include <fstream> // Para basic_ifstream, basic_ostream, basic_ofstream, operator<<, basic_ios, basic_istream::read, basic_ostream::write, endl, ios, basic_istream, ifstream, operator|, basic_istream::seekg, basic_istream::tellg, ofstream, streamsize
|
||||
#include <iostream> // Para cerr
|
||||
#include <utility> // Para pair
|
||||
|
||||
const std::string ResourcePack::DEFAULT_ENCRYPT_KEY = "CCAE_RESOURCES__2024";
|
||||
|
||||
ResourcePack::ResourcePack()
|
||||
: loaded_(false) {}
|
||||
|
||||
ResourcePack::~ResourcePack() {
|
||||
clear();
|
||||
}
|
||||
|
||||
auto ResourcePack::calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t {
|
||||
uint32_t checksum = 0x12345678;
|
||||
for (unsigned char i : data) {
|
||||
checksum = ((checksum << 5) + checksum) + i;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
void ResourcePack::encryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||
if (key.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
data[i] ^= key[i % key.length()];
|
||||
}
|
||||
}
|
||||
|
||||
void ResourcePack::decryptData(std::vector<uint8_t>& data, const std::string& key) {
|
||||
encryptData(data, key);
|
||||
}
|
||||
|
||||
auto ResourcePack::loadPack(const std::string& pack_file) -> bool {
|
||||
std::ifstream file(pack_file, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open pack file: " << pack_file << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<char, 4> header;
|
||||
file.read(header.data(), 4);
|
||||
if (std::string(header.data(), 4) != "CCAE") {
|
||||
std::cerr << "Error: Invalid pack file format" << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t version;
|
||||
file.read(reinterpret_cast<char*>(&version), sizeof(version));
|
||||
if (version != 1) {
|
||||
std::cerr << "Error: Unsupported pack version: " << version << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t resource_count;
|
||||
file.read(reinterpret_cast<char*>(&resource_count), sizeof(resource_count));
|
||||
|
||||
resources_.clear();
|
||||
resources_.reserve(resource_count);
|
||||
|
||||
for (uint32_t i = 0; i < resource_count; ++i) {
|
||||
uint32_t filename_length;
|
||||
file.read(reinterpret_cast<char*>(&filename_length), sizeof(filename_length));
|
||||
|
||||
std::string filename(filename_length, '\0');
|
||||
file.read(filename.data(), filename_length);
|
||||
|
||||
ResourceEntry entry;
|
||||
entry.filename = filename;
|
||||
file.read(reinterpret_cast<char*>(&entry.offset), sizeof(entry.offset));
|
||||
file.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
|
||||
file.read(reinterpret_cast<char*>(&entry.checksum), sizeof(entry.checksum));
|
||||
|
||||
resources_[filename] = entry;
|
||||
}
|
||||
|
||||
uint64_t data_size;
|
||||
file.read(reinterpret_cast<char*>(&data_size), sizeof(data_size));
|
||||
|
||||
data_.resize(data_size);
|
||||
file.read(reinterpret_cast<char*>(data_.data()), data_size);
|
||||
|
||||
decryptData(data_, DEFAULT_ENCRYPT_KEY);
|
||||
|
||||
loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ResourcePack::savePack(const std::string& pack_file) -> bool {
|
||||
std::ofstream file(pack_file, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not create pack file: " << pack_file << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write("CCAE", 4);
|
||||
|
||||
uint32_t version = 1;
|
||||
file.write(reinterpret_cast<const char*>(&version), sizeof(version));
|
||||
|
||||
auto resource_count = static_cast<uint32_t>(resources_.size());
|
||||
file.write(reinterpret_cast<const char*>(&resource_count), sizeof(resource_count));
|
||||
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
auto filename_length = static_cast<uint32_t>(filename.length());
|
||||
file.write(reinterpret_cast<const char*>(&filename_length), sizeof(filename_length));
|
||||
file.write(filename.c_str(), filename_length);
|
||||
|
||||
file.write(reinterpret_cast<const char*>(&entry.offset), sizeof(entry.offset));
|
||||
file.write(reinterpret_cast<const char*>(&entry.size), sizeof(entry.size));
|
||||
file.write(reinterpret_cast<const char*>(&entry.checksum), sizeof(entry.checksum));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> encrypted_data = data_;
|
||||
encryptData(encrypted_data, DEFAULT_ENCRYPT_KEY);
|
||||
|
||||
uint64_t data_size = encrypted_data.size();
|
||||
file.write(reinterpret_cast<const char*>(&data_size), sizeof(data_size));
|
||||
file.write(reinterpret_cast<const char*>(encrypted_data.data()), data_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ResourcePack::addFile(const std::string& filename, const std::string& filepath) -> bool {
|
||||
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file: " << filepath << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
std::streamsize file_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<uint8_t> file_data(file_size);
|
||||
if (!file.read(reinterpret_cast<char*>(file_data.data()), file_size)) {
|
||||
std::cerr << "Error: Could not read file: " << filepath << '\n';
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceEntry entry;
|
||||
entry.filename = filename;
|
||||
entry.offset = data_.size();
|
||||
entry.size = file_data.size();
|
||||
entry.checksum = calculateChecksum(file_data);
|
||||
|
||||
data_.insert(data_.end(), file_data.begin(), file_data.end());
|
||||
resources_[filename] = entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ResourcePack::addDirectory(const std::string& directory) -> bool {
|
||||
if (!std::filesystem::exists(directory)) {
|
||||
std::cerr << "Error: Directory does not exist: " << directory << '\n';
|
||||
return false; // NOLINT(readability-simplify-boolean-expr)
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string filepath = entry.path().string();
|
||||
std::string filename = std::filesystem::relative(entry.path(), directory).string();
|
||||
|
||||
std::ranges::replace(filename, '\\', '/');
|
||||
|
||||
if (!addFile(filename, filepath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ResourcePack::getResource(const std::string& filename) -> std::vector<uint8_t> {
|
||||
auto it = resources_.find(filename);
|
||||
if (it == resources_.end()) {
|
||||
std::cerr << "Error: Resource not found: " << filename << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
const ResourceEntry& entry = it->second;
|
||||
if (entry.offset + entry.size > data_.size()) {
|
||||
std::cerr << "Error: Invalid resource data: " << filename << '\n';
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<uint8_t> result(data_.begin() + entry.offset,
|
||||
data_.begin() + entry.offset + entry.size);
|
||||
|
||||
uint32_t checksum = calculateChecksum(result);
|
||||
if (checksum != entry.checksum) {
|
||||
std::cerr << "Warning: Checksum mismatch for resource: " << filename << '\n';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto ResourcePack::hasResource(const std::string& filename) const -> bool {
|
||||
return resources_.contains(filename);
|
||||
}
|
||||
|
||||
void ResourcePack::clear() {
|
||||
resources_.clear();
|
||||
data_.clear();
|
||||
loaded_ = false;
|
||||
}
|
||||
|
||||
auto ResourcePack::getResourceCount() const -> size_t {
|
||||
return resources_.size();
|
||||
}
|
||||
|
||||
auto ResourcePack::getResourceList() const -> std::vector<std::string> {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(resources_.size());
|
||||
|
||||
for (const auto& [filename, entry] : resources_) {
|
||||
result.push_back(filename);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
44
source/core/resources/resource_pack.hpp
Normal file
44
source/core/resources/resource_pack.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // Para size_t
|
||||
#include <cstdint> // Para uint8_t, uint32_t, uint64_t
|
||||
#include <string> // Para string, basic_string, hash
|
||||
#include <unordered_map> // Para unordered_map
|
||||
#include <vector> // Para vector
|
||||
|
||||
struct ResourceEntry {
|
||||
std::string filename;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint32_t checksum;
|
||||
};
|
||||
|
||||
class ResourcePack {
|
||||
private:
|
||||
std::unordered_map<std::string, ResourceEntry> resources_;
|
||||
std::vector<uint8_t> data_;
|
||||
bool loaded_;
|
||||
|
||||
static auto calculateChecksum(const std::vector<uint8_t>& data) -> uint32_t;
|
||||
static void encryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||
static void decryptData(std::vector<uint8_t>& data, const std::string& key);
|
||||
|
||||
public:
|
||||
ResourcePack();
|
||||
~ResourcePack();
|
||||
|
||||
auto loadPack(const std::string& pack_file) -> bool;
|
||||
auto savePack(const std::string& pack_file) -> bool;
|
||||
|
||||
auto addFile(const std::string& filename, const std::string& filepath) -> bool;
|
||||
auto addDirectory(const std::string& directory) -> bool;
|
||||
|
||||
[[nodiscard]] auto getResource(const std::string& filename) -> std::vector<uint8_t>;
|
||||
[[nodiscard]] auto hasResource(const std::string& filename) const -> bool;
|
||||
|
||||
void clear();
|
||||
[[nodiscard]] auto getResourceCount() const -> size_t;
|
||||
[[nodiscard]] auto getResourceList() const -> std::vector<std::string>;
|
||||
|
||||
static const std::string DEFAULT_ENCRYPT_KEY;
|
||||
};
|
||||
Reference in New Issue
Block a user