neteja clang-tidy

This commit is contained in:
2026-05-16 22:47:41 +02:00
parent 17341f923d
commit a903343385
40 changed files with 1246 additions and 1384 deletions
+31 -30
View File
@@ -8,6 +8,7 @@
// Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp). // Implementación de stb_vorbis (debe estar ANTES de incluir jail_audio.hpp).
// clang-format off // clang-format off
#undef STB_VORBIS_HEADER_ONLY #undef STB_VORBIS_HEADER_ONLY
// NOLINTNEXTLINE(bugprone-suspicious-include) — stb_vorbis és single-file: el TU principal inclou el .c per portar la implementació.
#include "external/stb_vorbis.c" #include "external/stb_vorbis.c"
// stb_vorbis.c filtra les macros L, C i R (i PLAYBACK_*) al TU. Les netegem // stb_vorbis.c filtra les macros L, C i R (i PLAYBACK_*) al TU. Les netegem
// perquè xocarien amb noms de paràmetres de plantilla en altres headers. // perquè xocarien amb noms de paràmetres de plantilla en altres headers.
@@ -43,15 +44,15 @@ Audio::Audio() { initSDLAudio(); }
// Destructor // Destructor
Audio::~Audio() { Audio::~Audio() {
JA_Quit(); Ja::quit();
} }
// Método principal // Método principal
void Audio::update() { void Audio::update() {
JA_Update(); Ja::update();
// Sincronizar estado: detectar cuando la música se para (ej. fade-out completado) // Sincronizar estado: detectar cuando la música se para (ej. fade-out completado)
if (instance && instance->music_.state == MusicState::PLAYING && JA_GetMusicState() != JA_MUSIC_PLAYING) { if (instance != nullptr && instance->music_.state == MusicState::PLAYING && Ja::getMusicState() != Ja::MusicState::PLAYING) {
instance->music_.state = MusicState::STOPPED; instance->music_.state = MusicState::STOPPED;
} }
} }
@@ -65,18 +66,18 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
return; return;
} }
if (!music_enabled_) return; if (!music_enabled_) { return; }
auto* resource = AudioResource::getMusic(name); auto* resource = AudioResource::getMusic(name);
if (resource == nullptr) return; if (resource == nullptr) { return; }
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(resource, crossfade_ms, loop); Ja::crossfadeMusic(resource, crossfade_ms, loop);
} else { } else {
if (music_.state == MusicState::PLAYING) { if (music_.state == MusicState::PLAYING) {
JA_StopMusic(); Ja::stopMusic();
} }
JA_PlayMusic(resource, loop); Ja::playMusic(resource, loop);
} }
music_.name = name; music_.name = name;
@@ -85,16 +86,16 @@ void Audio::playMusic(const std::string& name, const int loop, const int crossfa
} }
// Reproduce la música por puntero (con crossfade opcional) // Reproduce la música por puntero (con crossfade opcional)
void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms) { void Audio::playMusic(Ja::Music* music, const int loop, const int crossfade_ms) {
if (!music_enabled_ || music == nullptr) return; if (!music_enabled_ || music == nullptr) { return; }
if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) { if (crossfade_ms > 0 && music_.state == MusicState::PLAYING) {
JA_CrossfadeMusic(music, crossfade_ms, loop); Ja::crossfadeMusic(music, crossfade_ms, loop);
} else { } else {
if (music_.state == MusicState::PLAYING) { if (music_.state == MusicState::PLAYING) {
JA_StopMusic(); Ja::stopMusic();
} }
JA_PlayMusic(music, loop); Ja::playMusic(music, loop);
} }
music_.name.clear(); // nom desconegut quan es passa per punter music_.name.clear(); // nom desconegut quan es passa per punter
@@ -105,7 +106,7 @@ void Audio::playMusic(JA_Music_t* music, const int loop, const int crossfade_ms)
// Pausa la música // Pausa la música
void Audio::pauseMusic() { void Audio::pauseMusic() {
if (music_enabled_ && music_.state == MusicState::PLAYING) { if (music_enabled_ && music_.state == MusicState::PLAYING) {
JA_PauseMusic(); Ja::pauseMusic();
music_.state = MusicState::PAUSED; music_.state = MusicState::PAUSED;
} }
} }
@@ -113,7 +114,7 @@ void Audio::pauseMusic() {
// Continua la música pausada // Continua la música pausada
void Audio::resumeMusic() { void Audio::resumeMusic() {
if (music_enabled_ && music_.state == MusicState::PAUSED) { if (music_enabled_ && music_.state == MusicState::PAUSED) {
JA_ResumeMusic(); Ja::resumeMusic();
music_.state = MusicState::PLAYING; music_.state = MusicState::PLAYING;
} }
} }
@@ -121,7 +122,7 @@ void Audio::resumeMusic() {
// Detiene la música // Detiene la música
void Audio::stopMusic() { void Audio::stopMusic() {
if (music_enabled_) { if (music_enabled_) {
JA_StopMusic(); Ja::stopMusic();
music_.state = MusicState::STOPPED; music_.state = MusicState::STOPPED;
} }
} }
@@ -129,42 +130,42 @@ void Audio::stopMusic() {
// Reproduce un sonido por nombre // Reproduce un sonido por nombre
void Audio::playSound(const std::string& name, Group group) const { void Audio::playSound(const std::string& name, Group group) const {
if (sound_enabled_) { if (sound_enabled_) {
JA_PlaySound(AudioResource::getSound(name), 0, static_cast<int>(group)); Ja::playSound(AudioResource::getSound(name), 0, static_cast<int>(group));
} }
} }
// Reproduce un sonido por puntero directo // Reproduce un sonido por puntero directo
void Audio::playSound(JA_Sound_t* sound, Group group) const { void Audio::playSound(Ja::Sound* sound, Group group) const {
if (sound_enabled_ && sound != nullptr) { if (sound_enabled_ && sound != nullptr) {
JA_PlaySound(sound, 0, static_cast<int>(group)); Ja::playSound(sound, 0, static_cast<int>(group));
} }
} }
// Detiene todos los sonidos // Detiene todos los sonidos
void Audio::stopAllSounds() const { void Audio::stopAllSounds() const {
if (sound_enabled_) { if (sound_enabled_) {
JA_StopChannel(-1); Ja::stopChannel(-1);
} }
} }
// Realiza un fundido de salida de la música // Realiza un fundido de salida de la música
void Audio::fadeOutMusic(int milliseconds) const { void Audio::fadeOutMusic(int milliseconds) const {
if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) { if (music_enabled_ && getRealMusicState() == MusicState::PLAYING) {
JA_FadeOutMusic(milliseconds); Ja::fadeOutMusic(milliseconds);
} }
} }
// Consulta directamente el estado real de la música en jailaudio // Consulta directamente el estado real de la música en jailaudio
auto Audio::getRealMusicState() -> MusicState { auto Audio::getRealMusicState() -> MusicState {
JA_Music_state ja_state = JA_GetMusicState(); Ja::MusicState ja_state = Ja::getMusicState();
switch (ja_state) { switch (ja_state) {
case JA_MUSIC_PLAYING: case Ja::MusicState::PLAYING:
return MusicState::PLAYING; return MusicState::PLAYING;
case JA_MUSIC_PAUSED: case Ja::MusicState::PAUSED:
return MusicState::PAUSED; return MusicState::PAUSED;
case JA_MUSIC_STOPPED: case Ja::MusicState::STOPPED:
case JA_MUSIC_INVALID: case Ja::MusicState::INVALID:
case JA_MUSIC_DISABLED: case Ja::MusicState::DISABLED:
default: default:
return MusicState::STOPPED; return MusicState::STOPPED;
} }
@@ -175,7 +176,7 @@ void Audio::setSoundVolume(float sound_volume, Group group) const {
if (sound_enabled_) { if (sound_enabled_) {
sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME); sound_volume = std::clamp(sound_volume, MIN_VOLUME, MAX_VOLUME);
const float CONVERTED_VOLUME = sound_volume * Options::audio.volume; const float CONVERTED_VOLUME = sound_volume * Options::audio.volume;
JA_SetSoundVolume(CONVERTED_VOLUME, static_cast<int>(group)); Ja::setSoundVolume(CONVERTED_VOLUME, static_cast<int>(group));
} }
} }
@@ -184,7 +185,7 @@ void Audio::setMusicVolume(float music_volume) const {
if (music_enabled_) { if (music_enabled_) {
music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME); music_volume = std::clamp(music_volume, MIN_VOLUME, MAX_VOLUME);
const float CONVERTED_VOLUME = music_volume * Options::audio.volume; const float CONVERTED_VOLUME = music_volume * Options::audio.volume;
JA_SetMusicVolume(CONVERTED_VOLUME); Ja::setMusicVolume(CONVERTED_VOLUME);
} }
} }
@@ -206,7 +207,7 @@ void Audio::initSDLAudio() {
if (!SDL_Init(SDL_INIT_AUDIO)) { if (!SDL_Init(SDL_INIT_AUDIO)) {
std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n'; std::cout << "SDL_AUDIO could not initialize! SDL Error: " << SDL_GetError() << '\n';
} else { } else {
JA_Init(FREQUENCY, SDL_AUDIO_S16LE, 2); Ja::init(FREQUENCY, SDL_AUDIO_S16LE, 2);
enable(Options::audio.enabled); enable(Options::audio.enabled);
} }
} }
+10 -5
View File
@@ -1,8 +1,13 @@
#pragma once #pragma once
#include <cmath> // Para std::lround
#include <cstdint> // Para int8_t, uint8_t #include <cstdint> // Para int8_t, uint8_t
#include <string> // Para string #include <string> // Para string
#include <utility> // Para move
namespace Ja {
struct Music;
struct Sound;
} // namespace Ja
// --- Clase Audio: gestor de audio (singleton) --- // --- Clase Audio: gestor de audio (singleton) ---
// Implementació canònica, byte-idèntica entre projectes. // Implementació canònica, byte-idèntica entre projectes.
@@ -42,7 +47,7 @@ class Audio {
// --- Control de música --- // --- Control de música ---
void playMusic(const std::string& name, int loop = -1, int crossfade_ms = 0); // Reproducir música por nombre (con crossfade opcional) void playMusic(const std::string& name, int loop = -1, int crossfade_ms = 0); // Reproducir música por nombre (con crossfade opcional)
void playMusic(struct JA_Music_t* music, int loop = -1, int crossfade_ms = 0); // Reproducir música por puntero (con crossfade opcional) void playMusic(Ja::Music* music, int loop = -1, int crossfade_ms = 0); // Reproducir música por puntero (con crossfade opcional)
void pauseMusic(); // Pausar reproducción de música void pauseMusic(); // Pausar reproducción de música
void resumeMusic(); // Continua la música pausada void resumeMusic(); // Continua la música pausada
void stopMusic(); // Detener completamente la música void stopMusic(); // Detener completamente la música
@@ -50,7 +55,7 @@ class Audio {
// --- Control de sonidos --- // --- Control de sonidos ---
void playSound(const std::string& name, Group group = Group::GAME) const; // Reproducir sonido puntual por nombre void playSound(const std::string& name, Group group = Group::GAME) const; // Reproducir sonido puntual por nombre
void playSound(struct JA_Sound_t* sound, Group group = Group::GAME) const; // Reproducir sonido puntual por puntero void playSound(Ja::Sound* sound, Group group = Group::GAME) const; // Reproducir sonido puntual por puntero
void stopAllSounds() const; // Detener todos los sonidos void stopAllSounds() const; // Detener todos los sonidos
// --- Control de volumen (API interna: float 0.0..1.0) --- // --- Control de volumen (API interna: float 0.0..1.0) ---
@@ -59,8 +64,8 @@ class Audio {
// --- Helpers de conversió per a la capa de presentació --- // --- Helpers de conversió per a la capa de presentació ---
// UI (menús, notificacions) manega enters 0..100; internament viu float 0..1. // UI (menús, notificacions) manega enters 0..100; internament viu float 0..1.
static constexpr auto toPercent(float volume) -> int { static auto toPercent(float volume) -> int {
return static_cast<int>(volume * 100.0F + 0.5F); return static_cast<int>(std::lround(volume * 100.0F));
} }
static constexpr auto fromPercent(int percent) -> float { static constexpr auto fromPercent(int percent) -> float {
return static_cast<float>(percent) / 100.0F; return static_cast<float>(percent) / 100.0F;
+2 -2
View File
@@ -3,11 +3,11 @@
#include "core/resources/resource.hpp" #include "core/resources/resource.hpp"
namespace AudioResource { namespace AudioResource {
JA_Music_t* getMusic(const std::string& name) { auto getMusic(const std::string& name) -> Ja::Music* {
return Resource::get()->getMusic(name); return Resource::get()->getMusic(name);
} }
JA_Sound_t* getSound(const std::string& name) { auto getSound(const std::string& name) -> Ja::Sound* {
return Resource::get()->getSound(name); return Resource::get()->getSound(name);
} }
} // namespace AudioResource } // namespace AudioResource
+8 -6
View File
@@ -1,17 +1,19 @@
#pragma once #pragma once
// --- Audio Resource Adapter --- // --- Audio Resource Adapter ---
// Aquest fitxer exposa una interfície comuna a Audio per obtenir JA_Music_t* / // Aquest fitxer exposa una interfície comuna a Audio per obtenir Ja::Music* /
// JA_Sound_t* per nom. Cada projecte la implementa en audio_adapter.cpp // Ja::Sound* per nom. Cada projecte la implementa en audio_adapter.cpp
// delegant al seu singleton de recursos (Resource::get(), Resource::Cache::get(), // delegant al seu singleton de recursos (Resource::get(), Resource::Cache::get(),
// etc.). Això permet que audio.hpp/audio.cpp siguin idèntics entre projectes. // etc.). Això permet que audio.hpp/audio.cpp siguin idèntics entre projectes.
#include <string> // Para string #include <string> // Para string
struct JA_Music_t; namespace Ja {
struct JA_Sound_t; struct Music;
struct Sound;
} // namespace Ja
namespace AudioResource { namespace AudioResource {
JA_Music_t* getMusic(const std::string& name); auto getMusic(const std::string& name) -> Ja::Music*;
JA_Sound_t* getSound(const std::string& name); auto getSound(const std::string& name) -> Ja::Sound*;
} // namespace AudioResource } // namespace AudioResource
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -5,7 +5,6 @@
#include <memory> // Para unique_ptr, allocator, shared_ptr, operator==, make_unique #include <memory> // Para unique_ptr, allocator, shared_ptr, operator==, make_unique
#include "core/input/input.hpp" // Para Input #include "core/input/input.hpp" // Para Input
#include "core/input/input_types.hpp" // Para InputAction
#include "core/locale/lang.hpp" // Para getText #include "core/locale/lang.hpp" // Para getText
#include "core/resources/resource.hpp" // Para Resource #include "core/resources/resource.hpp" // Para Resource
#include "game/options.hpp" // Para Gamepad #include "game/options.hpp" // Para Gamepad
@@ -17,9 +16,9 @@ DefineButtons::DefineButtons()
: input_(Input::get()) { : input_(Input::get()) {
clearButtons(); clearButtons();
const auto gamepads = input_->getGamepads(); const auto GAMEPADS = input_->getGamepads();
controller_names_.reserve(gamepads.size()); controller_names_.reserve(GAMEPADS.size());
std::ranges::transform(gamepads, std::back_inserter(controller_names_), Input::getControllerName); std::ranges::transform(GAMEPADS, std::back_inserter(controller_names_), Input::getControllerName);
// Crear la ventana de mensaje // Crear la ventana de mensaje
WindowMessage::Config config(param.service_menu.window_message); WindowMessage::Config config(param.service_menu.window_message);
+4 -6
View File
@@ -2,14 +2,12 @@
#include <algorithm> // Para __any_of_fn, any_of #include <algorithm> // Para __any_of_fn, any_of
#include <functional> // Para function #include <functional> // Para function
#include <iterator> // Para pair
#include <string> // Para basic_string, operator+, allocator, char_traits, string, to_string #include <string> // Para basic_string, operator+, allocator, char_traits, string, to_string
#include <utility> // Para pair #include <utility> // Para pair
#include <vector> // Para vector #include <vector> // Para vector
#include "core/audio/audio.hpp" // Para Audio #include "core/audio/audio.hpp" // Para Audio
#include "core/input/input.hpp" // Para Input #include "core/input/input.hpp" // Para Input
#include "core/input/input_types.hpp" // Para InputAction
#include "core/locale/lang.hpp" // Para getText, getLangFile, getLangName, getNextLangCode, loadFromFile #include "core/locale/lang.hpp" // Para getText, getLangFile, getLangName, getNextLangCode, loadFromFile
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/system/section.hpp" // Para Name, name, Options, options, AttractMode, attract_mode #include "core/system/section.hpp" // Para Name, name, Options, options, AttractMode, attract_mode
@@ -84,12 +82,12 @@ namespace GlobalInputs {
void nextPreset() { void nextPreset() {
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) { if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
Screen::nextCrtPiPreset(); Screen::nextCrtPiPreset();
const std::string name = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name; const std::string NAME = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
Notifier::get()->show({"CrtPi: " + name}); Notifier::get()->show({"CrtPi: " + NAME});
} else { } else {
Screen::nextPostFXPreset(); Screen::nextPostFXPreset();
const std::string name = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name; const std::string NAME = Options::postfx_presets.empty() ? "" : Options::postfx_presets.at(static_cast<size_t>(Options::video.shader.current_postfx_preset)).name;
Notifier::get()->show({"PostFX: " + name}); Notifier::get()->show({"PostFX: " + NAME});
} }
} }
+6 -5
View File
@@ -157,15 +157,15 @@ auto Input::getNumGamepads() const -> int { return gamepads_.size(); }
// Obtiene el gamepad a partir de un event.id // Obtiene el gamepad a partir de un event.id
auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepad(SDL_JoystickID id) const -> std::shared_ptr<Input::Gamepad> {
const auto it = std::ranges::find_if(gamepads_, const auto IT = std::ranges::find_if(gamepads_,
[id](const auto& gamepad) { return gamepad->instance_id == id; }); [id](const auto& gamepad) { return gamepad->instance_id == id; });
return it != gamepads_.end() ? *it : nullptr; return IT != gamepads_.end() ? *IT : nullptr;
} }
auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> { auto Input::getGamepadByName(const std::string& name) const -> std::shared_ptr<Input::Gamepad> {
const auto it = std::ranges::find_if(gamepads_, const auto IT = std::ranges::find_if(gamepads_,
[&name](const auto& gamepad) { return gamepad && gamepad->name == name; }); [&name](const auto& gamepad) { return gamepad && gamepad->name == name; });
return it != gamepads_.end() ? *it : nullptr; return IT != gamepads_.end() ? *IT : nullptr;
} }
// Obtiene el SDL_GamepadButton asignado a un action // Obtiene el SDL_GamepadButton asignado a un action
@@ -360,9 +360,10 @@ auto Input::handleEvent(const SDL_Event& event) -> std::string {
return addGamepad(event.gdevice.which); return addGamepad(event.gdevice.which);
case SDL_EVENT_GAMEPAD_REMOVED: case SDL_EVENT_GAMEPAD_REMOVED:
return removeGamepad(event.gdevice.which); return removeGamepad(event.gdevice.which);
} default:
return {}; return {};
} }
}
auto Input::addGamepad(int device_index) -> std::string { auto Input::addGamepad(int device_index) -> std::string {
installWebStandardMapping(device_index); installWebStandardMapping(device_index);
+2 -3
View File
@@ -6,7 +6,6 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <unordered_map> // Para unordered_map #include <unordered_map> // Para unordered_map
#include <utility> // Para pair
#include <vector> // Para vector #include <vector> // Para vector
#include "core/input/gamepad_config_manager.hpp" // for GamepadConfig (ptr only), GamepadConfigs #include "core/input/gamepad_config_manager.hpp" // for GamepadConfig (ptr only), GamepadConfigs
@@ -109,8 +108,8 @@ class Input {
// Evita nombres como "Retroid Controller (vendor: 1001) ..." en las notificaciones. // Evita nombres como "Retroid Controller (vendor: 1001) ..." en las notificaciones.
static auto trimName(const char* raw) -> std::string { static auto trimName(const char* raw) -> std::string {
std::string s(raw != nullptr ? raw : ""); std::string s(raw != nullptr ? raw : "");
const auto pos = s.find_first_of("(["); const auto POS = s.find_first_of("([");
if (pos != std::string::npos) { s.erase(pos); } if (POS != std::string::npos) { s.erase(POS); }
while (!s.empty() && s.back() == ' ') { s.pop_back(); } while (!s.empty() && s.back() == ' ') { s.pop_back(); }
return s; return s;
} }
+2 -1
View File
@@ -2,11 +2,12 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <cstdint>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
// --- Enums --- // --- Enums ---
enum class InputAction : int { // Acciones de entrada posibles en el juego enum class InputAction : std::uint8_t { // Acciones de entrada posibles en el juego
// Inputs de movimiento // Inputs de movimiento
UP, UP,
DOWN, DOWN,
+11 -11
View File
@@ -14,7 +14,7 @@
#include "game/gameplay/difficulty.hpp" // Para Difficulty #include "game/gameplay/difficulty.hpp" // Para Difficulty
#include "game/options.hpp" // Para SettingsOpt... #include "game/options.hpp" // Para SettingsOpt...
using json = nlohmann::json; using Json = nlohmann::json;
namespace Lang { namespace Lang {
std::unordered_map<std::string, std::string> texts; std::unordered_map<std::string, std::string> texts;
@@ -33,12 +33,12 @@ namespace Lang {
auto resource_data = ResourceHelper::loadFile(file_path); auto resource_data = ResourceHelper::loadFile(file_path);
try { try {
json j; Json j;
if (!resource_data.empty()) { if (!resource_data.empty()) {
// Cargar desde datos del pack // Cargar desde datos del pack
std::string content(resource_data.begin(), resource_data.end()); std::string content(resource_data.begin(), resource_data.end());
j = json::parse(content); j = Json::parse(content);
} else { } else {
// Fallback a filesystem directo // Fallback a filesystem directo
std::ifstream rfile(file_path); std::ifstream rfile(file_path);
@@ -81,23 +81,23 @@ namespace Lang {
// Obtiene un idioma del vector de idiomas a partir de un código // Obtiene un idioma del vector de idiomas a partir de un código
auto getLanguage(Code code) -> Language { auto getLanguage(Code code) -> Language {
const auto it = std::ranges::find_if(languages, const auto IT = std::ranges::find_if(languages,
[code](const auto& lang) { return lang.code == code; }); [code](const auto& lang) { return lang.code == code; });
return it != languages.end() ? *it : languages[0]; return IT != languages.end() ? *IT : languages[0];
} }
// Devuelve el código de un idioma a partir de un nombre // Devuelve el código de un idioma a partir de un nombre
auto getCodeFromName(const std::string& name) -> Code { auto getCodeFromName(const std::string& name) -> Code {
const auto it = std::ranges::find_if(languages, const auto IT = std::ranges::find_if(languages,
[&name](const auto& lang) { return lang.name == name; }); [&name](const auto& lang) { return lang.name == name; });
return it != languages.end() ? it->code : languages[0].code; return IT != languages.end() ? IT->code : languages[0].code;
} }
// Devuelve el nombre de un idioma a partir de un código // Devuelve el nombre de un idioma a partir de un código
auto getNameFromCode(Code code) -> std::string { auto getNameFromCode(Code code) -> std::string {
const auto it = std::ranges::find_if(languages, const auto IT = std::ranges::find_if(languages,
[code](const auto& lang) { return lang.code == code; }); [code](const auto& lang) { return lang.code == code; });
return it != languages.end() ? it->name : languages[0].name; return IT != languages.end() ? IT->name : languages[0].name;
} }
// Actualiza los nombres de los idiomas // Actualiza los nombres de los idiomas
@@ -144,9 +144,9 @@ namespace Lang {
// Obtiene una fichero a partir de un lang::Code // Obtiene una fichero a partir de un lang::Code
auto getLanguageFileName(Lang::Code code) -> std::string { auto getLanguageFileName(Lang::Code code) -> std::string {
const auto it = std::ranges::find_if(languages, const auto IT = std::ranges::find_if(languages,
[code](const auto& lang) { return lang.code == code; }); [code](const auto& lang) { return lang.code == code; });
const auto& file = (it != languages.end()) ? it->file_name : languages[0].file_name; const auto& file = (IT != languages.end()) ? IT->file_name : languages[0].file_name;
return Asset::get()->getPath(file); return Asset::get()->getPath(file);
} }
+2 -1
View File
@@ -1,12 +1,13 @@
#pragma once #pragma once
#include <cstdint> // Para std::uint8_t
#include <string> // Para string, basic_string #include <string> // Para string, basic_string
#include <utility> // Para move #include <utility> // Para move
// --- Namespace Lang: gestión de idiomas y textos --- // --- Namespace Lang: gestión de idiomas y textos ---
namespace Lang { namespace Lang {
// --- Enums --- // --- Enums ---
enum class Code : int { enum class Code : std::uint8_t {
SPANISH = 0, // Español SPANISH = 0, // Español
VALENCIAN = 1, // Valenciano VALENCIAN = 1, // Valenciano
ENGLISH = 2 // Inglés ENGLISH = 2 // Inglés
+14 -14
View File
@@ -29,10 +29,10 @@ Background::Background(float total_progress_to_complete)
moon_texture_(Resource::get()->getTexture("game_moon.png")), moon_texture_(Resource::get()->getTexture("game_moon.png")),
grass_sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("game_grass.png"), Resource::get()->getAnimation("game_grass.ani"))), grass_sprite_(std::make_unique<AnimatedSprite>(Resource::get()->getTexture("game_grass.png"), Resource::get()->getAnimation("game_grass.ani"))),
total_progress_to_complete_(total_progress_to_complete), TOTAL_PROGRESS_TO_COMPLETE(total_progress_to_complete),
progress_per_stage_(total_progress_to_complete_ / STAGES), PROGRESS_PER_STAGE(TOTAL_PROGRESS_TO_COMPLETE / STAGES),
sun_completion_progress_(total_progress_to_complete_ * SUN_COMPLETION_FACTOR), SUM_COMPLETION_PROGRESS(TOTAL_PROGRESS_TO_COMPLETE * SUN_COMPLETION_FACTOR),
minimum_completed_progress_(total_progress_to_complete_ * MINIMUM_COMPLETED_PROGRESS_PERCENTAGE), MINIMUM_COMPLETED_PROGRESS(TOTAL_PROGRESS_TO_COMPLETE * MINIMUM_COMPLETED_PROGRESS_PERCENTAGE),
rect_(SDL_FRect{.x = 0, .y = 0, .w = static_cast<float>(gradients_texture_->getWidth() / 2), .h = static_cast<float>(gradients_texture_->getHeight() / 2)}), rect_(SDL_FRect{.x = 0, .y = 0, .w = static_cast<float>(gradients_texture_->getWidth() / 2), .h = static_cast<float>(gradients_texture_->getHeight() / 2)}),
src_rect_({.x = 0, .y = 0, .w = 320, .h = 240}), src_rect_({.x = 0, .y = 0, .w = 320, .h = 240}),
@@ -167,7 +167,7 @@ void Background::incrementProgress(float amount) {
if (state_ == State::NORMAL) { if (state_ == State::NORMAL) {
float old_progress = progress_; float old_progress = progress_;
progress_ += amount; progress_ += amount;
progress_ = std::min(progress_, total_progress_to_complete_); progress_ = std::min(progress_, TOTAL_PROGRESS_TO_COMPLETE);
// Notifica el cambio si hay callback y el progreso cambió // Notifica el cambio si hay callback y el progreso cambió
if (progress_callback_ && progress_ != old_progress) { if (progress_callback_ && progress_ != old_progress) {
@@ -179,7 +179,7 @@ void Background::incrementProgress(float amount) {
// Establece la progresión absoluta // Establece la progresión absoluta
void Background::setProgress(float absolute_progress) { void Background::setProgress(float absolute_progress) {
float old_progress = progress_; float old_progress = progress_;
progress_ = std::clamp(absolute_progress, 0.0F, total_progress_to_complete_); progress_ = std::clamp(absolute_progress, 0.0F, TOTAL_PROGRESS_TO_COMPLETE);
// Notifica el cambio si hay callback y el progreso cambió // Notifica el cambio si hay callback y el progreso cambió
if (progress_callback_ && progress_ != old_progress) { if (progress_callback_ && progress_ != old_progress) {
@@ -282,27 +282,27 @@ void Background::updateProgression(float delta_time) {
float eased_t = easeOutCubic(static_cast<double>(t)); float eased_t = easeOutCubic(static_cast<double>(t));
// Interpolación desde progreso inicial hasta mínimo // Interpolación desde progreso inicial hasta mínimo
float progress_range = completion_initial_progress_ - minimum_completed_progress_; float progress_range = completion_initial_progress_ - MINIMUM_COMPLETED_PROGRESS;
progress_ = completion_initial_progress_ - (progress_range * eased_t); progress_ = completion_initial_progress_ - (progress_range * eased_t);
} else { } else {
// Transición completada, fijar al valor mínimo // Transición completada, fijar al valor mínimo
progress_ = minimum_completed_progress_; progress_ = MINIMUM_COMPLETED_PROGRESS;
} }
} }
// Calcula la transición de los diferentes fondos // Calcula la transición de los diferentes fondos
const float GRADIENT_NUMBER_FLOAT = std::min(progress_ / progress_per_stage_, 3.0F); const float GRADIENT_NUMBER_FLOAT = std::min(progress_ / PROGRESS_PER_STAGE, 3.0F);
const float PERCENT = GRADIENT_NUMBER_FLOAT - static_cast<int>(GRADIENT_NUMBER_FLOAT); const float PERCENT = GRADIENT_NUMBER_FLOAT - static_cast<int>(GRADIENT_NUMBER_FLOAT);
gradient_number_ = static_cast<size_t>(GRADIENT_NUMBER_FLOAT); gradient_number_ = static_cast<size_t>(GRADIENT_NUMBER_FLOAT);
transition_ = PERCENT; transition_ = PERCENT;
// Calcula la posición del sol // Calcula la posición del sol
const float SUN_PROGRESSION = std::min(progress_ / sun_completion_progress_, 1.0F); const float SUN_PROGRESSION = std::min(progress_ / SUM_COMPLETION_PROGRESS, 1.0F);
sun_index_ = static_cast<size_t>(SUN_PROGRESSION * (sun_path_.size() - 1)); sun_index_ = static_cast<size_t>(SUN_PROGRESSION * (sun_path_.size() - 1));
// Calcula la posición de la luna // Calcula la posición de la luna
const float MOON_PROGRESSION = std::min(progress_ / total_progress_to_complete_, 1.0F); const float MOON_PROGRESSION = std::min(progress_ / TOTAL_PROGRESS_TO_COMPLETE, 1.0F);
moon_index_ = static_cast<size_t>(MOON_PROGRESSION * (moon_path_.size() - 1)); moon_index_ = static_cast<size_t>(MOON_PROGRESSION * (moon_path_.size() - 1));
// Actualiza la velocidad de las nubes // Actualiza la velocidad de las nubes
@@ -318,12 +318,12 @@ void Background::updateCloudsSpeed() {
// Velocidad base según progreso (de -3.0 a -120.0 píxeles/segundo, igual que la versión original) // Velocidad base según progreso (de -3.0 a -120.0 píxeles/segundo, igual que la versión original)
float base_clouds_speed = (-CLOUDS_INITIAL_SPEED_PX_PER_S) + float base_clouds_speed = (-CLOUDS_INITIAL_SPEED_PX_PER_S) +
(-CLOUDS_FINAL_SPEED_RANGE_PX_PER_S * (progress_ / total_progress_to_complete_)); (-CLOUDS_FINAL_SPEED_RANGE_PX_PER_S * (progress_ / TOTAL_PROGRESS_TO_COMPLETE));
// En estado completado, las nubes se ralentizan gradualmente // En estado completado, las nubes se ralentizan gradualmente
if (state_ == State::COMPLETED) { if (state_ == State::COMPLETED) {
float completion_factor = (progress_ - minimum_completed_progress_) / float completion_factor = (progress_ - MINIMUM_COMPLETED_PROGRESS) /
(total_progress_to_complete_ - minimum_completed_progress_); (TOTAL_PROGRESS_TO_COMPLETE - MINIMUM_COMPLETED_PROGRESS);
completion_factor = std::max(0.1F, completion_factor); completion_factor = std::max(0.1F, completion_factor);
base_clouds_speed *= completion_factor; base_clouds_speed *= completion_factor;
} }
+6 -5
View File
@@ -4,6 +4,7 @@
#include <array> // Para array #include <array> // Para array
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <cstdint> // Para std::uint8_t
#include <functional> // Para function #include <functional> // Para function
#include <memory> // Para unique_ptr, shared_ptr #include <memory> // Para unique_ptr, shared_ptr
#include <vector> // Para vector #include <vector> // Para vector
@@ -19,7 +20,7 @@ class AnimatedSprite;
class Background { class Background {
public: public:
// --- Enums --- // --- Enums ---
enum class State { enum class State : std::uint8_t {
NORMAL, // Progresión normal del día NORMAL, // Progresión normal del día
COMPLETED // Reducción gradual de la actividad COMPLETED // Reducción gradual de la actividad
}; };
@@ -87,10 +88,10 @@ class Background {
std::unique_ptr<AnimatedSprite> grass_sprite_; // Sprite con la hierba std::unique_ptr<AnimatedSprite> grass_sprite_; // Sprite con la hierba
// --- Variables de configuración --- // --- Variables de configuración ---
const float total_progress_to_complete_; // Progreso total para completar const float TOTAL_PROGRESS_TO_COMPLETE; // Progreso total para completar
const float progress_per_stage_; // Progreso por etapa const float PROGRESS_PER_STAGE; // Progreso por etapa
const float sun_completion_progress_; // Progreso de completado del sol const float SUM_COMPLETION_PROGRESS; // Progreso de completado del sol
const float minimum_completed_progress_; // Progreso mínimo calculado dinámicamente const float MINIMUM_COMPLETED_PROGRESS; // Progreso mínimo calculado dinámicamente
ProgressCallback progress_callback_; // Callback para notificar cambios de progreso ProgressCallback progress_callback_; // Callback para notificar cambios de progreso
// --- Variables de estado --- // --- Variables de estado ---
+1 -1
View File
@@ -481,7 +481,7 @@ void Fade::activate() {
case Type::DIAGONAL: { case Type::DIAGONAL: {
rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)}; rect1_ = {.x = 0, .y = 0, .w = static_cast<float>(param.game.width / num_squares_width_), .h = static_cast<float>(param.game.height / num_squares_height_)};
square_.clear(); square_.clear();
square_age_.assign(num_squares_width_ * num_squares_height_, -1); square_age_.assign(static_cast<size_t>(num_squares_width_) * num_squares_height_, -1);
for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) { for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) {
rect1_.x = (i % num_squares_width_) * rect1_.w; rect1_.x = (i % num_squares_width_) * rect1_.w;
rect1_.y = (i / num_squares_width_) * rect1_.h; rect1_.y = (i / num_squares_width_) * rect1_.h;
+131 -119
View File
@@ -8,87 +8,105 @@
#include <string> // Para char_traits, operator==, basic_string, string #include <string> // Para char_traits, operator==, basic_string, string
namespace GIF { namespace GIF {
namespace {
inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) { inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) {
std::memcpy(dst, buffer, size); std::memcpy(dst, buffer, size);
buffer += size; buffer += size;
} }
void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) { // Llavor del diccionari LZW: 0..N-1 com a entrades base, i salta 2 (clear_code + stop_code).
if (code_length < 2 || code_length > 12) { void resetDictionary(std::vector<DictionaryEntry> &dict, int code_length, int &dictionary_ind) {
std::cout << "Invalid LZW code length: " << code_length << '\n'; dict.resize(1 << (code_length + 1));
throw std::runtime_error("Invalid LZW code length");
}
int i, bit;
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind;
unsigned int mask = 0x01;
int reset_code_length = code_length;
int clear_code = 1 << code_length;
int stop_code = clear_code + 1;
int match_len = 0;
dictionary.resize(1 << (code_length + 1));
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) { for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) {
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); dict[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind);
dictionary[dictionary_ind].prev = -1; dict[dictionary_ind].prev = -1;
dictionary[dictionary_ind].len = 1; dict[dictionary_ind].len = 1;
} }
dictionary_ind += 2; dictionary_ind += 2;
}
while (input_length > 0) { // Llig `code_length + 1` bits LSB-first del flux d'entrada. Llança si s'acaba el buffer.
auto readNextCode(const uint8_t *&input, int &input_length, int code_length, unsigned int &mask) -> int {
int code = 0; int code = 0;
for (i = 0; i < (code_length + 1); i++) { for (int i = 0; i < code_length + 1; i++) {
if (input_length <= 0) { if (input_length <= 0) {
std::cout << "Unexpected end of input in decompress" << '\n'; std::cout << "Unexpected end of input in decompress" << '\n';
throw std::runtime_error("Unexpected end of input in decompress"); throw std::runtime_error("Unexpected end of input in decompress");
} }
bit = ((*input & mask) != 0) ? 1 : 0; const int BIT = ((*input & mask) != 0) ? 1 : 0;
mask <<= 1; mask <<= 1;
if (mask == 0x100) { if (mask == 0x100) {
mask = 0x01; mask = 0x01;
input++; input++;
input_length--; input_length--;
} }
code |= (bit << i); code |= (BIT << i);
}
return code;
} }
if (code == clear_code) { // Afig una nova entrada al diccionari. Resol el cas especial KwKwK (code == dictionary_ind)
code_length = reset_code_length; // començant la cadena des de `prev` en lloc de des de `code`.
dictionary.resize(1 << (code_length + 1)); void addDictionaryEntry(std::vector<DictionaryEntry> &dict, int dictionary_ind, int code, int prev) {
for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) { int ptr = (code == dictionary_ind) ? prev : code;
dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); while (dict[ptr].prev != -1) {
dictionary[dictionary_ind].prev = -1; ptr = dict[ptr].prev;
dictionary[dictionary_ind].len = 1;
} }
dictionary_ind += 2; dict[dictionary_ind].byte = dict[ptr].byte;
dict[dictionary_ind].prev = prev;
dict[dictionary_ind].len = dict[prev].len + 1;
}
// Escriu la cadena de bytes associada a `code` en `out` (en ordre invers seguint .prev).
// Retorna la longitud del match per avançar el cursor de l'eixida.
auto emitMatch(const std::vector<DictionaryEntry> &dict, int code, uint8_t *out) -> int {
const int MATCH_LEN = dict[code].len;
int cur_code = code;
while (cur_code != -1) {
out[dict[cur_code].len - 1] = dict[cur_code].byte;
if (dict[cur_code].prev == cur_code) {
std::cout << "Internal error; self-reference detected." << '\n';
throw std::runtime_error("Internal error in decompress: self-reference");
}
cur_code = dict[cur_code].prev;
}
return MATCH_LEN;
}
// Descompone (uncompress) el bloque comprimido usando LZW.
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) {
if (code_length < 2 || code_length > 12) {
std::cout << "Invalid LZW code length: " << code_length << '\n';
throw std::runtime_error("Invalid LZW code length");
}
int prev = -1;
std::vector<DictionaryEntry> dictionary;
int dictionary_ind = 0;
unsigned int mask = 0x01;
const int RESET_CODE_LENGTH = code_length;
const int CLEAR_CODE = 1 << code_length;
const int STOP_CODE = CLEAR_CODE + 1;
resetDictionary(dictionary, code_length, dictionary_ind);
while (input_length > 0) {
const int CODE = readNextCode(input, input_length, code_length, mask);
if (CODE == CLEAR_CODE) {
code_length = RESET_CODE_LENGTH;
resetDictionary(dictionary, code_length, dictionary_ind);
prev = -1; prev = -1;
continue; continue;
} else if (code == stop_code) {
break;
} }
if (CODE == STOP_CODE) { break; }
if (prev > -1 && code_length < 12) { if (prev > -1 && code_length < 12) {
if (code > dictionary_ind) { if (CODE > dictionary_ind) {
std::cout << "LZW error: code (" << code << ") exceeds dictionary_ind (" << dictionary_ind << ")" << '\n'; std::cout << "LZW error: code (" << CODE << ") exceeds dictionary_ind (" << dictionary_ind << ")" << '\n';
throw std::runtime_error("LZW error: code exceeds dictionary_ind."); throw std::runtime_error("LZW error: code exceeds dictionary_ind.");
} }
addDictionaryEntry(dictionary, dictionary_ind, CODE, prev);
int ptr;
if (code == dictionary_ind) {
ptr = prev;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
} else {
ptr = code;
while (dictionary[ptr].prev != -1)
ptr = dictionary[ptr].prev;
dictionary[dictionary_ind].byte = dictionary[ptr].byte;
}
dictionary[dictionary_ind].prev = prev;
dictionary[dictionary_ind].len = dictionary[prev].len + 1;
dictionary_ind++; dictionary_ind++;
if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) { if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) {
@@ -97,28 +115,19 @@ namespace GIF {
} }
} }
prev = code; prev = CODE;
if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) { if (CODE < 0 || static_cast<size_t>(CODE) >= dictionary.size()) {
std::cout << "Invalid LZW code " << code << ", dictionary size " << static_cast<unsigned long>(dictionary.size()) << '\n'; std::cout << "Invalid LZW code " << CODE << ", dictionary size " << static_cast<unsigned long>(dictionary.size()) << '\n';
throw std::runtime_error("LZW error: invalid code encountered"); throw std::runtime_error("LZW error: invalid code encountered");
} }
int curCode = code; out += emitMatch(dictionary, CODE, out);
match_len = dictionary[curCode].len;
while (curCode != -1) {
out[dictionary[curCode].len - 1] = dictionary[curCode].byte;
if (dictionary[curCode].prev == curCode) {
std::cout << "Internal error; self-reference detected." << '\n';
throw std::runtime_error("Internal error in decompress: self-reference");
}
curCode = dictionary[curCode].prev;
}
out += match_len;
} }
} }
std::vector<uint8_t> Gif::readSubBlocks(const uint8_t *&buffer) { // Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
auto readSubBlocks(const uint8_t *&buffer) -> std::vector<uint8_t> {
std::vector<uint8_t> data; std::vector<uint8_t> data;
uint8_t block_size = *buffer; uint8_t block_size = *buffer;
buffer++; buffer++;
@@ -131,7 +140,8 @@ namespace GIF {
return data; return data;
} }
std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) { // Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
auto processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) -> std::vector<uint8_t> {
ImageDescriptor image_descriptor; ImageDescriptor image_descriptor;
readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor)); readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor));
@@ -146,38 +156,15 @@ namespace GIF {
return uncompressed_data; return uncompressed_data;
} }
std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) { // Procesa el stream completo del GIF y devuelve los datos sin comprimir.
auto processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h) -> std::vector<uint8_t> {
uint8_t header[6]; uint8_t header[6];
std::memcpy(header, buffer, 6); std::memcpy(header, buffer, 6);
buffer += 6; buffer += 6;
ScreenDescriptor screen_descriptor; std::string header_str(reinterpret_cast<char *>(header), 6);
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor)); if (header_str != "GIF87a" && header_str != "GIF89a") {
buffer += sizeof(ScreenDescriptor); std::cout << "Formato de archivo GIF inválido: " << header_str << '\n';
std::vector<uint32_t> global_color_table;
if (screen_descriptor.fields & 0x80) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
}
return global_color_table;
}
std::vector<uint8_t> Gif::processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h) {
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
std::string headerStr(reinterpret_cast<char *>(header), 6);
if (headerStr != "GIF87a" && headerStr != "GIF89a") {
std::cout << "Formato de archivo GIF inválido: " << headerStr << '\n';
throw std::runtime_error("Formato de archivo GIF inválido."); throw std::runtime_error("Formato de archivo GIF inválido.");
} }
@@ -189,11 +176,11 @@ namespace GIF {
int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1;
std::vector<RGB> global_color_table; std::vector<RGB> global_color_table;
if (screen_descriptor.fields & 0x80) { if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1)); const size_t GLOBAL_COLOR_TABLE_SIZE = 1U << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size); global_color_table.resize(GLOBAL_COLOR_TABLE_SIZE);
std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); std::memcpy(global_color_table.data(), buffer, 3 * GLOBAL_COLOR_TABLE_SIZE);
buffer += 3 * global_color_table_size; buffer += 3 * GLOBAL_COLOR_TABLE_SIZE;
} }
uint8_t block_type = *buffer++; uint8_t block_type = *buffer++;
@@ -202,34 +189,34 @@ namespace GIF {
uint8_t extension_label = *buffer++; uint8_t extension_label = *buffer++;
switch (extension_label) { switch (extension_label) {
case GRAPHIC_CONTROL: { case GRAPHIC_CONTROL: {
uint8_t blockSize = *buffer++; uint8_t block_size = *buffer++;
buffer += blockSize; buffer += block_size;
uint8_t subBlockSize = *buffer++; uint8_t sub_block_size = *buffer++;
while (subBlockSize != 0) { while (sub_block_size != 0) {
buffer += subBlockSize; buffer += sub_block_size;
subBlockSize = *buffer++; sub_block_size = *buffer++;
} }
break; break;
} }
case APPLICATION_EXTENSION: case APPLICATION_EXTENSION:
case COMMENT_EXTENSION: case COMMENT_EXTENSION:
case PLAINTEXT_EXTENSION: { case PLAINTEXT_EXTENSION: {
uint8_t blockSize = *buffer++; uint8_t block_size = *buffer++;
buffer += blockSize; buffer += block_size;
uint8_t subBlockSize = *buffer++; uint8_t sub_block_size = *buffer++;
while (subBlockSize != 0) { while (sub_block_size != 0) {
buffer += subBlockSize; buffer += sub_block_size;
subBlockSize = *buffer++; sub_block_size = *buffer++;
} }
break; break;
} }
default: { default: {
uint8_t blockSize = *buffer++; uint8_t block_size = *buffer++;
buffer += blockSize; buffer += block_size;
uint8_t subBlockSize = *buffer++; uint8_t sub_block_size = *buffer++;
while (subBlockSize != 0) { while (sub_block_size != 0) {
buffer += subBlockSize; buffer += sub_block_size;
subBlockSize = *buffer++; sub_block_size = *buffer++;
} }
break; break;
} }
@@ -245,8 +232,33 @@ namespace GIF {
return std::vector<uint8_t>{}; return std::vector<uint8_t>{};
} }
} // namespace
std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) { auto loadPalette(const uint8_t *buffer) -> std::vector<uint32_t> {
uint8_t header[6];
std::memcpy(header, buffer, 6);
buffer += 6;
ScreenDescriptor screen_descriptor;
std::memcpy(&screen_descriptor, buffer, sizeof(ScreenDescriptor));
buffer += sizeof(ScreenDescriptor);
std::vector<uint32_t> global_color_table;
if ((screen_descriptor.fields & 0x80) != 0) {
int global_color_table_size = 1 << (((screen_descriptor.fields & 0x07) + 1));
global_color_table.resize(global_color_table_size);
for (int i = 0; i < global_color_table_size; ++i) {
uint8_t r = buffer[0];
uint8_t g = buffer[1];
uint8_t b = buffer[2];
global_color_table[i] = (r << 16) | (g << 8) | b;
buffer += 3;
}
}
return global_color_table;
}
auto loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) -> std::vector<uint8_t> {
return processGifStream(buffer, w, h); return processGifStream(buffer, w, h);
} }
+2 -19
View File
@@ -64,29 +64,12 @@ namespace GIF {
uint8_t foreground_color, background_color; uint8_t foreground_color, background_color;
}; };
class Gif {
public:
// Descompone (uncompress) el bloque comprimido usando LZW.
// Este método puede lanzar std::runtime_error en caso de error.
void decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out);
// Carga la paleta (global color table) a partir de un buffer, // Carga la paleta (global color table) a partir de un buffer,
// retornándola en un vector de uint32_t (cada color se compone de R, G, B). // retornándola en un vector de uint32_t (cada color se compone de R, G, B).
std::vector<uint32_t> loadPalette(const uint8_t *buffer); auto loadPalette(const uint8_t *buffer) -> std::vector<uint32_t>;
// Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y // Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y
// asigna el ancho y alto mediante referencias. // asigna el ancho y alto mediante referencias.
std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h); auto loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) -> std::vector<uint8_t>;
private:
// Lee los sub-bloques de datos y los acumula en un std::vector<uint8_t>.
std::vector<uint8_t> readSubBlocks(const uint8_t *&buffer);
// Procesa el Image Descriptor y retorna el vector de datos sin comprimir.
std::vector<uint8_t> processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits);
// Procesa el stream completo del GIF y devuelve los datos sin comprimir.
std::vector<uint8_t> processGifStream(const uint8_t *buffer, uint16_t &w, uint16_t &h);
};
} // namespace GIF } // namespace GIF
+19 -18
View File
@@ -320,27 +320,21 @@ void Screen::renderShake() {
} }
} }
#ifdef _DEBUG #ifdef _DEBUG
// Muestra información por pantalla // Compone la línia d'informació de debug: "fps - driver - shader preset"
void Screen::renderInfo() const { auto Screen::buildDebugInfoText() const -> std::string {
if (debug_info_.show) {
const Color GOLD(0xFF, 0xD7, 0x00);
const Color GOLD_SHADOW = GOLD.DARKEN(150);
// Construir texto: fps - driver - preset
std::string info_text = std::to_string(fps_.last_value) + " fps"; std::string info_text = std::to_string(fps_.last_value) + " fps";
// Driver GPU // Driver GPU
if (shader_backend_ && shader_backend_->isHardwareAccelerated()) { if (shader_backend_ && shader_backend_->isHardwareAccelerated()) {
const std::string DRIVER = shader_backend_->getDriverName(); const std::string DRIVER = shader_backend_->getDriverName();
if (!DRIVER.empty()) { info_text += DRIVER.empty() ? "" : " - " + toLower(DRIVER);
info_text += " - " + toLower(DRIVER);
}
} else { } else {
info_text += " - sdl"; info_text += " - sdl";
} }
// Shader + preset // Shader + preset (només si està activat)
if (Options::video.shader.enabled) { if (!Options::video.shader.enabled) { return info_text; }
if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) { if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
const std::string PRESET_NAME = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name; const std::string PRESET_NAME = Options::crtpi_presets.empty() ? "" : Options::crtpi_presets.at(static_cast<size_t>(Options::video.shader.current_crtpi_preset)).name;
info_text += " - crtpi " + toLower(PRESET_NAME); info_text += " - crtpi " + toLower(PRESET_NAME);
@@ -349,12 +343,20 @@ void Screen::renderInfo() const {
info_text += " - postfx " + toLower(PRESET_NAME); info_text += " - postfx " + toLower(PRESET_NAME);
if (Options::video.supersampling.enabled) { info_text += " (ss)"; } if (Options::video.supersampling.enabled) { info_text += " (ss)"; }
} }
return info_text;
} }
// Centrado arriba // Muestra información por pantalla
const int TEXT_WIDTH = debug_info_.text->length(info_text); void Screen::renderInfo() const {
if (!debug_info_.show) { return; }
const Color GOLD(0xFF, 0xD7, 0x00);
const Color GOLD_SHADOW = GOLD.DARKEN(150);
const std::string INFO_TEXT = buildDebugInfoText();
const int TEXT_WIDTH = debug_info_.text->length(INFO_TEXT);
const int X_POS = (static_cast<int>(param.game.width) - TEXT_WIDTH) / 2; const int X_POS = (static_cast<int>(param.game.width) - TEXT_WIDTH) / 2;
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, X_POS, 1, info_text, 1, GOLD, 1, GOLD_SHADOW); debug_info_.text->writeDX(Text::COLOR | Text::STROKE, X_POS, 1, INFO_TEXT, 1, GOLD, 1, GOLD_SHADOW);
#ifdef RECORDING #ifdef RECORDING
const std::string REC_TEXT = "recording"; const std::string REC_TEXT = "recording";
@@ -363,7 +365,6 @@ void Screen::renderInfo() const {
debug_info_.text->writeDX(Text::COLOR | Text::STROKE, REC_X, 1 + debug_info_.text->getCharacterSize(), REC_TEXT, 1, GOLD, 1, GOLD_SHADOW); debug_info_.text->writeDX(Text::COLOR | Text::STROKE, REC_X, 1 + debug_info_.text->getCharacterSize(), REC_TEXT, 1, GOLD, 1, GOLD_SHADOW);
#endif #endif
} }
}
#endif #endif
// Inicializa shaders (SDL3GPU) // Inicializa shaders (SDL3GPU)
void Screen::initShaders() { void Screen::initShaders() {
@@ -380,8 +381,8 @@ void Screen::initShaders() {
Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER); Options::video.gpu.acceleration ? Options::video.gpu.preferred_driver : FALLBACK_DRIVER);
} }
if (!self->shader_backend_->isHardwareAccelerated()) { if (!self->shader_backend_->isHardwareAccelerated()) {
const bool ok = self->shader_backend_->init(self->window_, self->game_canvas_, "", ""); const bool OK = self->shader_backend_->init(self->window_, self->game_canvas_, "", "");
std::cout << "Screen::initShaders: SDL3GPUShader::init() = " << (ok ? "OK" : "FAILED") << '\n'; std::cout << "Screen::initShaders: SDL3GPUShader::init() = " << (OK ? "OK" : "FAILED") << '\n';
} }
if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) { if (self->shader_backend_ && self->shader_backend_->isHardwareAccelerated()) {
self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale); self->shader_backend_->setLinearUpscale(Options::video.supersampling.linear_upscale);
+1
View File
@@ -241,6 +241,7 @@ class Screen {
void renderFlash(); // Dibuja el efecto de flash en la pantalla void renderFlash(); // Dibuja el efecto de flash en la pantalla
void renderShake(); // Aplica el efecto de agitar la pantalla void renderShake(); // Aplica el efecto de agitar la pantalla
void renderInfo() const; // Muestra información por pantalla void renderInfo() const; // Muestra información por pantalla
[[nodiscard]] auto buildDebugInfoText() const -> std::string; // Compone fps + driver + shader/preset para renderInfo
void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado void renderPresent(); // Selecciona y ejecuta el método de renderizado adecuado
void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend void applyCurrentPostFXPreset(); // Aplica el preset PostFX activo al backend
void applyCurrentCrtPiPreset(); // Aplica el preset CrtPi activo al backend void applyCurrentCrtPiPreset(); // Aplica el preset CrtPi activo al backend
@@ -702,7 +702,7 @@ namespace Rendering {
return; return;
} }
std::memcpy(mapped, pixels, static_cast<size_t>(width * height * 4)); std::memcpy(mapped, pixels, static_cast<size_t>(width) * height * 4);
SDL_UnmapGPUTransferBuffer(device_, upload_buffer_); SDL_UnmapGPUTransferBuffer(device_, upload_buffer_);
} }
+2 -1
View File
@@ -2,13 +2,14 @@
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <cstdint>
#include <string> #include <string>
#include <utility> #include <utility>
namespace Rendering { namespace Rendering {
/** @brief Identificador del shader de post-procesado activo */ /** @brief Identificador del shader de post-procesado activo */
enum class ShaderType { POSTFX, enum class ShaderType : std::uint8_t { POSTFX,
CRTPI }; CRTPI };
/** /**
+10 -13
View File
@@ -31,7 +31,7 @@ auto CardSprite::enable() -> bool {
// Ángulo inicial // Ángulo inicial
rotate_.angle = start_angle_; rotate_.angle = start_angle_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F}; rotate_.center = {.x = pos_.w / 2.0F, .y = pos_.h / 2.0F};
shadow_visible_ = true; shadow_visible_ = true;
return true; return true;
@@ -55,7 +55,7 @@ void CardSprite::startExit() {
// Rotación continua // Rotación continua
rotate_.enabled = true; rotate_.enabled = true;
rotate_.amount = exit_rotate_amount_; rotate_.amount = exit_rotate_amount_;
rotate_.center = {pos_.w / 2.0F, pos_.h / 2.0F}; rotate_.center = {.x = pos_.w / 2.0F, .y = pos_.h / 2.0F};
} }
// Actualiza según el estado // Actualiza según el estado
@@ -80,7 +80,7 @@ void CardSprite::updateEntering(float delta_time) {
double eased = entry_easing_(static_cast<double>(progress)); double eased = entry_easing_(static_cast<double>(progress));
// Zoom: de start_zoom_ a 1.0 con rebote // Zoom: de start_zoom_ a 1.0 con rebote
auto current_zoom = static_cast<float>(start_zoom_ + (1.0 - start_zoom_) * eased); auto current_zoom = static_cast<float>(start_zoom_ + ((1.0 - start_zoom_) * eased));
horizontal_zoom_ = current_zoom; horizontal_zoom_ = current_zoom;
vertical_zoom_ = current_zoom; vertical_zoom_ = current_zoom;
@@ -90,8 +90,8 @@ void CardSprite::updateEntering(float delta_time) {
// Posición: de entry_start a landing con easing suave (sin rebote) // Posición: de entry_start a landing con easing suave (sin rebote)
// Usamos easeOutCubic para que el desplazamiento sea fluido // Usamos easeOutCubic para que el desplazamiento sea fluido
double pos_eased = easeOutCubic(static_cast<double>(progress)); double pos_eased = easeOutCubic(static_cast<double>(progress));
auto current_x = static_cast<float>(entry_start_x_ + (landing_x_ - entry_start_x_) * pos_eased); auto current_x = static_cast<float>(entry_start_x_ + ((landing_x_ - entry_start_x_) * pos_eased));
auto current_y = static_cast<float>(entry_start_y_ + (landing_y_ - entry_start_y_) * pos_eased); auto current_y = static_cast<float>(entry_start_y_ + ((landing_y_ - entry_start_y_) * pos_eased));
setPos(current_x, current_y); setPos(current_x, current_y);
// Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez) // Detecta el primer toque (cuando el easing alcanza ~1.0 por primera vez)
@@ -117,12 +117,9 @@ void CardSprite::updateExiting(float delta_time) {
// Ganar altura gradualmente (zoom hacia el objetivo) // Ganar altura gradualmente (zoom hacia el objetivo)
if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) { if (exit_zoom_speed_ > 0.0F && horizontal_zoom_ < exit_target_zoom_) {
float new_zoom = horizontal_zoom_ + exit_zoom_speed_ * delta_time; const float NEW_ZOOM = std::min(horizontal_zoom_ + (exit_zoom_speed_ * delta_time), exit_target_zoom_);
if (new_zoom > exit_target_zoom_) { horizontal_zoom_ = NEW_ZOOM;
new_zoom = exit_target_zoom_; vertical_zoom_ = NEW_ZOOM;
}
horizontal_zoom_ = new_zoom;
vertical_zoom_ = new_zoom;
} }
if (isOffScreen()) { if (isOffScreen()) {
@@ -164,8 +161,8 @@ void CardSprite::renderShadow() {
// Offset respecto a la tarjeta: base + extra proporcional a la altura // Offset respecto a la tarjeta: base + extra proporcional a la altura
// La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0) // La sombra se aleja en diagonal abajo-derecha (opuesta a la luz en 0,0)
float offset_x = shadow_offset_x_ + height * SHADOW_HEIGHT_MULTIPLIER; float offset_x = shadow_offset_x_ + (height * SHADOW_HEIGHT_MULTIPLIER);
float offset_y = shadow_offset_y_ + height * SHADOW_HEIGHT_MULTIPLIER; float offset_y = shadow_offset_y_ + (height * SHADOW_HEIGHT_MULTIPLIER);
shadow_texture_->render( shadow_texture_->render(
pos_.x + offset_x, pos_.x + offset_x,
+2 -1
View File
@@ -2,6 +2,7 @@
#include <SDL3/SDL.h> // Para SDL_FPoint #include <SDL3/SDL.h> // Para SDL_FPoint
#include <cstdint> // Para std::uint8_t
#include <functional> // Para function #include <functional> // Para function
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
@@ -10,7 +11,7 @@
class Texture; class Texture;
// --- Estados de la tarjeta --- // --- Estados de la tarjeta ---
enum class CardState { enum class CardState : std::uint8_t {
IDLE, // No activada todavía IDLE, // No activada todavía
ENTERING, // Animación de entrada (zoom + rotación + desplazamiento con rebote) ENTERING, // Animación de entrada (zoom + rotación + desplazamiento con rebote)
LANDED, // En reposo sobre la mesa LANDED, // En reposo sobre la mesa
+3 -2
View File
@@ -2,6 +2,7 @@
#include <SDL3/SDL.h> // Para SDL_FPoint #include <SDL3/SDL.h> // Para SDL_FPoint
#include <cstdint> // Para std::uint8_t
#include <functional> // Para std::function #include <functional> // Para std::function
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <utility> #include <utility>
@@ -12,12 +13,12 @@
class Texture; class Texture;
// --- Enums --- // --- Enums ---
enum class PathType { // Tipos de recorrido enum class PathType : std::uint8_t { // Tipos de recorrido
VERTICAL, VERTICAL,
HORIZONTAL, HORIZONTAL,
}; };
enum class PathCentered { // Centrado del recorrido enum class PathCentered : std::uint8_t { // Centrado del recorrido
ON_X, ON_X,
ON_Y, ON_Y,
NONE, NONE,
@@ -1,7 +1,5 @@
#include "core/rendering/sprite/smart_sprite.hpp" #include "core/rendering/sprite/smart_sprite.hpp"
#include "core/rendering/sprite/moving_sprite.hpp" // Para MovingSprite
// Actualiza la posición y comprueba si ha llegado a su destino (time-based) // Actualiza la posición y comprueba si ha llegado a su destino (time-based)
void SmartSprite::update(float delta_time) { void SmartSprite::update(float delta_time) {
if (enabled_) { if (enabled_) {
@@ -3,16 +3,16 @@
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <utility> #include <utility>
#include "core/rendering/sprite/animated_sprite.hpp" // Para AnimatedSprite #include "core/rendering/sprite/moving_sprite.hpp" // Para MovingSprite
class Texture; class Texture;
// --- Clase SmartSprite: sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente --- // --- Clase SmartSprite: sprite que se mueve hacia un destino y puede deshabilitarse automáticamente ---
class SmartSprite : public AnimatedSprite { class SmartSprite : public MovingSprite {
public: public:
// --- Constructor y destructor --- // --- Constructor y destructor ---
explicit SmartSprite(std::shared_ptr<Texture> texture) explicit SmartSprite(std::shared_ptr<Texture> texture)
: AnimatedSprite(std::move(texture)) {} : MovingSprite(std::move(texture)) {}
~SmartSprite() override = default; ~SmartSprite() override = default;
// --- Métodos principales --- // --- Métodos principales ---
+2 -5
View File
@@ -251,11 +251,9 @@ auto Texture::loadSurface(const std::string& file_path) -> std::shared_ptr<Surfa
} }
} }
// Crear un objeto Gif y llamar a la función loadGif
GIF::Gif gif;
Uint16 w = 0; Uint16 w = 0;
Uint16 h = 0; Uint16 h = 0;
std::vector<Uint8> raw_pixels = gif.loadGif(buffer.data(), w, h); std::vector<Uint8> raw_pixels = GIF::loadGif(buffer.data(), w, h);
if (raw_pixels.empty()) { if (raw_pixels.empty()) {
std::cout << "Error: No se pudo cargar el GIF " << file_path << '\n'; std::cout << "Error: No se pudo cargar el GIF " << file_path << '\n';
return nullptr; return nullptr;
@@ -329,8 +327,7 @@ auto Texture::loadPaletteFromFile(const std::string& file_path) -> Palette {
} }
// Usar la nueva función loadPalette, que devuelve un vector<uint32_t> // Usar la nueva función loadPalette, que devuelve un vector<uint32_t>
GIF::Gif gif; std::vector<uint32_t> pal = GIF::loadPalette(buffer.data());
std::vector<uint32_t> pal = gif.loadPalette(buffer.data());
if (pal.empty()) { if (pal.empty()) {
std::cout << "Advertencia: No se encontró paleta en el archivo " << file_path << '\n'; std::cout << "Advertencia: No se encontró paleta en el archivo " << file_path << '\n';
return palette; // Devuelve un vector vacío si no hay paleta return palette; // Devuelve un vector vacío si no hay paleta
+3 -1
View File
@@ -2,10 +2,12 @@
#include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetTextureColorMod, SDL_Renderer, SDL_Texture #include <SDL3/SDL.h> // Para SDL_FRect, SDL_SetTextureColorMod, SDL_Renderer, SDL_Texture
#include <cstdint> // Para std::uint8_t
#include "utils/color.hpp" // Para Color #include "utils/color.hpp" // Para Color
// --- Enums --- // --- Enums ---
enum class TiledBGMode : int { // Modos de funcionamiento para el tileado de fondo enum class TiledBGMode : std::uint8_t { // Modos de funcionamiento para el tileado de fondo
CIRCLE = 0, CIRCLE = 0,
DIAGONAL = 1, DIAGONAL = 1,
RANDOM = 2, RANDOM = 2,
+2 -1
View File
@@ -1,6 +1,7 @@
#include "core/rendering/writer.hpp" #include "core/rendering/writer.hpp"
#include "core/rendering/text.hpp" // Para Text // Text es completat ací per `text_->write/length` via shared_ptr; include-cleaner no detecta l'ús indirecte.
#include "core/rendering/text.hpp" // IWYU pragma: keep
// Actualiza el objeto (delta_time en ms) // Actualiza el objeto (delta_time en ms)
void Writer::update(float delta_time) { void Writer::update(float delta_time) {
+1 -1
View File
@@ -10,7 +10,7 @@
class Asset { class Asset {
public: public:
// --- Enums --- // --- Enums ---
enum class Type : int { enum class Type : std::uint8_t {
BITMAP, // Imágenes BITMAP, // Imágenes
MUSIC, // Música MUSIC, // Música
SOUND, // Sonidos SOUND, // Sonidos
@@ -4,6 +4,8 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include "core/resources/resource_loader.hpp"
bool AssetIntegrated::resource_pack_enabled = false; bool AssetIntegrated::resource_pack_enabled = false;
void AssetIntegrated::initWithResourcePack(const std::string& executable_path, void AssetIntegrated::initWithResourcePack(const std::string& executable_path,
@@ -1,9 +1,6 @@
#pragma once #pragma once
#include <memory>
#include "core/resources/asset.hpp" #include "core/resources/asset.hpp"
#include "core/resources/resource_loader.hpp"
// Extensión de Asset que integra ResourceLoader // Extensión de Asset que integra ResourceLoader
class AssetIntegrated : public Asset { class AssetIntegrated : public Asset {
+73 -106
View File
@@ -5,16 +5,12 @@
#include <algorithm> // Para ranges::transform, ranges::find_if #include <algorithm> // Para ranges::transform, ranges::find_if
#include <array> // Para array #include <array> // Para array
#include <cstdlib> // Para exit #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 <iostream> // Para std::cout
#include <iterator> // Para back_inserter #include <iterator> // Para back_inserter
#include <ranges> // Para __find_if_fn, find_if, __find_fn, find
#include <stdexcept> // Para runtime_error #include <stdexcept> // Para runtime_error
#include <utility> // Para move #include <utility> // Para move
#include "core/audio/jail_audio.hpp" // Para JA_LoadMusic, JA_LoadSound, JA_DeleteMusic, JA_DeleteSound #include "core/audio/jail_audio.hpp" // Para Ja::loadMusic, Ja::loadSound, Ja::deleteMusic, Ja::deleteSound
#include "core/locale/lang.hpp" // Para getText #include "core/locale/lang.hpp" // Para getText
#include "core/rendering/screen.hpp" // Para Screen #include "core/rendering/screen.hpp" // Para Screen
#include "core/rendering/text.hpp" // Para Text #include "core/rendering/text.hpp" // Para Text
@@ -27,9 +23,6 @@
#include "utils/utils.hpp" // Para getFileName #include "utils/utils.hpp" // Para getFileName
#include "version.h" // Para APP_NAME, GIT_HASH #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 // Helper para cargar archivos de audio desde pack o filesystem en memoria
namespace { namespace {
struct AudioData { struct AudioData {
@@ -140,37 +133,37 @@ void Resource::loadEssentialTextures() {
// Inicializa las listas de recursos sin cargar el contenido (modo lazy) // Inicializa las listas de recursos sin cargar el contenido (modo lazy)
void Resource::initResourceLists() { void Resource::initResourceLists() {
const auto file_to_name = [](const auto& file) { return getFileName(file); }; const auto FILE_TO_NAME = [](const auto& file) { return getFileName(file); };
// Inicializa lista de sonidos // Inicializa lista de sonidos
const auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND); const auto SOUND_LIST = Asset::get()->getListByType(Asset::Type::SOUND);
sounds_.clear(); sounds_.clear();
sounds_.reserve(sound_list.size()); sounds_.reserve(SOUND_LIST.size());
std::ranges::transform(sound_list, std::back_inserter(sounds_), [&](const auto& file) { return ResourceSound(file_to_name(file)); }); std::ranges::transform(SOUND_LIST, std::back_inserter(sounds_), [&](const auto& file) { return ResourceSound(FILE_TO_NAME(file)); });
// Inicializa lista de músicas // Inicializa lista de músicas
const auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC); const auto MUSIC_LIST = Asset::get()->getListByType(Asset::Type::MUSIC);
musics_.clear(); musics_.clear();
musics_.reserve(music_list.size()); musics_.reserve(MUSIC_LIST.size());
std::ranges::transform(music_list, std::back_inserter(musics_), [&](const auto& file) { return ResourceMusic(file_to_name(file)); }); std::ranges::transform(MUSIC_LIST, std::back_inserter(musics_), [&](const auto& file) { return ResourceMusic(FILE_TO_NAME(file)); });
// Inicializa lista de texturas // Inicializa lista de texturas
const auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP); const auto TEXTURE_LIST = Asset::get()->getListByType(Asset::Type::BITMAP);
textures_.clear(); textures_.clear();
textures_.reserve(texture_list.size()); textures_.reserve(TEXTURE_LIST.size());
std::ranges::transform(texture_list, std::back_inserter(textures_), [&](const auto& file) { return ResourceTexture(file_to_name(file)); }); std::ranges::transform(TEXTURE_LIST, std::back_inserter(textures_), [&](const auto& file) { return ResourceTexture(FILE_TO_NAME(file)); });
// Inicializa lista de ficheros de texto // Inicializa lista de ficheros de texto
const auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT); const auto TEXT_FILE_LIST = Asset::get()->getListByType(Asset::Type::FONT);
text_files_.clear(); text_files_.clear();
text_files_.reserve(text_file_list.size()); text_files_.reserve(TEXT_FILE_LIST.size());
std::ranges::transform(text_file_list, std::back_inserter(text_files_), [&](const auto& file) { return ResourceTextFile(file_to_name(file)); }); std::ranges::transform(TEXT_FILE_LIST, std::back_inserter(text_files_), [&](const auto& file) { return ResourceTextFile(FILE_TO_NAME(file)); });
// Inicializa lista de animaciones // Inicializa lista de animaciones
const auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION); const auto ANIMATION_LIST = Asset::get()->getListByType(Asset::Type::ANIMATION);
animations_.clear(); animations_.clear();
animations_.reserve(animation_list.size()); animations_.reserve(ANIMATION_LIST.size());
std::ranges::transform(animation_list, std::back_inserter(animations_), [&](const auto& file) { return ResourceAnimation(file_to_name(file)); }); std::ranges::transform(ANIMATION_LIST, std::back_inserter(animations_), [&](const auto& file) { return ResourceAnimation(FILE_TO_NAME(file)); });
// Los demos se cargan directamente sin mostrar progreso (son pocos y pequeños) // Los demos se cargan directamente sin mostrar progreso (son pocos y pequeños)
loadDemoDataQuiet(); loadDemoDataQuiet();
@@ -196,7 +189,7 @@ void Resource::initResourceLists() {
} }
// Obtiene el sonido a partir de un nombre (con carga perezosa) // Obtiene el sonido a partir de un nombre (con carga perezosa)
auto Resource::getSound(const std::string& name) -> JA_Sound_t* { auto Resource::getSound(const std::string& name) -> Ja::Sound* {
auto it = std::ranges::find_if(sounds_, [&name](const auto& s) -> auto { return s.name == name; }); auto it = std::ranges::find_if(sounds_, [&name](const auto& s) -> auto { return s.name == name; });
if (it != sounds_.end()) { if (it != sounds_.end()) {
@@ -212,7 +205,7 @@ auto Resource::getSound(const std::string& name) -> JA_Sound_t* {
} }
// Obtiene la música a partir de un nombre (con carga perezosa) // Obtiene la música a partir de un nombre (con carga perezosa)
auto Resource::getMusic(const std::string& name) -> JA_Music_t* { auto Resource::getMusic(const std::string& name) -> Ja::Music* {
auto it = std::ranges::find_if(musics_, [&name](const auto& m) -> auto { return m.name == name; }); auto it = std::ranges::find_if(musics_, [&name](const auto& m) -> auto { return m.name == name; });
if (it != musics_.end()) { if (it != musics_.end()) {
@@ -303,48 +296,48 @@ auto Resource::getDemoData(int index) -> DemoData& {
// --- Métodos de carga perezosa --- // --- Métodos de carga perezosa ---
auto Resource::loadSoundLazy(const std::string& name) -> JA_Sound_t* { auto Resource::loadSoundLazy(const std::string& name) -> Ja::Sound* {
auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND); auto sound_list = Asset::get()->getListByType(Asset::Type::SOUND);
for (const auto& file : sound_list) { for (const auto& file : sound_list) {
if (getFileName(file) == name) { if (getFileName(file) == name) {
auto audio_data = loadAudioData(file); auto audio_data = loadAudioData(file);
if (!audio_data.data.empty()) { if (!audio_data.data.empty()) {
return JA_LoadSound(audio_data.data.data(), audio_data.data.size()); return Ja::loadSound(audio_data.data.data(), audio_data.data.size());
} }
// Fallback a cargar desde disco si no está en pack // Fallback a cargar desde disco si no está en pack
return JA_LoadSound(file.c_str()); return Ja::loadSound(file.c_str());
} }
} }
return nullptr; return nullptr;
} }
auto Resource::loadMusicLazy(const std::string& name) -> JA_Music_t* { auto Resource::loadMusicLazy(const std::string& name) -> Ja::Music* {
auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC); auto music_list = Asset::get()->getListByType(Asset::Type::MUSIC);
for (const auto& file : music_list) { for (const auto& file : music_list) {
if (getFileName(file) == name) { if (getFileName(file) == name) {
auto audio_data = loadAudioData(file); auto audio_data = loadAudioData(file);
if (!audio_data.data.empty()) { if (!audio_data.data.empty()) {
return JA_LoadMusic(audio_data.data.data(), audio_data.data.size()); return Ja::loadMusic(audio_data.data.data(), audio_data.data.size());
} }
// Fallback a cargar desde disco si no está en pack // Fallback a cargar desde disco si no está en pack
return JA_LoadMusic(file.c_str()); return Ja::loadMusic(file.c_str());
} }
} }
return nullptr; return nullptr;
} }
auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture> { auto Resource::loadTextureLazy(const std::string& name) -> std::shared_ptr<Texture> {
const auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP); const auto TEXTURE_LIST = Asset::get()->getListByType(Asset::Type::BITMAP);
const auto it = std::ranges::find_if(texture_list, const auto IT = std::ranges::find_if(TEXTURE_LIST,
[&name](const auto& file) { return getFileName(file) == name; }); [&name](const auto& file) { return getFileName(file) == name; });
return it != texture_list.end() ? std::make_shared<Texture>(Screen::get()->getRenderer(), *it) : nullptr; return IT != TEXTURE_LIST.end() ? std::make_shared<Texture>(Screen::get()->getRenderer(), *IT) : nullptr;
} }
auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File> { auto Resource::loadTextFileLazy(const std::string& name) -> std::shared_ptr<Text::File> {
const auto text_file_list = Asset::get()->getListByType(Asset::Type::FONT); const auto TEXT_FILE_LIST = Asset::get()->getListByType(Asset::Type::FONT);
const auto it = std::ranges::find_if(text_file_list, const auto IT = std::ranges::find_if(TEXT_FILE_LIST,
[&name](const auto& file) { return getFileName(file) == name; }); [&name](const auto& file) { return getFileName(file) == name; });
return it != text_file_list.end() ? Text::loadFile(*it) : nullptr; return IT != TEXT_FILE_LIST.end() ? Text::loadFile(*IT) : nullptr;
} }
auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> { auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
@@ -369,22 +362,22 @@ auto Resource::loadTextLazy(const std::string& name) -> std::shared_ptr<Text> {
{.key = "smb2", .texture_file = "smb2.png", .text_file = "smb2.txt"}, {.key = "smb2", .texture_file = "smb2.png", .text_file = "smb2.txt"},
{.key = "smb2_grad", .texture_file = "smb2_grad.png", .text_file = "smb2.txt"}}; {.key = "smb2_grad", .texture_file = "smb2_grad.png", .text_file = "smb2.txt"}};
const auto it = std::ranges::find_if(TEXT_MAPPINGS, const auto IT = std::ranges::find_if(TEXT_MAPPINGS,
[&name](const auto& mapping) { return mapping.key == name; }); [&name](const auto& mapping) { return mapping.key == name; });
if (it == TEXT_MAPPINGS.end()) { if (IT == TEXT_MAPPINGS.end()) {
return nullptr; return nullptr;
} }
auto texture = getTexture(it->texture_file); // Esto cargará la textura si no está cargada auto texture = getTexture(IT->texture_file); // Esto cargará la textura si no está cargada
auto text_file = getTextFile(it->text_file); // Esto cargará el archivo de texto si no está cargado auto text_file = getTextFile(IT->text_file); // Esto cargará el archivo de texto si no está cargado
return (texture && text_file) ? std::make_shared<Text>(texture, text_file) : nullptr; return (texture && text_file) ? std::make_shared<Text>(texture, text_file) : nullptr;
} }
auto Resource::loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer { auto Resource::loadAnimationLazy(const std::string& name) -> AnimationsFileBuffer {
const auto animation_list = Asset::get()->getListByType(Asset::Type::ANIMATION); const auto ANIMATION_LIST = Asset::get()->getListByType(Asset::Type::ANIMATION);
const auto it = std::ranges::find_if(animation_list, const auto IT = std::ranges::find_if(ANIMATION_LIST,
[&name](const auto& file) { return getFileName(file) == name; }); [&name](const auto& file) { return getFileName(file) == name; });
if (it != animation_list.end()) { if (IT != ANIMATION_LIST.end()) {
return loadAnimationsFromFile(*it); return loadAnimationsFromFile(*IT);
} }
// Si no se encuentra, retorna vector vacío // Si no se encuentra, retorna vector vacío
return AnimationsFileBuffer{}; return AnimationsFileBuffer{};
@@ -427,80 +420,54 @@ auto Resource::isLoadDone() const -> bool {
return stage_ == LoadStage::DONE; return stage_ == LoadStage::DONE;
} }
// Avança una etapa que descarrega una llista d'assets.
void Resource::advanceListLoadStage(const std::vector<std::string>& list, void (Resource::*load_one)(size_t), LoadStage next_stage) {
if (stage_index_ >= list.size()) {
stage_ = next_stage;
stage_index_ = 0;
return;
}
(this->*load_one)(stage_index_++);
}
// Bombea la máquina de etapas hasta agotar el presupuesto de tiempo o completar la carga. // Bombea la máquina de etapas hasta agotar el presupuesto de tiempo o completar la carga.
// Devuelve true cuando ya no queda nada por cargar. // Devuelve true cuando ya no queda nada por cargar.
auto Resource::loadStep(int budget_ms) -> bool { auto Resource::loadStep(int budget_ms) -> bool {
if (stage_ == LoadStage::DONE) { return true; } if (stage_ == LoadStage::DONE) { return true; }
const Uint64 start_ns = SDL_GetTicksNS(); const Uint64 START_NS = SDL_GetTicksNS();
const Uint64 budget_ns = static_cast<Uint64>(budget_ms) * 1'000'000ULL; const Uint64 BUDGET_NS = static_cast<Uint64>(budget_ms) * 1'000'000ULL;
while (stage_ != LoadStage::DONE) { while (stage_ != LoadStage::DONE) {
switch (stage_) { switch (stage_) {
case LoadStage::SOUNDS: { case LoadStage::SOUNDS: {
auto list = Asset::get()->getListByType(Asset::Type::SOUND);
if (stage_index_ == 0) { sounds_.clear(); } if (stage_index_ == 0) { sounds_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::SOUND), &Resource::loadOneSound, LoadStage::MUSICS);
stage_ = LoadStage::MUSICS;
stage_index_ = 0;
break;
}
loadOneSound(stage_index_++);
break; break;
} }
case LoadStage::MUSICS: { case LoadStage::MUSICS: {
auto list = Asset::get()->getListByType(Asset::Type::MUSIC);
if (stage_index_ == 0) { musics_.clear(); } if (stage_index_ == 0) { musics_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::MUSIC), &Resource::loadOneMusic, LoadStage::TEXTURES);
stage_ = LoadStage::TEXTURES;
stage_index_ = 0;
break;
}
loadOneMusic(stage_index_++);
break; break;
} }
case LoadStage::TEXTURES: { case LoadStage::TEXTURES: {
auto list = Asset::get()->getListByType(Asset::Type::BITMAP);
if (stage_index_ == 0) { textures_.clear(); } if (stage_index_ == 0) { textures_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::BITMAP), &Resource::loadOneTexture, LoadStage::TEXT_FILES);
stage_ = LoadStage::TEXT_FILES;
stage_index_ = 0;
break;
}
loadOneTexture(stage_index_++);
break; break;
} }
case LoadStage::TEXT_FILES: { case LoadStage::TEXT_FILES: {
auto list = Asset::get()->getListByType(Asset::Type::FONT);
if (stage_index_ == 0) { text_files_.clear(); } if (stage_index_ == 0) { text_files_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::FONT), &Resource::loadOneTextFile, LoadStage::ANIMATIONS);
stage_ = LoadStage::ANIMATIONS;
stage_index_ = 0;
break;
}
loadOneTextFile(stage_index_++);
break; break;
} }
case LoadStage::ANIMATIONS: { case LoadStage::ANIMATIONS: {
auto list = Asset::get()->getListByType(Asset::Type::ANIMATION);
if (stage_index_ == 0) { animations_.clear(); } if (stage_index_ == 0) { animations_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::ANIMATION), &Resource::loadOneAnimation, LoadStage::DEMO_DATA);
stage_ = LoadStage::DEMO_DATA;
stage_index_ = 0;
break;
}
loadOneAnimation(stage_index_++);
break; break;
} }
case LoadStage::DEMO_DATA: { case LoadStage::DEMO_DATA: {
auto list = Asset::get()->getListByType(Asset::Type::DEMODATA);
if (stage_index_ == 0) { demos_.clear(); } if (stage_index_ == 0) { demos_.clear(); }
if (stage_index_ >= list.size()) { advanceListLoadStage(Asset::get()->getListByType(Asset::Type::DEMODATA), &Resource::loadOneDemoData, LoadStage::CREATE_TEXT);
stage_ = LoadStage::CREATE_TEXT;
stage_index_ = 0;
break;
}
loadOneDemoData(stage_index_++);
break; break;
} }
case LoadStage::CREATE_TEXT: case LoadStage::CREATE_TEXT:
@@ -519,7 +486,7 @@ auto Resource::loadStep(int budget_ms) -> bool {
break; break;
} }
if ((SDL_GetTicksNS() - start_ns) >= budget_ns) { break; } if ((SDL_GetTicksNS() - START_NS) >= BUDGET_NS) { break; }
} }
return stage_ == LoadStage::DONE; return stage_ == LoadStage::DONE;
@@ -542,11 +509,11 @@ void Resource::loadOneSound(size_t idx) {
auto name = getFileName(path); auto name = getFileName(path);
updateLoadingProgress(name); updateLoadingProgress(name);
auto audio_data = loadAudioData(path); auto audio_data = loadAudioData(path);
JA_Sound_t* sound = nullptr; Ja::Sound* sound = nullptr;
if (!audio_data.data.empty()) { if (!audio_data.data.empty()) {
sound = JA_LoadSound(audio_data.data.data(), audio_data.data.size()); sound = Ja::loadSound(audio_data.data.data(), audio_data.data.size());
} else { } else {
sound = JA_LoadSound(path.c_str()); sound = Ja::loadSound(path.c_str());
} }
if (sound == nullptr) { if (sound == nullptr) {
std::cout << "Sound load failed: " << name << '\n'; std::cout << "Sound load failed: " << name << '\n';
@@ -561,11 +528,11 @@ void Resource::loadOneMusic(size_t idx) {
auto name = getFileName(path); auto name = getFileName(path);
updateLoadingProgress(name); updateLoadingProgress(name);
auto audio_data = loadAudioData(path); auto audio_data = loadAudioData(path);
JA_Music_t* music = nullptr; Ja::Music* music = nullptr;
if (!audio_data.data.empty()) { if (!audio_data.data.empty()) {
music = JA_LoadMusic(audio_data.data.data(), audio_data.data.size()); music = Ja::loadMusic(audio_data.data.data(), audio_data.data.size());
} else { } else {
music = JA_LoadMusic(path.c_str()); music = Ja::loadMusic(path.c_str());
} }
if (music == nullptr) { if (music == nullptr) {
std::cout << "Music load failed: " << name << '\n'; std::cout << "Music load failed: " << name << '\n';
@@ -627,10 +594,10 @@ void Resource::createPlayerTextures() {
const auto& player = players[player_idx]; // Obtenemos el jugador actual const auto& player = players[player_idx]; // Obtenemos el jugador actual
// Encontrar el archivo original de la textura // Encontrar el archivo original de la textura
const auto texture_list = Asset::get()->getListByType(Asset::Type::BITMAP); const auto TEXTURE_LIST = Asset::get()->getListByType(Asset::Type::BITMAP);
const auto it = std::ranges::find_if(texture_list, const auto IT = std::ranges::find_if(TEXTURE_LIST,
[&player](const auto& file) { return getFileName(file) == player.base_texture; }); [&player](const auto& file) { return getFileName(file) == player.base_texture; });
const std::string texture_file_path = (it != texture_list.end()) ? *it : std::string{}; const std::string TEXTURE_FILE_PATH = (IT != TEXTURE_LIST.end()) ? *IT : std::string{};
// Crear las 4 texturas con sus respectivas paletas // Crear las 4 texturas con sus respectivas paletas
for (int palette_idx = 0; palette_idx < 4; ++palette_idx) { for (int palette_idx = 0; palette_idx < 4; ++palette_idx) {
@@ -646,7 +613,7 @@ void Resource::createPlayerTextures() {
texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32()); texture->setPaletteColor(0, 56, param.player.outline_color[player_idx].TO_UINT32());
} else { } else {
// Crear textura nueva desde archivo usando ResourceHelper // Crear textura nueva desde archivo usando ResourceHelper
texture = std::make_shared<Texture>(Screen::get()->getRenderer(), texture_file_path); texture = std::make_shared<Texture>(Screen::get()->getRenderer(), TEXTURE_FILE_PATH);
// Añadir todas las paletas // Añadir todas las paletas
texture->addPaletteFromPalFile(Asset::get()->getPath(player.palette_files[0])); texture->addPaletteFromPalFile(Asset::get()->getPath(player.palette_files[0]));
@@ -769,7 +736,7 @@ void Resource::createText() {
void Resource::clearSounds() { void Resource::clearSounds() {
for (auto& sound : sounds_) { for (auto& sound : sounds_) {
if (sound.sound != nullptr) { if (sound.sound != nullptr) {
JA_DeleteSound(sound.sound); Ja::deleteSound(sound.sound);
sound.sound = nullptr; sound.sound = nullptr;
} }
} }
@@ -780,7 +747,7 @@ void Resource::clearSounds() {
void Resource::clearMusics() { void Resource::clearMusics() {
for (auto& music : musics_) { for (auto& music : musics_) {
if (music.music != nullptr) { if (music.music != nullptr) {
JA_DeleteMusic(music.music); Ja::deleteMusic(music.music);
music.music = nullptr; music.music = nullptr;
} }
} }
@@ -880,10 +847,10 @@ void Resource::renderProgress() {
// Carga los datos para el modo demostración (sin mostrar progreso) // Carga los datos para el modo demostración (sin mostrar progreso)
void Resource::loadDemoDataQuiet() { void Resource::loadDemoDataQuiet() {
const auto list = Asset::get()->getListByType(Asset::Type::DEMODATA); const auto LIST = Asset::get()->getListByType(Asset::Type::DEMODATA);
demos_.clear(); demos_.clear();
demos_.reserve(list.size()); demos_.reserve(LIST.size());
std::ranges::transform(list, std::back_inserter(demos_), [this](const auto& l) { return loadDemoDataFromFile(l); }); std::ranges::transform(LIST, std::back_inserter(demos_), [](const auto& l) { return loadDemoDataFromFile(l); });
} }
// Inicializa los rectangulos que definen la barra de progreso // Inicializa los rectangulos que definen la barra de progreso
+19 -12
View File
@@ -3,6 +3,7 @@
#include <SDL3/SDL.h> // Para SDL_FRect #include <SDL3/SDL.h> // Para SDL_FRect
#include <cstddef> // Para size_t #include <cstddef> // Para size_t
#include <cstdint> // Para std::uint8_t
#include <memory> // Para shared_ptr #include <memory> // Para shared_ptr
#include <string> // Para string #include <string> // Para string
#include <utility> // Para move #include <utility> // Para move
@@ -13,14 +14,16 @@
#include "core/rendering/texture.hpp" // Para Texture #include "core/rendering/texture.hpp" // Para Texture
#include "core/system/demo.hpp" // Para DemoData #include "core/system/demo.hpp" // Para DemoData
struct JA_Music_t; namespace Ja {
struct JA_Sound_t; struct Music;
struct Sound;
} // namespace Ja
// --- Clase Resource: gestiona todos los recursos del juego (singleton) --- // --- Clase Resource: gestiona todos los recursos del juego (singleton) ---
class Resource { class Resource {
public: public:
// --- Enum para el modo de carga --- // --- Enum para el modo de carga ---
enum class LoadingMode { enum class LoadingMode : std::uint8_t {
PRELOAD, // Carga todos los recursos al inicio PRELOAD, // Carga todos los recursos al inicio
LAZY_LOAD // Carga los recursos bajo demanda LAZY_LOAD // Carga los recursos bajo demanda
}; };
@@ -31,8 +34,8 @@ class Resource {
static auto get() -> Resource*; // Obtiene el puntero al objeto Resource static auto get() -> Resource*; // Obtiene el puntero al objeto Resource
// --- Métodos de acceso a recursos --- // --- Métodos de acceso a recursos ---
auto getSound(const std::string& name) -> JA_Sound_t*; // Obtiene el sonido por nombre auto getSound(const std::string& name) -> Ja::Sound*; // Obtiene el sonido por nombre
auto getMusic(const std::string& name) -> JA_Music_t*; // Obtiene la música por nombre auto getMusic(const std::string& name) -> Ja::Music*; // Obtiene la música por nombre
auto getTexture(const std::string& name) -> std::shared_ptr<Texture>; // Obtiene la textura 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 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 getText(const std::string& name) -> std::shared_ptr<Text>; // Obtiene el objeto de texto por nombre
@@ -58,18 +61,18 @@ class Resource {
// --- Estructuras para recursos individuales --- // --- Estructuras para recursos individuales ---
struct ResourceSound { struct ResourceSound {
std::string name; // Nombre del sonido std::string name; // Nombre del sonido
JA_Sound_t* sound; // Objeto con el sonido Ja::Sound* sound; // Objeto con el sonido
explicit ResourceSound(std::string name, JA_Sound_t* sound = nullptr) explicit ResourceSound(std::string name, Ja::Sound* sound = nullptr)
: name(std::move(name)), : name(std::move(name)),
sound(sound) {} sound(sound) {}
}; };
struct ResourceMusic { struct ResourceMusic {
std::string name; // Nombre de la música std::string name; // Nombre de la música
JA_Music_t* music; // Objeto con la música Ja::Music* music; // Objeto con la música
explicit ResourceMusic(std::string name, JA_Music_t* music = nullptr) explicit ResourceMusic(std::string name, Ja::Music* music = nullptr)
: name(std::move(name)), : name(std::move(name)),
music(music) {} music(music) {}
}; };
@@ -156,7 +159,7 @@ class Resource {
SDL_FRect loading_full_rect_; SDL_FRect loading_full_rect_;
// --- Estado del cargador incremental --- // --- Estado del cargador incremental ---
enum class LoadStage { enum class LoadStage : std::uint8_t {
SOUNDS, SOUNDS,
MUSICS, MUSICS,
TEXTURES, TEXTURES,
@@ -187,8 +190,8 @@ class Resource {
// --- Métodos para carga perezosa --- // --- Métodos para carga perezosa ---
void initResourceLists(); // Inicializa las listas de recursos sin cargar el contenido 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 loadSoundLazy(const std::string& name) -> Ja::Sound*; // 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 loadMusicLazy(const std::string& name) -> Ja::Music*; // 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 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 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 auto loadTextLazy(const std::string& name) -> std::shared_ptr<Text>; // Carga un objeto de texto específico bajo demanda
@@ -200,6 +203,10 @@ class Resource {
void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso
void updateProgressBar(); // Actualiza la barra de estado void updateProgressBar(); // Actualiza la barra de estado
// Avança una etapa que descarrega una llista d'assets: si `stage_index_` desborda la mida,
// salta a `next_stage`; si no, crida `load_one` per a l'element actual i incrementa.
void advanceListLoadStage(const std::vector<std::string>& list, void (Resource::*load_one)(size_t), LoadStage next_stage);
// --- Helpers del cargador incremental (cargan un único recurso) --- // --- Helpers del cargador incremental (cargan un único recurso) ---
void loadOneSound(size_t idx); void loadOneSound(size_t idx);
void loadOneMusic(size_t idx); void loadOneMusic(size_t idx);
+41 -67
View File
@@ -38,6 +38,38 @@
#include "game/ui/service_menu.hpp" // Para ServiceMenu #include "game/ui/service_menu.hpp" // Para ServiceMenu
#include "utils/param.hpp" // Para loadParamsFromFile #include "utils/param.hpp" // Para loadParamsFromFile
namespace {
// Llig un camp opcional d'un YAML cap a `dest`. Si no existeix, no toca `dest`.
// Si existeix però el tipus no encaixa, deixa el valor per defecte i avisa per stderr
// (un debug.yaml mal escrit no ha de tombar l'arrencada, però l'usuari ha de saber-ho).
template <typename T>
void loadYamlField(const fkyaml::node& yaml, const std::string& key, T& dest) {
if (!yaml.contains(key)) { return; }
try {
dest = yaml[key].get_value<T>();
} catch (...) {
std::cerr << "debug.yaml: valor invàlid per a '" << key << "', es manté el valor per defecte\n";
}
}
auto parseInitialSection(const std::string& value) -> Section::Name {
if (value == "logo") { return Section::Name::LOGO; }
if (value == "intro") { return Section::Name::INTRO; }
if (value == "title") { return Section::Name::TITLE; }
if (value == "credits") { return Section::Name::CREDITS; }
if (value == "instructions") { return Section::Name::INSTRUCTIONS; }
if (value == "hiscore") { return Section::Name::HI_SCORE_TABLE; }
return Section::Name::GAME; // "game" i qualsevol valor desconegut
}
auto parseInitialOptions(const std::string& value) -> Section::Options {
if (value == "none") { return Section::Options::NONE; }
if (value == "2p") { return Section::Options::GAME_PLAY_2P; }
if (value == "both") { return Section::Options::GAME_PLAY_BOTH; }
return Section::Options::GAME_PLAY_1P; // "1p" i qualsevol valor desconegut
}
} // namespace
// Constructor // Constructor
Director::Director() { Director::Director() {
Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO;
@@ -275,78 +307,20 @@ void Director::loadDebugConfig() {
file.close(); file.close();
try { try {
auto yaml = fkyaml::node::deserialize(content); auto yaml = fkyaml::node::deserialize(content);
if (yaml.contains("initial_section")) { loadYamlField(yaml, "initial_section", debug_config.initial_section);
try { loadYamlField(yaml, "initial_options", debug_config.initial_options);
debug_config.initial_section = yaml["initial_section"].get_value<std::string>(); loadYamlField(yaml, "initial_stage", debug_config.initial_stage);
} catch (...) {} loadYamlField(yaml, "show_render_info", debug_config.show_render_info);
} loadYamlField(yaml, "resource_loading", debug_config.resource_loading);
if (yaml.contains("initial_options")) { loadYamlField(yaml, "autoplay", debug_config.autoplay);
try { loadYamlField(yaml, "invincibility", debug_config.invincibility);
debug_config.initial_options = yaml["initial_options"].get_value<std::string>();
} catch (...) {}
}
if (yaml.contains("initial_stage")) {
try {
debug_config.initial_stage = yaml["initial_stage"].get_value<int>();
} catch (...) {}
}
if (yaml.contains("show_render_info")) {
try {
debug_config.show_render_info = yaml["show_render_info"].get_value<bool>();
} catch (...) {}
}
if (yaml.contains("resource_loading")) {
try {
debug_config.resource_loading = yaml["resource_loading"].get_value<std::string>();
} catch (...) {}
}
if (yaml.contains("autoplay")) {
try {
debug_config.autoplay = yaml["autoplay"].get_value<bool>();
} catch (...) {}
}
if (yaml.contains("invincibility")) {
try {
debug_config.invincibility = yaml["invincibility"].get_value<bool>();
} catch (...) {}
}
} catch (...) { } catch (...) {
std::cout << "Error parsing debug.yaml, using defaults" << '\n'; std::cout << "Error parsing debug.yaml, using defaults" << '\n';
} }
} }
// Mapear strings a enums Section::name = parseInitialSection(debug_config.initial_section);
const auto& sec = debug_config.initial_section; Section::options = parseInitialOptions(debug_config.initial_options);
if (sec == "logo") {
Section::name = Section::Name::LOGO;
} else if (sec == "intro") {
Section::name = Section::Name::INTRO;
} else if (sec == "title") {
Section::name = Section::Name::TITLE;
} else if (sec == "game") {
Section::name = Section::Name::GAME;
} else if (sec == "credits") {
Section::name = Section::Name::CREDITS;
} else if (sec == "instructions") {
Section::name = Section::Name::INSTRUCTIONS;
} else if (sec == "hiscore") {
Section::name = Section::Name::HI_SCORE_TABLE;
} else {
Section::name = Section::Name::GAME;
}
const auto& opt = debug_config.initial_options;
if (opt == "none") {
Section::options = Section::Options::NONE;
} else if (opt == "1p") {
Section::options = Section::Options::GAME_PLAY_1P;
} else if (opt == "2p") {
Section::options = Section::Options::GAME_PLAY_2P;
} else if (opt == "both") {
Section::options = Section::Options::GAME_PLAY_BOTH;
} else {
Section::options = Section::Options::GAME_PLAY_1P;
}
} }
// Crea la carpeta del sistema donde guardar datos // Crea la carpeta del sistema donde guardar datos
+4 -2
View File
@@ -7,8 +7,10 @@
#include "core/system/section.hpp" // Para Section::Name #include "core/system/section.hpp" // Para Section::Name
#include <cstdint> // Para std::uint8_t
namespace Lang { namespace Lang {
enum class Code : int; enum class Code : std::uint8_t;
} }
// Declaraciones adelantadas de las secciones // Declaraciones adelantadas de las secciones
@@ -70,7 +72,7 @@ class Director {
// --- Inicialización y cierre del sistema --- // --- Inicialización y cierre del sistema ---
void init(); // Inicializa la aplicación (pre-boot) void init(); // Inicializa la aplicación (pre-boot)
void finishBoot(); // Post-boot: inicializa lo que depende de recursos cargados static void finishBoot(); // Post-boot: inicializa lo que depende de recursos cargados
static void close(); // Cierra y libera recursos static void close(); // Cierra y libera recursos
// --- Configuración inicial --- // --- Configuración inicial ---
+5 -3
View File
@@ -1,5 +1,7 @@
#pragma once #pragma once
#include <cstdint>
/* /*
Namespace section: define los estados/secciones principales del programa, Namespace section: define los estados/secciones principales del programa,
así como las opciones y modos especiales (como el Attract Mode). así como las opciones y modos especiales (como el Attract Mode).
@@ -8,7 +10,7 @@
namespace Section { namespace Section {
// --- Enumeraciones de secciones del programa --- // --- Enumeraciones de secciones del programa ---
enum class Name { enum class Name : std::uint8_t {
RESET, // Inicialización RESET, // Inicialización
PRELOAD, // Carga incremental de recursos PRELOAD, // Carga incremental de recursos
LOGO, // Pantalla de logo LOGO, // Pantalla de logo
@@ -23,7 +25,7 @@ namespace Section {
}; };
// --- Opciones para la sección actual --- // --- Opciones para la sección actual ---
enum class Options { enum class Options : std::uint8_t {
GAME_PLAY_1P, // Iniciar el juego con el jugador 1 GAME_PLAY_1P, // Iniciar el juego con el jugador 1
GAME_PLAY_2P, // Iniciar el juego con el jugador 2 GAME_PLAY_2P, // Iniciar el juego con el jugador 2
GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores
@@ -37,7 +39,7 @@ namespace Section {
}; };
// --- Modos para el Attract Mode --- // --- Modos para el Attract Mode ---
enum class AttractMode { enum class AttractMode : std::uint8_t {
TITLE_TO_DEMO, // Pasar de título a demo TITLE_TO_DEMO, // Pasar de título a demo
TITLE_TO_LOGO, // Pasar de título a logo TITLE_TO_LOGO, // Pasar de título a logo
}; };
+3 -1
View File
@@ -1,10 +1,12 @@
#pragma once #pragma once
#include <cstdint>
// --- Namespace SystemShutdown: utilidad multiplataforma para apagar el sistema de forma segura --- // --- Namespace SystemShutdown: utilidad multiplataforma para apagar el sistema de forma segura ---
namespace SystemShutdown { namespace SystemShutdown {
// --- Enums --- // --- Enums ---
enum class ShutdownResult { enum class ShutdownResult : std::uint8_t {
SUCCESS = 0, // Éxito SUCCESS = 0, // Éxito
ERROR_PERMISSION, // Error de permisos insuficientes ERROR_PERMISSION, // Error de permisos insuficientes
ERROR_SYSTEM_CALL, // Error en la llamada al sistema ERROR_SYSTEM_CALL, // Error en la llamada al sistema
+2 -1
View File
@@ -1,11 +1,12 @@
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
// --- Namespace SystemUtils: utilidades multiplataforma para operaciones del sistema --- // --- Namespace SystemUtils: utilidades multiplataforma para operaciones del sistema ---
namespace SystemUtils { namespace SystemUtils {
// --- Enums --- // --- Enums ---
enum class Result { // Códigos de resultado para operaciones del sistema enum class Result : std::uint8_t { // Códigos de resultado para operaciones del sistema
SUCCESS = 0, SUCCESS = 0,
PERMISSION_DENIED, // Sin permisos para crear la carpeta PERMISSION_DENIED, // Sin permisos para crear la carpeta
PATH_TOO_LONG, // Ruta demasiado larga PATH_TOO_LONG, // Ruta demasiado larga
+113 -228
View File
@@ -26,12 +26,12 @@ namespace Options {
Keyboard keyboard; // Opciones para el teclado Keyboard keyboard; // Opciones para el teclado
PendingChanges pending_changes; // Opciones que se aplican al cerrar PendingChanges pending_changes; // Opciones que se aplican al cerrar
std::vector<PostFXPreset> postfx_presets = { std::vector<PostFXPreset> postfx_presets = {
{"CRT", 0.15F, 0.7F, 0.2F, 0.5F, 0.1F, 0.0F, 0.0F, 0.0F}, {.name = "CRT", .vignette = 0.15F, .scanlines = 0.7F, .chroma = 0.2F, .mask = 0.5F, .gamma = 0.1F, .curvature = 0.0F, .bleeding = 0.0F, .flicker = 0.0F},
{"NTSC", 0.4F, 0.5F, 0.2F, 0.3F, 0.3F, 0.0F, 0.6F, 0.0F}, {.name = "NTSC", .vignette = 0.4F, .scanlines = 0.5F, .chroma = 0.2F, .mask = 0.3F, .gamma = 0.3F, .curvature = 0.0F, .bleeding = 0.6F, .flicker = 0.0F},
{"Curved", 0.5F, 0.6F, 0.1F, 0.4F, 0.4F, 0.8F, 0.0F, 0.0F}, {.name = "Curved", .vignette = 0.5F, .scanlines = 0.6F, .chroma = 0.1F, .mask = 0.4F, .gamma = 0.4F, .curvature = 0.8F, .bleeding = 0.0F, .flicker = 0.0F},
{"Scanlines", 0.0F, 0.8F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0F}, {.name = "Scanlines", .vignette = 0.0F, .scanlines = 0.8F, .chroma = 0.0F, .mask = 0.0F, .gamma = 0.0F, .curvature = 0.0F, .bleeding = 0.0F, .flicker = 0.0F},
{"Subtle", 0.3F, 0.4F, 0.05F, 0.0F, 0.2F, 0.0F, 0.0F, 0.0F}, {.name = "Subtle", .vignette = 0.3F, .scanlines = 0.4F, .chroma = 0.05F, .mask = 0.0F, .gamma = 0.2F, .curvature = 0.0F, .bleeding = 0.0F, .flicker = 0.0F},
{"CRT Live", 0.15F, 0.6F, 0.3F, 0.3F, 0.1F, 0.0F, 0.4F, 0.8F}, {.name = "CRT Live", .vignette = 0.15F, .scanlines = 0.6F, .chroma = 0.3F, .mask = 0.3F, .gamma = 0.1F, .curvature = 0.0F, .bleeding = 0.4F, .flicker = 0.8F},
}; };
std::string postfx_file_path; std::string postfx_file_path;
std::vector<CrtPiPreset> crtpi_presets; std::vector<CrtPiPreset> crtpi_presets;
@@ -50,11 +50,17 @@ namespace Options {
void setCrtPiFile(const std::string& path) { crtpi_file_path = path; } void setCrtPiFile(const std::string& path) { crtpi_file_path = path; }
// Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión // Helper: extrae un campo float de un nodo YAML si existe, ignorando errores de conversión
static void parseFloatField(const fkyaml::node& node, const std::string& key, float& target) { // Llig un camp opcional d'un node YAML. Si no existeix, no toca `target`.
if (node.contains(key)) { // Si existeix però el tipus no encaixa, manté el valor previ i avisa per stderr
// (un fitxer de configuració parcialment malformat no ha de tombar l'arrencada,
// però l'usuari ha de saber quin camp ha quedat ignorat).
template <typename T>
void parseField(const fkyaml::node& node, const std::string& key, T& target) {
if (!node.contains(key)) { return; }
try { try {
target = node[key].get_value<float>(); target = node[key].get_value<T>();
} catch (...) {} } catch (...) {
std::cerr << "config YAML: valor invàlid per a '" << key << "', es manté el valor per defecte\n";
} }
} }
@@ -80,14 +86,14 @@ namespace Options {
if (p.contains("name")) { if (p.contains("name")) {
preset.name = p["name"].get_value<std::string>(); preset.name = p["name"].get_value<std::string>();
} }
parseFloatField(p, "vignette", preset.vignette); parseField(p, "vignette", preset.vignette);
parseFloatField(p, "scanlines", preset.scanlines); parseField(p, "scanlines", preset.scanlines);
parseFloatField(p, "chroma", preset.chroma); parseField(p, "chroma", preset.chroma);
parseFloatField(p, "mask", preset.mask); parseField(p, "mask", preset.mask);
parseFloatField(p, "gamma", preset.gamma); parseField(p, "gamma", preset.gamma);
parseFloatField(p, "curvature", preset.curvature); parseField(p, "curvature", preset.curvature);
parseFloatField(p, "bleeding", preset.bleeding); parseField(p, "bleeding", preset.bleeding);
parseFloatField(p, "flicker", preset.flicker); parseField(p, "flicker", preset.flicker);
postfx_presets.push_back(preset); postfx_presets.push_back(preset);
} }
} }
@@ -212,24 +218,6 @@ namespace Options {
return true; return true;
} }
// Helper: extrae un campo bool de un nodo YAML si existe, ignorando errores
static void parseBoolField(const fkyaml::node& node, const std::string& key, bool& target) {
if (node.contains(key)) {
try {
target = node[key].get_value<bool>();
} catch (...) {}
}
}
// Helper: extrae un campo int de un nodo YAML si existe, ignorando errores
static void parseIntField(const fkyaml::node& node, const std::string& key, int& target) {
if (node.contains(key)) {
try {
target = node[key].get_value<int>();
} catch (...) {}
}
}
// Rellena los presets CrtPi por defecto // Rellena los presets CrtPi por defecto
static void populateDefaultCrtPiPresets() { static void populateDefaultCrtPiPresets() {
crtpi_presets.clear(); crtpi_presets.clear();
@@ -289,20 +277,20 @@ namespace Options {
if (p.contains("name")) { if (p.contains("name")) {
preset.name = p["name"].get_value<std::string>(); preset.name = p["name"].get_value<std::string>();
} }
parseFloatField(p, "scanline_weight", preset.scanline_weight); parseField(p, "scanline_weight", preset.scanline_weight);
parseFloatField(p, "scanline_gap_brightness", preset.scanline_gap_brightness); parseField(p, "scanline_gap_brightness", preset.scanline_gap_brightness);
parseFloatField(p, "bloom_factor", preset.bloom_factor); parseField(p, "bloom_factor", preset.bloom_factor);
parseFloatField(p, "input_gamma", preset.input_gamma); parseField(p, "input_gamma", preset.input_gamma);
parseFloatField(p, "output_gamma", preset.output_gamma); parseField(p, "output_gamma", preset.output_gamma);
parseFloatField(p, "mask_brightness", preset.mask_brightness); parseField(p, "mask_brightness", preset.mask_brightness);
parseFloatField(p, "curvature_x", preset.curvature_x); parseField(p, "curvature_x", preset.curvature_x);
parseFloatField(p, "curvature_y", preset.curvature_y); parseField(p, "curvature_y", preset.curvature_y);
parseIntField(p, "mask_type", preset.mask_type); parseField(p, "mask_type", preset.mask_type);
parseBoolField(p, "enable_scanlines", preset.enable_scanlines); parseField(p, "enable_scanlines", preset.enable_scanlines);
parseBoolField(p, "enable_multisample", preset.enable_multisample); parseField(p, "enable_multisample", preset.enable_multisample);
parseBoolField(p, "enable_gamma", preset.enable_gamma); parseField(p, "enable_gamma", preset.enable_gamma);
parseBoolField(p, "enable_curvature", preset.enable_curvature); parseField(p, "enable_curvature", preset.enable_curvature);
parseBoolField(p, "enable_sharper", preset.enable_sharper); parseField(p, "enable_sharper", preset.enable_sharper);
crtpi_presets.push_back(preset); crtpi_presets.push_back(preset);
} }
} }
@@ -353,98 +341,46 @@ namespace Options {
void loadWindowFromYaml(const fkyaml::node& yaml) { void loadWindowFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("window")) { return; } if (!yaml.contains("window")) { return; }
const auto& win = yaml["window"]; const auto& win = yaml["window"];
if (win.contains("zoom")) { int zoom = window.zoom;
try { parseField(win, "zoom", zoom);
int val = win["zoom"].get_value<int>(); window.zoom = (zoom > 0) ? zoom : window.zoom;
window.zoom = (val > 0) ? val : window.zoom;
} catch (...) {}
}
} }
void loadVideoFromYaml(const fkyaml::node& yaml) { void loadVideoFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("video")) { return; } if (!yaml.contains("video")) { return; }
const auto& vid = yaml["video"]; const auto& vid = yaml["video"];
if (vid.contains("fullscreen")) { parseField(vid, "fullscreen", video.fullscreen);
try { parseField(vid, "vsync", video.vsync);
video.fullscreen = vid["fullscreen"].get_value<bool>(); parseField(vid, "integer_scale", video.integer_scale);
} catch (...) {}
} int scale_mode_int = static_cast<int>(video.scale_mode);
if (vid.contains("scale_mode")) { parseField(vid, "scale_mode", scale_mode_int);
try { video.scale_mode = static_cast<SDL_ScaleMode>(scale_mode_int);
video.scale_mode = static_cast<SDL_ScaleMode>(vid["scale_mode"].get_value<int>());
} catch (...) {}
}
if (vid.contains("vsync")) {
try {
video.vsync = vid["vsync"].get_value<bool>();
} catch (...) {}
}
if (vid.contains("integer_scale")) {
try {
video.integer_scale = vid["integer_scale"].get_value<bool>();
} catch (...) {}
}
// --- GPU ---
if (vid.contains("gpu")) { if (vid.contains("gpu")) {
const auto& gpu_node = vid["gpu"]; const auto& gpu_node = vid["gpu"];
if (gpu_node.contains("acceleration")) { parseField(gpu_node, "acceleration", video.gpu.acceleration);
try { parseField(gpu_node, "preferred_driver", video.gpu.preferred_driver);
video.gpu.acceleration = gpu_node["acceleration"].get_value<bool>();
} catch (...) {}
}
if (gpu_node.contains("preferred_driver")) {
try {
video.gpu.preferred_driver = gpu_node["preferred_driver"].get_value<std::string>();
} catch (...) {}
}
} }
// --- Shader config ---
if (vid.contains("shader")) { if (vid.contains("shader")) {
const auto& sh = vid["shader"]; const auto& sh = vid["shader"];
if (sh.contains("enabled")) { parseField(sh, "enabled", video.shader.enabled);
try { parseField(sh, "postfx_preset", video.shader.current_postfx_preset_name);
video.shader.enabled = sh["enabled"].get_value<bool>(); parseField(sh, "crtpi_preset", video.shader.current_crtpi_preset_name);
} catch (...) {} std::string shader_name;
} parseField(sh, "current_shader", shader_name);
if (sh.contains("current_shader")) { if (!shader_name.empty()) {
try { video.shader.current_shader = (shader_name == "crtpi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
auto s = sh["current_shader"].get_value<std::string>();
video.shader.current_shader = (s == "crtpi") ? Rendering::ShaderType::CRTPI : Rendering::ShaderType::POSTFX;
} catch (...) {}
}
if (sh.contains("postfx_preset")) {
try {
video.shader.current_postfx_preset_name = sh["postfx_preset"].get_value<std::string>();
} catch (...) {}
}
if (sh.contains("crtpi_preset")) {
try {
video.shader.current_crtpi_preset_name = sh["crtpi_preset"].get_value<std::string>();
} catch (...) {}
} }
} }
// --- Supersampling ---
if (vid.contains("supersampling")) { if (vid.contains("supersampling")) {
const auto& ss_node = vid["supersampling"]; const auto& ss_node = vid["supersampling"];
if (ss_node.contains("enabled")) { parseField(ss_node, "enabled", video.supersampling.enabled);
try { parseField(ss_node, "linear_upscale", video.supersampling.linear_upscale);
video.supersampling.enabled = ss_node["enabled"].get_value<bool>(); parseField(ss_node, "downscale_algo", video.supersampling.downscale_algo);
} catch (...) {}
}
if (ss_node.contains("linear_upscale")) {
try {
video.supersampling.linear_upscale = ss_node["linear_upscale"].get_value<bool>();
} catch (...) {}
}
if (ss_node.contains("downscale_algo")) {
try {
video.supersampling.downscale_algo = ss_node["downscale_algo"].get_value<int>();
} catch (...) {}
}
} }
} }
@@ -452,50 +388,30 @@ namespace Options {
if (!yaml.contains("audio")) { return; } if (!yaml.contains("audio")) { return; }
const auto& aud = yaml["audio"]; const auto& aud = yaml["audio"];
if (aud.contains("enabled")) { parseField(aud, "enabled", audio.enabled);
try { parseField(aud, "volume", audio.volume);
audio.enabled = aud["enabled"].get_value<bool>(); audio.volume = std::clamp(audio.volume, 0.0F, 1.0F);
} catch (...) {}
}
if (aud.contains("volume")) {
try {
audio.volume = std::clamp(aud["volume"].get_value<float>(), 0.0F, 1.0F);
} catch (...) {}
}
if (aud.contains("music")) { if (aud.contains("music")) {
const auto& mus = aud["music"]; const auto& mus = aud["music"];
if (mus.contains("enabled")) { parseField(mus, "enabled", audio.music.enabled);
try { parseField(mus, "volume", audio.music.volume);
audio.music.enabled = mus["enabled"].get_value<bool>(); audio.music.volume = std::clamp(audio.music.volume, 0.0F, 1.0F);
} catch (...) {}
}
if (mus.contains("volume")) {
try {
audio.music.volume = std::clamp(mus["volume"].get_value<float>(), 0.0F, 1.0F);
} catch (...) {}
}
} }
if (aud.contains("sound")) { if (aud.contains("sound")) {
const auto& snd = aud["sound"]; const auto& snd = aud["sound"];
if (snd.contains("enabled")) { parseField(snd, "enabled", audio.sound.enabled);
try { parseField(snd, "volume", audio.sound.volume);
audio.sound.enabled = snd["enabled"].get_value<bool>(); audio.sound.volume = std::clamp(audio.sound.volume, 0.0F, 1.0F);
} catch (...) {}
}
if (snd.contains("volume")) {
try {
audio.sound.volume = std::clamp(snd["volume"].get_value<float>(), 0.0F, 1.0F);
} catch (...) {}
}
} }
} }
void loadLoadingFromYaml(const fkyaml::node& yaml) { void loadLoadingFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("loading")) { return; } if (!yaml.contains("loading")) { return; }
const auto& ld = yaml["loading"]; const auto& ld = yaml["loading"];
parseBoolField(ld, "show", loading.show); parseField(ld, "show", loading.show);
parseBoolField(ld, "show_resource_name", loading.show_resource_name); parseField(ld, "show_resource_name", loading.show_resource_name);
parseBoolField(ld, "wait_for_input", loading.wait_for_input); parseField(ld, "wait_for_input", loading.wait_for_input);
} }
void loadGameFromYaml(const fkyaml::node& yaml) { void loadGameFromYaml(const fkyaml::node& yaml) {
@@ -503,37 +419,23 @@ namespace Options {
const auto& game = yaml["game"]; const auto& game = yaml["game"];
if (game.contains("language")) { if (game.contains("language")) {
try { int lang_int = static_cast<int>(settings.language);
auto lang = static_cast<Lang::Code>(game["language"].get_value<int>()); parseField(game, "language", lang_int);
if (lang == Lang::Code::ENGLISH || lang == Lang::Code::VALENCIAN || lang == Lang::Code::SPANISH) { const auto LANG = static_cast<Lang::Code>(lang_int);
settings.language = lang; settings.language = (LANG == Lang::Code::ENGLISH || LANG == Lang::Code::VALENCIAN || LANG == Lang::Code::SPANISH)
} else { ? LANG
settings.language = Lang::Code::ENGLISH; : Lang::Code::ENGLISH;
}
pending_changes.new_language = settings.language; pending_changes.new_language = settings.language;
} catch (...) {}
} }
if (game.contains("difficulty")) { if (game.contains("difficulty")) {
try { int diff_int = static_cast<int>(settings.difficulty);
settings.difficulty = static_cast<Difficulty::Code>(game["difficulty"].get_value<int>()); parseField(game, "difficulty", diff_int);
settings.difficulty = static_cast<Difficulty::Code>(diff_int);
pending_changes.new_difficulty = settings.difficulty; pending_changes.new_difficulty = settings.difficulty;
} catch (...) {}
}
if (game.contains("autofire")) {
try {
settings.autofire = game["autofire"].get_value<bool>();
} catch (...) {}
}
if (game.contains("shutdown_enabled")) {
try {
settings.shutdown_enabled = game["shutdown_enabled"].get_value<bool>();
} catch (...) {}
}
if (game.contains("params_file")) {
try {
settings.params_file = game["params_file"].get_value<std::string>();
} catch (...) {}
} }
parseField(game, "autofire", settings.autofire);
parseField(game, "shutdown_enabled", settings.shutdown_enabled);
parseField(game, "params_file", settings.params_file);
} }
void loadControllersFromYaml(const fkyaml::node& yaml) { void loadControllersFromYaml(const fkyaml::node& yaml) {
@@ -543,26 +445,15 @@ namespace Options {
size_t i = 0; size_t i = 0;
for (const auto& ctrl : controllers) { for (const auto& ctrl : controllers) {
if (i >= GamepadManager::size()) { break; } if (i >= GamepadManager::size()) { break; }
if (ctrl.contains("name")) { parseField(ctrl, "name", gamepad_manager[i].name);
try { parseField(ctrl, "path", gamepad_manager[i].path);
gamepad_manager[i].name = ctrl["name"].get_value<std::string>(); int player_int = 0;
} catch (...) {} parseField(ctrl, "player", player_int);
}
if (ctrl.contains("path")) {
try {
gamepad_manager[i].path = ctrl["path"].get_value<std::string>();
} catch (...) {}
}
if (ctrl.contains("player")) {
try {
int player_int = ctrl["player"].get_value<int>();
if (player_int == 1) { if (player_int == 1) {
gamepad_manager[i].player_id = Player::Id::PLAYER1; gamepad_manager[i].player_id = Player::Id::PLAYER1;
} else if (player_int == 2) { } else if (player_int == 2) {
gamepad_manager[i].player_id = Player::Id::PLAYER2; gamepad_manager[i].player_id = Player::Id::PLAYER2;
} }
} catch (...) {}
}
++i; ++i;
} }
} }
@@ -570,11 +461,9 @@ namespace Options {
void loadKeyboardFromYaml(const fkyaml::node& yaml) { void loadKeyboardFromYaml(const fkyaml::node& yaml) {
if (!yaml.contains("keyboard")) { return; } if (!yaml.contains("keyboard")) { return; }
const auto& kb = yaml["keyboard"]; const auto& kb = yaml["keyboard"];
if (kb.contains("player")) { int player_int = static_cast<int>(keyboard.player_id);
try { parseField(kb, "player", player_int);
keyboard.player_id = static_cast<Player::Id>(kb["player"].get_value<int>()); keyboard.player_id = static_cast<Player::Id>(player_int);
} catch (...) {}
}
} }
// Carga el fichero de configuración // Carga el fichero de configuración
@@ -595,11 +484,7 @@ namespace Options {
// Comprobar versión: si no coincide, regenerar config por defecto // Comprobar versión: si no coincide, regenerar config por defecto
int file_version = 0; int file_version = 0;
if (yaml.contains("version")) { parseField(yaml, "version", file_version);
try {
file_version = yaml["version"].get_value<int>();
} catch (...) {}
}
if (file_version != Settings::CURRENT_CONFIG_VERSION) { if (file_version != Settings::CURRENT_CONFIG_VERSION) {
std::cout << "Config version " << file_version << " != expected " << Settings::CURRENT_CONFIG_VERSION << ". Recreating defaults." << '\n'; std::cout << "Config version " << file_version << " != expected " << Settings::CURRENT_CONFIG_VERSION << ". Recreating defaults." << '\n';
init(); init();
@@ -820,14 +705,14 @@ namespace Options {
continue; continue;
} }
const auto it = std::ranges::find_if(physical_gamepads, const auto IT = std::ranges::find_if(physical_gamepads,
[this, &desired_path, &assigned_instances](const auto& pg) { [&desired_path, &assigned_instances](const auto& pg) {
return pg->path == desired_path && !isGamepadAssigned(pg, assigned_instances); return pg->path == desired_path && !isGamepadAssigned(pg, assigned_instances);
}); });
if (it != physical_gamepads.end()) { if (IT != physical_gamepads.end()) {
gamepads_[i].instance = *it; gamepads_[i].instance = *IT;
gamepads_[i].name = (*it)->name; gamepads_[i].name = (*IT)->name;
assigned_instances.push_back(*it); assigned_instances.push_back(*IT);
} }
} }
} }
@@ -849,15 +734,15 @@ namespace Options {
continue; continue;
} }
const auto it = std::ranges::find_if(physical_gamepads, const auto IT = std::ranges::find_if(physical_gamepads,
[this, &desired_name, &assigned_instances](const auto& pg) { [&desired_name, &assigned_instances](const auto& pg) {
return pg->name == desired_name && !isGamepadAssigned(pg, assigned_instances); return pg->name == desired_name && !isGamepadAssigned(pg, assigned_instances);
}); });
if (it != physical_gamepads.end()) { if (IT != physical_gamepads.end()) {
gamepads_[i].instance = *it; gamepads_[i].instance = *IT;
gamepads_[i].name = (*it)->name; gamepads_[i].name = (*IT)->name;
gamepads_[i].path = (*it)->path; gamepads_[i].path = (*IT)->path;
assigned_instances.push_back(*it); assigned_instances.push_back(*IT);
} }
} }
} }
@@ -871,15 +756,15 @@ namespace Options {
continue; continue;
} }
const auto it = std::ranges::find_if(physical_gamepads, const auto IT = std::ranges::find_if(physical_gamepads,
[this, &assigned_instances](const auto& pg) { [&assigned_instances](const auto& pg) {
return !isGamepadAssigned(pg, assigned_instances); return !isGamepadAssigned(pg, assigned_instances);
}); });
if (it != physical_gamepads.end()) { if (IT != physical_gamepads.end()) {
gamepads_[i].instance = *it; gamepads_[i].instance = *IT;
gamepads_[i].name = (*it)->name; gamepads_[i].name = (*IT)->name;
gamepads_[i].path = (*it)->path; gamepads_[i].path = (*IT)->path;
assigned_instances.push_back(*it); assigned_instances.push_back(*IT);
} }
} }
} }