From dabba4117934e2ad051d16b6bdcd9066bb864990 Mon Sep 17 00:00:00 2001 From: Sergio Date: Fri, 18 Jul 2025 20:01:13 +0200 Subject: [PATCH] =?UTF-8?q?pasaeta=20loca=20de=20clang-format=20(despres?= =?UTF-8?q?=20m'arrepentir=C3=A9=20pero=20bueno)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-format | 11 + .clang-tidy | 40 + source/animated_sprite.cpp | 402 +- source/animated_sprite.h | 63 +- source/asset.cpp | 115 +- source/asset.h | 99 +- source/audio.cpp | 73 +- source/audio.h | 80 +- source/background.cpp | 103 +- source/background.h | 158 +- source/balloon.cpp | 634 +- source/balloon.h | 270 +- source/balloon_formations.cpp | 728 +- source/balloon_formations.h | 38 +- source/balloon_manager.cpp | 235 +- source/balloon_manager.h | 108 +- source/bullet.cpp | 75 +- source/bullet.h | 96 +- source/define_buttons.cpp | 104 +- source/define_buttons.h | 65 +- source/director.cpp | 1046 ++- source/director.h | 80 +- source/enter_name.cpp | 112 +- source/enter_name.h | 52 +- source/explosions.cpp | 43 +- source/explosions.h | 24 +- source/external/gif.cpp | 442 +- source/external/gif.h | 152 +- source/external/jail_audio.cpp | 198 +- source/external/jail_audio.h | 6 +- source/external/jail_shader.cpp | 755 +- source/external/jail_shader.h | 14 +- source/external/stb_image.h | 12766 +++++++++++++--------------- source/external/stb_vorbis.h | 8797 +++++++++---------- source/fade.cpp | 535 +- source/fade.h | 133 +- source/game_logo.cpp | 220 +- source/game_logo.h | 80 +- source/global_events.cpp | 25 +- source/global_events.h | 9 +- source/global_inputs.cpp | 588 +- source/global_inputs.h | 9 +- source/input.cpp | 625 +- source/input.h | 228 +- source/item.cpp | 367 +- source/item.h | 69 +- source/lang.cpp | 228 +- source/lang.h | 73 +- source/main.cpp | 7 +- source/manage_hiscore_table.cpp | 62 +- source/manage_hiscore_table.h | 20 +- source/mouse.cpp | 46 +- source/mouse.h | 21 +- source/moving_sprite.cpp | 151 +- source/moving_sprite.h | 71 +- source/notifier.cpp | 157 +- source/notifier.h | 92 +- source/options.cpp | 657 +- source/options.h | 339 +- source/param.cpp | 875 +- source/param.h | 155 +- source/path_sprite.cpp | 226 +- source/path_sprite.h | 101 +- source/player.cpp | 1491 ++-- source/player.h | 247 +- source/resource.cpp | 232 +- source/resource.h | 166 +- source/scoreboard.cpp | 654 +- source/scoreboard.h | 182 +- source/screen.cpp | 243 +- source/screen.h | 352 +- source/section.cpp | 11 +- source/section.h | 78 +- source/sections/credits.cpp | 225 +- source/sections/credits.h | 94 +- source/sections/game.cpp | 3133 +++---- source/sections/game.h | 392 +- source/sections/hiscore_table.cpp | 629 +- source/sections/hiscore_table.h | 108 +- source/sections/instructions.cpp | 541 +- source/sections/instructions.h | 121 +- source/sections/intro.cpp | 951 +-- source/sections/intro.h | 114 +- source/sections/logo.cpp | 298 +- source/sections/logo.h | 84 +- source/sections/title.cpp | 856 +- source/sections/title.h | 132 +- source/smart_sprite.cpp | 150 +- source/smart_sprite.h | 65 +- source/sprite.cpp | 46 +- source/sprite.h | 24 +- source/stage.cpp | 74 +- source/stage.h | 44 +- source/tabe.cpp | 184 +- source/tabe.h | 113 +- source/text.cpp | 400 +- source/text.h | 88 +- source/texture.cpp | 587 +- source/texture.h | 85 +- source/tiled_bg.cpp | 128 +- source/tiled_bg.h | 54 +- source/ui/menu_option.h | 112 +- source/ui/menu_renderer.cpp | 109 +- source/ui/menu_renderer.h | 20 +- source/ui/service_menu.cpp | 203 +- source/ui/service_menu.h | 20 +- source/ui/ui_message.cpp | 51 +- source/ui/ui_message.h | 39 +- source/utils.cpp | 291 +- source/utils.h | 258 +- source/writer.cpp | 118 +- source/writer.h | 85 +- 112 files changed, 22361 insertions(+), 26474 deletions(-) create mode 100644 .clang-format create mode 100644 .clang-tidy diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3652702 --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 0 # Sin límite de longitud de línea +BreakBeforeBraces: Attach # Llaves en la misma línea +AllowShortIfStatementsOnASingleLine: true +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AlignOperands: false +AlignAfterOpenBracket: DontAlign +BinPackArguments: false +BinPackParameters: false diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..21354e4 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,40 @@ +Checks: > + readability-identifier-naming, + readability-*, + modernize-*, + clang-analyzer-* + +WarningsAsErrors: '*' + +HeaderFilterRegex: '.*' +FormatStyle: file + +CheckOptions: + # Variables locales en snake_case + - { key: readability-identifier-naming.VariableCase, value: lower_case } + + # Miembros privados en snake_case con sufijo _ + - { key: readability-identifier-naming.PrivateMemberCase, value: lower_case } + - { key: readability-identifier-naming.PrivateMemberSuffix, value: _ } + + # Namespaces en CamelCase + - { key: readability-identifier-naming.NamespaceCase, value: CamelCase } + + # Constantes y constexpr en UPPER_CASE + - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } + - { key: readability-identifier-naming.ConstexprVariableCase, value: UPPER_CASE } + - { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE } + + # Clases, structs y enums en CamelCase + - { key: readability-identifier-naming.ClassCase, value: CamelCase } + - { key: readability-identifier-naming.StructCase, value: CamelCase } + - { key: readability-identifier-naming.EnumCase, value: CamelCase } + + # Valores de enums en UPPER_CASE + - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } + + # Métodos en camelBack + - { key: readability-identifier-naming.MethodCase, value: camelBack } + + # Funciones en camelBack + - { key: readability-identifier-naming.FunctionCase, value: camelBack } diff --git a/source/animated_sprite.cpp b/source/animated_sprite.cpp index 71222a0..5db7e66 100644 --- a/source/animated_sprite.cpp +++ b/source/animated_sprite.cpp @@ -1,280 +1,240 @@ #include "animated_sprite.h" -#include // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError -#include // Para size_t -#include // Para min -#include // Para basic_istream, basic_ifstream, basic_ios, ifst... -#include // Para basic_stringstream -#include // Para runtime_error -#include // Para pair +#include // Para SDL_LogWarn, SDL_LogCategory, SDL_LogError +#include // Para size_t -#include "texture.h" // Para Texture -#include "utils.h" // Para printWithDots +#include // Para min +#include // Para basic_istream, basic_ifstream, basic_ios, ifst... +#include // Para basic_stringstream +#include // Para runtime_error +#include // Para pair + +#include "texture.h" // Para Texture +#include "utils.h" // Para printWithDots // Carga las animaciones en un vector(Animations) desde un fichero -AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path) -{ - std::ifstream file(file_path); - if (!file) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); - throw std::runtime_error("Fichero no encontrado: " + file_path); - } +AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path) { + std::ifstream file(file_path); + if (!file) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); + throw std::runtime_error("Fichero no encontrado: " + file_path); + } - printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]"); + printWithDots("Animation : ", file_path.substr(file_path.find_last_of("\\/") + 1), "[ LOADED ]"); - std::vector buffer; - std::string line; - while (std::getline(file, line)) - { - if (!line.empty()) - buffer.push_back(line); - } + std::vector buffer; + std::string line; + while (std::getline(file, line)) { + if (!line.empty()) + buffer.push_back(line); + } - return buffer; + return buffer; } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const std::string &file_path) - : MovingSprite(texture) -{ - // Carga las animaciones - if (!file_path.empty()) - { - AnimationsFileBuffer v = loadAnimationsFromFile(file_path); - loadFromAnimationsFileBuffer(v); - } + : MovingSprite(texture) { + // Carga las animaciones + if (!file_path.empty()) { + AnimationsFileBuffer v = loadAnimationsFromFile(file_path); + loadFromAnimationsFileBuffer(v); + } } // Constructor AnimatedSprite::AnimatedSprite(std::shared_ptr texture, const AnimationsFileBuffer &animations) - : MovingSprite(texture) -{ - if (!animations.empty()) - { - loadFromAnimationsFileBuffer(animations); - } + : MovingSprite(texture) { + if (!animations.empty()) { + loadFromAnimationsFileBuffer(animations); + } } // Obtiene el índice de la animación a partir del nombre -int AnimatedSprite::getIndex(const std::string &name) -{ - auto it = animation_indices_.find(name); - if (it != animation_indices_.end()) - { - // Si se encuentra la animación en el mapa, devuelve su índice - return it->second; - } +int AnimatedSprite::getIndex(const std::string &name) { + auto it = animation_indices_.find(name); + if (it != animation_indices_.end()) { + // Si se encuentra la animación en el mapa, devuelve su índice + return it->second; + } - // Si no se encuentra, muestra una advertencia y devuelve -1 - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str()); - return -1; + // Si no se encuentra, muestra una advertencia y devuelve -1 + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "** Warning: could not find \"%s\" animation", name.c_str()); + return -1; } // Calcula el frame correspondiente a la animación -void AnimatedSprite::animate() -{ - if (animations_[current_animation_].speed == 0) - { - return; - } +void AnimatedSprite::animate() { + if (animations_[current_animation_].speed == 0) { + return; + } - // Calcula el frame actual a partir del contador - animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed; + // Calcula el frame actual a partir del contador + animations_[current_animation_].current_frame = animations_[current_animation_].counter / animations_[current_animation_].speed; - // Si alcanza el final de la animación, reinicia el contador de la animación - // en función de la variable loop y coloca el nuevo frame - if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) - { - if (animations_[current_animation_].loop == -1) - { // Si no hay loop, deja el último frame - animations_[current_animation_].current_frame = animations_[current_animation_].frames.size(); - animations_[current_animation_].completed = true; - } - else - { // Si hay loop, vuelve al frame indicado - animations_[current_animation_].counter = 0; - animations_[current_animation_].current_frame = animations_[current_animation_].loop; - } - } - // En caso contrario - else - { - // Escoge el frame correspondiente de la animación - setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); + // Si alcanza el final de la animación, reinicia el contador de la animación + // en función de la variable loop y coloca el nuevo frame + if (animations_[current_animation_].current_frame >= animations_[current_animation_].frames.size()) { + if (animations_[current_animation_].loop == -1) { // Si no hay loop, deja el último frame + animations_[current_animation_].current_frame = animations_[current_animation_].frames.size(); + animations_[current_animation_].completed = true; + } else { // Si hay loop, vuelve al frame indicado + animations_[current_animation_].counter = 0; + animations_[current_animation_].current_frame = animations_[current_animation_].loop; + } + } + // En caso contrario + else { + // Escoge el frame correspondiente de la animación + setSpriteClip(animations_[current_animation_].frames[animations_[current_animation_].current_frame]); - // Incrementa el contador de la animacion - animations_[current_animation_].counter++; - } + // Incrementa el contador de la animacion + animations_[current_animation_].counter++; + } } // Comprueba si ha terminado la animación -bool AnimatedSprite::animationIsCompleted() -{ - return animations_[current_animation_].completed; +bool AnimatedSprite::animationIsCompleted() { + return animations_[current_animation_].completed; } // Establece la animacion actual -void AnimatedSprite::setCurrentAnimation(const std::string &name, bool reset) -{ - const auto NEW_ANIMATION = getIndex(name); - if (current_animation_ != NEW_ANIMATION) - { - const auto OLD_ANIMATION = current_animation_; - current_animation_ = NEW_ANIMATION; - if (reset) - { - animations_[current_animation_].current_frame = 0; - animations_[current_animation_].counter = 0; - animations_[current_animation_].completed = false; - } - else - { - animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); - animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; - animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; - } - } +void AnimatedSprite::setCurrentAnimation(const std::string &name, bool reset) { + const auto NEW_ANIMATION = getIndex(name); + if (current_animation_ != NEW_ANIMATION) { + const auto OLD_ANIMATION = current_animation_; + current_animation_ = NEW_ANIMATION; + if (reset) { + animations_[current_animation_].current_frame = 0; + animations_[current_animation_].counter = 0; + animations_[current_animation_].completed = false; + } else { + animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); + animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; + animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; + } + } } // Establece la animacion actual -void AnimatedSprite::setCurrentAnimation(int index, bool reset) -{ - const auto NEW_ANIMATION = index; - if (current_animation_ != NEW_ANIMATION) - { - const auto OLD_ANIMATION = current_animation_; - current_animation_ = NEW_ANIMATION; - if (reset) - { - animations_[current_animation_].current_frame = 0; - animations_[current_animation_].counter = 0; - animations_[current_animation_].completed = false; - } - else - { - animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); - animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; - animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; - } - } +void AnimatedSprite::setCurrentAnimation(int index, bool reset) { + const auto NEW_ANIMATION = index; + if (current_animation_ != NEW_ANIMATION) { + const auto OLD_ANIMATION = current_animation_; + current_animation_ = NEW_ANIMATION; + if (reset) { + animations_[current_animation_].current_frame = 0; + animations_[current_animation_].counter = 0; + animations_[current_animation_].completed = false; + } else { + animations_[current_animation_].current_frame = std::min(animations_[OLD_ANIMATION].current_frame, animations_[current_animation_].frames.size()); + animations_[current_animation_].counter = animations_[OLD_ANIMATION].counter; + animations_[current_animation_].completed = animations_[OLD_ANIMATION].completed; + } + } } // Actualiza las variables del objeto -void AnimatedSprite::update() -{ - animate(); - MovingSprite::update(); +void AnimatedSprite::update() { + animate(); + MovingSprite::update(); } // Reinicia la animación -void AnimatedSprite::resetAnimation() -{ - animations_[current_animation_].current_frame = 0; - animations_[current_animation_].counter = 0; - animations_[current_animation_].completed = false; +void AnimatedSprite::resetAnimation() { + animations_[current_animation_].current_frame = 0; + animations_[current_animation_].counter = 0; + animations_[current_animation_].completed = false; } // Carga la animación desde un vector de cadenas -void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source) -{ - float frame_width = 1; - float frame_height = 1; - int frames_per_row = 1; - int max_tiles = 1; +void AnimatedSprite::loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source) { + float frame_width = 1; + float frame_height = 1; + int frames_per_row = 1; + int max_tiles = 1; - size_t index = 0; - while (index < source.size()) - { - std::string line = source.at(index); + size_t index = 0; + while (index < source.size()) { + std::string line = source.at(index); - // Parsea el fichero para buscar variables y valores - if (line != "[animation]") - { - // Encuentra la posición del carácter '=' - size_t pos = line.find("="); + // Parsea el fichero para buscar variables y valores + if (line != "[animation]") { + // Encuentra la posición del carácter '=' + size_t pos = line.find("="); - // Procesa las dos subcadenas - if (pos != std::string::npos) - { - std::string key = line.substr(0, pos); - int value = std::stoi(line.substr(pos + 1)); - if (key == "frame_width") - frame_width = value; - else if (key == "frame_height") - frame_height = value; - else - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); + // Procesa las dos subcadenas + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + int value = std::stoi(line.substr(pos + 1)); + if (key == "frame_width") + frame_width = value; + else if (key == "frame_height") + frame_height = value; + else + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); - frames_per_row = texture_->getWidth() / frame_width; - const int w = texture_->getWidth() / frame_width; - const int h = texture_->getHeight() / frame_height; - max_tiles = w * h; - } - } + frames_per_row = texture_->getWidth() / frame_width; + const int w = texture_->getWidth() / frame_width; + const int h = texture_->getHeight() / frame_height; + max_tiles = w * h; + } + } - // Si la línea contiene el texto [animation] se realiza el proceso de carga de una animación - if (line == "[animation]") - { - Animation animation; - do - { - index++; - line = source.at(index); - size_t pos = line.find("="); + // Si la línea contiene el texto [animation] se realiza el proceso de carga de una animación + if (line == "[animation]") { + Animation animation; + do { + index++; + line = source.at(index); + size_t pos = line.find("="); - if (pos != std::string::npos) - { - std::string key = line.substr(0, pos); - std::string value = line.substr(pos + 1); + if (pos != std::string::npos) { + std::string key = line.substr(0, pos); + std::string value = line.substr(pos + 1); - if (key == "name") - animation.name = value; - else if (key == "speed") - animation.speed = std::stoi(value); - else if (key == "loop") - animation.loop = std::stoi(value); - else if (key == "frames") - { - // Se introducen los valores separados por comas en un vector - std::stringstream ss(value); - std::string tmp; - SDL_FRect rect = {0, 0, frame_width, frame_height}; - while (getline(ss, tmp, ',')) - { - // Comprueba que el tile no sea mayor que el máximo índice permitido - const int num_tile = std::stoi(tmp); - if (num_tile <= max_tiles) - { - rect.x = (num_tile % frames_per_row) * frame_width; - rect.y = (num_tile / frames_per_row) * frame_height; - animation.frames.emplace_back(rect); - } - } - } - else - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); - } - } while (line != "[/animation]"); + if (key == "name") + animation.name = value; + else if (key == "speed") + animation.speed = std::stoi(value); + else if (key == "loop") + animation.loop = std::stoi(value); + else if (key == "frames") { + // Se introducen los valores separados por comas en un vector + std::stringstream ss(value); + std::string tmp; + SDL_FRect rect = {0, 0, frame_width, frame_height}; + while (getline(ss, tmp, ',')) { + // Comprueba que el tile no sea mayor que el máximo índice permitido + const int num_tile = std::stoi(tmp); + if (num_tile <= max_tiles) { + rect.x = (num_tile % frames_per_row) * frame_width; + rect.y = (num_tile / frames_per_row) * frame_height; + animation.frames.emplace_back(rect); + } + } + } else + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: unknown parameter %s", key.c_str()); + } + } while (line != "[/animation]"); - // Añade la animación al vector de animaciones - animations_.emplace_back(animation); + // Añade la animación al vector de animaciones + animations_.emplace_back(animation); - // Rellena el mapa con el nombre y el nuevo índice - animation_indices_[animation.name] = animations_.size() - 1; - } + // Rellena el mapa con el nombre y el nuevo índice + animation_indices_[animation.name] = animations_.size() - 1; + } - // Una vez procesada la línea, aumenta el índice para pasar a la siguiente - index++; - } + // Una vez procesada la línea, aumenta el índice para pasar a la siguiente + index++; + } - // Pone un valor por defecto - setWidth(frame_width); - setHeight(frame_height); + // Pone un valor por defecto + setWidth(frame_width); + setHeight(frame_height); } // Establece la velocidad de la animación -void AnimatedSprite::setAnimationSpeed(size_t value) -{ - animations_[current_animation_].speed = value; +void AnimatedSprite::setAnimationSpeed(size_t value) { + animations_[current_animation_].speed = value; } diff --git a/source/animated_sprite.h b/source/animated_sprite.h index 22b858d..c49ecbb 100644 --- a/source/animated_sprite.h +++ b/source/animated_sprite.h @@ -1,27 +1,27 @@ #pragma once -#include // Para SDL_FRect -#include // Para size_t -#include // Para shared_ptr -#include // Para basic_string, string, hash -#include // Para unordered_map -#include // Para vector +#include // Para SDL_FRect +#include // Para size_t -#include "moving_sprite.h" // Para MovingSprite +#include // Para shared_ptr +#include // Para basic_string, string, hash +#include // Para unordered_map +#include // Para vector + +#include "moving_sprite.h" // Para MovingSprite // Declaración adelantada class Texture; // Estructura de Animación -struct Animation -{ - std::string name; // Nombre de la animación - std::vector frames; // Frames que componen la animación - int speed; // Velocidad de reproducción - int loop; // Frame de vuelta al terminar (-1 para no repetir) - bool completed; // Indica si la animación ha finalizado - size_t current_frame; // Frame actual en reproducción - int counter; // Contador para la animación +struct Animation { + std::string name; // Nombre de la animación + std::vector frames; // Frames que componen la animación + int speed; // Velocidad de reproducción + int loop; // Frame de vuelta al terminar (-1 para no repetir) + bool completed; // Indica si la animación ha finalizado + size_t current_frame; // Frame actual en reproducción + int counter; // Contador para la animación Animation() : name(std::string()), speed(5), loop(0), completed(false), current_frame(0), counter(0) {} }; @@ -33,9 +33,8 @@ using AnimationsFileBuffer = std::vector; AnimationsFileBuffer loadAnimationsFromFile(const std::string &file_path); // Clase AnimatedSprite: Sprite animado que hereda de MovingSprite -class AnimatedSprite : public MovingSprite -{ -public: +class AnimatedSprite : public MovingSprite { + public: // --- Constructores y destructor --- AnimatedSprite(std::shared_ptr texture, const std::string &file_path); AnimatedSprite(std::shared_ptr texture, const AnimationsFileBuffer &animations); @@ -43,28 +42,28 @@ public: virtual ~AnimatedSprite() override = default; // --- Métodos principales --- - void update() override; // Actualiza la animación + void update() override; // Actualiza la animación // --- Control de animaciones --- - void setCurrentAnimation(const std::string &name = "default", bool reset = true); // Establece la animación por nombre - void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice - void resetAnimation(); // Reinicia la animación actual - void setAnimationSpeed(size_t value); // Establece la velocidad de la animación - size_t getAnimationSpeed() const { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual + void setCurrentAnimation(const std::string &name = "default", bool reset = true); // Establece la animación por nombre + void setCurrentAnimation(int index = 0, bool reset = true); // Establece la animación por índice + void resetAnimation(); // Reinicia la animación actual + void setAnimationSpeed(size_t value); // Establece la velocidad de la animación + size_t getAnimationSpeed() const { return animations_[current_animation_].speed; } // Obtiene la velocidad de la animación actual // --- Consultas --- - bool animationIsCompleted(); // Comprueba si la animación ha terminado - int getIndex(const std::string &name); // Obtiene el índice de una animación por nombre + bool animationIsCompleted(); // Comprueba si la animación ha terminado + int getIndex(const std::string &name); // Obtiene el índice de una animación por nombre -protected: + protected: // --- Datos de animación --- - std::vector animations_; // Vector de animaciones disponibles - int current_animation_ = 0; // Índice de la animación activa + std::vector animations_; // Vector de animaciones disponibles + int current_animation_ = 0; // Índice de la animación activa // --- Mapa para búsqueda rápida de animaciones por nombre --- std::unordered_map animation_indices_; // --- Métodos internos --- - void animate(); // Calcula el frame actual de la animación - void loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source); // Carga animaciones desde un buffer + void animate(); // Calcula el frame actual de la animación + void loadFromAnimationsFileBuffer(const AnimationsFileBuffer &source); // Carga animaciones desde un buffer }; \ No newline at end of file diff --git a/source/asset.cpp b/source/asset.cpp index 2ef067b..2baa3ac 100644 --- a/source/asset.cpp +++ b/source/asset.cpp @@ -1,11 +1,12 @@ #include "asset.h" -#include // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError +#include // Para SDL_LogInfo, SDL_LogCategory, SDL_LogError + #include // Para find_if, max #include // Para basic_ifstream, ifstream #include // Para allocator, string, char_traits, operator+ -#include "utils.h" // Para getFileName +#include "utils.h" // Para getFileName // Singleton Asset *Asset::instance_ = nullptr; @@ -20,62 +21,48 @@ void Asset::destroy() { delete Asset::instance_; } Asset *Asset::get() { return Asset::instance_; } // Añade un elemento a la lista -void Asset::add(const std::string &file, AssetType type, bool required, bool absolute) -{ +void Asset::add(const std::string &file, AssetType type, bool required, bool absolute) { file_list_.emplace_back(absolute ? file : executable_path_ + file, type, required); longest_name_ = std::max(longest_name_, static_cast(file_list_.back().file.size())); } // Devuelve la ruta completa a un fichero a partir de una cadena -std::string Asset::get(const std::string &text) const -{ - auto it = std::find_if(file_list_.begin(), file_list_.end(), - [&text](const auto &f) - { - return getFileName(f.file) == text; - }); +std::string Asset::get(const std::string &text) const { + auto it = std::find_if(file_list_.begin(), file_list_.end(), [&text](const auto &f) { + return getFileName(f.file) == text; + }); - if (it != file_list_.end()) - { + if (it != file_list_.end()) { return it->file; - } - else - { + } else { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Warning: file %s not found", text.c_str()); return ""; } } // Comprueba que existen todos los elementos -bool Asset::check() const -{ +bool Asset::check() const { bool success = true; SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES"); // Comprueba la lista de ficheros clasificándolos por tipo - for (int type = 0; type < static_cast(AssetType::COUNT); ++type) - { + for (int type = 0; type < static_cast(AssetType::COUNT); ++type) { // Comprueba si hay ficheros de ese tipo bool any = false; - for (const auto &f : file_list_) - { - if (f.required && f.type == static_cast(type)) - { + for (const auto &f : file_list_) { + if (f.required && f.type == static_cast(type)) { any = true; } } // Si hay ficheros de ese tipo, comprueba si existen - if (any) - { + if (any) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> %s FILES", getTypeName(static_cast(type)).c_str()); - for (const auto &f : file_list_) - { - if (f.required && f.type == static_cast(type)) - { + for (const auto &f : file_list_) { + if (f.required && f.type == static_cast(type)) { success &= checkFile(f.file); } } @@ -85,12 +72,9 @@ bool Asset::check() const } // Resultado - if (success) - { + if (success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES COMPLETED.\n"); - } - else - { + } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "\n** CHECKING FILES FAILED.\n"); } @@ -98,14 +82,12 @@ bool Asset::check() const } // Comprueba que existe un fichero -bool Asset::checkFile(const std::string &path) const -{ +bool Asset::checkFile(const std::string &path) const { std::ifstream file(path); bool success = file.good(); file.close(); - if (!success) - { + if (!success) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Checking file: %s [ ERROR ]", getFileName(path).c_str()); } @@ -113,42 +95,37 @@ bool Asset::checkFile(const std::string &path) const } // Devuelve el nombre del tipo de recurso -std::string Asset::getTypeName(AssetType type) const -{ - switch (type) - { - case AssetType::BITMAP: - return "BITMAP"; - case AssetType::MUSIC: - return "MUSIC"; - case AssetType::SOUND: - return "SOUND"; - case AssetType::FONT: - return "FONT"; - case AssetType::LANG: - return "LANG"; - case AssetType::DATA: - return "DATA"; - case AssetType::DEMODATA: - return "DEMODATA"; - case AssetType::ANIMATION: - return "ANIMATION"; - case AssetType::PALETTE: - return "PALETTE"; - default: - return "ERROR"; +std::string Asset::getTypeName(AssetType type) const { + switch (type) { + case AssetType::BITMAP: + return "BITMAP"; + case AssetType::MUSIC: + return "MUSIC"; + case AssetType::SOUND: + return "SOUND"; + case AssetType::FONT: + return "FONT"; + case AssetType::LANG: + return "LANG"; + case AssetType::DATA: + return "DATA"; + case AssetType::DEMODATA: + return "DEMODATA"; + case AssetType::ANIMATION: + return "ANIMATION"; + case AssetType::PALETTE: + return "PALETTE"; + default: + return "ERROR"; } } // Devuelve la lista de recursos de un tipo -std::vector Asset::getListByType(AssetType type) const -{ +std::vector Asset::getListByType(AssetType type) const { std::vector list; - for (auto f : file_list_) - { - if (f.type == type) - { + for (auto f : file_list_) { + if (f.type == type) { list.push_back(f.file); } } diff --git a/source/asset.h b/source/asset.h index 3b698f7..ae216df 100644 --- a/source/asset.h +++ b/source/asset.h @@ -1,66 +1,63 @@ #pragma once -#include // Para string, basic_string -#include // Para vector +#include // Para string, basic_string +#include // Para vector // Tipos de recursos gestionados por Asset -enum class AssetType : int -{ - BITMAP, - MUSIC, - SOUND, - FONT, - LANG, - DATA, - DEMODATA, - ANIMATION, - PALETTE, - COUNT, +enum class AssetType : int { + BITMAP, + MUSIC, + SOUND, + FONT, + LANG, + DATA, + DEMODATA, + ANIMATION, + PALETTE, + COUNT, }; // Clase Asset: gestor de recursos (singleton) -class Asset -{ -public: - // --- Métodos de singleton --- - static void init(const std::string &executable_path); // Inicializa el objeto Asset - static void destroy(); // Libera el objeto Asset - static Asset *get(); // Obtiene el puntero al objeto Asset +class Asset { + public: + // --- Métodos de singleton --- + static void init(const std::string &executable_path); // Inicializa el objeto Asset + static void destroy(); // Libera el objeto Asset + static Asset *get(); // Obtiene el puntero al objeto Asset - // --- Métodos para la gestión de recursos --- - void add(const std::string &file, AssetType type, bool required = true, bool absolute = false); // Añade un recurso a la lista - std::string get(const std::string &text) const; // Obtiene la ruta completa de un recurso a partir de su nombre - bool check() const; // Verifica la existencia de todos los recursos requeridos - std::vector getListByType(AssetType type) const; // Devuelve una lista de archivos de un tipo concreto + // --- Métodos para la gestión de recursos --- + void add(const std::string &file, AssetType type, bool required = true, bool absolute = false); // Añade un recurso a la lista + std::string get(const std::string &text) const; // Obtiene la ruta completa de un recurso a partir de su nombre + bool check() const; // Verifica la existencia de todos los recursos requeridos + std::vector getListByType(AssetType type) const; // Devuelve una lista de archivos de un tipo concreto -private: - // --- Estructura interna para almacenar información de cada recurso --- - struct AssetItem - { - std::string file; // Ruta del fichero desde la raíz del directorio - AssetType type; // Tipo de recurso - bool required; // Indica si el fichero es obligatorio + private: + // --- Estructura interna para almacenar información de cada recurso --- + struct AssetItem { + std::string file; // Ruta del fichero desde la raíz del directorio + AssetType type; // Tipo de recurso + bool required; // Indica si el fichero es obligatorio - AssetItem(const std::string &filePath, AssetType assetType, bool isRequired) - : file(filePath), type(assetType), required(isRequired) {} - }; + AssetItem(const std::string &filePath, AssetType assetType, bool isRequired) + : file(filePath), type(assetType), required(isRequired) {} + }; - // --- Variables internas --- - int longest_name_ = 0; // Longitud del nombre más largo - std::vector file_list_; // Lista con todas las rutas de recursos - std::string executable_path_; // Ruta del ejecutable + // --- Variables internas --- + int longest_name_ = 0; // Longitud del nombre más largo + std::vector file_list_; // Lista con todas las rutas de recursos + std::string executable_path_; // Ruta del ejecutable - // --- Métodos internos --- - bool checkFile(const std::string &path) const; // Verifica si un archivo existe - std::string getTypeName(AssetType type) const; // Devuelve el nombre textual del tipo de recurso + // --- Métodos internos --- + bool checkFile(const std::string &path) const; // Verifica si un archivo existe + std::string getTypeName(AssetType type) const; // Devuelve el nombre textual del tipo de recurso - // --- Patrón Singleton --- - explicit Asset(const std::string &executable_path) - : executable_path_(executable_path) {} - ~Asset() = default; - Asset(const Asset &) = delete; - Asset &operator=(const Asset &) = delete; + // --- Patrón Singleton --- + explicit Asset(const std::string &executable_path) + : executable_path_(executable_path) {} + ~Asset() = default; + Asset(const Asset &) = delete; + Asset &operator=(const Asset &) = delete; - // --- Singleton --- - static Asset *instance_; + // --- Singleton --- + static Asset *instance_; }; \ No newline at end of file diff --git a/source/audio.cpp b/source/audio.cpp index 4ec56d6..a897397 100644 --- a/source/audio.cpp +++ b/source/audio.cpp @@ -1,11 +1,12 @@ #include "audio.h" -#include // Para SDL_LogInfo, SDL_LogCategory, SDL_G... +#include // Para SDL_LogInfo, SDL_LogCategory, SDL_G... + #include // Para clamp -#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM... -#include "options.h" // Para AudioOptions, audio, MusicOptions -#include "resource.h" // Para Resource +#include "external/jail_audio.h" // Para JA_FadeOutMusic, JA_Init, JA_PauseM... +#include "options.h" // Para AudioOptions, audio, MusicOptions +#include "resource.h" // Para Resource // Singleton Audio *Audio::instance_ = nullptr; @@ -26,70 +27,56 @@ Audio::Audio() { initSDLAudio(); } Audio::~Audio() { JA_Quit(); } // Reproduce la música -void Audio::playMusic(const std::string &name, const int loop) -{ +void Audio::playMusic(const std::string &name, const int loop) { music_.name = name; music_.loop = loop; - if (music_enabled_ && music_.state != MusicState::PLAYING) - { + if (music_enabled_ && music_.state != MusicState::PLAYING) { JA_PlayMusic(Resource::get()->getMusic(name), loop); music_.state = MusicState::PLAYING; } } // Pausa la música -void Audio::pauseMusic() -{ - if (music_enabled_ && music_.state == MusicState::PLAYING) - { +void Audio::pauseMusic() { + if (music_enabled_ && music_.state == MusicState::PLAYING) { JA_PauseMusic(); music_.state = MusicState::PAUSED; } } // Detiene la música -void Audio::stopMusic() -{ - if (music_enabled_) - { +void Audio::stopMusic() { + if (music_enabled_) { JA_StopMusic(); music_.state = MusicState::STOPPED; } } // Reproduce un sonido -void Audio::playSound(const std::string &name, Group group) -{ - if (sound_enabled_) - { +void Audio::playSound(const std::string &name, Group group) { + if (sound_enabled_) { JA_PlaySound(Resource::get()->getSound(name), 0, static_cast(group)); } } // Detiene todos los sonidos -void Audio::stopAllSounds() -{ - if (sound_enabled_) - { +void Audio::stopAllSounds() { + if (sound_enabled_) { JA_StopChannel(-1); } } // Realiza un fundido de salida de la música -void Audio::fadeOutMusic(int milliseconds) -{ - if (music_enabled_) - { +void Audio::fadeOutMusic(int milliseconds) { + if (music_enabled_) { JA_FadeOutMusic(milliseconds); } } // Establece el volumen de los sonidos -void Audio::setSoundVolume(int sound_volume, Group group) -{ - if (sound_enabled_) - { +void Audio::setSoundVolume(int sound_volume, Group group) { + if (sound_enabled_) { sound_volume = std::clamp(sound_volume, 0, 100); const float CONVERTED_VOLUME = (sound_volume / 100.0f) * (Options::audio.volume / 100.0f); JA_SetSoundVolume(CONVERTED_VOLUME, static_cast(group)); @@ -97,10 +84,8 @@ void Audio::setSoundVolume(int sound_volume, Group group) } // Establece el volumen de la música -void Audio::setMusicVolume(int music_volume) -{ - if (music_enabled_) - { +void Audio::setMusicVolume(int music_volume) { + if (music_enabled_) { music_volume = std::clamp(music_volume, 0, 100); const float CONVERTED_VOLUME = (music_volume / 100.0f) * (Options::audio.volume / 100.0f); JA_SetMusicVolume(CONVERTED_VOLUME); @@ -108,14 +93,12 @@ void Audio::setMusicVolume(int music_volume) } // Aplica la configuración -void Audio::applySettings() -{ +void Audio::applySettings() { enable(Options::audio.enabled); } // Establecer estado general -void Audio::enable(bool value) -{ +void Audio::enable(bool value) { enabled_ = value; setSoundVolume(enabled_ ? Options::audio.sound.volume : 0); @@ -123,14 +106,10 @@ void Audio::enable(bool value) } // Inicializa SDL Audio -void Audio::initSDLAudio() -{ - if (!SDL_Init(SDL_INIT_AUDIO)) - { +void Audio::initSDLAudio() { + if (!SDL_Init(SDL_INIT_AUDIO)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_AUDIO could not initialize! SDL Error: %s", SDL_GetError()); - } - else - { + } else { SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_AUDIO: INITIALIZING\n"); JA_Init(48000, SDL_AUDIO_S16LE, 2); diff --git a/source/audio.h b/source/audio.h index f970c16..8e945b1 100644 --- a/source/audio.h +++ b/source/audio.h @@ -3,65 +3,61 @@ #include // Clase Audio: gestor de audio (singleton) -class Audio -{ -public: - enum class Group : int - { +class Audio { + public: + enum class Group : int { ALL = -1, GAME = 0, INTERFACE = 1 }; // --- Métodos de singleton --- - static void init(); // Inicializa el objeto Audio - static void destroy(); // Libera el objeto Audio - static Audio *get(); // Obtiene el puntero al objeto Audio + static void init(); // Inicializa el objeto Audio + static void destroy(); // Libera el objeto Audio + static Audio *get(); // Obtiene el puntero al objeto Audio // --- Control de Música --- - void playMusic(const std::string &name, int loop = -1); // Reproducir música en bucle - void pauseMusic(); // Pausar reproducción de música - void stopMusic(); // Detener completamente la música - void fadeOutMusic(int milliseconds); // Fundido de salida de la música + void playMusic(const std::string &name, int loop = -1); // Reproducir música en bucle + void pauseMusic(); // Pausar reproducción de música + void stopMusic(); // Detener completamente la música + void fadeOutMusic(int milliseconds); // Fundido de salida de la música // --- Control de Sonidos --- - void playSound(const std::string &name, Group group = Group::GAME); // Reproducir sonido puntual - void stopAllSounds(); // Detener todos los sonidos + void playSound(const std::string &name, Group group = Group::GAME); // Reproducir sonido puntual + void stopAllSounds(); // Detener todos los sonidos // --- Configuración General --- - void enable(bool value); // Establecer estado general - void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general - void applySettings(); // Aplica la configuración + void enable(bool value); // Establecer estado general + void toggleEnabled() { enabled_ = !enabled_; } // Alternar estado general + void applySettings(); // Aplica la configuración // --- Configuración de Sonidos --- - void enableSound() { sound_enabled_ = true; } // Habilitar sonidos - void disableSound() { sound_enabled_ = false; } // Deshabilitar sonidos - void enableSound(bool value) { sound_enabled_ = value; } // Establecer estado de sonidos - void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alternar estado de sonidos + void enableSound() { sound_enabled_ = true; } // Habilitar sonidos + void disableSound() { sound_enabled_ = false; } // Deshabilitar sonidos + void enableSound(bool value) { sound_enabled_ = value; } // Establecer estado de sonidos + void toggleSound() { sound_enabled_ = !sound_enabled_; } // Alternar estado de sonidos // --- Configuración de Música --- - void enableMusic() { music_enabled_ = true; } // Habilitar música - void disableMusic() { music_enabled_ = false; } // Deshabilitar música - void enableMusic(bool value) { music_enabled_ = value; } // Establecer estado de música - void toggleMusic() { music_enabled_ = !music_enabled_; } // Alternar estado de música + void enableMusic() { music_enabled_ = true; } // Habilitar música + void disableMusic() { music_enabled_ = false; } // Deshabilitar música + void enableMusic(bool value) { music_enabled_ = value; } // Establecer estado de música + void toggleMusic() { music_enabled_ = !music_enabled_; } // Alternar estado de música // --- Control de Volumen --- - void setSoundVolume(int volume, Group group = Group::ALL); // Ajustar volumen de efectos - void setMusicVolume(int volume); // Ajustar volumen de música + void setSoundVolume(int volume, Group group = Group::ALL); // Ajustar volumen de efectos + void setMusicVolume(int volume); // Ajustar volumen de música -private: - enum class MusicState - { + private: + enum class MusicState { PLAYING, PAUSED, STOPPED, }; - struct Music - { - MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa) - std::string name; // Última pista de música reproducida - bool loop; // Indica si la última pista de música se debe reproducir en bucle + struct Music { + MusicState state; // Estado actual de la música (reproduciendo, detenido, en pausa) + std::string name; // Última pista de música reproducida + bool loop; // Indica si la última pista de música se debe reproducir en bucle // Constructor para inicializar la música con valores predeterminados Music() : state(MusicState::STOPPED), name(""), loop(false) {} @@ -74,18 +70,18 @@ private: Music music_; // --- Variables de Estado --- - bool enabled_ = true; // Estado general del audio - bool sound_enabled_ = true; // Estado de los efectos de sonido - bool music_enabled_ = true; // Estado de la música + bool enabled_ = true; // Estado general del audio + bool sound_enabled_ = true; // Estado de los efectos de sonido + bool music_enabled_ = true; // Estado de la música // --- Inicializa SDL Audio --- void initSDLAudio(); // --- Patrón Singleton --- - Audio(); // Constructor privado - ~Audio(); // Destructor privado - Audio(const Audio &) = delete; // Evitar copia - Audio &operator=(const Audio &) = delete; // Evitar asignación + Audio(); // Constructor privado + ~Audio(); // Destructor privado + Audio(const Audio &) = delete; // Evitar copia + Audio &operator=(const Audio &) = delete; // Evitar asignación // --- Singleton --- static Audio *instance_; diff --git a/source/background.cpp b/source/background.cpp index 3967fe5..9ee7dbe 100644 --- a/source/background.cpp +++ b/source/background.cpp @@ -1,17 +1,18 @@ #define _USE_MATH_DEFINES #include "background.h" -#include // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat... +#include // Para SDL_SetRenderTarget, SDL_FRect, SDL_Creat... + #include // Para clamp, max #include // Para cos, sin, M_PI #include // Para basic_string -#include "moving_sprite.h" // Para MovingSprite -#include "param.h" // Para Param, ParamBackground, param -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "sprite.h" // Para Sprite -#include "texture.h" // Para Texture +#include "moving_sprite.h" // Para MovingSprite +#include "param.h" // Para Param, ParamBackground, param +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "sprite.h" // Para Sprite +#include "texture.h" // Para Texture // Constructor Background::Background() @@ -47,8 +48,7 @@ Background::Background() const float TOP_CLOUDS_TEXTURE_HEIGHT = top_clouds_texture_->getHeight() / 4; const float BOTTOM_CLOUDS_TEXTURE_HEIGHT = bottom_clouds_texture_->getHeight() / 4; - for (int i = 0; i < 4; ++i) - { + for (int i = 0; i < 4; ++i) { top_clouds_rect_[i] = {0, i * TOP_CLOUDS_TEXTURE_HEIGHT, static_cast(top_clouds_texture_->getWidth()), TOP_CLOUDS_TEXTURE_HEIGHT}; bottom_clouds_rect_[i] = {0, i * BOTTOM_CLOUDS_TEXTURE_HEIGHT, static_cast(bottom_clouds_texture_->getWidth()), BOTTOM_CLOUDS_TEXTURE_HEIGHT}; } @@ -107,15 +107,13 @@ Background::Background() } // Destructor -Background::~Background() -{ +Background::~Background() { SDL_DestroyTexture(canvas_); SDL_DestroyTexture(color_texture_); } // Actualiza la lógica del objeto -void Background::update() -{ +void Background::update() { // Actualiza el valor de alpha_ updateAlphaColorTexture(); @@ -140,8 +138,7 @@ void Background::update() } // Dibuja el gradiente de fondo -void Background::renderGradient() -{ +void Background::renderGradient() { // Dibuja el gradiente de detras gradients_texture_->setAlpha(255); gradient_sprite_->setSpriteClip(gradient_rect_[(gradient_number_ + 1) % 4]); @@ -154,8 +151,7 @@ void Background::renderGradient() } // Dibuja las nubes de arriba -void Background::renderTopClouds() -{ +void Background::renderTopClouds() { // Dibuja el primer conjunto de nubes, las de detras top_clouds_texture_->setAlpha(255); top_clouds_sprite_a_->setSpriteClip(top_clouds_rect_[(gradient_number_ + 1) % 4]); @@ -172,8 +168,7 @@ void Background::renderTopClouds() } // Dibuja las nubes de abajo -void Background::renderBottomClouds() -{ +void Background::renderBottomClouds() { // Dibuja el primer conjunto de nubes, las de detras bottom_clouds_texture_->setAlpha(255); bottom_clouds_sprite_a_->setSpriteClip(bottom_clouds_rect_[(gradient_number_ + 1) % 4]); @@ -190,8 +185,7 @@ void Background::renderBottomClouds() } // Compone todos los elementos del fondo en la textura -void Background::fillCanvas() -{ +void Background::fillCanvas() { // Cambia el destino del renderizador auto temp = SDL_GetRenderTarget(renderer_); SDL_SetRenderTarget(renderer_, canvas_); @@ -220,8 +214,7 @@ void Background::fillCanvas() } // Dibuja el objeto -void Background::render() -{ +void Background::render() { // Fondo SDL_RenderTexture(renderer_, canvas_, &src_rect_, &dst_rect_); @@ -230,26 +223,22 @@ void Background::render() } // Ajusta el valor de la variable -void Background::setCloudsSpeed(float value) -{ +void Background::setCloudsSpeed(float value) { clouds_speed_ = value; } // Ajusta el valor de la variable -void Background::setGradientNumber(int value) -{ +void Background::setGradientNumber(int value) { gradient_number_ = value % 4; } // Ajusta el valor de la variable -void Background::setTransition(float value) -{ +void Background::setTransition(float value) { transition_ = std::clamp(value, 0.0f, 1.0f); } // Establece la posición del objeto -void Background::setPos(SDL_FRect pos) -{ +void Background::setPos(SDL_FRect pos) { dst_rect_ = pos; // Si cambian las medidas del destino, hay que cambiar las del origen para evitar deformar la imagen @@ -260,8 +249,7 @@ void Background::setPos(SDL_FRect pos) } // Establece el color_ de atenuación -void Background::setColor(Color color) -{ +void Background::setColor(Color color) { attenuate_color_ = color; // Colorea la textura @@ -275,8 +263,7 @@ void Background::setColor(Color color) } // Establece la transparencia de la atenuación -void Background::setAlpha(int alpha) -{ +void Background::setAlpha(int alpha) { // Evita que se asignen valores fuera de rango alpha_ = std::clamp(alpha, 0, 255); @@ -286,22 +273,17 @@ void Background::setAlpha(int alpha) } // Actualiza el valor de alpha_ -void Background::updateAlphaColorTexture() -{ - if (alpha_color_text_ == alpha_color_text_temp_) - { +void Background::updateAlphaColorTexture() { + if (alpha_color_text_ == alpha_color_text_temp_) { return; - } - else - { + } else { alpha_color_text_ > alpha_color_text_temp_ ? ++alpha_color_text_temp_ : --alpha_color_text_temp_; SDL_SetTextureAlphaMod(color_texture_, alpha_color_text_temp_); } } // Actualiza las nubes -void Background::updateClouds() -{ +void Background::updateClouds() { // Aplica la velocidad calculada a las nubes top_clouds_sprite_a_->setVelX(clouds_speed_); top_clouds_sprite_b_->setVelX(clouds_speed_); @@ -315,37 +297,31 @@ void Background::updateClouds() bottom_clouds_sprite_b_->update(); // Calcula el offset de las nubes - if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) - { + if (top_clouds_sprite_a_->getPosX() < -top_clouds_sprite_a_->getWidth()) { top_clouds_sprite_a_->setPosX(top_clouds_sprite_a_->getWidth()); } - if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) - { + if (top_clouds_sprite_b_->getPosX() < -top_clouds_sprite_b_->getWidth()) { top_clouds_sprite_b_->setPosX(top_clouds_sprite_b_->getWidth()); } - if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) - { + if (bottom_clouds_sprite_a_->getPosX() < -bottom_clouds_sprite_a_->getWidth()) { bottom_clouds_sprite_a_->setPosX(bottom_clouds_sprite_a_->getWidth()); } - if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) - { + if (bottom_clouds_sprite_b_->getPosX() < -bottom_clouds_sprite_b_->getWidth()) { bottom_clouds_sprite_b_->setPosX(bottom_clouds_sprite_b_->getWidth()); } } // Precalcula el vector con el recorrido del sol -void Background::createSunPath() -{ +void Background::createSunPath() { constexpr float CENTER_X = 170; const float center_y = base_ - 80; constexpr float RADIUS = 120; // Generar puntos de la curva desde 90 a 180 grados - for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01) - { + for (double theta = M_PI / 2; theta <= M_PI; theta += 0.01) { float x = CENTER_X + (RADIUS * cos(theta)); float y = center_y - (RADIUS * sin(theta)); sun_path_.push_back({x, y}); @@ -354,22 +330,19 @@ void Background::createSunPath() // Agregar puntos en línea recta después de la curva constexpr int EXTRA_PIXELS = 40; SDL_FPoint last_point = sun_path_.back(); - for (int i = 1; i <= EXTRA_PIXELS; ++i) - { + for (int i = 1; i <= EXTRA_PIXELS; ++i) { sun_path_.push_back({last_point.x, last_point.y + i}); } } // Precalcula el vector con el recorrido de la luna -void Background::createMoonPath() -{ +void Background::createMoonPath() { constexpr float CENTER_X = 100; const float center_y = base_ - 50; constexpr float RADIUS = 140; // Generar puntos de la curva desde 0 a 90 grados - for (double theta = 0; theta <= M_PI / 2; theta += 0.01) - { + for (double theta = 0; theta <= M_PI / 2; theta += 0.01) { float x = CENTER_X + (RADIUS * cos(theta)); float y = center_y - (RADIUS * sin(theta)); moon_path_.push_back({x, y}); @@ -377,15 +350,13 @@ void Background::createMoonPath() } // Establece la posición del sol -void Background::setSunProgression(float progress) -{ +void Background::setSunProgression(float progress) { progress = std::clamp(progress, 0.0f, 1.0f); sun_index_ = static_cast(progress * (sun_path_.size() - 1)); } // Establece la posición de la luna -void Background::setMoonProgression(float progress) -{ +void Background::setMoonProgression(float progress) { progress = std::clamp(progress, 0.0f, 1.0f); moon_index_ = static_cast(progress * (moon_path_.size() - 1)); } \ No newline at end of file diff --git a/source/background.h b/source/background.h index b1e2d65..ab66576 100644 --- a/source/background.h +++ b/source/background.h @@ -1,11 +1,12 @@ #pragma once -#include // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer -#include // Para size_t -#include // Para unique_ptr, shared_ptr -#include // Para vector +#include // Para SDL_FRect, SDL_FPoint, SDL_Texture, SDL_Renderer +#include // Para size_t -#include "utils.h" // Para Color +#include // Para unique_ptr, shared_ptr +#include // Para vector + +#include "utils.h" // Para Color class MovingSprite; class Sprite; @@ -25,89 +26,88 @@ class Texture; - setAlpha(int alpha) -> Ajusta la transparencia de la capa de atenuación */ -class Background -{ -public: - // Constructor y Destructor - Background(); - ~Background(); +class Background { + public: + // Constructor y Destructor + Background(); + ~Background(); - // Actualización y renderizado - void update(); // Actualiza la lógica del objeto - void render(); // Dibuja el objeto + // Actualización y renderizado + void update(); // Actualiza la lógica del objeto + void render(); // Dibuja el objeto - // Configuración de posición - void setPos(SDL_FRect pos); // Establece la posición del objeto + // Configuración de posición + void setPos(SDL_FRect pos); // Establece la posición del objeto - // Configuración de animaciones y efectos - void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes - void setGradientNumber(int value); // Establece el degradado de fondo a usar - void setTransition(float value); // Ajusta la transición entre texturas de fondo + // Configuración de animaciones y efectos + void setCloudsSpeed(float value); // Ajusta la velocidad de desplazamiento de las nubes + void setGradientNumber(int value); // Establece el degradado de fondo a usar + void setTransition(float value); // Ajusta la transición entre texturas de fondo - // Configuración de efectos visuales - void setColor(Color color); // Establece el color de atenuación - void setAlpha(int alpha); // Ajusta la transparencia del fondo + // Configuración de efectos visuales + void setColor(Color color); // Establece el color de atenuación + void setAlpha(int alpha); // Ajusta la transparencia del fondo - // Configuración del sol y la luna - void setSunProgression(float progress); // Establece la posición del sol - void setMoonProgression(float progress); // Establece la posición de la luna + // Configuración del sol y la luna + void setSunProgression(float progress); // Establece la posición del sol + void setMoonProgression(float progress); // Establece la posición de la luna -private: - // Objetos y punteros - SDL_Renderer *renderer_; // Renderizador de la ventana + private: + // Objetos y punteros + SDL_Renderer *renderer_; // Renderizador de la ventana - // Texturas - std::shared_ptr buildings_texture_; - std::shared_ptr top_clouds_texture_; - std::shared_ptr bottom_clouds_texture_; - std::shared_ptr grass_texture_; - std::shared_ptr gradients_texture_; - std::shared_ptr sun_texture_; - std::shared_ptr moon_texture_; + // Texturas + std::shared_ptr buildings_texture_; + std::shared_ptr top_clouds_texture_; + std::shared_ptr bottom_clouds_texture_; + std::shared_ptr grass_texture_; + std::shared_ptr gradients_texture_; + std::shared_ptr sun_texture_; + std::shared_ptr moon_texture_; - // Sprites - std::unique_ptr top_clouds_sprite_a_; - std::unique_ptr top_clouds_sprite_b_; - std::unique_ptr bottom_clouds_sprite_a_; - std::unique_ptr bottom_clouds_sprite_b_; - std::unique_ptr buildings_sprite_; - std::unique_ptr gradient_sprite_; - std::unique_ptr grass_sprite_; - std::unique_ptr sun_sprite_; - std::unique_ptr moon_sprite_; + // Sprites + std::unique_ptr top_clouds_sprite_a_; + std::unique_ptr top_clouds_sprite_b_; + std::unique_ptr bottom_clouds_sprite_a_; + std::unique_ptr bottom_clouds_sprite_b_; + std::unique_ptr buildings_sprite_; + std::unique_ptr gradient_sprite_; + std::unique_ptr grass_sprite_; + std::unique_ptr sun_sprite_; + std::unique_ptr moon_sprite_; - // Buffers de renderizado - SDL_Texture *canvas_; // Textura para componer el fondo - SDL_Texture *color_texture_; // Textura para atenuar el fondo + // Buffers de renderizado + SDL_Texture *canvas_; // Textura para componer el fondo + SDL_Texture *color_texture_; // Textura para atenuar el fondo - // Variables de control - SDL_FRect gradient_rect_[4]; - SDL_FRect top_clouds_rect_[4]; - SDL_FRect bottom_clouds_rect_[4]; - int gradient_number_ = 0; - int alpha_ = 0; - float clouds_speed_ = 0; - float transition_ = 0; - int counter_ = 0; - SDL_FRect rect_; - SDL_FRect src_rect_; - SDL_FRect dst_rect_; - int base_; - Color attenuate_color_; - int alpha_color_text_; - int alpha_color_text_temp_; - std::vector sun_path_; - std::vector moon_path_; - size_t sun_index_ = 0; - size_t moon_index_ = 0; + // Variables de control + SDL_FRect gradient_rect_[4]; + SDL_FRect top_clouds_rect_[4]; + SDL_FRect bottom_clouds_rect_[4]; + int gradient_number_ = 0; + int alpha_ = 0; + float clouds_speed_ = 0; + float transition_ = 0; + int counter_ = 0; + SDL_FRect rect_; + SDL_FRect src_rect_; + SDL_FRect dst_rect_; + int base_; + Color attenuate_color_; + int alpha_color_text_; + int alpha_color_text_temp_; + std::vector sun_path_; + std::vector moon_path_; + size_t sun_index_ = 0; + size_t moon_index_ = 0; - // Métodos internos - void renderGradient(); // Dibuja el gradiente de fondo - void renderTopClouds(); // Dibuja las nubes superiores - void renderBottomClouds(); // Dibuja las nubes inferiores - void fillCanvas(); // Compone todos los elementos en la textura - void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación - void updateClouds(); // Actualiza el movimiento de las nubes - void createSunPath(); // Precalcula el recorrido del sol - void createMoonPath(); // Precalcula el recorrido de la luna + // Métodos internos + void renderGradient(); // Dibuja el gradiente de fondo + void renderTopClouds(); // Dibuja las nubes superiores + void renderBottomClouds(); // Dibuja las nubes inferiores + void fillCanvas(); // Compone todos los elementos en la textura + void updateAlphaColorTexture(); // Actualiza el alpha de la textura de atenuación + void updateClouds(); // Actualiza el movimiento de las nubes + void createSunPath(); // Precalcula el recorrido del sol + void createMoonPath(); // Precalcula el recorrido de la luna }; diff --git a/source/balloon.cpp b/source/balloon.cpp index b378e4f..c83635c 100644 --- a/source/balloon.cpp +++ b/source/balloon.cpp @@ -1,440 +1,386 @@ #include "balloon.h" -#include // Para clamp -#include // Para array -#include // Para fabs +#include // Para clamp +#include // Para array +#include // Para fabs -#include "animated_sprite.h" // Para AnimatedSprite -#include "audio.h" // Para Audio -#include "param.h" // Para Param, ParamBalloon, param -#include "sprite.h" // Para Sprite -#include "texture.h" // Para Texture +#include "animated_sprite.h" // Para AnimatedSprite +#include "audio.h" // Para Audio +#include "param.h" // Para Param, ParamBalloon, param +#include "sprite.h" // Para Sprite +#include "texture.h" // Para Texture // Constructor Balloon::Balloon(float x, float y, BalloonType type, BalloonSize size, float vel_x, float speed, Uint16 creation_timer, SDL_FRect play_area, std::shared_ptr texture, const std::vector &animation) - : sprite_(std::make_unique(texture, animation)), - x_(x), - y_(y), - vx_(vel_x), - being_created_(creation_timer > 0), - invulnerable_(creation_timer > 0), - stopped_(creation_timer > 0), - creation_counter_(creation_timer), - creation_counter_ini_(creation_timer), - type_(type), - size_(size), - speed_(speed), - play_area_(play_area) -{ - switch (type_) - { - case BalloonType::BALLOON: - { - vy_ = 0; - max_vy_ = 3.0f; + : sprite_(std::make_unique(texture, animation)), + x_(x), + y_(y), + vx_(vel_x), + being_created_(creation_timer > 0), + invulnerable_(creation_timer > 0), + stopped_(creation_timer > 0), + creation_counter_(creation_timer), + creation_counter_ini_(creation_timer), + type_(type), + size_(size), + speed_(speed), + play_area_(play_area) { + switch (type_) { + case BalloonType::BALLOON: { + vy_ = 0; + max_vy_ = 3.0f; - const int index = static_cast(size_); - gravity_ = param.balloon.settings.at(index).grav; - default_vy_ = param.balloon.settings.at(index).vel; - h_ = w_ = BALLOON_SIZE[index]; - power_ = BALLOON_POWER[index]; - menace_ = BALLOON_MENACE[index]; - score_ = BALLOON_SCORE[index]; - bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; - popping_sound_ = BALLOON_POPPING_SOUND[index]; + const int index = static_cast(size_); + gravity_ = param.balloon.settings.at(index).grav; + default_vy_ = param.balloon.settings.at(index).vel; + h_ = w_ = BALLOON_SIZE[index]; + power_ = BALLOON_POWER[index]; + menace_ = BALLOON_MENACE[index]; + score_ = BALLOON_SCORE[index]; + bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; + popping_sound_ = BALLOON_POPPING_SOUND[index]; - break; - } + break; + } - case BalloonType::FLOATER: - { - default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f); - gravity_ = 0.00f; + case BalloonType::FLOATER: { + default_vy_ = max_vy_ = vy_ = fabs(vx_ * 2.0f); + gravity_ = 0.00f; - const int index = static_cast(size_); - h_ = w_ = BALLOON_SIZE[index]; - power_ = BALLOON_POWER[index]; - menace_ = BALLOON_MENACE[index]; - score_ = BALLOON_SCORE[index]; - bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; - popping_sound_ = BALLOON_POPPING_SOUND[index]; + const int index = static_cast(size_); + h_ = w_ = BALLOON_SIZE[index]; + power_ = BALLOON_POWER[index]; + menace_ = BALLOON_MENACE[index]; + score_ = BALLOON_SCORE[index]; + bouncing_sound_ = BALLOON_BOUNCING_SOUND[index]; + popping_sound_ = BALLOON_POPPING_SOUND[index]; - break; - } + break; + } - case BalloonType::POWERBALL: - { - constexpr int index = 3; - h_ = w_ = BALLOON_SIZE[4]; - bouncing_sound_ = BALLOON_BOUNCING_SOUND[3]; - popping_sound_ = "power_ball_explosion.wav"; - power_ = score_ = menace_ = 0; + case BalloonType::POWERBALL: { + constexpr int index = 3; + h_ = w_ = BALLOON_SIZE[4]; + bouncing_sound_ = BALLOON_BOUNCING_SOUND[3]; + popping_sound_ = "power_ball_explosion.wav"; + power_ = score_ = menace_ = 0; - vy_ = 0; - max_vy_ = 3.0f; - gravity_ = param.balloon.settings.at(index).grav; - default_vy_ = param.balloon.settings.at(index).vel; + vy_ = 0; + max_vy_ = 3.0f; + gravity_ = param.balloon.settings.at(index).grav; + default_vy_ = param.balloon.settings.at(index).vel; - sprite_->setRotate(creation_timer <= 0); - sprite_->setRotateAmount(vx_ > 0.0f ? 2.0 : -2.0); + sprite_->setRotate(creation_timer <= 0); + sprite_->setRotateAmount(vx_ > 0.0f ? 2.0 : -2.0); - break; - } + break; + } - default: - break; - } + default: + break; + } - // Configura el sprite - sprite_->setWidth(w_); - sprite_->setHeight(h_); - shiftSprite(); + // Configura el sprite + sprite_->setWidth(w_); + sprite_->setHeight(h_); + shiftSprite(); - // Alinea el circulo de colisión con el objeto - collider_.r = w_ / 2; - shiftColliders(); + // Alinea el circulo de colisión con el objeto + collider_.r = w_ / 2; + shiftColliders(); - // Establece la animación a usar - setAnimation(); + // Establece la animación a usar + setAnimation(); } // Centra el globo en la posición X -void Balloon::alignTo(int x) -{ - x_ = static_cast(x - (w_ / 2)); - const int min_x = play_area_.x; - const int max_x = play_area_.w - w_; - x_ = std::clamp(x_, static_cast(min_x), static_cast(max_x)); +void Balloon::alignTo(int x) { + x_ = static_cast(x - (w_ / 2)); + const int min_x = play_area_.x; + const int max_x = play_area_.w - w_; + x_ = std::clamp(x_, static_cast(min_x), static_cast(max_x)); } // Pinta el globo en la pantalla -void Balloon::render() -{ - if (type_ == BalloonType::POWERBALL) - { - // Renderiza el fondo azul - { - auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); - sp->setSpriteClip(0, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); - sp->render(); - } +void Balloon::render() { + if (type_ == BalloonType::POWERBALL) { + // Renderiza el fondo azul + { + auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); + sp->setSpriteClip(0, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); + sp->render(); + } - // Renderiza la estrella - if (!invulnerable_) - { - SDL_FPoint p = {24.0f, 24.0f}; - sprite_->setRotatingCenter(p); - sprite_->render(); - } + // Renderiza la estrella + if (!invulnerable_) { + SDL_FPoint p = {24.0f, 24.0f}; + sprite_->setRotatingCenter(p); + sprite_->render(); + } - // Añade la máscara del borde y los reflejos - { - auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); - sp->setSpriteClip(BALLOON_SIZE[4] * 2, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); - sp->render(); - } - } - else - { - // Renderizado para el resto de globos - if (isBeingCreated()) - { - // Renderizado con transparencia - sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0f / (float)creation_counter_ini_))); - sprite_->render(); - sprite_->getTexture()->setAlpha(255); - } - else - { - // Renderizado normal - sprite_->render(); - } - } + // Añade la máscara del borde y los reflejos + { + auto sp = std::make_unique(sprite_->getTexture(), sprite_->getPosition()); + sp->setSpriteClip(BALLOON_SIZE[4] * 2, 0, BALLOON_SIZE[4], BALLOON_SIZE[4]); + sp->render(); + } + } else { + // Renderizado para el resto de globos + if (isBeingCreated()) { + // Renderizado con transparencia + sprite_->getTexture()->setAlpha(255 - (int)((float)creation_counter_ * (255.0f / (float)creation_counter_ini_))); + sprite_->render(); + sprite_->getTexture()->setAlpha(255); + } else { + // Renderizado normal + sprite_->render(); + } + } } // Actualiza la posición y estados del globo -void Balloon::move() -{ - // Comprueba si se puede mover - if (!isStopped()) - { - // Mueve el globo en horizontal - x_ += vx_ * speed_; +void Balloon::move() { + // Comprueba si se puede mover + if (!isStopped()) { + // Mueve el globo en horizontal + x_ += vx_ * speed_; - // Colisión en las partes laterales de la zona de juego - const int clip = 2; - const float min_x = play_area_.x - clip; - const float max_x = play_area_.x + play_area_.w - w_ + clip; - if (x_ < min_x || x_ > max_x) - { - if (bouncing_sound_enabled_) - playSound(bouncing_sound_); - x_ = std::clamp(x_, min_x, max_x); - vx_ = -vx_; - // Activa el efecto de rebote o invierte la rotación - if (type_ == BalloonType::POWERBALL) - { - sprite_->switchRotate(); - } - else - { - enableBounce(); - } - } + // Colisión en las partes laterales de la zona de juego + const int clip = 2; + const float min_x = play_area_.x - clip; + const float max_x = play_area_.x + play_area_.w - w_ + clip; + if (x_ < min_x || x_ > max_x) { + if (bouncing_sound_enabled_) + playSound(bouncing_sound_); + x_ = std::clamp(x_, min_x, max_x); + vx_ = -vx_; + // Activa el efecto de rebote o invierte la rotación + if (type_ == BalloonType::POWERBALL) { + sprite_->switchRotate(); + } else { + enableBounce(); + } + } - // Mueve el globo en vertical - y_ += vy_ * speed_; + // Mueve el globo en vertical + y_ += vy_ * speed_; - // Colisión en la parte superior solo si el globo va de subida - if (vy_ < 0) - { - const int min_y = play_area_.y; - if (y_ < min_y) - { - if (bouncing_sound_enabled_) - playSound(bouncing_sound_); - y_ = min_y; - vy_ = -vy_; - enableBounce(); - } - } + // Colisión en la parte superior solo si el globo va de subida + if (vy_ < 0) { + const int min_y = play_area_.y; + if (y_ < min_y) { + if (bouncing_sound_enabled_) + playSound(bouncing_sound_); + y_ = min_y; + vy_ = -vy_; + enableBounce(); + } + } - // Colisión en la parte inferior de la zona de juego - const int max_y = play_area_.y + play_area_.h - h_; - if (y_ > max_y) - { - if (bouncing_sound_enabled_) - playSound(bouncing_sound_); - y_ = max_y; - vy_ = -default_vy_; - if (type_ != BalloonType::POWERBALL) - { - enableBounce(); - } - else - { - setInvulnerable(false); - } - } + // Colisión en la parte inferior de la zona de juego + const int max_y = play_area_.y + play_area_.h - h_; + if (y_ > max_y) { + if (bouncing_sound_enabled_) + playSound(bouncing_sound_); + y_ = max_y; + vy_ = -default_vy_; + if (type_ != BalloonType::POWERBALL) { + enableBounce(); + } else { + setInvulnerable(false); + } + } - /* + /* - Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle - Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por - tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya - recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial + Para aplicar la gravedad, el diseño original la aplicaba en cada iteración del bucle + Al añadir el modificador de velocidad se reduce la distancia que recorre el objeto y por + tanto recibe mas gravedad. Para solucionarlo se va a aplicar la gravedad cuando se haya + recorrido una distancia igual a la velocidad en Y, que era el cálculo inicial - */ + */ - // Incrementa la variable que calcula la distancia acumulada en Y - travel_y_ += speed_; + // Incrementa la variable que calcula la distancia acumulada en Y + travel_y_ += speed_; - // Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad - if (travel_y_ >= 1.0f) - { - // Quita el excedente - travel_y_ -= 1.0f; + // Si la distancia acumulada en Y es igual a la velocidad, se aplica la gravedad + if (travel_y_ >= 1.0f) { + // Quita el excedente + travel_y_ -= 1.0f; - // Aplica la gravedad al objeto sin pasarse de una velocidad máxima - vy_ += gravity_; - } - } + // Aplica la gravedad al objeto sin pasarse de una velocidad máxima + vy_ += gravity_; + } + } } // Actualiza al globo a su posicion, animación y controla los contadores -void Balloon::update() -{ - move(); - updateState(); - updateBounce(); - shiftSprite(); - shiftColliders(); - sprite_->update(); - ++counter_; +void Balloon::update() { + move(); + updateState(); + updateBounce(); + shiftSprite(); + shiftColliders(); + sprite_->update(); + ++counter_; } // Actualiza los estados del globo -void Balloon::updateState() -{ - // Si se está creando - if (isBeingCreated()) - { - // Actualiza el valor de las variables - stop(); - setInvulnerable(true); +void Balloon::updateState() { + // Si se está creando + if (isBeingCreated()) { + // Actualiza el valor de las variables + stop(); + setInvulnerable(true); - if (creation_counter_ > 0) - { - // Desplaza lentamente el globo hacia abajo y hacia un lado - if (creation_counter_ % 10 == 0) - { - y_++; - x_ += vx_; + if (creation_counter_ > 0) { + // Desplaza lentamente el globo hacia abajo y hacia un lado + if (creation_counter_ % 10 == 0) { + y_++; + x_ += vx_; - // Comprueba no se salga por los laterales - const int min_x = play_area_.x; - const int max_x = play_area_.w - w_; + // Comprueba no se salga por los laterales + const int min_x = play_area_.x; + const int max_x = play_area_.w - w_; - if (x_ < min_x || x_ > max_x) - { - // Corrige y cambia el sentido de la velocidad - x_ -= vx_; - vx_ = -vx_; - } - } - --creation_counter_; - } + if (x_ < min_x || x_ > max_x) { + // Corrige y cambia el sentido de la velocidad + x_ -= vx_; + vx_ = -vx_; + } + } + --creation_counter_; + } - else - { - // El contador ha llegado a cero - being_created_ = false; - start(); - setInvulnerable(false); - setAnimation(); - } - } + else { + // El contador ha llegado a cero + being_created_ = false; + start(); + setInvulnerable(false); + setAnimation(); + } + } } // Establece la animación correspondiente al estado -void Balloon::setAnimation() -{ - std::string creating_animation; - std::string normal_animation; +void Balloon::setAnimation() { + std::string creating_animation; + std::string normal_animation; - switch (type_) - { - case BalloonType::POWERBALL: - creating_animation = "powerball"; - normal_animation = "powerball"; - break; - case BalloonType::FLOATER: - creating_animation = param.balloon.color.at(2); - normal_animation = param.balloon.color.at(3); - break; - default: - creating_animation = param.balloon.color.at(0); - normal_animation = param.balloon.color.at(1); - break; - } + switch (type_) { + case BalloonType::POWERBALL: + creating_animation = "powerball"; + normal_animation = "powerball"; + break; + case BalloonType::FLOATER: + creating_animation = param.balloon.color.at(2); + normal_animation = param.balloon.color.at(3); + break; + default: + creating_animation = param.balloon.color.at(0); + normal_animation = param.balloon.color.at(1); + break; + } - // Establece el frame de animación - if (use_reversed_colors_) - sprite_->setCurrentAnimation(creating_animation); - else - sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation); + // Establece el frame de animación + if (use_reversed_colors_) + sprite_->setCurrentAnimation(creating_animation); + else + sprite_->setCurrentAnimation(isBeingCreated() ? creating_animation : normal_animation); } // Detiene el globo -void Balloon::stop() -{ - stopped_ = true; - if (isPowerBall()) - { - sprite_->setRotate(!stopped_); - } +void Balloon::stop() { + stopped_ = true; + if (isPowerBall()) { + sprite_->setRotate(!stopped_); + } } // Pone el globo en movimiento -void Balloon::start() -{ - stopped_ = false; - if (isPowerBall()) - { - sprite_->setRotate(!stopped_); - } +void Balloon::start() { + stopped_ = false; + if (isPowerBall()) { + sprite_->setRotate(!stopped_); + } } // Alinea el circulo de colisión con la posición del objeto globo -void Balloon::shiftColliders() -{ - collider_.x = static_cast(x_) + collider_.r; - collider_.y = static_cast(y_) + collider_.r; +void Balloon::shiftColliders() { + collider_.x = static_cast(x_) + collider_.r; + collider_.y = static_cast(y_) + collider_.r; } // Alinea el sprite con la posición del objeto globo -void Balloon::shiftSprite() -{ - sprite_->setPosX(x_); - sprite_->setPosY(y_); +void Balloon::shiftSprite() { + sprite_->setPosX(x_); + sprite_->setPosY(y_); } // Establece el nivel de zoom del sprite -void Balloon::zoomSprite() -{ - sprite_->setZoomW(bouncing_.zoomW); - sprite_->setZoomH(bouncing_.zoomH); +void Balloon::zoomSprite() { + sprite_->setZoomW(bouncing_.zoomW); + sprite_->setZoomH(bouncing_.zoomH); } // Activa el efecto -void Balloon::enableBounce() -{ - bouncing_.enabled = true; - bouncing_.reset(); - zoomSprite(); +void Balloon::enableBounce() { + bouncing_.enabled = true; + bouncing_.reset(); + zoomSprite(); } // Detiene el efecto -void Balloon::disableBounce() -{ - bouncing_.enabled = false; - bouncing_.reset(); - zoomSprite(); +void Balloon::disableBounce() { + bouncing_.enabled = false; + bouncing_.reset(); + zoomSprite(); } // Aplica el efecto -void Balloon::updateBounce() -{ - if (bouncing_.enabled) - { - const int index = bouncing_.counter / bouncing_.speed; - bouncing_.zoomW = bouncing_.w[index]; - bouncing_.zoomH = bouncing_.h[index]; +void Balloon::updateBounce() { + if (bouncing_.enabled) { + const int index = bouncing_.counter / bouncing_.speed; + bouncing_.zoomW = bouncing_.w[index]; + bouncing_.zoomH = bouncing_.h[index]; - zoomSprite(); + zoomSprite(); - if (++bouncing_.counter / bouncing_.speed >= MAX_BOUNCE) - { - disableBounce(); - } - } + if (++bouncing_.counter / bouncing_.speed >= MAX_BOUNCE) { + disableBounce(); + } + } } // Pone el color alternativo en el globo -void Balloon::useReverseColor() -{ - if (!isBeingCreated()) - { - use_reversed_colors_ = true; - setAnimation(); - } +void Balloon::useReverseColor() { + if (!isBeingCreated()) { + use_reversed_colors_ = true; + setAnimation(); + } } // Pone el color normal en el globo -void Balloon::useNormalColor() -{ - use_reversed_colors_ = false; - setAnimation(); +void Balloon::useNormalColor() { + use_reversed_colors_ = false; + setAnimation(); } // Reproduce sonido -void Balloon::playSound(const std::string &name) -{ - if (!sound_enabled_) - return; +void Balloon::playSound(const std::string &name) { + if (!sound_enabled_) + return; - static auto audio = Audio::get(); - audio->playSound(name); + static auto audio = Audio::get(); + audio->playSound(name); } // Explota el globo -void Balloon::pop(bool should_sound) -{ - if (should_sound) - { - if (poping_sound_enabled_) - playSound(popping_sound_); - } +void Balloon::pop(bool should_sound) { + if (should_sound) { + if (poping_sound_enabled_) + playSound(popping_sound_); + } - enabled_ = false; + enabled_ = false; } \ No newline at end of file diff --git a/source/balloon.h b/source/balloon.h index 4c8a0a3..e2e927c 100644 --- a/source/balloon.h +++ b/source/balloon.h @@ -1,17 +1,18 @@ #pragma once -#include // Para Uint8, Uint16, SDL_FRect, Uint32 -#include // Para shared_ptr, unique_ptr -#include // Para basic_string, string -#include // Para vector +#include // Para Uint8, Uint16, SDL_FRect, Uint32 -#include "animated_sprite.h" // Para AnimatedSprite -#include "utils.h" // Para Circle +#include // Para shared_ptr, unique_ptr +#include // Para basic_string, string +#include // Para vector + +#include "animated_sprite.h" // Para AnimatedSprite +#include "utils.h" // Para Circle class Texture; // --- Constantes relacionadas con globos --- -constexpr int MAX_BOUNCE = 10; // Cantidad de elementos del vector de deformación +constexpr int MAX_BOUNCE = 10; // Cantidad de elementos del vector de deformación constexpr int BALLOON_SCORE[] = {50, 100, 200, 400}; constexpr int BALLOON_POWER[] = {1, 3, 7, 15}; @@ -20,19 +21,17 @@ constexpr int BALLOON_SIZE[] = {10, 16, 26, 48, 49}; const std::string BALLOON_BOUNCING_SOUND[] = {"bubble1.wav", "bubble2.wav", "bubble3.wav", "bubble4.wav"}; const std::string BALLOON_POPPING_SOUND[] = {"balloon1.wav", "balloon2.wav", "balloon3.wav", "balloon4.wav"}; -enum class BalloonSize : Uint8 -{ - SIZE1 = 0, - SIZE2 = 1, - SIZE3 = 2, - SIZE4 = 3, +enum class BalloonSize : Uint8 { + SIZE1 = 0, + SIZE2 = 1, + SIZE3 = 2, + SIZE4 = 3, }; -enum class BalloonType : Uint8 -{ - BALLOON = 0, - FLOATER = 1, - POWERBALL = 2, +enum class BalloonType : Uint8 { + BALLOON = 0, + FLOATER = 1, + POWERBALL = 2, }; constexpr float BALLOON_VELX_POSITIVE = 0.7f; @@ -48,133 +47,130 @@ constexpr int POWERBALL_SCREENPOWER_MINIMUM = 10; constexpr int POWERBALL_COUNTER = 8; // --- Clase Balloon --- -class Balloon -{ -public: - // --- Constructores y destructor --- - Balloon( - float x, - float y, - BalloonType type, - BalloonSize size, - float vel_x, - float speed, - Uint16 creation_timer, - SDL_FRect play_area, - std::shared_ptr texture, - const std::vector &animation); - ~Balloon() = default; +class Balloon { + public: + // --- Constructores y destructor --- + Balloon( + float x, + float y, + BalloonType type, + BalloonSize size, + float vel_x, + float speed, + Uint16 creation_timer, + SDL_FRect play_area, + std::shared_ptr texture, + const std::vector &animation); + ~Balloon() = default; - // --- Métodos principales --- - void alignTo(int x); // Centra el globo en la posición X - void render(); // Pinta el globo en la pantalla - void move(); // Actualiza la posición y estados del globo - void update(); // Actualiza el globo (posición, animación, contadores) - void stop(); // Detiene el globo - void start(); // Pone el globo en movimiento - void pop(bool should_sound = false); // Explota el globo + // --- Métodos principales --- + void alignTo(int x); // Centra el globo en la posición X + void render(); // Pinta el globo en la pantalla + void move(); // Actualiza la posición y estados del globo + void update(); // Actualiza el globo (posición, animación, contadores) + void stop(); // Detiene el globo + void start(); // Pone el globo en movimiento + void pop(bool should_sound = false); // Explota el globo - // --- Métodos de color --- - void useReverseColor(); // Pone el color alternativo en el globo - void useNormalColor(); // Pone el color normal en el globo + // --- Métodos de color --- + void useReverseColor(); // Pone el color alternativo en el globo + void useNormalColor(); // Pone el color normal en el globo - // --- Getters --- - float getPosX() const { return x_; } - float getPosY() const { return y_; } - int getWidth() const { return w_; } - int getHeight() const { return h_; } - BalloonSize getSize() const { return size_; } - BalloonType getType() const { return type_; } - Uint16 getScore() const { return score_; } - Circle &getCollider() { return collider_; } - Uint8 getMenace() const { return isEnabled() ? menace_ : 0; } - Uint8 getPower() const { return power_; } - bool isStopped() const { return stopped_; } - bool isPowerBall() const { return type_ == BalloonType::POWERBALL; } - bool isInvulnerable() const { return invulnerable_; } - bool isBeingCreated() const { return being_created_; } - bool isEnabled() const { return enabled_; } - bool isUsingReversedColor() { return use_reversed_colors_; } - bool canBePopped() const { return !isBeingCreated(); } + // --- Getters --- + float getPosX() const { return x_; } + float getPosY() const { return y_; } + int getWidth() const { return w_; } + int getHeight() const { return h_; } + BalloonSize getSize() const { return size_; } + BalloonType getType() const { return type_; } + Uint16 getScore() const { return score_; } + Circle &getCollider() { return collider_; } + Uint8 getMenace() const { return isEnabled() ? menace_ : 0; } + Uint8 getPower() const { return power_; } + bool isStopped() const { return stopped_; } + bool isPowerBall() const { return type_ == BalloonType::POWERBALL; } + bool isInvulnerable() const { return invulnerable_; } + bool isBeingCreated() const { return being_created_; } + bool isEnabled() const { return enabled_; } + bool isUsingReversedColor() { return use_reversed_colors_; } + bool canBePopped() const { return !isBeingCreated(); } - // --- Setters --- - void setVelY(float vel_y) { vy_ = vel_y; } - void setSpeed(float speed) { speed_ = speed; } - void setInvulnerable(bool value) { invulnerable_ = value; } - void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; } - void setPoppingSound(bool value) { poping_sound_enabled_ = value; } - void setSound(bool value) { sound_enabled_ = value; } + // --- Setters --- + void setVelY(float vel_y) { vy_ = vel_y; } + void setSpeed(float speed) { speed_ = speed; } + void setInvulnerable(bool value) { invulnerable_ = value; } + void setBouncingSound(bool value) { bouncing_sound_enabled_ = value; } + void setPoppingSound(bool value) { poping_sound_enabled_ = value; } + void setSound(bool value) { sound_enabled_ = value; } -private: - // --- Estructura para el efecto de rebote --- - struct Bouncing - { - bool enabled = false; // Si el efecto está activo - Uint8 counter = 0; // Contador para el efecto - Uint8 speed = 2; // Velocidad del efecto - float zoomW = 1.0f; // Zoom en anchura - float zoomH = 1.0f; // Zoom en altura - float despX = 0.0f; // Desplazamiento X antes de pintar - float despY = 0.0f; // Desplazamiento Y antes de pintar + private: + // --- Estructura para el efecto de rebote --- + struct Bouncing { + bool enabled = false; // Si el efecto está activo + Uint8 counter = 0; // Contador para el efecto + Uint8 speed = 2; // Velocidad del efecto + float zoomW = 1.0f; // Zoom en anchura + float zoomH = 1.0f; // Zoom en altura + float despX = 0.0f; // Desplazamiento X antes de pintar + float despY = 0.0f; // Desplazamiento Y antes de pintar - float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; // Zoom ancho - float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; // Zoom alto + float w[MAX_BOUNCE] = {1.10f, 1.05f, 1.00f, 0.95f, 0.90f, 0.95f, 1.00f, 1.02f, 1.05f, 1.02f}; // Zoom ancho + float h[MAX_BOUNCE] = {0.90f, 0.95f, 1.00f, 1.05f, 1.10f, 1.05f, 1.00f, 0.98f, 0.95f, 0.98f}; // Zoom alto - Bouncing() = default; - void reset() - { - counter = 0; - zoomW = 1.0f; - zoomH = 1.0f; - despX = 0.0f; - despY = 0.0f; - } - } bouncing_; + Bouncing() = default; + void reset() { + counter = 0; + zoomW = 1.0f; + zoomH = 1.0f; + despX = 0.0f; + despY = 0.0f; + } + } bouncing_; - // --- Objetos y punteros --- - std::unique_ptr sprite_; // Sprite del objeto globo + // --- Objetos y punteros --- + std::unique_ptr sprite_; // Sprite del objeto globo - // --- Variables de estado y físicas --- - float x_; // Posición X - float y_; // Posición Y - float w_; // Ancho - float h_; // Alto - float vx_; // Velocidad X - float vy_; // Velocidad Y - float gravity_; // Aceleración en Y - float default_vy_; // Velocidad inicial al rebotar - float max_vy_; // Máxima velocidad en Y - bool being_created_; // Si el globo se está creando - bool enabled_ = true; // Si el globo está activo - bool invulnerable_; // Si el globo es invulnerable - bool stopped_; // Si el globo está parado - bool use_reversed_colors_ = false; // Si se usa el color alternativo - Circle collider_; // Círculo de colisión - Uint16 creation_counter_; // Temporizador de creación - Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación - Uint16 score_; // Puntos al destruir el globo - BalloonType type_; // Tipo de globo - BalloonSize size_; // Tamaño de globo - Uint8 menace_; // Amenaza que genera el globo - Uint32 counter_ = 0; // Contador interno - float travel_y_ = 1.0f; // Distancia a recorrer en Y antes de aplicar gravedad - float speed_; // Velocidad del globo - Uint8 power_; // Poder que alberga el globo - SDL_FRect play_area_; // Zona de movimiento del globo - std::string bouncing_sound_; // Archivo de sonido al rebotar - std::string popping_sound_; // Archivo de sonido al explotar - bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar - bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar - bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido + // --- Variables de estado y físicas --- + float x_; // Posición X + float y_; // Posición Y + float w_; // Ancho + float h_; // Alto + float vx_; // Velocidad X + float vy_; // Velocidad Y + float gravity_; // Aceleración en Y + float default_vy_; // Velocidad inicial al rebotar + float max_vy_; // Máxima velocidad en Y + bool being_created_; // Si el globo se está creando + bool enabled_ = true; // Si el globo está activo + bool invulnerable_; // Si el globo es invulnerable + bool stopped_; // Si el globo está parado + bool use_reversed_colors_ = false; // Si se usa el color alternativo + Circle collider_; // Círculo de colisión + Uint16 creation_counter_; // Temporizador de creación + Uint16 creation_counter_ini_; // Valor inicial del temporizador de creación + Uint16 score_; // Puntos al destruir el globo + BalloonType type_; // Tipo de globo + BalloonSize size_; // Tamaño de globo + Uint8 menace_; // Amenaza que genera el globo + Uint32 counter_ = 0; // Contador interno + float travel_y_ = 1.0f; // Distancia a recorrer en Y antes de aplicar gravedad + float speed_; // Velocidad del globo + Uint8 power_; // Poder que alberga el globo + SDL_FRect play_area_; // Zona de movimiento del globo + std::string bouncing_sound_; // Archivo de sonido al rebotar + std::string popping_sound_; // Archivo de sonido al explotar + bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar + bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar + bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido - // --- Métodos internos --- - void shiftColliders(); // Alinea el círculo de colisión - void shiftSprite(); // Alinea el sprite - void zoomSprite(); // Establece el nivel de zoom del sprite - void enableBounce(); // Activa el efecto de rebote - void disableBounce(); // Detiene el efecto de rebote - void updateBounce(); // Aplica el efecto de rebote - void updateState(); // Actualiza los estados del globo - void setAnimation(); // Establece la animación correspondiente - void playSound(const std::string &name); // Reproduce sonido + // --- Métodos internos --- + void shiftColliders(); // Alinea el círculo de colisión + void shiftSprite(); // Alinea el sprite + void zoomSprite(); // Establece el nivel de zoom del sprite + void enableBounce(); // Activa el efecto de rebote + void disableBounce(); // Detiene el efecto de rebote + void updateBounce(); // Aplica el efecto de rebote + void updateState(); // Actualiza los estados del globo + void setAnimation(); // Establece la animación correspondiente + void playSound(const std::string &name); // Reproduce sonido }; \ No newline at end of file diff --git a/source/balloon_formations.cpp b/source/balloon_formations.cpp index d6e59a1..041a7b6 100644 --- a/source/balloon_formations.cpp +++ b/source/balloon_formations.cpp @@ -1,457 +1,377 @@ #include "balloon_formations.h" -#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE -#include "param.h" // Para param -#include "utils.h" // Para ParamGame, Param, Zone, BLOCK +#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE +#include "param.h" // Para param +#include "utils.h" // Para ParamGame, Param, Zone, BLOCK -void BalloonFormations::initBalloonFormations() -{ - constexpr int y4 = -BLOCK; - const int x4_0 = param.game.play_area.rect.x; - const int x4_100 = param.game.play_area.rect.w - BALLOON_SIZE[3]; +void BalloonFormations::initBalloonFormations() { + constexpr int y4 = -BLOCK; + const int x4_0 = param.game.play_area.rect.x; + const int x4_100 = param.game.play_area.rect.w - BALLOON_SIZE[3]; - constexpr int y3 = -BLOCK; - const int x3_0 = param.game.play_area.rect.x; - const int x3_100 = param.game.play_area.rect.w - BALLOON_SIZE[2]; + constexpr int y3 = -BLOCK; + const int x3_0 = param.game.play_area.rect.x; + const int x3_100 = param.game.play_area.rect.w - BALLOON_SIZE[2]; - constexpr int y2 = -BLOCK; - const int x2_0 = param.game.play_area.rect.x; - const int x2_100 = param.game.play_area.rect.w - BALLOON_SIZE[1]; + constexpr int y2 = -BLOCK; + const int x2_0 = param.game.play_area.rect.x; + const int x2_100 = param.game.play_area.rect.w - BALLOON_SIZE[1]; - constexpr int y1 = -BLOCK; - const int x1_0 = param.game.play_area.rect.x; - const int x1_50 = param.game.play_area.center_x - (BALLOON_SIZE[0] / 2); - const int x1_100 = param.game.play_area.rect.w - BALLOON_SIZE[0]; + constexpr int y1 = -BLOCK; + const int x1_0 = param.game.play_area.rect.x; + const int x1_50 = param.game.play_area.center_x - (BALLOON_SIZE[0] / 2); + const int x1_100 = param.game.play_area.rect.w - BALLOON_SIZE[0]; - balloon_formation_.reserve(NUMBER_OF_BALLOON_FORMATIONS); + balloon_formation_.reserve(NUMBER_OF_BALLOON_FORMATIONS); - constexpr int CREATION_TIME = 300; + constexpr int CREATION_TIME = 300; - // #00 - Dos enemigos BALLOON4 uno a cada extremo - { - std::vector init_params = { - BalloonFormationParams(x4_0, y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME), - BalloonFormationParams(x4_100, y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)}; - balloon_formation_.emplace_back(2, init_params); - } + // #00 - Dos enemigos BALLOON4 uno a cada extremo + { + std::vector init_params = { + BalloonFormationParams(x4_0, y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME), + BalloonFormationParams(x4_100, y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)}; + balloon_formation_.emplace_back(2, init_params); + } - // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro - { - std::vector init_params = { - BalloonFormationParams(param.game.play_area.first_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME), - BalloonFormationParams(param.game.play_area.third_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)}; - balloon_formation_.emplace_back(2, init_params); - } + // #01 - Dos enemigos BALLOON4 uno a cada cuarto. Ambos van hacia el centro + { + std::vector init_params = { + BalloonFormationParams(param.game.play_area.first_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME), + BalloonFormationParams(param.game.play_area.third_quarter_x - (BALLOON_SIZE[3] / 2), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME)}; + balloon_formation_.emplace_back(2, init_params); + } - // #02 - Cuatro enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro - { - std::vector init_params; - for (int i = 0; i < 4; ++i) - { - init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(4, init_params); - } + // #02 - Cuatro enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro + { + std::vector init_params; + for (int i = 0; i < 4; ++i) { + init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(4, init_params); + } - // #03 - Cuatro enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro - { - std::vector init_params; - for (int i = 0; i < 4; ++i) - { - init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(4, init_params); - } + // #03 - Cuatro enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro + { + std::vector init_params; + for (int i = 0; i < 4; ++i) { + init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(4, init_params); + } - // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #04 - Tres enemigos BALLOON3. 0, 25, 50. Hacia la derecha + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #05 - Tres enemigos BALLOON3. 50, 75, 100. Hacia la izquierda + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #06 - Tres enemigos BALLOON3. 0, 0, 0. Hacia la derecha + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #07 - Tres enemigos BALLOON3. 100, 100, 100. Hacia la izquierda + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha - { - std::vector init_params; - for (int i = 0; i < 6; ++i) - { - init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(6, init_params); - } + // #08 - Seis enemigos BALLOON1. 0, 0, 0, 0, 0, 0. Hacia la derecha + { + std::vector init_params; + for (int i = 0; i < 6; ++i) { + init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(6, init_params); + } - // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda - { - std::vector init_params; - for (int i = 0; i < 6; ++i) - { - init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(6, init_params); - } + // #09 - Seis enemigos BALLOON1. 100, 100, 100, 100, 100, 100. Hacia la izquierda + { + std::vector init_params; + for (int i = 0; i < 6; ++i) { + init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(6, init_params); + } - // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda. Hacia la derecha - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #10 - Tres enemigos BALLOON4 seguidos desde la izquierda. Hacia la derecha + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #11 - Tres enemigos BALLOON4 seguidos desde la derecha. Hacia la izquierda - { - std::vector init_params; - for (int i = 0; i < 3; ++i) - { - init_params.emplace_back(x4_100 - (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i)); - } - balloon_formation_.emplace_back(3, init_params); - } + // #11 - Tres enemigos BALLOON4 seguidos desde la derecha. Hacia la izquierda + { + std::vector init_params; + for (int i = 0; i < 3; ++i) { + init_params.emplace_back(x4_100 - (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME - (15 * i)); + } + balloon_formation_.emplace_back(3, init_params); + } - // #12 - Seis enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro - { - std::vector init_params; - for (int i = 0; i < 6; ++i) - { - init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(6, init_params); - } + // #12 - Seis enemigos BALLOON2 uno detrás del otro. A la izquierda y hacia el centro + { + std::vector init_params; + for (int i = 0; i < 6; ++i) { + init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(6, init_params); + } - // #13 - Seis enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro - { - std::vector init_params; - for (int i = 0; i < 6; ++i) - { - init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(6, init_params); - } + // #13 - Seis enemigos BALLOON2 uno detrás del otro. A la derecha y hacia el centro + { + std::vector init_params; + for (int i = 0; i < 6; ++i) { + init_params.emplace_back(x2_100 - (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(6, init_params); + } - // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados - { - std::vector init_params; - for (int i = 0; i < 5; ++i) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(5, init_params); - } + // #14 - Cinco enemigos BALLOON3. Hacia la derecha. Separados + { + std::vector init_params; + for (int i = 0; i < 5; ++i) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(5, init_params); + } - // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados - { - std::vector init_params; - for (int i = 0; i < 5; ++i) - { - init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(5, init_params); - } + // #15 - Cinco enemigos BALLOON3. Hacia la izquierda. Separados + { + std::vector init_params; + for (int i = 0; i < 5; ++i) { + init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(5, init_params); + } - // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos - { - std::vector init_params; - for (int i = 0; i < 5; ++i) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(5, init_params); - } + // #16 - Cinco enemigos BALLOON3. Hacia la derecha. Juntos + { + std::vector init_params; + for (int i = 0; i < 5; ++i) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(5, init_params); + } - // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos - { - std::vector init_params; - for (int i = 0; i < 5; ++i) - { - init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(5, init_params); - } + // #17 - Cinco enemigos BALLOON3. Hacia la izquierda. Juntos + { + std::vector init_params; + for (int i = 0; i < 5; ++i) { + init_params.emplace_back(x3_100 - (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(5, init_params); + } - // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos - { - std::vector init_params; - for (int i = 0; i < 12; ++i) - { - init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(12, init_params); - } + // #18 - Doce enemigos BALLOON1. Hacia la derecha. Juntos + { + std::vector init_params; + for (int i = 0; i < 12; ++i) { + init_params.emplace_back(x1_0 + (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(12, init_params); + } - // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos - { - std::vector init_params; - for (int i = 0; i < 12; ++i) - { - init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); - } - balloon_formation_.emplace_back(12, init_params); - } + // #19 - Doce enemigos BALLOON1. Hacia la izquierda. Juntos + { + std::vector init_params; + for (int i = 0; i < 12; ++i) { + init_params.emplace_back(x1_100 - (i * (BALLOON_SIZE[0] + 1)), y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (10 * i)); + } + balloon_formation_.emplace_back(12, init_params); + } - // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos - { - std::vector init_params; - const int half = 4 / 2; - for (int i = 0; i < 4; ++i) - { - if (i < half) - { - init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); - } - else - { - init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); - } - } - balloon_formation_.emplace_back(4, init_params); - } + // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos + { + std::vector init_params; + const int half = 4 / 2; + for (int i = 0; i < 4; ++i) { + if (i < half) { + init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); + } else { + init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); + } + } + balloon_formation_.emplace_back(4, init_params); + } - // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos - { - std::vector init_params; - const int half = 4 / 2; - for (int i = 0; i < 4; ++i) - { - if (i < half) - { - init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); - } - else - { - init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); - } - } - balloon_formation_.emplace_back(4, init_params); - } + // #20 - Dos enemigos BALLOON4 seguidos desde la izquierda/derecha. Simétricos + { + std::vector init_params; + const int half = 4 / 2; + for (int i = 0; i < 4; ++i) { + if (i < half) { + init_params.emplace_back(x4_0 + (i * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); + } else { + init_params.emplace_back(x4_100 - ((i - half) * (BALLOON_SIZE[3] + 1)), y4, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE4, CREATION_TIME + (0 * i)); + } + } + balloon_formation_.emplace_back(4, init_params); + } - // #21 - Diez enemigos BALLOON2 uno detrás del otro. Izquierda/derecha. Simétricos - { - std::vector init_params; - const int half = 10 / 2; - for (int i = 0; i < 10; ++i) - { - if (i < half) - { - init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * i)); - } - else - { - init_params.emplace_back(x2_100 - ((i - half) * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * (i - half))); - } - } - balloon_formation_.emplace_back(10, init_params); - } + // #21 - Diez enemigos BALLOON2 uno detrás del otro. Izquierda/derecha. Simétricos + { + std::vector init_params; + const int half = 10 / 2; + for (int i = 0; i < 10; ++i) { + if (i < half) { + init_params.emplace_back(x2_0 + (i * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * i)); + } else { + init_params.emplace_back(x2_100 - ((i - half) * (BALLOON_SIZE[1] + 1)), y2, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE2, CREATION_TIME - (3 * (i - half))); + } + } + balloon_formation_.emplace_back(10, init_params); + } - // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simétricos - { - std::vector init_params; - const int half = 10 / 2; - for (int i = 0; i < 10; ++i) - { - if (i < half) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - else - { - init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half))); - } - } - balloon_formation_.emplace_back(10, init_params); - } + // #22 - Diez enemigos BALLOON3. Hacia la derecha/izquierda. Separados. Simétricos + { + std::vector init_params; + const int half = 10 / 2; + for (int i = 0; i < 10; ++i) { + if (i < half) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } else { + init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] * 2)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half))); + } + } + balloon_formation_.emplace_back(10, init_params); + } - // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simétricos - { - std::vector init_params; - const int half = 10 / 2; - for (int i = 0; i < 10; ++i) - { - if (i < half) - { - init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); - } - else - { - init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half))); - } - } - balloon_formation_.emplace_back(10, init_params); - } + // #23 - Diez enemigos BALLOON3. Hacia la derecha. Juntos. Simétricos + { + std::vector init_params; + const int half = 10 / 2; + for (int i = 0; i < 10; ++i) { + if (i < half) { + init_params.emplace_back(x3_0 + (i * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * i)); + } else { + init_params.emplace_back(x3_100 - ((i - half) * (BALLOON_SIZE[2] + 1)), y3, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE3, CREATION_TIME - (10 * (i - half))); + } + } + balloon_formation_.emplace_back(10, init_params); + } - // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simétricos - { - std::vector init_params; - const int half = 30 / 2; - for (int i = 0; i < 30; ++i) - { - if (i < half) - { - init_params.emplace_back(x1_50, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * i)); - } - else - { - init_params.emplace_back(x1_50, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * (i - half))); - } - } - balloon_formation_.emplace_back(30, init_params); - } + // #24 - Treinta enemigos BALLOON1. Del centro hacia los extremos. Juntos. Simétricos + { + std::vector init_params; + const int half = 30 / 2; + for (int i = 0; i < 30; ++i) { + if (i < half) { + init_params.emplace_back(x1_50, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * i)); + } else { + init_params.emplace_back(x1_50, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME + (5 * (i - half))); + } + } + balloon_formation_.emplace_back(30, init_params); + } - // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simétricos - { - std::vector init_params; - const int half = 30 / 2; - for (int i = 0; i < 30; ++i) - { - if (i < half) - { - init_params.emplace_back(x1_50 + 20, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * i)); - } - else - { - init_params.emplace_back(x1_50 - 20, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * (i - half))); - } - } - balloon_formation_.emplace_back(30, init_params); - } + // #25 - Treinta enemigos BALLOON1. Del centro hacia adentro. Juntos. Simétricos + { + std::vector init_params; + const int half = 30 / 2; + for (int i = 0; i < 30; ++i) { + if (i < half) { + init_params.emplace_back(x1_50 + 20, y1, BALLOON_VELX_NEGATIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * i)); + } else { + init_params.emplace_back(x1_50 - 20, y1, BALLOON_VELX_POSITIVE, BalloonType::BALLOON, BalloonSize::SIZE1, CREATION_TIME - (5 * (i - half))); + } + } + balloon_formation_.emplace_back(30, init_params); + } - // Reservar espacio para el vector - balloon_formation_.resize(100); + // Reservar espacio para el vector + balloon_formation_.resize(100); - // Crea las mismas formaciones pero con hexágonos a partir de la posición 50 del vector - for (int k = 0; k < 50; k++) - { - std::vector init_params; - for (int i = 0; i < balloon_formation_.at(k).number_of_balloons; i++) - { - init_params.emplace_back( - balloon_formation_.at(k).init.at(i).x, - balloon_formation_.at(k).init.at(i).y, - balloon_formation_.at(k).init.at(i).vel_x, - BalloonType::FLOATER, - balloon_formation_.at(k).init.at(i).size, - balloon_formation_.at(k).init.at(i).creation_counter); - } - balloon_formation_.at(k + 50) = BalloonFormationUnit(balloon_formation_.at(k).number_of_balloons, init_params); - } + // Crea las mismas formaciones pero con hexágonos a partir de la posición 50 del vector + for (int k = 0; k < 50; k++) { + std::vector init_params; + for (int i = 0; i < balloon_formation_.at(k).number_of_balloons; i++) { + init_params.emplace_back( + balloon_formation_.at(k).init.at(i).x, + balloon_formation_.at(k).init.at(i).y, + balloon_formation_.at(k).init.at(i).vel_x, + BalloonType::FLOATER, + balloon_formation_.at(k).init.at(i).size, + balloon_formation_.at(k).init.at(i).creation_counter); + } + balloon_formation_.at(k + 50) = BalloonFormationUnit(balloon_formation_.at(k).number_of_balloons, init_params); + } - // TEST - std::vector test_params = { - {10, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE1, 200}, - {50, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE2, 200}, - {90, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE3, 200}, - {140, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE4, 200}}; + // TEST + std::vector test_params = { + {10, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE1, 200}, + {50, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE2, 200}, + {90, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE3, 200}, + {140, y1, 0, BalloonType::FLOATER, BalloonSize::SIZE4, 200}}; - balloon_formation_.at(99) = BalloonFormationUnit(4, test_params); + balloon_formation_.at(99) = BalloonFormationUnit(4, test_params); } // Inicializa los conjuntos de formaciones -void BalloonFormations::initBalloonFormationPools() -{ - // Reserva espacio para cada pool de formaciones - balloon_formation_pool_.resize(NUMBER_OF_SETS_PER_POOL); +void BalloonFormations::initBalloonFormationPools() { + // Reserva espacio para cada pool de formaciones + balloon_formation_pool_.resize(NUMBER_OF_SETS_PER_POOL); - // Set #0 - balloon_formation_pool_.at(0) = { - &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), - &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(5), - &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), - &balloon_formation_.at(9)}; + // Set #0 + balloon_formation_pool_.at(0) = { + &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)}; - // Set #1 - balloon_formation_pool_.at(1) = { - &balloon_formation_.at(10), &balloon_formation_.at(11), &balloon_formation_.at(12), - &balloon_formation_.at(13), &balloon_formation_.at(14), &balloon_formation_.at(15), - &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), - &balloon_formation_.at(19)}; + // Set #1 + balloon_formation_pool_.at(1) = { + &balloon_formation_.at(10), &balloon_formation_.at(11), &balloon_formation_.at(12), &balloon_formation_.at(13), &balloon_formation_.at(14), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)}; - // Set #2 - balloon_formation_pool_.at(2) = { - &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), - &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(55), - &balloon_formation_.at(56), &balloon_formation_.at(57), &balloon_formation_.at(58), - &balloon_formation_.at(59)}; + // Set #2 + balloon_formation_pool_.at(2) = { + &balloon_formation_.at(0), &balloon_formation_.at(1), &balloon_formation_.at(2), &balloon_formation_.at(3), &balloon_formation_.at(4), &balloon_formation_.at(55), &balloon_formation_.at(56), &balloon_formation_.at(57), &balloon_formation_.at(58), &balloon_formation_.at(59)}; - // Set #3 - balloon_formation_pool_.at(3) = { - &balloon_formation_.at(50), &balloon_formation_.at(51), &balloon_formation_.at(52), - &balloon_formation_.at(53), &balloon_formation_.at(54), &balloon_formation_.at(5), - &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), - &balloon_formation_.at(9)}; + // Set #3 + balloon_formation_pool_.at(3) = { + &balloon_formation_.at(50), &balloon_formation_.at(51), &balloon_formation_.at(52), &balloon_formation_.at(53), &balloon_formation_.at(54), &balloon_formation_.at(5), &balloon_formation_.at(6), &balloon_formation_.at(7), &balloon_formation_.at(8), &balloon_formation_.at(9)}; - // Set #4 - balloon_formation_pool_.at(4) = { - &balloon_formation_.at(60), &balloon_formation_.at(61), &balloon_formation_.at(62), - &balloon_formation_.at(63), &balloon_formation_.at(64), &balloon_formation_.at(65), - &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), - &balloon_formation_.at(69)}; + // Set #4 + balloon_formation_pool_.at(4) = { + &balloon_formation_.at(60), &balloon_formation_.at(61), &balloon_formation_.at(62), &balloon_formation_.at(63), &balloon_formation_.at(64), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)}; - // Set #5 - balloon_formation_pool_.at(5) = { - &balloon_formation_.at(10), &balloon_formation_.at(61), &balloon_formation_.at(12), - &balloon_formation_.at(63), &balloon_formation_.at(14), &balloon_formation_.at(65), - &balloon_formation_.at(16), &balloon_formation_.at(67), &balloon_formation_.at(18), - &balloon_formation_.at(69)}; + // Set #5 + balloon_formation_pool_.at(5) = { + &balloon_formation_.at(10), &balloon_formation_.at(61), &balloon_formation_.at(12), &balloon_formation_.at(63), &balloon_formation_.at(14), &balloon_formation_.at(65), &balloon_formation_.at(16), &balloon_formation_.at(67), &balloon_formation_.at(18), &balloon_formation_.at(69)}; - // Set #6 - balloon_formation_pool_.at(6) = { - &balloon_formation_.at(60), &balloon_formation_.at(11), &balloon_formation_.at(62), - &balloon_formation_.at(13), &balloon_formation_.at(64), &balloon_formation_.at(15), - &balloon_formation_.at(66), &balloon_formation_.at(17), &balloon_formation_.at(68), - &balloon_formation_.at(19)}; + // Set #6 + balloon_formation_pool_.at(6) = { + &balloon_formation_.at(60), &balloon_formation_.at(11), &balloon_formation_.at(62), &balloon_formation_.at(13), &balloon_formation_.at(64), &balloon_formation_.at(15), &balloon_formation_.at(66), &balloon_formation_.at(17), &balloon_formation_.at(68), &balloon_formation_.at(19)}; - // Set #7 - balloon_formation_pool_.at(7) = { - &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), - &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(65), - &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), - &balloon_formation_.at(69)}; + // Set #7 + balloon_formation_pool_.at(7) = { + &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(65), &balloon_formation_.at(66), &balloon_formation_.at(67), &balloon_formation_.at(68), &balloon_formation_.at(69)}; - // Set #8 - balloon_formation_pool_.at(8) = { - &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), - &balloon_formation_.at(73), &balloon_formation_.at(74), &balloon_formation_.at(15), - &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), - &balloon_formation_.at(19)}; + // Set #8 + balloon_formation_pool_.at(8) = { + &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74), &balloon_formation_.at(15), &balloon_formation_.at(16), &balloon_formation_.at(17), &balloon_formation_.at(18), &balloon_formation_.at(19)}; - // Set #9 - balloon_formation_pool_.at(9) = { - &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), - &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(70), - &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), - &balloon_formation_.at(74)}; + // Set #9 + balloon_formation_pool_.at(9) = { + &balloon_formation_.at(20), &balloon_formation_.at(21), &balloon_formation_.at(22), &balloon_formation_.at(23), &balloon_formation_.at(24), &balloon_formation_.at(70), &balloon_formation_.at(71), &balloon_formation_.at(72), &balloon_formation_.at(73), &balloon_formation_.at(74)}; } diff --git a/source/balloon_formations.h b/source/balloon_formations.h index bc7dc06..8208fb3 100644 --- a/source/balloon_formations.h +++ b/source/balloon_formations.h @@ -2,7 +2,7 @@ #include -#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE +#include "balloon.h" // Para BALLOON_VELX_NEGATIVE, BALLOON_VELX_POSITIVE // --- Constantes de configuración --- constexpr int NUMBER_OF_BALLOON_FORMATIONS = 100; @@ -11,14 +11,13 @@ constexpr int NUMBER_OF_SETS_PER_POOL = 10; constexpr int NUMBER_OF_STAGES = 10; // --- Estructuras de datos --- -struct BalloonFormationParams -{ - int x = 0; // Posición en el eje X donde crear el globo - int y = 0; // Posición en el eje Y donde crear el globo - float vel_x = 0.0f; // Velocidad inicial en el eje X - BalloonType type = BalloonType::BALLOON; // Tipo de globo - BalloonSize size = BalloonSize::SIZE1; // Tamaño de globo - int creation_counter = 0; // Temporizador para la creación del globo +struct BalloonFormationParams { + int x = 0; // Posición en el eje X donde crear el globo + int y = 0; // Posición en el eje Y donde crear el globo + float vel_x = 0.0f; // Velocidad inicial en el eje X + BalloonType type = BalloonType::BALLOON; // Tipo de globo + BalloonSize size = BalloonSize::SIZE1; // Tamaño de globo + int creation_counter = 0; // Temporizador para la creación del globo // Constructor por defecto BalloonFormationParams() = default; @@ -28,10 +27,9 @@ struct BalloonFormationParams : x(x_val), y(y_val), vel_x(vel_x_val), type(type_val), size(size_val), creation_counter(creation_counter_val) {} }; -struct BalloonFormationUnit -{ - int number_of_balloons; // Cantidad de globos que forman la formación - std::vector init; // Vector con todas las inicializaciones de los globos de la formación +struct BalloonFormationUnit { + int number_of_balloons; // Cantidad de globos que forman la formación + std::vector init; // Vector con todas las inicializaciones de los globos de la formación // Constructor con parámetros BalloonFormationUnit(int num_balloons, const std::vector &init_params) @@ -44,12 +42,10 @@ struct BalloonFormationUnit using BalloonFormationPool = std::vector; // --- Clase BalloonFormations --- -class BalloonFormations -{ -public: +class BalloonFormations { + public: // --- Constructor y destructor --- - BalloonFormations() - { + BalloonFormations() { initBalloonFormations(); initBalloonFormationPools(); } @@ -60,10 +56,10 @@ public: const BalloonFormationUnit &getSet(int pool, int set) { return *balloon_formation_pool_.at(pool).at(set); } const BalloonFormationUnit &getSet(int set) const { return balloon_formation_.at(set); } -private: + private: // --- Datos --- - std::vector balloon_formation_; // Vector con todas las formaciones enemigas - std::vector balloon_formation_pool_; // Conjuntos de formaciones enemigas + std::vector balloon_formation_; // Vector con todas las formaciones enemigas + std::vector balloon_formation_pool_; // Conjuntos de formaciones enemigas // --- Métodos internos de inicialización --- void initBalloonFormations(); diff --git a/source/balloon_manager.cpp b/source/balloon_manager.cpp index 0beed7c..469b000 100644 --- a/source/balloon_manager.cpp +++ b/source/balloon_manager.cpp @@ -1,17 +1,18 @@ #include "balloon_manager.h" -#include // Para rand -#include // Para remove_if -#include // Para accumulate +#include // Para rand -#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VELX... -#include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma... -#include "explosions.h" // Para Explosions -#include "param.h" // Para Param, ParamGame, param -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "stage.h" // Para addPower -#include "utils.h" // Para Zone, Color, flash_color +#include // Para remove_if +#include // Para accumulate + +#include "balloon.h" // Para Balloon, BALLOON_SCORE, BALLOON_VELX... +#include "balloon_formations.h" // Para BalloonFormationParams, BalloonForma... +#include "explosions.h" // Para Explosions +#include "param.h" // Para Param, ParamGame, param +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "stage.h" // Para addPower +#include "utils.h" // Para Zone, Color, flash_color // Constructor BalloonManager::BalloonManager() @@ -19,8 +20,7 @@ BalloonManager::BalloonManager() balloon_formations_(std::make_unique()) { init(); } // Inicializa -void BalloonManager::init() -{ +void BalloonManager::init() { // Texturas - Globos balloon_textures_.emplace_back(Resource::get()->getTexture("balloon1.png")); balloon_textures_.emplace_back(Resource::get()->getTexture("balloon2.png")); @@ -55,10 +55,8 @@ void BalloonManager::init() } // Actualiza -void BalloonManager::update() -{ - for (auto balloon : balloons_) - { +void BalloonManager::update() { + for (auto balloon : balloons_) { balloon->update(); } updateBalloonDeployCounter(); @@ -66,32 +64,25 @@ void BalloonManager::update() } // Renderiza los objetos -void BalloonManager::render() -{ - for (auto &balloon : balloons_) - { +void BalloonManager::render() { + for (auto &balloon : balloons_) { balloon->render(); } explosions_->render(); } // Crea una formación de enemigos -void BalloonManager::deployBalloonFormation(int stage) -{ +void BalloonManager::deployBalloonFormation(int stage) { // Solo despliega una formación enemiga si ha pasado cierto tiempo desde la última - if (balloon_deploy_counter_ == 0) - { + if (balloon_deploy_counter_ == 0) { // En este punto se decide entre crear una powerball o una formación enemiga - if ((rand() % 100 < 15) && (canPowerBallBeCreated())) - { + if ((rand() % 100 < 15) && (canPowerBallBeCreated())) { // Crea una powerball createPowerBall(); // Da un poco de margen para que se creen mas enemigos balloon_deploy_counter_ = 10; - } - else - { + } else { // Decrementa el contador de despliegues enemigos de la PowerBall power_ball_counter_ = (power_ball_counter_ > 0) ? (power_ball_counter_ - 1) : 0; @@ -99,8 +90,7 @@ void BalloonManager::deployBalloonFormation(int stage) auto formation = rand() % 10; // Evita repetir la ultima formación enemiga desplegada - if (formation == last_balloon_deploy_) - { + if (formation == last_balloon_deploy_) { ++formation %= 10; } @@ -108,8 +98,7 @@ void BalloonManager::deployBalloonFormation(int stage) const auto set = balloon_formations_->getSet(stage, formation); const auto num_enemies = set.number_of_balloons; - for (int i = 0; i < num_enemies; ++i) - { + for (int i = 0; i < num_enemies; ++i) { auto p = set.init[i]; createBalloon( p.x, @@ -127,42 +116,34 @@ void BalloonManager::deployBalloonFormation(int stage) } // Crea una formación de enemigos específica -void BalloonManager::deploySet(int set_number) -{ +void BalloonManager::deploySet(int set_number) { const auto set = balloon_formations_->getSet(set_number); const auto num_enemies = set.number_of_balloons; - for (int i = 0; i < num_enemies; ++i) - { + for (int i = 0; i < num_enemies; ++i) { auto p = set.init[i]; createBalloon(p.x, p.y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter); } } // Crea una formación de enemigos específica -void BalloonManager::deploySet(int set_number, int y) -{ +void BalloonManager::deploySet(int set_number, int y) { const auto set = balloon_formations_->getSet(set_number); const auto num_enemies = set.number_of_balloons; - for (int i = 0; i < num_enemies; ++i) - { + for (int i = 0; i < num_enemies; ++i) { auto p = set.init[i]; createBalloon(p.x, y, p.type, p.size, p.vel_x, balloon_speed_, p.creation_counter); } } // Vacia del vector de globos los globos que ya no sirven -void BalloonManager::freeBalloons() -{ - auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) - { return !balloon->isEnabled(); }); +void BalloonManager::freeBalloons() { + auto it = std::remove_if(balloons_.begin(), balloons_.end(), [](const auto &balloon) { return !balloon->isEnabled(); }); balloons_.erase(it, balloons_.end()); } // Actualiza la variable enemyDeployCounter -void BalloonManager::updateBalloonDeployCounter() -{ - if (balloon_deploy_counter_ > 0) - { +void BalloonManager::updateBalloonDeployCounter() { + if (balloon_deploy_counter_ > 0) { --balloon_deploy_counter_; } } @@ -171,17 +152,13 @@ void BalloonManager::updateBalloonDeployCounter() bool BalloonManager::canPowerBallBeCreated() { return (!power_ball_enabled_) && (calculateScreenPower() > POWERBALL_SCREENPOWER_MINIMUM) && (power_ball_counter_ == 0); } // Calcula el poder actual de los globos en pantalla -int BalloonManager::calculateScreenPower() -{ - return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) - { return sum + (balloon->isEnabled() ? balloon->getPower() : 0); }); +int BalloonManager::calculateScreenPower() { + return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getPower() : 0); }); } // Crea un globo nuevo en el vector de globos -std::shared_ptr BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer) -{ - if (can_deploy_balloons_) - { +std::shared_ptr BalloonManager::createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer) { + if (can_deploy_balloons_) { const int INDEX = static_cast(size); balloons_.emplace_back(std::make_shared(x, y, type, size, velx, speed, creation_timer, play_area_, balloon_textures_.at(INDEX), balloon_animations_.at(INDEX))); balloons_.back()->setSound(sound_enabled_); @@ -194,10 +171,8 @@ std::shared_ptr BalloonManager::createBalloon(float x, int y, BalloonTy } // Crea un globo a partir de otro globo -void BalloonManager::createChildBalloon(const std::shared_ptr &balloon, const std::string &direction) -{ - if (can_deploy_balloons_) - { +void BalloonManager::createChildBalloon(const std::shared_ptr &balloon, const std::string &direction) { + if (can_deploy_balloons_) { // Calcula parametros const float VX = direction == "LEFT" ? BALLOON_VELX_NEGATIVE : BALLOON_VELX_POSITIVE; const auto SIZE = static_cast(static_cast(balloon->getSize()) - 1); @@ -214,22 +189,18 @@ void BalloonManager::createChildBalloon(const std::shared_ptr &balloon, b->setVelY(b->getType() == BalloonType::BALLOON ? -2.50f : BALLOON_VELX_NEGATIVE * 2.0f); // Herencia de estados - if (balloon->isStopped()) - { + if (balloon->isStopped()) { b->stop(); } - if (balloon->isUsingReversedColor()) - { + if (balloon->isUsingReversedColor()) { b->useReverseColor(); } } } // Crea una PowerBall -void BalloonManager::createPowerBall() -{ - if (can_deploy_balloons_) - { +void BalloonManager::createPowerBall() { + if (can_deploy_balloons_) { constexpr int VALUES = 6; constexpr float POS_Y = -BALLOON_SIZE[4]; constexpr int CREATION_TIME = 0; @@ -251,33 +222,26 @@ void BalloonManager::createPowerBall() } // Establece la velocidad de los globos -void BalloonManager::setBalloonSpeed(float speed) -{ +void BalloonManager::setBalloonSpeed(float speed) { balloon_speed_ = speed; - for (auto &balloon : balloons_) - { + for (auto &balloon : balloons_) { balloon->setSpeed(speed); } } // Explosiona un globo. Lo destruye y crea otros dos si es el caso -int BalloonManager::popBalloon(std::shared_ptr balloon) -{ +int BalloonManager::popBalloon(std::shared_ptr balloon) { Stage::addPower(1); int score = 0; - if (balloon->getType() == BalloonType::POWERBALL) - { + if (balloon->getType() == BalloonType::POWERBALL) { balloon->pop(true); score = destroyAllBalloons(); power_ball_enabled_ = false; balloon_deploy_counter_ = 20; - } - else - { + } else { score = balloon->getScore(); - if (balloon->getSize() != BalloonSize::SIZE1) - { + if (balloon->getSize() != BalloonSize::SIZE1) { createChildBalloon(balloon, "LEFT"); createChildBalloon(balloon, "RIGHT"); } @@ -291,28 +255,26 @@ int BalloonManager::popBalloon(std::shared_ptr balloon) } // Explosiona un globo. Lo destruye = no crea otros globos -int BalloonManager::destroyBalloon(std::shared_ptr &balloon) -{ +int BalloonManager::destroyBalloon(std::shared_ptr &balloon) { int score = 0; // Calcula la puntuación y el poder que generaria el globo en caso de romperlo a él y a sus hijos - switch (balloon->getSize()) - { - case BalloonSize::SIZE4: - score = BALLOON_SCORE[3] + (2 * BALLOON_SCORE[2]) + (4 * BALLOON_SCORE[1]) + (8 * BALLOON_SCORE[0]); - break; - case BalloonSize::SIZE3: - score = BALLOON_SCORE[2] + (2 * BALLOON_SCORE[1]) + (4 * BALLOON_SCORE[0]); - break; - case BalloonSize::SIZE2: - score = BALLOON_SCORE[1] + (2 * BALLOON_SCORE[0]); - break; - case BalloonSize::SIZE1: - score = BALLOON_SCORE[0]; - break; - default: - score = 0; - break; + switch (balloon->getSize()) { + case BalloonSize::SIZE4: + score = BALLOON_SCORE[3] + (2 * BALLOON_SCORE[2]) + (4 * BALLOON_SCORE[1]) + (8 * BALLOON_SCORE[0]); + break; + case BalloonSize::SIZE3: + score = BALLOON_SCORE[2] + (2 * BALLOON_SCORE[1]) + (4 * BALLOON_SCORE[0]); + break; + case BalloonSize::SIZE2: + score = BALLOON_SCORE[1] + (2 * BALLOON_SCORE[0]); + break; + case BalloonSize::SIZE1: + score = BALLOON_SCORE[0]; + break; + default: + score = 0; + break; } // Aumenta el poder de la fase @@ -326,11 +288,9 @@ int BalloonManager::destroyBalloon(std::shared_ptr &balloon) } // Destruye todos los globos -int BalloonManager::destroyAllBalloons() -{ +int BalloonManager::destroyAllBalloons() { int score = 0; - for (auto &balloon : balloons_) - { + for (auto &balloon : balloons_) { score += destroyBalloon(balloon); } @@ -342,59 +302,46 @@ int BalloonManager::destroyAllBalloons() } // Detiene todos los globos -void BalloonManager::stopAllBalloons() -{ - for (auto &balloon : balloons_) - { +void BalloonManager::stopAllBalloons() { + for (auto &balloon : balloons_) { balloon->stop(); } } // Pone en marcha todos los globos -void BalloonManager::startAllBalloons() -{ - for (auto &balloon : balloons_) - { - if (!balloon->isBeingCreated()) - { +void BalloonManager::startAllBalloons() { + for (auto &balloon : balloons_) { + if (!balloon->isBeingCreated()) { balloon->start(); } } } // Cambia el color de todos los globos -void BalloonManager::reverseColorsToAllBalloons() -{ - for (auto &balloon : balloons_) - { - if (balloon->isStopped()) - { +void BalloonManager::reverseColorsToAllBalloons() { + for (auto &balloon : balloons_) { + if (balloon->isStopped()) { balloon->useReverseColor(); } } } // Cambia el color de todos los globos -void BalloonManager::normalColorsToAllBalloons() -{ - for (auto &balloon : balloons_) - { +void BalloonManager::normalColorsToAllBalloons() { + for (auto &balloon : balloons_) { balloon->useNormalColor(); } } // Crea dos globos gordos -void BalloonManager::createTwoBigBalloons() -{ +void BalloonManager::createTwoBigBalloons() { deploySet(1); } // Crea una disposición de globos aleatoria -void BalloonManager::createRandomBalloons() -{ +void BalloonManager::createRandomBalloons() { const int num_balloons = 2 + rand() % 4; - for (int i = 0; i < num_balloons; ++i) - { + for (int i = 0; i < num_balloons; ++i) { const float x = param.game.game_area.rect.x + (rand() % static_cast(param.game.game_area.rect.w)) - BALLOON_SIZE[3]; const int y = param.game.game_area.rect.y + (rand() % 50); const BalloonSize size = static_cast(rand() % 4); @@ -405,37 +352,29 @@ void BalloonManager::createRandomBalloons() } // Obtiene el nivel de ameza actual generado por los globos -int BalloonManager::getMenace() -{ - return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) - { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); +int BalloonManager::getMenace() { + return std::accumulate(balloons_.begin(), balloons_.end(), 0, [](int sum, const auto &balloon) { return sum + (balloon->isEnabled() ? balloon->getMenace() : 0); }); } // Establece el sonido de los globos -void BalloonManager::setSounds(bool value) -{ +void BalloonManager::setSounds(bool value) { sound_enabled_ = value; - for (auto &balloon : balloons_) - { + for (auto &balloon : balloons_) { balloon->setSound(value); } } // Activa o desactiva los sonidos de rebote los globos -void BalloonManager::setBouncingSounds(bool value) -{ +void BalloonManager::setBouncingSounds(bool value) { bouncing_sound_enabled_ = value; - for (auto &balloon : balloons_) - { + for (auto &balloon : balloons_) { balloon->setBouncingSound(value); } } // Activa o desactiva los sonidos de los globos al explotar -void BalloonManager::setPoppingSounds(bool value) -{ +void BalloonManager::setPoppingSounds(bool value) { poping_sound_enabled_ = value; - for (auto &balloon : balloons_) - { + for (auto &balloon : balloons_) { balloon->setPoppingSound(value); } } \ No newline at end of file diff --git a/source/balloon_manager.h b/source/balloon_manager.h index ad4f2fb..4ceab00 100644 --- a/source/balloon_manager.h +++ b/source/balloon_manager.h @@ -1,92 +1,92 @@ #pragma once -#include // Para SDL_FRect -#include // Para shared_ptr, unique_ptr -#include // Para string -#include // Para vector +#include // Para SDL_FRect -#include "balloon.h" // Para BALLOON_SPEED, Balloon -#include "balloon_formations.h" // Para BalloonFormations -#include "explosions.h" // Para Explosions -#include "param.h" // Para Param, ParamGame, param -#include "utils.h" // Para Zone +#include // Para shared_ptr, unique_ptr +#include // Para string +#include // Para vector + +#include "balloon.h" // Para BALLOON_SPEED, Balloon +#include "balloon_formations.h" // Para BalloonFormations +#include "explosions.h" // Para Explosions +#include "param.h" // Para Param, ParamGame, param +#include "utils.h" // Para Zone class Texture; using Balloons = std::vector>; // Clase BalloonManager -class BalloonManager -{ -public: +class BalloonManager { + public: // Constructor y Destructor BalloonManager(); ~BalloonManager() = default; // Actualización y Renderizado - void update(); // Actualiza el estado de los globos - void render(); // Renderiza los globos en pantalla + void update(); // Actualiza el estado de los globos + void render(); // Renderiza los globos en pantalla // Gestión de globos - void freeBalloons(); // Libera globos que ya no sirven + void freeBalloons(); // Libera globos que ya no sirven // Creación de formaciones enemigas - void deployBalloonFormation(int stage); // Crea una formación de enemigos aleatoria - void deploySet(int set); // Crea una formación específica - void deploySet(int set, int y); // Crea una formación específica con coordenadas + void deployBalloonFormation(int stage); // Crea una formación de enemigos aleatoria + void deploySet(int set); // Crea una formación específica + void deploySet(int set, int y); // Crea una formación específica con coordenadas // Creación de globos - std::shared_ptr createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer); // Crea un nuevo globo - void createChildBalloon(const std::shared_ptr &balloon, const std::string &direction); // Crea un globo a partir de otro - void createPowerBall(); // Crea una PowerBall - void createTwoBigBalloons(); // Crea dos globos grandes - void createRandomBalloons(); // Crea una disposición aleatoria de globos + std::shared_ptr createBalloon(float x, int y, BalloonType type, BalloonSize size, float velx, float speed, int creation_timer); // Crea un nuevo globo + void createChildBalloon(const std::shared_ptr &balloon, const std::string &direction); // Crea un globo a partir de otro + void createPowerBall(); // Crea una PowerBall + void createTwoBigBalloons(); // Crea dos globos grandes + void createRandomBalloons(); // Crea una disposición aleatoria de globos // Control de velocidad y despliegue - void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos - void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base - void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos - void updateBalloonDeployCounter(); // Actualiza el contador de despliegue - bool canPowerBallBeCreated(); // Indica si se puede crear una PowerBall - int calculateScreenPower(); // Calcula el poder de los globos en pantalla + void setBalloonSpeed(float speed); // Ajusta la velocidad de los globos + void setDefaultBalloonSpeed(float speed) { default_balloon_speed_ = speed; }; // Establece la velocidad base + void resetBalloonSpeed() { setBalloonSpeed(default_balloon_speed_); }; // Restablece la velocidad de los globos + void updateBalloonDeployCounter(); // Actualiza el contador de despliegue + bool canPowerBallBeCreated(); // Indica si se puede crear una PowerBall + int calculateScreenPower(); // Calcula el poder de los globos en pantalla // Manipulación de globos existentes - int popBalloon(std::shared_ptr balloon); // Explosiona un globo, creando otros si aplica - int destroyBalloon(std::shared_ptr &balloon); // Explosiona un globo sin crear otros - int destroyAllBalloons(); // Destruye todos los globos - void stopAllBalloons(); // Detiene el movimiento de los globos - void startAllBalloons(); // Reactiva el movimiento de los globos + int popBalloon(std::shared_ptr balloon); // Explosiona un globo, creando otros si aplica + int destroyBalloon(std::shared_ptr &balloon); // Explosiona un globo sin crear otros + int destroyAllBalloons(); // Destruye todos los globos + void stopAllBalloons(); // Detiene el movimiento de los globos + void startAllBalloons(); // Reactiva el movimiento de los globos // Cambios de apariencia - void reverseColorsToAllBalloons(); // Invierte los colores de los globos - void normalColorsToAllBalloons(); // Restaura los colores originales + void reverseColorsToAllBalloons(); // Invierte los colores de los globos + void normalColorsToAllBalloons(); // Restaura los colores originales // Configuración de sonido - void setSounds(bool value); // Activa o desactiva los sonidos de los globos - void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos - void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar + void setSounds(bool value); // Activa o desactiva los sonidos de los globos + void setBouncingSounds(bool value); // Activa o desactiva los sonidos de rebote los globos + void setPoppingSounds(bool value); // Activa o desactiva los sonidos de los globos al explotar // Configuración de juego - void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego - void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos - void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos + void setPlayArea(SDL_FRect play_area) { play_area_ = play_area; }; // Define el área de juego + void setCreationTimeEnabled(bool value) { creation_time_enabled_ = value; }; // Activa o desactiva el tiempo de creación de globos + void enableBalloonDeployment(bool value) { can_deploy_balloons_ = value; }; // Activa o desactiva la generación de globos // Obtención de información - int getMenace(); // Obtiene el nivel de amenaza generado por los globos + int getMenace(); // Obtiene el nivel de amenaza generado por los globos float getBalloonSpeed() const { return balloon_speed_; } Balloons &getBalloons() { return balloons_; } int getNumBalloons() const { return balloons_.size(); } -private: - Balloons balloons_; // Vector con los globos activos - std::unique_ptr explosions_; // Objeto para gestionar explosiones - std::unique_ptr balloon_formations_; // Objeto para manejar formaciones enemigas + private: + Balloons balloons_; // Vector con los globos activos + std::unique_ptr explosions_; // Objeto para gestionar explosiones + std::unique_ptr balloon_formations_; // Objeto para manejar formaciones enemigas - std::vector> balloon_textures_; // Texturas de los globos - std::vector> explosions_textures_; // Texturas de explosiones + std::vector> balloon_textures_; // Texturas de los globos + std::vector> explosions_textures_; // Texturas de explosiones - std::vector> balloon_animations_; // Animaciones de los globos - std::vector> explosions_animations_; // Animaciones de las explosiones + std::vector> balloon_animations_; // Animaciones de los globos + std::vector> explosions_animations_; // Animaciones de las explosiones // Variables de control de globos float balloon_speed_ = BALLOON_SPEED[0]; @@ -98,9 +98,9 @@ private: SDL_FRect play_area_ = param.game.play_area.rect; bool creation_time_enabled_ = true; bool can_deploy_balloons_ = true; - bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar - bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar - bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido + bool bouncing_sound_enabled_ = false; // Si debe sonar el globo al rebotar + bool poping_sound_enabled_ = true; // Si debe sonar el globo al explotar + bool sound_enabled_ = true; // Indica si los globos deben hacer algun sonido // Metodos privados void init(); diff --git a/source/bullet.cpp b/source/bullet.cpp index 341f6cb..19a1cf6 100644 --- a/source/bullet.cpp +++ b/source/bullet.cpp @@ -1,10 +1,10 @@ #include "bullet.h" -#include // Para allocator, unique_ptr, make_unique -#include // Para char_traits, basic_string, operator+, string +#include // Para allocator, unique_ptr, make_unique +#include // Para char_traits, basic_string, operator+, string -#include "param.h" // Para Param, ParamGame, param -#include "resource.h" // Para Resource +#include "param.h" // Para Param, ParamGame, param +#include "resource.h" // Para Resource // Constructor Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner) @@ -12,29 +12,27 @@ Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner pos_x_(x), pos_y_(y), bullet_type_(bullet_type), - owner_(owner) -{ - vel_x_ = (bullet_type_ == BulletType::LEFT) ? VEL_X_LEFT_ - : (bullet_type_ == BulletType::RIGHT) ? VEL_X_RIGHT_ - : 0; + owner_(owner) { + vel_x_ = (bullet_type_ == BulletType::LEFT) ? VEL_X_LEFT_ + : (bullet_type_ == BulletType::RIGHT) ? VEL_X_RIGHT_ + : 0; std::string powered_type = powered ? "powered_" : "normal_"; - switch (bullet_type) - { - case BulletType::UP: - sprite_->setCurrentAnimation(powered_type + "up"); - break; + switch (bullet_type) { + case BulletType::UP: + sprite_->setCurrentAnimation(powered_type + "up"); + break; - case BulletType::LEFT: - sprite_->setCurrentAnimation(powered_type + "left"); - break; + case BulletType::LEFT: + sprite_->setCurrentAnimation(powered_type + "left"); + break; - case BulletType::RIGHT: - sprite_->setCurrentAnimation(powered_type + "right"); - break; + case BulletType::RIGHT: + sprite_->setCurrentAnimation(powered_type + "right"); + break; - default: - break; + default: + break; } collider_.r = WIDTH / 2; @@ -42,32 +40,27 @@ Bullet::Bullet(float x, float y, BulletType bullet_type, bool powered, int owner } // Implementación de render (llama al render del sprite_) -void Bullet::render() -{ +void Bullet::render() { if (bullet_type_ != BulletType::NONE) sprite_->render(); } // Actualiza el estado del objeto -BulletMoveStatus Bullet::update() -{ +BulletMoveStatus Bullet::update() { sprite_->update(); return move(); } // Implementación del movimiento usando BulletMoveStatus -BulletMoveStatus Bullet::move() -{ +BulletMoveStatus Bullet::move() { pos_x_ += vel_x_; - if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) - { + if (pos_x_ < param.game.play_area.rect.x - WIDTH || pos_x_ > param.game.play_area.rect.w) { disable(); return BulletMoveStatus::OUT; } pos_y_ += VEL_Y_; - if (pos_y_ < param.game.play_area.rect.y - HEIGHT) - { + if (pos_y_ < param.game.play_area.rect.y - HEIGHT) { disable(); return BulletMoveStatus::OUT; } @@ -78,34 +71,28 @@ BulletMoveStatus Bullet::move() return BulletMoveStatus::OK; } -bool Bullet::isEnabled() const -{ +bool Bullet::isEnabled() const { return bullet_type_ != BulletType::NONE; } -void Bullet::disable() -{ +void Bullet::disable() { bullet_type_ = BulletType::NONE; } -int Bullet::getOwner() const -{ +int Bullet::getOwner() const { return owner_; } -Circle &Bullet::getCollider() -{ +Circle &Bullet::getCollider() { return collider_; } -void Bullet::shiftColliders() -{ +void Bullet::shiftColliders() { collider_.x = pos_x_ + collider_.r; collider_.y = pos_y_ + collider_.r; } -void Bullet::shiftSprite() -{ +void Bullet::shiftSprite() { sprite_->setX(pos_x_); sprite_->setY(pos_y_); } diff --git a/source/bullet.h b/source/bullet.h index 6415d88..152a34a 100644 --- a/source/bullet.h +++ b/source/bullet.h @@ -1,70 +1,68 @@ #pragma once -#include // Para Uint8 -#include // Para unique_ptr +#include // Para Uint8 -#include "animated_sprite.h" // Para AnimatedSprite -#include "utils.h" // Para Circle +#include // Para unique_ptr + +#include "animated_sprite.h" // Para AnimatedSprite +#include "utils.h" // Para Circle // Tipos de balas -enum class BulletType : Uint8 -{ - UP, - LEFT, - RIGHT, - NONE +enum class BulletType : Uint8 { + UP, + LEFT, + RIGHT, + NONE }; // Resultado del movimiento de la bala -enum class BulletMoveStatus : Uint8 -{ - OK = 0, - OUT = 1 +enum class BulletMoveStatus : Uint8 { + OK = 0, + OUT = 1 }; // Clase Bullet -class Bullet -{ -public: - // Constantes - static constexpr float WIDTH = 12.0f; - static constexpr float HEIGHT = 12.0f; +class Bullet { + public: + // Constantes + static constexpr float WIDTH = 12.0f; + static constexpr float HEIGHT = 12.0f; - // Constructor y Destructor - Bullet(float x, float y, BulletType bullet_type, bool powered, int owner); - ~Bullet() = default; + // Constructor y Destructor + Bullet(float x, float y, BulletType bullet_type, bool powered, int owner); + ~Bullet() = default; - // Métodos principales - void render(); // Dibuja la bala en pantalla - BulletMoveStatus update(); // Actualiza el estado del objeto + // Métodos principales + void render(); // Dibuja la bala en pantalla + BulletMoveStatus update(); // Actualiza el estado del objeto - // Estado de la bala - bool isEnabled() const; // Comprueba si está activa - void disable(); // Desactiva la bala + // Estado de la bala + bool isEnabled() const; // Comprueba si está activa + void disable(); // Desactiva la bala - // Getters - int getOwner() const; // Devuelve el identificador del dueño - Circle &getCollider(); // Devuelve el círculo de colisión + // Getters + int getOwner() const; // Devuelve el identificador del dueño + Circle &getCollider(); // Devuelve el círculo de colisión -private: - // Constantes - static constexpr float VEL_Y_ = -3.0f; - static constexpr float VEL_X_LEFT_ = -2.0f; - static constexpr float VEL_X_RIGHT_ = 2.0f; + private: + // Constantes + static constexpr float VEL_Y_ = -3.0f; + static constexpr float VEL_X_LEFT_ = -2.0f; + static constexpr float VEL_X_RIGHT_ = 2.0f; - // Propiedades - std::unique_ptr sprite_; // Sprite con los gráficos + // Propiedades + std::unique_ptr sprite_; // Sprite con los gráficos - float pos_x_; // Posición en el eje X - float pos_y_; // Posición en el eje Y - float vel_x_; // Velocidad en el eje X + float pos_x_; // Posición en el eje X + float pos_y_; // Posición en el eje Y + float vel_x_; // Velocidad en el eje X - BulletType bullet_type_; // Tipo de bala - int owner_; // Identificador del dueño - Circle collider_; // Círculo de colisión + BulletType bullet_type_; // Tipo de bala + int owner_; // Identificador del dueño + Circle collider_; // Círculo de colisión - // Métodos internos - void shiftColliders(); // Ajusta el círculo de colisión - void shiftSprite(); // Ajusta el sprite - BulletMoveStatus move(); // Mueve la bala y devuelve su estado + // Métodos internos + void shiftColliders(); // Ajusta el círculo de colisión + void shiftSprite(); // Ajusta el sprite + BulletMoveStatus move(); // Mueve la bala y devuelve su estado }; diff --git a/source/define_buttons.cpp b/source/define_buttons.cpp index b6b25c6..218129a 100644 --- a/source/define_buttons.cpp +++ b/source/define_buttons.cpp @@ -1,34 +1,30 @@ #include "define_buttons.h" -#include "input.h" // Para Input, InputAction -#include "lang.h" // Para getText -#include "options.h" // Para OptionsController, Options, options -#include "param.h" // Para Param, param, ParamGame, ParamTitle -#include "resource.h" // Para Resource -#include "text.h" // Para Text +#include "input.h" // Para Input, InputAction +#include "lang.h" // Para getText +#include "options.h" // Para OptionsController, Options, options +#include "param.h" // Para Param, param, ParamGame, ParamTitle +#include "resource.h" // Para Resource +#include "text.h" // Para Text // Constructor DefineButtons::DefineButtons() : input_(Input::get()), - text_(Resource::get()->getText("8bithud")) -{ + text_(Resource::get()->getText("8bithud")) { // Inicializa variables x_ = param.game.width / 2; y_ = param.title.press_start_position; clearButtons(); - for (int i = 0; i < input_->getNumControllers(); ++i) - { + for (int i = 0; i < input_->getNumControllers(); ++i) { controller_names_.emplace_back(input_->getControllerName(i)); } } // Dibuja el objeto en pantalla -void DefineButtons::render() -{ - if (enabled_) - { +void DefineButtons::render() { + if (enabled_) { text_->writeCentered(x_, y_ - 10, Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::controllers.at(index_controller_).player_id)); text_->writeCentered(x_, y_, controller_names_.at(index_controller_)); text_->writeCentered(x_, y_ + 10, buttons_.at(index_button_).label); @@ -36,27 +32,22 @@ void DefineButtons::render() } // Comprueba el botón que se ha pulsado -void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) -{ +void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent &event) { // Solo pilla botones del mando que toca - if (input_->getJoyIndex(event.which) != static_cast(index_controller_)) - { + if (input_->getJoyIndex(event.which) != static_cast(index_controller_)) { return; } const auto button = static_cast(event.button); - if (checkButtonNotInUse(button)) - { + if (checkButtonNotInUse(button)) { buttons_.at(index_button_).button = button; incIndexButton(); } } // Asigna los botones definidos al input_ -void DefineButtons::bindButtons() -{ - for (const auto &button : buttons_) - { +void DefineButtons::bindButtons() { + for (const auto &button : buttons_) { input_->bindGameControllerButton(index_controller_, button.input, button.button); } @@ -66,29 +57,24 @@ void DefineButtons::bindButtons() } // Comprueba los eventos -void DefineButtons::checkEvents(const SDL_Event &event) -{ - if (enabled_) - { - switch (event.type) - { - case SDL_EVENT_GAMEPAD_BUTTON_DOWN: - doControllerButtonDown(event.gbutton); - break; - case SDL_EVENT_GAMEPAD_BUTTON_UP: - checkEnd(); - break; - default: - break; +void DefineButtons::checkEvents(const SDL_Event &event) { + if (enabled_) { + switch (event.type) { + case SDL_EVENT_GAMEPAD_BUTTON_DOWN: + doControllerButtonDown(event.gbutton); + break; + case SDL_EVENT_GAMEPAD_BUTTON_UP: + checkEnd(); + break; + default: + break; } } } // Habilita el objeto -bool DefineButtons::enable(int index) -{ - if (index < input_->getNumControllers()) - { +bool DefineButtons::enable(int index) { + if (index < input_->getNumControllers()) { enabled_ = true; finished_ = false; index_controller_ = index; @@ -104,37 +90,28 @@ bool DefineButtons::enable(int index) bool DefineButtons::isEnabled() const { return enabled_; } // Incrementa el indice de los botones -void DefineButtons::incIndexButton() -{ - if (index_button_ < buttons_.size() - 1) - { +void DefineButtons::incIndexButton() { + if (index_button_ < buttons_.size() - 1) { ++index_button_; - } - else - { + } else { finished_ = true; } } // Guarda los cambios en las opciones -void DefineButtons::saveBindingsToOptions() -{ +void DefineButtons::saveBindingsToOptions() { // Modifica las opciones para colocar los valores asignados auto &controller = Options::controllers.at(index_controller_); controller.name = input_->getControllerName(index_controller_); - for (size_t j = 0; j < controller.inputs.size(); ++j) - { + for (size_t j = 0; j < controller.inputs.size(); ++j) { controller.buttons.at(j) = input_->getControllerBinding(index_controller_, controller.inputs.at(j)); } } // Comprueba que un botón no esté ya asignado -bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -{ - for (const auto &b : buttons_) - { - if (b.button == button) - { +bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) { + for (const auto &b : buttons_) { + if (b.button == button) { return false; } } @@ -142,8 +119,7 @@ bool DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) } // Limpia la asignación de botones -void DefineButtons::clearButtons() -{ +void DefineButtons::clearButtons() { buttons_.clear(); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_INVALID); buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_INVALID); @@ -153,11 +129,9 @@ void DefineButtons::clearButtons() } // Comprueba si ha finalizado -void DefineButtons::checkEnd() -{ +void DefineButtons::checkEnd() { // Comprueba si ha finalizado - if (finished_) - { + if (finished_) { // Asigna los botones definidos al input_ bindButtons(); diff --git a/source/define_buttons.h b/source/define_buttons.h index 5de6465..e7f7ce2 100644 --- a/source/define_buttons.h +++ b/source/define_buttons.h @@ -1,10 +1,11 @@ #pragma once -#include // Para SDL_GamepadButton, SDL_Event, SDL_GamepadButtonEvent -#include // Para size_t -#include // Para shared_ptr -#include // Para basic_string, string -#include // Para vector +#include // Para SDL_GamepadButton, SDL_Event, SDL_GamepadButtonEvent +#include // Para size_t + +#include // Para shared_ptr +#include // Para basic_string, string +#include // Para vector // Declaraciones adelantadas class Input; @@ -12,48 +13,46 @@ class Text; enum class InputAction : int; // Estructura para definir botones -struct DefineButtonsButton -{ - std::string label; // Texto en pantalla - InputAction input; // Acción asociada - SDL_GamepadButton button; // Botón del mando +struct DefineButtonsButton { + std::string label; // Texto en pantalla + InputAction input; // Acción asociada + SDL_GamepadButton button; // Botón del mando DefineButtonsButton(const std::string &lbl, InputAction inp, SDL_GamepadButton btn) : label(lbl), input(inp), button(btn) {} }; // Clase DefineButtons -class DefineButtons -{ -public: +class DefineButtons { + public: DefineButtons(); ~DefineButtons() = default; - void render(); // Dibuja el objeto en pantalla - void checkEvents(const SDL_Event &event); // Procesa los eventos - bool enable(int index_controller); // Habilita la redefinición de botones - bool isEnabled() const; // Comprueba si está habilitado + void render(); // Dibuja el objeto en pantalla + void checkEvents(const SDL_Event &event); // Procesa los eventos + bool enable(int index_controller); // Habilita la redefinición de botones + bool isEnabled() const; // Comprueba si está habilitado -private: + private: // Objetos - Input *input_ = nullptr; // Gestión de entrada - std::shared_ptr text_; // Renderizado de texto + Input *input_ = nullptr; // Gestión de entrada + std::shared_ptr text_; // Renderizado de texto // Variables - bool enabled_ = false; // Indica si está activo - int x_ = 0, y_ = 0; // Coordenadas de texto - std::vector buttons_; // Definiciones de botones - size_t index_controller_ = 0; // Índice del controlador asignado - size_t index_button_ = 0; // Índice del botón en proceso - std::vector controller_names_; // Nombres de los mandos + bool enabled_ = false; // Indica si está activo + int x_ = 0, y_ = 0; // Coordenadas de texto + std::vector buttons_; // Definiciones de botones + size_t index_controller_ = 0; // Índice del controlador asignado + size_t index_button_ = 0; // Índice del botón en proceso + std::vector controller_names_; // Nombres de los mandos bool finished_ = false; // Métodos internos - void incIndexButton(); // Incrementa el índice de botones - void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones - void bindButtons(); // Asigna botones al sistema de entrada - void saveBindingsToOptions(); // Guarda configuraciones - bool checkButtonNotInUse(SDL_GamepadButton button); // Verifica uso de botones - void clearButtons(); // Limpia asignaciones actuales - void checkEnd(); // Comprueba si ha finalizado + void incIndexButton(); // Incrementa el índice de botones + void doControllerButtonDown(const SDL_GamepadButtonEvent &event); // Procesa pulsaciones + void bindButtons(); // Asigna botones al sistema de entrada + void saveBindingsToOptions(); // Guarda configuraciones + bool checkButtonNotInUse(SDL_GamepadButton button); // Verifica uso de botones + void clearButtons(); // Limpia asignaciones actuales + void checkEnd(); // Comprueba si ha finalizado }; diff --git a/source/director.cpp b/source/director.cpp index eef6b7a..fbbe9c5 100644 --- a/source/director.cpp +++ b/source/director.cpp @@ -1,701 +1,655 @@ // IWYU pragma: no_include #include "director.h" -#include // Para SDL_Scancode, SDL_GamepadButton -#include // Para errno, EEXIST, EACCES, ENAMETOOLONG -#include // Para printf, perror -#include // Para mkdir, stat, S_IRWXU -#include // Para getuid -#include // Para min -#include // Para exit, EXIT_FAILURE, size_t, srand -#include // Para time -#include // Para make_unique, unique_ptr -#include // Para runtime_error -#include // Para operator+, basic_string, allocator -#include // Para vector +#include // Para SDL_Scancode, SDL_GamepadButton +#include // Para errno, EEXIST, EACCES, ENAMETOOLONG +#include // Para printf, perror +#include // Para mkdir, stat, S_IRWXU +#include // Para getuid -#include "asset.h" // Para Asset, AssetType -#include "audio.h" // Para Audio -#include "input.h" // Para Input, InputAction -#include "lang.h" // Para setLanguage -#include "manage_hiscore_table.h" // Para ManageHiScoreTable -#include "notifier.h" // Para Notifier -#include "options.h" // Para GamepadOptions, controllers, loa... -#include "param.h" // Para loadParamsFromFile -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, Options, name, options -#include "sections/credits.h" // Para Credits -#include "sections/game.h" // Para Game, GAME_MODE_DEMO_OFF, GAME_M... -#include "sections/hiscore_table.h" // Para HiScoreTable -#include "sections/instructions.h" // Para Instructions -#include "sections/intro.h" // Para Intro -#include "sections/logo.h" // Para Logo -#include "sections/title.h" // Para Title -#include "ui/service_menu.h" // Para ServiceMenu -#include "utils.h" // Para Overrides, overrides, getPath +#include // Para min +#include // Para exit, EXIT_FAILURE, size_t, srand +#include // Para time +#include // Para make_unique, unique_ptr +#include // Para runtime_error +#include // Para operator+, basic_string, allocator +#include // Para vector + +#include "asset.h" // Para Asset, AssetType +#include "audio.h" // Para Audio +#include "input.h" // Para Input, InputAction +#include "lang.h" // Para setLanguage +#include "manage_hiscore_table.h" // Para ManageHiScoreTable +#include "notifier.h" // Para Notifier +#include "options.h" // Para GamepadOptions, controllers, loa... +#include "param.h" // Para loadParamsFromFile +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, Options, name, options +#include "sections/credits.h" // Para Credits +#include "sections/game.h" // Para Game, GAME_MODE_DEMO_OFF, GAME_M... +#include "sections/hiscore_table.h" // Para HiScoreTable +#include "sections/instructions.h" // Para Instructions +#include "sections/intro.h" // Para Intro +#include "sections/logo.h" // Para Logo +#include "sections/title.h" // Para Title +#include "ui/service_menu.h" // Para ServiceMenu +#include "utils.h" // Para Overrides, overrides, getPath #ifndef _WIN32 -#include // Para getpwuid, passwd +#include // Para getpwuid, passwd #endif // Constructor -Director::Director(int argc, const char *argv[]) -{ +Director::Director(int argc, const char *argv[]) { #ifdef RECORDING - Section::name = Section::Name::GAME; - Section::options = Section::Options::GAME_PLAY_1P; + Section::name = Section::Name::GAME; + Section::options = Section::Options::GAME_PLAY_1P; #elif DEBUG - Section::name = Section::Name::LOGO; - Section::options = Section::Options::GAME_PLAY_1P; -#else // NORMAL GAME - Section::name = Section::Name::LOGO; - Section::options = Section::Options::NONE; + Section::name = Section::Name::LOGO; + Section::options = Section::Options::GAME_PLAY_1P; +#else // NORMAL GAME + Section::name = Section::Name::LOGO; + Section::options = Section::Options::NONE; #endif - Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; + Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; - // Establece el nivel de prioridad de la categoría de registro - SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR); + // Establece el nivel de prioridad de la categoría de registro + SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + SDL_SetLogPriority(SDL_LOG_CATEGORY_TEST, SDL_LOG_PRIORITY_ERROR); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Game start"); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Game start"); - // Inicia la semilla aleatoria usando el tiempo actual en segundos - std::srand(static_cast(std::time(nullptr))); + // Inicia la semilla aleatoria usando el tiempo actual en segundos + std::srand(static_cast(std::time(nullptr))); - // Comprueba los parametros del programa - checkProgramArguments(argc, argv); + // Comprueba los parametros del programa + checkProgramArguments(argc, argv); - // Crea la carpeta del sistema donde guardar los datos persistentes - createSystemFolder("jailgames"); - createSystemFolder("jailgames/coffee_crisis_arcade_edition"); + // Crea la carpeta del sistema donde guardar los datos persistentes + createSystemFolder("jailgames"); + createSystemFolder("jailgames/coffee_crisis_arcade_edition"); - init(); + init(); } -Director::~Director() -{ - close(); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nBye!"); +Director::~Director() { + close(); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nBye!"); } // Inicializa todo -void Director::init() -{ - // Configuración inicial de recursos - Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos - setFileList(); // Crea el índice de archivos - Options::loadFromFile(); // Carga el archivo de configuración - loadParams(); // Carga los parámetros del programa - loadScoreFile(); // Carga el archivo de puntuaciones +void Director::init() { + // Configuración inicial de recursos + Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos + setFileList(); // Crea el índice de archivos + Options::loadFromFile(); // Carga el archivo de configuración + loadParams(); // Carga los parámetros del programa + loadScoreFile(); // Carga el archivo de puntuaciones - // Inicialización de subsistemas principales - Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma - Screen::init(); // Inicializa la pantalla y el sistema de renderizado - Audio::init(); // Activa el sistema de audio - Resource::init(); // Inicializa el sistema de gestión de recursos - Input::init(Asset::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles - bindInputs(); // Asigna los controles a la entrada del sistema - ServiceMenu::init(); // Inicializa el menú de servicio - Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones - Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones + // Inicialización de subsistemas principales + Lang::setLanguage(Options::settings.language); // Carga el archivo de idioma + Screen::init(); // Inicializa la pantalla y el sistema de renderizado + Audio::init(); // Activa el sistema de audio + Resource::init(); // Inicializa el sistema de gestión de recursos + Input::init(Asset::get()->get("gamecontrollerdb.txt")); // Carga configuración de controles + bindInputs(); // Asigna los controles a la entrada del sistema + ServiceMenu::init(); // Inicializa el menú de servicio + Notifier::init(std::string(), Resource::get()->getText("8bithud")); // Inicialización del sistema de notificaciones + Screen::get()->getSingletons(); // Obtiene los punteros al resto de singletones } // Cierra todo y libera recursos del sistema y de los singletons -void Director::close() -{ - // Guarda las opciones actuales en el archivo de configuración - Options::saveToFile(); +void Director::close() { + // Guarda las opciones actuales en el archivo de configuración + Options::saveToFile(); - // Libera los singletons y recursos en orden inverso al de inicialización - Notifier::destroy(); // Libera el sistema de notificaciones - ServiceMenu::destroy(); // Libera el sistema de menú de servicio - Input::destroy(); // Libera el sistema de entrada - Resource::destroy(); // Libera el sistema de recursos gráficos y de texto - Audio::destroy(); // Libera el sistema de audio - Screen::destroy(); // Libera el sistema de pantalla y renderizado - Asset::destroy(); // Libera el gestor de archivos + // Libera los singletons y recursos en orden inverso al de inicialización + Notifier::destroy(); // Libera el sistema de notificaciones + ServiceMenu::destroy(); // Libera el sistema de menú de servicio + Input::destroy(); // Libera el sistema de entrada + Resource::destroy(); // Libera el sistema de recursos gráficos y de texto + Audio::destroy(); // Libera el sistema de audio + Screen::destroy(); // Libera el sistema de pantalla y renderizado + Asset::destroy(); // Libera el gestor de archivos - // Libera todos los recursos de SDL - SDL_Quit(); + // Libera todos los recursos de SDL + SDL_Quit(); - // Apaga el sistema - shutdownSystem(Section::options == Section::Options::SHUTDOWN); + // Apaga el sistema + shutdownSystem(Section::options == Section::Options::SHUTDOWN); } // Carga los parametros -void Director::loadParams() -{ - // Carga los parametros para configurar el juego +void Director::loadParams() { + // Carga los parametros para configurar el juego #ifdef ANBERNIC - const std::string paramFilePath = asset->get("param_320x240.txt"); + const std::string paramFilePath = asset->get("param_320x240.txt"); #else - const std::string paramFilePath = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt"); + const std::string paramFilePath = overrides.param_file == "--320x240" ? Asset::get()->get("param_320x240.txt") : Asset::get()->get("param_320x256.txt"); #endif - loadParamsFromFile(paramFilePath); + loadParamsFromFile(paramFilePath); } // Carga el fichero de puntuaciones -void Director::loadScoreFile() -{ - auto manager = std::make_unique(Options::settings.hi_score_table); +void Director::loadScoreFile() { + auto manager = std::make_unique(Options::settings.hi_score_table); #ifdef DEBUG - manager->clear(); + manager->clear(); #else - if (overrides.clear_hi_score_table) - { - manager->clear(); - } - else - { - manager->loadFromFile(Asset::get()->get("score.bin")); - } + if (overrides.clear_hi_score_table) { + manager->clear(); + } else { + manager->loadFromFile(Asset::get()->get("score.bin")); + } #endif } // Asigna los botones y teclas al objeto Input -void Director::bindInputs() -{ - // Teclado - Movimiento del jugador - Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP); - Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN); - Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT); - Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT); +void Director::bindInputs() { + // Teclado - Movimiento del jugador + Input::get()->bindKey(InputAction::UP, SDL_SCANCODE_UP); + Input::get()->bindKey(InputAction::DOWN, SDL_SCANCODE_DOWN); + Input::get()->bindKey(InputAction::LEFT, SDL_SCANCODE_LEFT); + Input::get()->bindKey(InputAction::RIGHT, SDL_SCANCODE_RIGHT); - // Teclado - Disparo del jugador - Input::get()->bindKey(InputAction::FIRE_LEFT, SDL_SCANCODE_Q); - Input::get()->bindKey(InputAction::FIRE_CENTER, SDL_SCANCODE_W); - Input::get()->bindKey(InputAction::FIRE_RIGHT, SDL_SCANCODE_E); + // Teclado - Disparo del jugador + Input::get()->bindKey(InputAction::FIRE_LEFT, SDL_SCANCODE_Q); + Input::get()->bindKey(InputAction::FIRE_CENTER, SDL_SCANCODE_W); + Input::get()->bindKey(InputAction::FIRE_RIGHT, SDL_SCANCODE_E); - // Teclado - Interfaz - Input::get()->bindKey(InputAction::START, SDL_SCANCODE_RETURN); + // Teclado - Interfaz + Input::get()->bindKey(InputAction::START, SDL_SCANCODE_RETURN); - // Teclado - Menu de servicio - Input::get()->bindKey(InputAction::SERVICE, SDL_SCANCODE_0); - Input::get()->bindKey(InputAction::SM_SELECT, SDL_SCANCODE_RETURN); - Input::get()->bindKey(InputAction::SM_BACK, SDL_SCANCODE_BACKSPACE); + // Teclado - Menu de servicio + Input::get()->bindKey(InputAction::SERVICE, SDL_SCANCODE_0); + Input::get()->bindKey(InputAction::SM_SELECT, SDL_SCANCODE_RETURN); + Input::get()->bindKey(InputAction::SM_BACK, SDL_SCANCODE_BACKSPACE); - // Teclado - Control del programa - Input::get()->bindKey(InputAction::EXIT, SDL_SCANCODE_ESCAPE); - Input::get()->bindKey(InputAction::PAUSE, SDL_SCANCODE_P); - Input::get()->bindKey(InputAction::BACK, SDL_SCANCODE_BACKSPACE); + // Teclado - Control del programa + Input::get()->bindKey(InputAction::EXIT, SDL_SCANCODE_ESCAPE); + Input::get()->bindKey(InputAction::PAUSE, SDL_SCANCODE_P); + Input::get()->bindKey(InputAction::BACK, SDL_SCANCODE_BACKSPACE); - Input::get()->bindKey(InputAction::WINDOW_DEC_SIZE, SDL_SCANCODE_F1); - Input::get()->bindKey(InputAction::WINDOW_INC_SIZE, SDL_SCANCODE_F2); - Input::get()->bindKey(InputAction::WINDOW_FULLSCREEN, SDL_SCANCODE_F3); - Input::get()->bindKey(InputAction::TOGGLE_VIDEO_SHADERS, SDL_SCANCODE_F4); - Input::get()->bindKey(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, SDL_SCANCODE_F5); - Input::get()->bindKey(InputAction::TOGGLE_VIDEO_VSYNC, SDL_SCANCODE_F6); + Input::get()->bindKey(InputAction::WINDOW_DEC_SIZE, SDL_SCANCODE_F1); + Input::get()->bindKey(InputAction::WINDOW_INC_SIZE, SDL_SCANCODE_F2); + Input::get()->bindKey(InputAction::WINDOW_FULLSCREEN, SDL_SCANCODE_F3); + Input::get()->bindKey(InputAction::TOGGLE_VIDEO_SHADERS, SDL_SCANCODE_F4); + Input::get()->bindKey(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, SDL_SCANCODE_F5); + Input::get()->bindKey(InputAction::TOGGLE_VIDEO_VSYNC, SDL_SCANCODE_F6); - Input::get()->bindKey(InputAction::TOGGLE_AUDIO, SDL_SCANCODE_F7); - Input::get()->bindKey(InputAction::TOGGLE_AUTO_FIRE, SDL_SCANCODE_F8); - Input::get()->bindKey(InputAction::CHANGE_LANG, SDL_SCANCODE_F9); + Input::get()->bindKey(InputAction::TOGGLE_AUDIO, SDL_SCANCODE_F7); + Input::get()->bindKey(InputAction::TOGGLE_AUTO_FIRE, SDL_SCANCODE_F8); + Input::get()->bindKey(InputAction::CHANGE_LANG, SDL_SCANCODE_F9); - Input::get()->bindKey(InputAction::RESET, SDL_SCANCODE_F10); - Input::get()->bindKey(InputAction::SHOW_INFO, SDL_SCANCODE_F12); + Input::get()->bindKey(InputAction::RESET, SDL_SCANCODE_F10); + Input::get()->bindKey(InputAction::SHOW_INFO, SDL_SCANCODE_F12); - // Asigna botones a inputs - const int NUM_GAMEPADS = Input::get()->getNumControllers(); - for (int i = 0; i < NUM_GAMEPADS; ++i) - { - // Mando - Movimiento del jugador - Input::get()->bindGameControllerButton(i, InputAction::UP, SDL_GAMEPAD_BUTTON_DPAD_UP); - Input::get()->bindGameControllerButton(i, InputAction::DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN); - Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT); - Input::get()->bindGameControllerButton(i, InputAction::RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); + // Asigna botones a inputs + const int NUM_GAMEPADS = Input::get()->getNumControllers(); + for (int i = 0; i < NUM_GAMEPADS; ++i) { + // Mando - Movimiento del jugador + Input::get()->bindGameControllerButton(i, InputAction::UP, SDL_GAMEPAD_BUTTON_DPAD_UP); + Input::get()->bindGameControllerButton(i, InputAction::DOWN, SDL_GAMEPAD_BUTTON_DPAD_DOWN); + Input::get()->bindGameControllerButton(i, InputAction::LEFT, SDL_GAMEPAD_BUTTON_DPAD_LEFT); + Input::get()->bindGameControllerButton(i, InputAction::RIGHT, SDL_GAMEPAD_BUTTON_DPAD_RIGHT); - // Mando - Disparo del jugador - Input::get()->bindGameControllerButton(i, InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST); - Input::get()->bindGameControllerButton(i, InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH); - Input::get()->bindGameControllerButton(i, InputAction::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST); + // Mando - Disparo del jugador + Input::get()->bindGameControllerButton(i, InputAction::FIRE_LEFT, SDL_GAMEPAD_BUTTON_WEST); + Input::get()->bindGameControllerButton(i, InputAction::FIRE_CENTER, SDL_GAMEPAD_BUTTON_NORTH); + Input::get()->bindGameControllerButton(i, InputAction::FIRE_RIGHT, SDL_GAMEPAD_BUTTON_EAST); - // Mando - Interfaz - Input::get()->bindGameControllerButton(i, InputAction::START, SDL_GAMEPAD_BUTTON_START); - Input::get()->bindGameControllerButton(i, InputAction::SERVICE, SDL_GAMEPAD_BUTTON_BACK); - } + // Mando - Interfaz + Input::get()->bindGameControllerButton(i, InputAction::START, SDL_GAMEPAD_BUTTON_START); + Input::get()->bindGameControllerButton(i, InputAction::SERVICE, SDL_GAMEPAD_BUTTON_BACK); + } - // Mapea las asignaciones a los botones desde el archivo de configuración, si se da el caso - const size_t max_controllers = std::min(2, NUM_GAMEPADS); - for (size_t i = 0; i < max_controllers; ++i) - { - for (auto &controller : Options::controllers) - { - if (Input::get()->getControllerName(i) == controller.name) - { - for (size_t j = 0; j < controller.inputs.size(); ++j) - { - Input::get()->bindGameControllerButton(i, controller.inputs.at(j), controller.buttons.at(j)); - } - } - } - } + // Mapea las asignaciones a los botones desde el archivo de configuración, si se da el caso + const size_t max_controllers = std::min(2, NUM_GAMEPADS); + for (size_t i = 0; i < max_controllers; ++i) { + for (auto &controller : Options::controllers) { + if (Input::get()->getControllerName(i) == controller.name) { + for (size_t j = 0; j < controller.inputs.size(); ++j) { + Input::get()->bindGameControllerButton(i, controller.inputs.at(j), controller.buttons.at(j)); + } + } + } + } - // Asigna botones a inputs desde otros inputs - for (int i = 0; i < NUM_GAMEPADS; ++i) - { - // Mando - Menu de servicio - Input::get()->bindGameControllerButton(i, InputAction::SM_SELECT, InputAction::FIRE_LEFT); - Input::get()->bindGameControllerButton(i, InputAction::SM_BACK, InputAction::FIRE_CENTER); - } + // Asigna botones a inputs desde otros inputs + for (int i = 0; i < NUM_GAMEPADS; ++i) { + // Mando - Menu de servicio + Input::get()->bindGameControllerButton(i, InputAction::SM_SELECT, InputAction::FIRE_LEFT); + Input::get()->bindGameControllerButton(i, InputAction::SM_BACK, InputAction::FIRE_CENTER); + } - // Guarda las asignaciones de botones en las opciones de los dos primeros mandos - for (size_t i = 0; i < max_controllers; ++i) - { - // Variables asociadas al mando - Options::controllers.at(i).index = i; - Options::controllers.at(i).name = Input::get()->getControllerName(i); - Options::controllers.at(i).plugged = true; - // Asignaciones de botones - for (size_t j = 0; j < Options::controllers.at(i).inputs.size(); ++j) - { - Options::controllers.at(i).buttons.at(j) = Input::get()->getControllerBinding(i, Options::controllers.at(i).inputs.at(j)); - } - } + // Guarda las asignaciones de botones en las opciones de los dos primeros mandos + for (size_t i = 0; i < max_controllers; ++i) { + // Variables asociadas al mando + Options::controllers.at(i).index = i; + Options::controllers.at(i).name = Input::get()->getControllerName(i); + Options::controllers.at(i).plugged = true; + // Asignaciones de botones + for (size_t j = 0; j < Options::controllers.at(i).inputs.size(); ++j) { + Options::controllers.at(i).buttons.at(j) = Input::get()->getControllerBinding(i, Options::controllers.at(i).inputs.at(j)); + } + } - // Asegura que algún jugador tenga el teclado asignado - if (Options::getPlayerWhoUsesKeyboard() == 0) - { - Options::setKeyboardToPlayer(1); - } + // Asegura que algún jugador tenga el teclado asignado + if (Options::getPlayerWhoUsesKeyboard() == 0) { + Options::setKeyboardToPlayer(1); + } } // Crea el indice de ficheros -void Director::setFileList() -{ +void Director::setFileList() { #ifdef MACOS_BUNDLE - const std::string prefix = "/../Resources"; + const std::string prefix = "/../Resources"; #else - const std::string prefix; + const std::string prefix; #endif - // Ficheros de configuración - Asset::get()->add(system_folder_ + "/config.txt", AssetType::DATA, false, true); - Asset::get()->add(system_folder_ + "/score.bin", AssetType::DATA, false, true); - Asset::get()->add(prefix + "/data/config/param_320x240.txt", AssetType::DATA); - Asset::get()->add(prefix + "/data/config/param_320x256.txt", AssetType::DATA); - Asset::get()->add(prefix + "/data/config/demo1.bin", AssetType::DEMODATA); - Asset::get()->add(prefix + "/data/config/demo2.bin", AssetType::DEMODATA); - Asset::get()->add(prefix + "/data/config/gamecontrollerdb.txt", AssetType::DATA); + // Ficheros de configuración + Asset::get()->add(system_folder_ + "/config.txt", AssetType::DATA, false, true); + Asset::get()->add(system_folder_ + "/score.bin", AssetType::DATA, false, true); + Asset::get()->add(prefix + "/data/config/param_320x240.txt", AssetType::DATA); + Asset::get()->add(prefix + "/data/config/param_320x256.txt", AssetType::DATA); + Asset::get()->add(prefix + "/data/config/demo1.bin", AssetType::DEMODATA); + Asset::get()->add(prefix + "/data/config/demo2.bin", AssetType::DEMODATA); + Asset::get()->add(prefix + "/data/config/gamecontrollerdb.txt", AssetType::DATA); - // Musicas - Asset::get()->add(prefix + "/data/music/intro.ogg", AssetType::MUSIC); - Asset::get()->add(prefix + "/data/music/playing.ogg", AssetType::MUSIC); - Asset::get()->add(prefix + "/data/music/title.ogg", AssetType::MUSIC); - Asset::get()->add(prefix + "/data/music/credits.ogg", AssetType::MUSIC); + // Musicas + Asset::get()->add(prefix + "/data/music/intro.ogg", AssetType::MUSIC); + Asset::get()->add(prefix + "/data/music/playing.ogg", AssetType::MUSIC); + Asset::get()->add(prefix + "/data/music/title.ogg", AssetType::MUSIC); + Asset::get()->add(prefix + "/data/music/credits.ogg", AssetType::MUSIC); - // Sonidos - Asset::get()->add(prefix + "/data/sound/balloon1.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/balloon2.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/balloon3.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/balloon4.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/bubble1.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/bubble2.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/bubble3.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/bubble4.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/bullet.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/clock.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/coffee_out.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/continue_clock.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/debian_drop.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/debian_pickup.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/hi_score_achieved.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/item_drop.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/item_pickup.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/jump.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/logo.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/notify.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/player_collision.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/power_ball_explosion.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/service_menu_adjust.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/service_menu_move.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/service_menu_select.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/stage_change.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/tabe_hit.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/tabe.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/title.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_aw_aw_aw.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_coffee.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_get_ready.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_no.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_power_up.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/voice_thankyou.wav", AssetType::SOUND); - Asset::get()->add(prefix + "/data/sound/walk.wav", AssetType::SOUND); + // Sonidos + Asset::get()->add(prefix + "/data/sound/balloon1.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/balloon2.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/balloon3.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/balloon4.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/bubble1.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/bubble2.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/bubble3.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/bubble4.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/bullet.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/clock.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/coffee_out.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/continue_clock.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/debian_drop.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/debian_pickup.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/hi_score_achieved.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/item_drop.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/item_pickup.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/jump.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/logo.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/notify.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/player_collision.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/power_ball_explosion.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/service_menu_adjust.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/service_menu_move.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/service_menu_select.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/stage_change.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/tabe_hit.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/tabe.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/title.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_aw_aw_aw.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_coffee.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_get_ready.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_no.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_power_up.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/voice_thankyou.wav", AssetType::SOUND); + Asset::get()->add(prefix + "/data/sound/walk.wav", AssetType::SOUND); - // Shaders - Asset::get()->add(prefix + "/data/shaders/crtpi_256.glsl", AssetType::DATA); - Asset::get()->add(prefix + "/data/shaders/crtpi_240.glsl", AssetType::DATA); + // Shaders + Asset::get()->add(prefix + "/data/shaders/crtpi_256.glsl", AssetType::DATA); + Asset::get()->add(prefix + "/data/shaders/crtpi_240.glsl", AssetType::DATA); - // Texturas - Balloons - Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.ani", AssetType::ANIMATION); + // Texturas - Balloons + Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon1.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon2.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon3.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/balloon4.ani", AssetType::ANIMATION); - // Texturas - Explosiones - Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.ani", AssetType::ANIMATION); + // Texturas - Explosiones + Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion1.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion2.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion3.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/explosion4.ani", AssetType::ANIMATION); - // Texturas - Power Ball - Asset::get()->add(prefix + "/data/gfx/balloon/powerball.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/balloon/powerball.ani", AssetType::ANIMATION); + // Texturas - Power Ball + Asset::get()->add(prefix + "/data/gfx/balloon/powerball.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/balloon/powerball.ani", AssetType::ANIMATION); - // Texturas - Bala - Asset::get()->add(prefix + "/data/gfx/bullet/bullet.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/bullet/bullet.ani", AssetType::ANIMATION); + // Texturas - Bala + Asset::get()->add(prefix + "/data/gfx/bullet/bullet.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/bullet/bullet.ani", AssetType::ANIMATION); - // Texturas - Tabe - Asset::get()->add(prefix + "/data/gfx/tabe/tabe.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/tabe/tabe.ani", AssetType::ANIMATION); + // Texturas - Tabe + Asset::get()->add(prefix + "/data/gfx/tabe/tabe.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/tabe/tabe.ani", AssetType::ANIMATION); - // Texturas - Juego - Asset::get()->add(prefix + "/data/gfx/game/game_buildings.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_clouds1.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_clouds2.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_grass.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_power_meter.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_sky_colors.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_sun.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/game/game_moon.png", AssetType::BITMAP); + // Texturas - Juego + Asset::get()->add(prefix + "/data/gfx/game/game_buildings.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_clouds1.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_clouds2.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_grass.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_power_meter.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_sky_colors.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_sun.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/game/game_moon.png", AssetType::BITMAP); - // Texturas - Intro - Asset::get()->add(prefix + "/data/gfx/intro/intro1.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/intro/intro2.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/intro/intro3.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/intro/intro4.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/intro/intro5.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/intro/intro6.png", AssetType::BITMAP); + // Texturas - Intro + Asset::get()->add(prefix + "/data/gfx/intro/intro1.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/intro/intro2.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/intro/intro3.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/intro/intro4.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/intro/intro5.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/intro/intro6.png", AssetType::BITMAP); - // Texturas - Logo - Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames_mini.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/logo/logo_since_1998.png", AssetType::BITMAP); + // Texturas - Logo + Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/logo/logo_jailgames_mini.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/logo/logo_since_1998.png", AssetType::BITMAP); - // Texturas - Items - Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_clock.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_clock.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_coffee.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_coffee.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_debian.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_debian.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.ani", AssetType::ANIMATION); + // Texturas - Items + Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_points1_disk.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_points2_gavina.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_points3_pacmar.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_clock.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_clock.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_coffee.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_coffee.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_debian.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_debian.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/item/item_coffee_machine.ani", AssetType::ANIMATION); - // Texturas - Titulo - Asset::get()->add(prefix + "/data/gfx/title/title_bg_tile.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/title/title_coffee.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/title/title_crisis.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/title/title_arcade_edition.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/title/title_dust.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/title/title_dust.ani", AssetType::ANIMATION); + // Texturas - Titulo + Asset::get()->add(prefix + "/data/gfx/title/title_bg_tile.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/title/title_coffee.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/title/title_crisis.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/title/title_arcade_edition.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/title/title_dust.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/title/title_dust.ani", AssetType::ANIMATION); - // Texturas - Jugador 1 - Asset::get()->add(prefix + "/data/gfx/player/player1.gif", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/player/player1_coffee1.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player1_coffee2.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player1_invencible.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player1_power.png", AssetType::BITMAP); + // Texturas - Jugador 1 + Asset::get()->add(prefix + "/data/gfx/player/player1.gif", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/player/player1_coffee1.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player1_coffee2.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player1_invencible.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player1_power.png", AssetType::BITMAP); - // Texturas - Jugador 2 - Asset::get()->add(prefix + "/data/gfx/player/player2.gif", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/gfx/player/player2_coffee1.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player2_coffee2.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player2_invencible.pal", AssetType::PALETTE); - Asset::get()->add(prefix + "/data/gfx/player/player2_power.png", AssetType::BITMAP); + // Texturas - Jugador 2 + Asset::get()->add(prefix + "/data/gfx/player/player2.gif", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/gfx/player/player2_coffee1.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player2_coffee2.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player2_invencible.pal", AssetType::PALETTE); + Asset::get()->add(prefix + "/data/gfx/player/player2_power.png", AssetType::BITMAP); - // Animaciones del jugador - Asset::get()->add(prefix + "/data/gfx/player/player.ani", AssetType::ANIMATION); - Asset::get()->add(prefix + "/data/gfx/player/player_power.ani", AssetType::ANIMATION); + // Animaciones del jugador + Asset::get()->add(prefix + "/data/gfx/player/player.ani", AssetType::ANIMATION); + Asset::get()->add(prefix + "/data/gfx/player/player_power.ani", AssetType::ANIMATION); - // Fuentes de texto - Asset::get()->add(prefix + "/data/font/8bithud.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/8bithud.txt", AssetType::FONT); - Asset::get()->add(prefix + "/data/font/aseprite.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/aseprite.txt", AssetType::FONT); - Asset::get()->add(prefix + "/data/font/smb2.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/smb2_grad.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/smb2.txt", AssetType::FONT); - Asset::get()->add(prefix + "/data/font/04b_25.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25.txt", AssetType::FONT); - Asset::get()->add(prefix + "/data/font/04b_25_2x.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_2x.txt", AssetType::FONT); - Asset::get()->add(prefix + "/data/font/04b_25_metal.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_grey.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_flat.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_reversed.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_flat_2x.png", AssetType::BITMAP); - Asset::get()->add(prefix + "/data/font/04b_25_reversed_2x.png", AssetType::BITMAP); + // Fuentes de texto + Asset::get()->add(prefix + "/data/font/8bithud.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/8bithud.txt", AssetType::FONT); + Asset::get()->add(prefix + "/data/font/aseprite.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/aseprite.txt", AssetType::FONT); + Asset::get()->add(prefix + "/data/font/smb2.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/smb2_grad.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/smb2.txt", AssetType::FONT); + Asset::get()->add(prefix + "/data/font/04b_25.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25.txt", AssetType::FONT); + Asset::get()->add(prefix + "/data/font/04b_25_2x.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_2x.txt", AssetType::FONT); + Asset::get()->add(prefix + "/data/font/04b_25_metal.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_grey.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_flat.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_reversed.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_flat_2x.png", AssetType::BITMAP); + Asset::get()->add(prefix + "/data/font/04b_25_reversed_2x.png", AssetType::BITMAP); - // Textos - Asset::get()->add(prefix + "/data/lang/es_ES.json", AssetType::LANG); - Asset::get()->add(prefix + "/data/lang/en_UK.json", AssetType::LANG); - Asset::get()->add(prefix + "/data/lang/ba_BA.json", AssetType::LANG); + // Textos + Asset::get()->add(prefix + "/data/lang/es_ES.json", AssetType::LANG); + Asset::get()->add(prefix + "/data/lang/en_UK.json", AssetType::LANG); + Asset::get()->add(prefix + "/data/lang/ba_BA.json", AssetType::LANG); - // Si falta algun fichero, sale del programa - if (!Asset::get()->check()) - { - throw std::runtime_error("Falta algun fichero"); - } + // Si falta algun fichero, sale del programa + if (!Asset::get()->check()) { + throw std::runtime_error("Falta algun fichero"); + } } // Comprueba los parametros del programa -void Director::checkProgramArguments(int argc, const char *argv[]) -{ - // Establece la ruta del programa - executable_path_ = getPath(argv[0]); +void Director::checkProgramArguments(int argc, const char *argv[]) { + // Establece la ruta del programa + executable_path_ = getPath(argv[0]); - // Comprueba el resto de parámetros - for (int i = 1; i < argc; ++i) - { - std::string arg = argv[i]; + // Comprueba el resto de parámetros + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; - if (arg == "--320x240") - { - overrides.param_file = arg; - } - else if (arg == "--clear_score") - { - overrides.clear_hi_score_table = true; - } - } + if (arg == "--320x240") { + overrides.param_file = arg; + } else if (arg == "--clear_score") { + overrides.clear_hi_score_table = true; + } + } } // Crea la carpeta del sistema donde guardar datos -void Director::createSystemFolder(const std::string &folder) -{ +void Director::createSystemFolder(const std::string &folder) { #ifdef _WIN32 - system_folder_ = std::string(getenv("APPDATA")) + "/" + folder; + system_folder_ = std::string(getenv("APPDATA")) + "/" + folder; #elif __APPLE__ - struct passwd *pw = getpwuid(getuid()); - const char *homedir = pw->pw_dir; - system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder; + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder; #elif __linux__ - struct passwd *pw = getpwuid(getuid()); - const char *homedir = pw->pw_dir; - system_folder_ = std::string(homedir) + "/.config/" + folder; + struct passwd *pw = getpwuid(getuid()); + const char *homedir = pw->pw_dir; + system_folder_ = std::string(homedir) + "/.config/" + folder; - { - // Intenta crear ".config", per si no existeix - std::string config_base_folder = std::string(homedir) + "/.config"; - int ret = mkdir(config_base_folder.c_str(), S_IRWXU); - if (ret == -1 && errno != EEXIST) - { - printf("ERROR CREATING CONFIG BASE FOLDER."); - exit(EXIT_FAILURE); - } - } + { + // Intenta crear ".config", per si no existeix + std::string config_base_folder = std::string(homedir) + "/.config"; + int ret = mkdir(config_base_folder.c_str(), S_IRWXU); + if (ret == -1 && errno != EEXIST) { + printf("ERROR CREATING CONFIG BASE FOLDER."); + exit(EXIT_FAILURE); + } + } #endif - struct stat st = {0}; - if (stat(system_folder_.c_str(), &st) == -1) - { - errno = 0; + struct stat st = {0}; + if (stat(system_folder_.c_str(), &st) == -1) { + errno = 0; #ifdef _WIN32 - int ret = mkdir(system_folder_.c_str()); + int ret = mkdir(system_folder_.c_str()); #else - int ret = mkdir(system_folder_.c_str(), S_IRWXU); + int ret = mkdir(system_folder_.c_str(), S_IRWXU); #endif - if (ret == -1) - { - switch (errno) - { - case EACCES: - printf("the parent directory does not allow write"); - exit(EXIT_FAILURE); + if (ret == -1) { + switch (errno) { + case EACCES: + printf("the parent directory does not allow write"); + exit(EXIT_FAILURE); - case EEXIST: - printf("pathname already exists"); - exit(EXIT_FAILURE); + case EEXIST: + printf("pathname already exists"); + exit(EXIT_FAILURE); - case ENAMETOOLONG: - printf("pathname is too long"); - exit(EXIT_FAILURE); + case ENAMETOOLONG: + printf("pathname is too long"); + exit(EXIT_FAILURE); - default: - perror("mkdir"); - exit(EXIT_FAILURE); - } - } - } + default: + perror("mkdir"); + exit(EXIT_FAILURE); + } + } + } } // Ejecuta la sección con el logo -void Director::runLogo() -{ - auto logo = std::make_unique(); - logo->run(); +void Director::runLogo() { + auto logo = std::make_unique(); + logo->run(); } // Ejecuta la sección con la secuencia de introducción -void Director::runIntro() -{ - auto intro = std::make_unique(); - intro->run(); +void Director::runIntro() { + auto intro = std::make_unique(); + intro->run(); } // Ejecuta la sección con el título del juego -void Director::runTitle() -{ - auto title = std::make_unique(); - title->run(); +void Director::runTitle() { + auto title = std::make_unique<Title>(); + title->run(); } // Ejecuta la sección donde se juega al juego -void Director::runGame() -{ - int player_id = 1; +void Director::runGame() { + int player_id = 1; - switch (Section::options) - { - case Section::Options::GAME_PLAY_1P: - player_id = 1; - break; + switch (Section::options) { + case Section::Options::GAME_PLAY_1P: + player_id = 1; + break; - case Section::Options::GAME_PLAY_2P: - player_id = 2; - break; + case Section::Options::GAME_PLAY_2P: + player_id = 2; + break; - case Section::Options::GAME_PLAY_BOTH: - player_id = 0; - break; + case Section::Options::GAME_PLAY_BOTH: + player_id = 0; + break; - default: - player_id = 1; - break; - } + default: + player_id = 1; + break; + } #ifdef DEBUG - constexpr int CURRENT_STAGE = 0; + constexpr int CURRENT_STAGE = 0; #else - constexpr int CURRENT_STAGE = 0; + constexpr int CURRENT_STAGE = 0; #endif - auto game = std::make_unique<Game>(player_id, CURRENT_STAGE, GAME_MODE_DEMO_OFF); - game->run(); + auto game = std::make_unique<Game>(player_id, CURRENT_STAGE, GAME_MODE_DEMO_OFF); + game->run(); } // Ejecuta la sección donde se muestran las instrucciones -void Director::runInstructions() -{ - auto instructions = std::make_unique<Instructions>(); - instructions->run(); +void Director::runInstructions() { + auto instructions = std::make_unique<Instructions>(); + instructions->run(); } // Ejecuta la sección donde se muestran los creditos del programa -void Director::runCredits() -{ - auto credits = std::make_unique<Credits>(); - credits->run(); +void Director::runCredits() { + auto credits = std::make_unique<Credits>(); + credits->run(); } // Ejecuta la sección donde se muestra la tabla de puntuaciones -void Director::runHiScoreTable() -{ - auto hi_score_table = std::make_unique<HiScoreTable>(); - hi_score_table->run(); +void Director::runHiScoreTable() { + auto hi_score_table = std::make_unique<HiScoreTable>(); + hi_score_table->run(); } // Ejecuta el juego en modo demo -void Director::runDemoGame() -{ - const auto PLAYER_ID = (rand() % 2) + 1; - constexpr auto CURRENT_STAGE = 0; - auto game = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, GAME_MODE_DEMO_ON); - game->run(); +void Director::runDemoGame() { + const auto PLAYER_ID = (rand() % 2) + 1; + constexpr auto CURRENT_STAGE = 0; + auto game = std::make_unique<Game>(PLAYER_ID, CURRENT_STAGE, GAME_MODE_DEMO_ON); + game->run(); } // Reinicia objetos y vuelve a la sección inicial -void Director::reset() -{ - Options::saveToFile(); - Options::loadFromFile(); - Lang::setLanguage(Options::settings.language); - Audio::get()->stopMusic(); - Audio::get()->stopAllSounds(); - if (Section::options == Section::Options::RELOAD || true) - { - Resource::get()->reload(); - } - Input::get()->discoverGameControllers(); - bindInputs(); - ServiceMenu::get()->reset(); - Section::name = Section::Name::LOGO; +void Director::reset() { + Options::saveToFile(); + Options::loadFromFile(); + Lang::setLanguage(Options::settings.language); + Audio::get()->stopMusic(); + Audio::get()->stopAllSounds(); + if (Section::options == Section::Options::RELOAD || true) { + Resource::get()->reload(); + } + Input::get()->discoverGameControllers(); + bindInputs(); + ServiceMenu::get()->reset(); + Section::name = Section::Name::LOGO; } -int Director::run() -{ - // Bucle principal - while (Section::name != Section::Name::QUIT) - { - switch (Section::name) - { - case Section::Name::RESET: - reset(); - break; - case Section::Name::LOGO: - runLogo(); - break; - case Section::Name::INTRO: - runIntro(); - break; - case Section::Name::TITLE: - runTitle(); - break; - case Section::Name::GAME: - runGame(); - break; - case Section::Name::HI_SCORE_TABLE: - runHiScoreTable(); - break; - case Section::Name::GAME_DEMO: - runDemoGame(); - break; - case Section::Name::INSTRUCTIONS: - runInstructions(); - break; - case Section::Name::CREDITS: - runCredits(); - break; - default: - break; - } - } +int Director::run() { + // Bucle principal + while (Section::name != Section::Name::QUIT) { + switch (Section::name) { + case Section::Name::RESET: + reset(); + break; + case Section::Name::LOGO: + runLogo(); + break; + case Section::Name::INTRO: + runIntro(); + break; + case Section::Name::TITLE: + runTitle(); + break; + case Section::Name::GAME: + runGame(); + break; + case Section::Name::HI_SCORE_TABLE: + runHiScoreTable(); + break; + case Section::Name::GAME_DEMO: + runDemoGame(); + break; + case Section::Name::INSTRUCTIONS: + runInstructions(); + break; + case Section::Name::CREDITS: + runCredits(); + break; + default: + break; + } + } - return 0; + return 0; } // Apaga el sistema -void Director::shutdownSystem(bool should_shutdown) -{ - if (should_shutdown) - { +void Director::shutdownSystem(bool should_shutdown) { + if (should_shutdown) { #ifdef _WIN32 - // Apaga el sistema en Windows - system("shutdown /s /t 5"); + // Apaga el sistema en Windows + system("shutdown /s /t 5"); #elif __APPLE__ - // Apaga el sistema en macOS - system("sudo shutdown -h +0.1"); + // Apaga el sistema en macOS + system("sudo shutdown -h +0.1"); #elif __linux__ - // Apaga el sistema en Linux - system("sleep 5; shutdown -h now"); + // Apaga el sistema en Linux + system("sleep 5; shutdown -h now"); #else // Sistema operativo no compatible #error "Sistema operativo no soportado" #endif - } + } } \ No newline at end of file diff --git a/source/director.h b/source/director.h index bc779f9..725b00a 100644 --- a/source/director.h +++ b/source/director.h @@ -1,55 +1,53 @@ #pragma once -#include <string> // Para manejar cadenas de texto +#include <string> // Para manejar cadenas de texto -namespace Lang -{ - enum class Code : int; +namespace Lang { +enum class Code : int; } -class Director -{ -public: - // --- Constructor y destructor --- - Director(int argc, const char *argv[]); - ~Director(); +class Director { + public: + // --- Constructor y destructor --- + Director(int argc, const char *argv[]); + ~Director(); - // --- Bucle principal --- - int run(); + // --- Bucle principal --- + int run(); -private: - // --- Variables internas --- - std::string executable_path_; // Ruta del ejecutable - std::string system_folder_; // Carpeta del sistema para almacenar datos + private: + // --- Variables internas --- + std::string executable_path_; // Ruta del ejecutable + std::string system_folder_; // Carpeta del sistema para almacenar datos - // --- Inicialización y cierre del sistema --- - void init(); // Inicializa la aplicación - void close(); // Cierra y libera recursos + // --- Inicialización y cierre del sistema --- + void init(); // Inicializa la aplicación + void close(); // Cierra y libera recursos - // --- Configuración inicial --- - void loadParams(); // Carga los parámetros del programa - void loadScoreFile(); // Carga el fichero de puntuaciones - void createSystemFolder(const std::string &folder); // Crea la carpeta del sistema + // --- Configuración inicial --- + void loadParams(); // Carga los parámetros del programa + void loadScoreFile(); // Carga el fichero de puntuaciones + void createSystemFolder(const std::string &folder); // Crea la carpeta del sistema - // --- Gestión de entrada y archivos --- - void bindInputs(); // Asigna botones y teclas al sistema de entrada - void setFileList(); // Crea el índice de archivos disponibles - void checkProgramArguments(int argc, const char *argv[]); // Verifica los parámetros del programa + // --- Gestión de entrada y archivos --- + void bindInputs(); // Asigna botones y teclas al sistema de entrada + void setFileList(); // Crea el índice de archivos disponibles + void checkProgramArguments(int argc, const char *argv[]); // Verifica los parámetros del programa - // --- Secciones del programa --- - void runLogo(); // Ejecuta la pantalla con el logo - void runIntro(); // Ejecuta la introducción del juego - void runTitle(); // Ejecuta la pantalla de título - void runGame(); // Inicia el juego - void runInstructions(); // Muestra las instrucciones - void runCredits(); // Muestra los créditos del juego - void runHiScoreTable(); // Muestra la tabla de puntuaciones - void runDemoGame(); // Ejecuta el modo demo - void reset(); // Reinicia objetos y vuelve a la sección inicial + // --- Secciones del programa --- + void runLogo(); // Ejecuta la pantalla con el logo + void runIntro(); // Ejecuta la introducción del juego + void runTitle(); // Ejecuta la pantalla de título + void runGame(); // Inicia el juego + void runInstructions(); // Muestra las instrucciones + void runCredits(); // Muestra los créditos del juego + void runHiScoreTable(); // Muestra la tabla de puntuaciones + void runDemoGame(); // Ejecuta el modo demo + void reset(); // Reinicia objetos y vuelve a la sección inicial - // --- Gestión de archivos de idioma --- - std::string getLangFile(Lang::Code code); // Obtiene un fichero de idioma según el código + // --- Gestión de archivos de idioma --- + std::string getLangFile(Lang::Code code); // Obtiene un fichero de idioma según el código - // --- Apagado del sistema --- - void shutdownSystem(bool should_shutdown); // Apaga el sistema + // --- Apagado del sistema --- + void shutdownSystem(bool should_shutdown); // Apaga el sistema }; diff --git a/source/enter_name.cpp b/source/enter_name.cpp index 1e58d67..cc6e53f 100644 --- a/source/enter_name.cpp +++ b/source/enter_name.cpp @@ -1,10 +1,11 @@ #include "enter_name.h" -#include <stddef.h> // Para size_t -#include <stdlib.h> // Para rand -#include <string_view> // Para basic_string_view, string_view +#include <stddef.h> // Para size_t +#include <stdlib.h> // Para rand -#include "utils.h" // Para trim +#include <string_view> // Para basic_string_view, string_view + +#include "utils.h" // Para trim // Constructor EnterName::EnterName() @@ -12,18 +13,15 @@ EnterName::EnterName() character_index_{0} {} // Inicializa el objeto -void EnterName::init(const std::string &name) -{ +void EnterName::init(const std::string &name) { // No se pasa ningún nombre - if (name.empty()) - { + if (name.empty()) { name_ = "A"; position_ = 0; position_overflow_ = false; } // Se pasa un nombre - else - { + else { name_ = name; position_ = name_.length(); position_overflow_ = position_ >= NAME_SIZE ? true : false; @@ -34,28 +32,22 @@ void EnterName::init(const std::string &name) } // Incrementa la posición -void EnterName::incPosition() -{ - if (position_overflow_) - { +void EnterName::incPosition() { + if (position_overflow_) { // Si ya estamos en overflow, no incrementamos más. return; } ++position_; - if (position_ >= NAME_SIZE) - { - position_ = NAME_SIZE; // Mantenemos en el índice máximo válido. - position_overflow_ = true; // Activamos el flag de overflow. - } - else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGHT + if (position_ >= NAME_SIZE) { + position_ = NAME_SIZE; // Mantenemos en el índice máximo válido. + position_overflow_ = true; // Activamos el flag de overflow. + } else if (position_ > 0) // No es necesario verificar position_ < MAX_NAME_LENGHT { // Copiamos el índice del carácter anterior si es posible. character_index_[position_] = character_index_[position_ - 1]; - } - else - { + } else { // Si position_ es 0, inicializamos el carácter actual. character_index_[position_] = 0; } @@ -64,36 +56,27 @@ void EnterName::incPosition() } // Decrementa la posición -void EnterName::decPosition() -{ - if (position_overflow_) - { +void EnterName::decPosition() { + if (position_overflow_) { // Si estaba en overflow, lo desactivamos y mantenemos position_ en el máximo. position_overflow_ = false; position_ = NAME_SIZE - 1; - } - else - { - if (position_ > 0) - { + } else { + if (position_ > 0) { --position_; // Limpiamos el carácter siguiente si el índice es válido. - if (position_ + 1 < NAME_SIZE) - { + if (position_ + 1 < NAME_SIZE) { character_index_[position_ + 1] = 0; } - } - else - { + } else { // Si position_ es 0, aseguramos que no vaya a ser negativo y limpiamos el carácter actual. position_ = 0; // character_index_[position_] = 0; } // Si position_ es menor que NAME_LENGHT, aseguramos que el overflow esté desactivado. - if (position_ < NAME_SIZE) - { + if (position_ < NAME_SIZE) { position_overflow_ = false; } } @@ -102,71 +85,57 @@ void EnterName::decPosition() } // Incrementa el índice -void EnterName::incIndex() -{ - if (position_overflow_) - { +void EnterName::incIndex() { + if (position_overflow_) { return; } ++character_index_[position_]; - if (character_index_[position_] >= static_cast<int>(character_list_.size())) - { + if (character_index_[position_] >= static_cast<int>(character_list_.size())) { character_index_[position_] = 0; } updateNameFromCharacterIndex(); } // Decrementa el índice -void EnterName::decIndex() -{ - if (position_overflow_) - { +void EnterName::decIndex() { + if (position_overflow_) { return; } --character_index_[position_]; - if (character_index_[position_] < 0) - { + if (character_index_[position_] < 0) { character_index_[position_] = character_list_.size() - 1; } updateNameFromCharacterIndex(); } // Actualiza el nombre a partir de la lista de índices -void EnterName::updateNameFromCharacterIndex() -{ +void EnterName::updateNameFromCharacterIndex() { name_.clear(); - for (size_t i = 0; i < NAME_SIZE; ++i) - { + for (size_t i = 0; i < NAME_SIZE; ++i) { name_.push_back(character_list_[character_index_[i]]); } name_ = trim(name_); } // Actualiza la variable -void EnterName::initCharacterIndex(const std::string &name) -{ +void EnterName::initCharacterIndex(const std::string &name) { // Rellena de espacios - for (size_t i = 0; i < NAME_SIZE; ++i) - { + for (size_t i = 0; i < NAME_SIZE; ++i) { character_index_[i] = 0; } // Coloca los índices en función de los caracteres que forman el nombre - for (size_t i = 0; i < name.substr(0, NAME_SIZE).size(); ++i) - { + for (size_t i = 0; i < name.substr(0, NAME_SIZE).size(); ++i) { character_index_[i] = findIndex(name.at(i)); } } // Encuentra el indice de un caracter en "character_list_" -int EnterName::findIndex(char character) const -{ - for (size_t i = 0; i < character_list_.size(); ++i) - { - if (character == character_list_.at(i)) - { +int EnterName::findIndex(char character) const { + for (size_t i = 0; i < character_list_.size(); ++i) { + if (character == character_list_.at(i)) { return i; } } @@ -174,18 +143,15 @@ int EnterName::findIndex(char character) const } // Devuelve un nombre al azar -std::string EnterName::getRandomName() -{ +std::string EnterName::getRandomName() { static constexpr std::array<std::string_view, 8> NAMES = { "BAL1", "TABE", "DOC", "MON", "SAM1", "JORDI", "JDES", "PEPE"}; return std::string(NAMES[rand() % NAMES.size()]); } // Obtiene el nombre final introducido -std::string EnterName::getFinalName() -{ +std::string EnterName::getFinalName() { auto name = trim(name_.substr(0, position_)); - if (name.empty()) - { + if (name.empty()) { name = getRandomName(); } name_ = name; diff --git a/source/enter_name.h b/source/enter_name.h index cec761d..ab7da80 100644 --- a/source/enter_name.h +++ b/source/enter_name.h @@ -1,43 +1,43 @@ #pragma once -#include <stddef.h> // Para size_t -#include <array> // Para array -#include <string> // Para string, basic_string +#include <stddef.h> // Para size_t -#include "utils.h" // Para trim +#include <array> // Para array +#include <string> // Para string, basic_string + +#include "utils.h" // Para trim // Tamaño máximo del nombre constexpr size_t NAME_SIZE = 5; // Clase EnterName -class EnterName -{ -public: +class EnterName { + public: EnterName(); ~EnterName() = default; - void init(const std::string &name = ""); // Inicializa con un nombre opcional + void init(const std::string &name = ""); // Inicializa con un nombre opcional - void incPosition(); // Incrementa la posición del carácter actual - void decPosition(); // Decrementa la posición del carácter actual - void incIndex(); // Incrementa el índice del carácter en la lista - void decIndex(); // Decrementa el índice del carácter en la lista + void incPosition(); // Incrementa la posición del carácter actual + void decPosition(); // Decrementa la posición del carácter actual + void incIndex(); // Incrementa el índice del carácter en la lista + void decIndex(); // Decrementa el índice del carácter en la lista - std::string getFinalName(); // Obtiene el nombre final introducido - std::string getCurrentName() const { return trim(name_); } // Obtiene el nombre actual en proceso + std::string getFinalName(); // Obtiene el nombre final introducido + std::string getCurrentName() const { return trim(name_); } // Obtiene el nombre actual en proceso - int getPosition() const { return position_; } // Posición actual del carácter editado - bool getPositionOverflow() const { return position_overflow_; } // Indica si la posición excede el límite + int getPosition() const { return position_; } // Posición actual del carácter editado + bool getPositionOverflow() const { return position_overflow_; } // Indica si la posición excede el límite -private: - std::string character_list_; // Lista de caracteres permitidos - std::string name_; // Nombre en proceso - size_t position_ = 0; // Índice del carácter que se edita - bool position_overflow_ = false; // Flag para exceder límite - std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_" + private: + std::string character_list_; // Lista de caracteres permitidos + std::string name_; // Nombre en proceso + size_t position_ = 0; // Índice del carácter que se edita + bool position_overflow_ = false; // Flag para exceder límite + std::array<int, NAME_SIZE> character_index_; // Índices a "character_list_" - void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_" - void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre - int findIndex(char character) const; // Busca el índice de un carácter en "character_list_" - static std::string getRandomName(); // Devuelve un nombre al azar + void updateNameFromCharacterIndex(); // Actualiza "name_" según "character_index_" + void initCharacterIndex(const std::string &name); // Inicializa índices desde el nombre + int findIndex(char character) const; // Busca el índice de un carácter en "character_list_" + static std::string getRandomName(); // Devuelve un nombre al azar }; \ No newline at end of file diff --git a/source/explosions.cpp b/source/explosions.cpp index b29869a..f9a67a6 100644 --- a/source/explosions.cpp +++ b/source/explosions.cpp @@ -1,14 +1,12 @@ #include "explosions.h" -#include "animated_sprite.h" // Para AnimatedSprite +#include "animated_sprite.h" // Para AnimatedSprite -class Texture; // lines 4-4 +class Texture; // lines 4-4 // Actualiza la lógica de la clase -void Explosions::update() -{ - for (auto &explosion : explosions_) - { +void Explosions::update() { + for (auto &explosion : explosions_) { explosion->update(); } @@ -17,37 +15,29 @@ void Explosions::update() } // Dibuja el objeto en pantalla -void Explosions::render() -{ - for (auto &explosion : explosions_) - { +void Explosions::render() { + for (auto &explosion : explosions_) { explosion->render(); } } // Añade texturas al objeto -void Explosions::addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) -{ +void Explosions::addTexture(int size, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) { textures_.emplace_back(ExplosionTexture(size, texture, animation)); } // Añade una explosión -void Explosions::add(int x, int y, int size) -{ +void Explosions::add(int x, int y, int size) { const auto INDEX = getIndexBySize(size); explosions_.emplace_back(std::make_unique<AnimatedSprite>(textures_[INDEX].texture, textures_[INDEX].animation)); explosions_.back()->setPos(x, y); } // Vacia el vector de elementos finalizados -void Explosions::freeExplosions() -{ - if (explosions_.empty() == false) - { - for (int i = explosions_.size() - 1; i >= 0; --i) - { - if (explosions_[i]->animationIsCompleted()) - { +void Explosions::freeExplosions() { + if (explosions_.empty() == false) { + for (int i = explosions_.size() - 1; i >= 0; --i) { + if (explosions_[i]->animationIsCompleted()) { explosions_.erase(explosions_.begin() + i); } } @@ -55,12 +45,9 @@ void Explosions::freeExplosions() } // Busca una textura a partir del tamaño -int Explosions::getIndexBySize(int size) -{ - for (int i = 0; i < (int)textures_.size(); ++i) - { - if (size == textures_[i].size) - { +int Explosions::getIndexBySize(int size) { + for (int i = 0; i < (int)textures_.size(); ++i) { + if (size == textures_[i].size) { return i; } } diff --git a/source/explosions.h b/source/explosions.h index 817c6c8..45c5fa0 100644 --- a/source/explosions.h +++ b/source/explosions.h @@ -1,28 +1,26 @@ #pragma once -#include <memory> // Para unique_ptr, shared_ptr -#include <string> // Para string -#include <vector> // Para vector +#include <memory> // Para unique_ptr, shared_ptr +#include <string> // Para string +#include <vector> // Para vector -#include "animated_sprite.h" // Para AnimatedSprite +#include "animated_sprite.h" // Para AnimatedSprite class Texture; // Estructura para almacenar la información de una textura de explosión -struct ExplosionTexture -{ - int size; // Tamaño de la explosión - std::shared_ptr<Texture> texture; // Textura para la explosión - std::vector<std::string> animation; // Animación para la textura +struct ExplosionTexture { + int size; // Tamaño de la explosión + std::shared_ptr<Texture> texture; // Textura para la explosión + std::vector<std::string> animation; // Animación para la textura ExplosionTexture(int sz, std::shared_ptr<Texture> tex, const std::vector<std::string> &anim) : size(sz), texture(tex), animation(anim) {} }; // Clase Explosions -class Explosions -{ -public: +class Explosions { + public: // Constructor y destructor Explosions() = default; ~Explosions() = default; @@ -39,7 +37,7 @@ public: // Añade una explosión void add(int x, int y, int size); -private: + private: // Vector con las texturas a utilizar std::vector<ExplosionTexture> textures_; diff --git a/source/external/gif.cpp b/source/external/gif.cpp index c2f32ba..8f535dc 100644 --- a/source/external/gif.cpp +++ b/source/external/gif.cpp @@ -1,249 +1,214 @@ #include "gif.h" -#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo +#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, SDL_LogInfo + #include <cstring> // Para memcpy, size_t #include <stdexcept> // Para runtime_error #include <string> // Para char_traits, operator==, basic_string, string -namespace GIF -{ - inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) - { - std::memcpy(dst, buffer, size); - buffer += size; +namespace GIF { +inline void readBytes(const uint8_t *&buffer, void *dst, size_t size) { + std::memcpy(dst, buffer, size); + buffer += size; +} + +void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) { + if (code_length < 2 || code_length > 12) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code length: %d", code_length); + throw std::runtime_error("Invalid LZW code length"); } - void Gif::decompress(int code_length, const uint8_t *input, int input_length, uint8_t *out) - { - if (code_length < 2 || code_length > 12) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code length: %d", code_length); - 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++) { + dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); + dictionary[dictionary_ind].prev = -1; + dictionary[dictionary_ind].len = 1; + } + dictionary_ind += 2; + + while (input_length > 0) { + int code = 0; + for (i = 0; i < (code_length + 1); i++) { + if (input_length <= 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unexpected end of input in decompress"); + throw std::runtime_error("Unexpected end of input in decompress"); + } + bit = ((*input & mask) != 0) ? 1 : 0; + mask <<= 1; + if (mask == 0x100) { + mask = 0x01; + input++; + input_length--; + } + code |= (bit << i); } - 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++) - { - dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); - dictionary[dictionary_ind].prev = -1; - dictionary[dictionary_ind].len = 1; + if (code == clear_code) { + code_length = reset_code_length; + dictionary.resize(1 << (code_length + 1)); + for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) { + dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); + dictionary[dictionary_ind].prev = -1; + dictionary[dictionary_ind].len = 1; + } + dictionary_ind += 2; + prev = -1; + continue; + } else if (code == stop_code) { + break; } - dictionary_ind += 2; - while (input_length > 0) - { - int code = 0; - for (i = 0; i < (code_length + 1); i++) - { - if (input_length <= 0) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unexpected end of input in decompress"); - throw std::runtime_error("Unexpected end of input in decompress"); - } - bit = ((*input & mask) != 0) ? 1 : 0; - mask <<= 1; - if (mask == 0x100) - { - mask = 0x01; - input++; - input_length--; - } - code |= (bit << i); + if (prev > -1 && code_length < 12) { + if (code > dictionary_ind) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LZW error: code (%d) exceeds dictionary_ind (%d)", code, dictionary_ind); + throw std::runtime_error("LZW error: code exceeds dictionary_ind."); } - if (code == clear_code) - { - code_length = reset_code_length; + 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++; + + if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) { + code_length++; dictionary.resize(1 << (code_length + 1)); - for (dictionary_ind = 0; dictionary_ind < (1 << code_length); dictionary_ind++) - { - dictionary[dictionary_ind].byte = static_cast<uint8_t>(dictionary_ind); - dictionary[dictionary_ind].prev = -1; - dictionary[dictionary_ind].len = 1; - } - dictionary_ind += 2; - prev = -1; - continue; } - else if (code == stop_code) - { - break; - } - - if (prev > -1 && code_length < 12) - { - if (code > dictionary_ind) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "LZW error: code (%d) exceeds dictionary_ind (%d)", code, dictionary_ind); - throw std::runtime_error("LZW error: code exceeds dictionary_ind."); - } - - 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++; - - if ((dictionary_ind == (1 << (code_length + 1))) && (code_length < 11)) - { - code_length++; - dictionary.resize(1 << (code_length + 1)); - } - } - - prev = code; - - if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code %d, dictionary size %lu", code, static_cast<unsigned long>(dictionary.size())); - throw std::runtime_error("LZW error: invalid code encountered"); - } - - int curCode = code; - match_len = dictionary[curCode].len; - while (curCode != -1) - { - out[dictionary[curCode].len - 1] = dictionary[curCode].byte; - if (dictionary[curCode].prev == curCode) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Internal error; self-reference detected."); - 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) - { - std::vector<uint8_t> data; - uint8_t block_size = *buffer; + prev = code; + + if (code < 0 || static_cast<size_t>(code) >= dictionary.size()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid LZW code %d, dictionary size %lu", code, static_cast<unsigned long>(dictionary.size())); + throw std::runtime_error("LZW error: invalid code encountered"); + } + + int curCode = code; + match_len = dictionary[curCode].len; + while (curCode != -1) { + out[dictionary[curCode].len - 1] = dictionary[curCode].byte; + if (dictionary[curCode].prev == curCode) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Internal error; self-reference detected."); + 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) { + std::vector<uint8_t> data; + uint8_t block_size = *buffer; + buffer++; + while (block_size != 0) { + data.insert(data.end(), buffer, buffer + block_size); + buffer += block_size; + block_size = *buffer; buffer++; - while (block_size != 0) - { - data.insert(data.end(), buffer, buffer + block_size); - buffer += block_size; - block_size = *buffer; - buffer++; + } + return data; +} + +std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) { + ImageDescriptor image_descriptor; + readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor)); + + uint8_t lzw_code_size; + readBytes(buffer, &lzw_code_size, sizeof(uint8_t)); + + std::vector<uint8_t> compressed_data = readSubBlocks(buffer); + int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height; + std::vector<uint8_t> uncompressed_data(uncompressed_data_length); + + decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data()); + return uncompressed_data; +} + +std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) { + 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) { + 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 data; + } + 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") { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Formato de archivo GIF inválido: %s", headerStr.c_str()); + throw std::runtime_error("Formato de archivo GIF inválido."); } - std::vector<uint8_t> Gif::processImageDescriptor(const uint8_t *&buffer, const std::vector<RGB> &gct, int resolution_bits) - { - ImageDescriptor image_descriptor; - readBytes(buffer, &image_descriptor, sizeof(ImageDescriptor)); + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Procesando GIF con cabecera: %s", headerStr.c_str()); - uint8_t lzw_code_size; - readBytes(buffer, &lzw_code_size, sizeof(uint8_t)); + ScreenDescriptor screen_descriptor; + readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor)); - std::vector<uint8_t> compressed_data = readSubBlocks(buffer); - int uncompressed_data_length = image_descriptor.image_width * image_descriptor.image_height; - std::vector<uint8_t> uncompressed_data(uncompressed_data_length); + w = screen_descriptor.width; + h = screen_descriptor.height; - decompress(lzw_code_size, compressed_data.data(), static_cast<int>(compressed_data.size()), uncompressed_data.data()); - return uncompressed_data; + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Resolución del GIF: %dx%d", w, h); + + int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; + std::vector<RGB> 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); + std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); + buffer += 3 * global_color_table_size; } - std::vector<uint32_t> Gif::loadPalette(const uint8_t *buffer) - { - 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) - { - 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") - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Formato de archivo GIF inválido: %s", headerStr.c_str()); - throw std::runtime_error("Formato de archivo GIF inválido."); - } - - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Procesando GIF con cabecera: %s", headerStr.c_str()); - - ScreenDescriptor screen_descriptor; - readBytes(buffer, &screen_descriptor, sizeof(ScreenDescriptor)); - - w = screen_descriptor.width; - h = screen_descriptor.height; - - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Resolución del GIF: %dx%d", w, h); - - int color_resolution_bits = ((screen_descriptor.fields & 0x70) >> 4) + 1; - std::vector<RGB> 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); - std::memcpy(global_color_table.data(), buffer, 3 * global_color_table_size); - buffer += 3 * global_color_table_size; - } - - uint8_t block_type = *buffer++; - while (block_type != TRAILER) - { - if (block_type == EXTENSION_INTRODUCER) - { - uint8_t extension_label = *buffer++; - switch (extension_label) - { - case GRAPHIC_CONTROL: - { + uint8_t block_type = *buffer++; + while (block_type != TRAILER) { + if (block_type == EXTENSION_INTRODUCER) { + uint8_t extension_label = *buffer++; + switch (extension_label) { + case GRAPHIC_CONTROL: { uint8_t blockSize = *buffer++; buffer += blockSize; uint8_t subBlockSize = *buffer++; - while (subBlockSize != 0) - { + while (subBlockSize != 0) { buffer += subBlockSize; subBlockSize = *buffer++; } @@ -251,51 +216,42 @@ namespace GIF } case APPLICATION_EXTENSION: case COMMENT_EXTENSION: - case PLAINTEXT_EXTENSION: - { + case PLAINTEXT_EXTENSION: { uint8_t blockSize = *buffer++; buffer += blockSize; uint8_t subBlockSize = *buffer++; - while (subBlockSize != 0) - { + while (subBlockSize != 0) { buffer += subBlockSize; subBlockSize = *buffer++; } break; } - default: - { + default: { uint8_t blockSize = *buffer++; buffer += blockSize; uint8_t subBlockSize = *buffer++; - while (subBlockSize != 0) - { + while (subBlockSize != 0) { buffer += subBlockSize; subBlockSize = *buffer++; } break; } - } } - else if (block_type == IMAGE_DESCRIPTOR) - { - return processImageDescriptor(buffer, global_color_table, color_resolution_bits); - } - else - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized block type: 0x%X", block_type); - return std::vector<uint8_t>{}; - } - block_type = *buffer++; + } else if (block_type == IMAGE_DESCRIPTOR) { + return processImageDescriptor(buffer, global_color_table, color_resolution_bits); + } else { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized block type: 0x%X", block_type); + return std::vector<uint8_t>{}; } - - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF procesado correctamente."); - return std::vector<uint8_t>{}; + block_type = *buffer++; } - std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) - { - return processGifStream(buffer, w, h); - } + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF procesado correctamente."); + return std::vector<uint8_t>{}; +} -} // namespace GIF +std::vector<uint8_t> Gif::loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h) { + return processGifStream(buffer, w, h); +} + +} // namespace GIF diff --git a/source/external/gif.h b/source/external/gif.h index 2692da0..b9e0dee 100644 --- a/source/external/gif.h +++ b/source/external/gif.h @@ -1,102 +1,92 @@ #pragma once -#include <cstdint> // Para uint8_t, uint16_t, uint32_t -#include <vector> // Para vector +#include <cstdint> // Para uint8_t, uint16_t, uint32_t +#include <vector> // Para vector -namespace GIF -{ +namespace GIF { - // Constantes definidas con constexpr, en lugar de macros - constexpr uint8_t EXTENSION_INTRODUCER = 0x21; - constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C; - constexpr uint8_t TRAILER = 0x3B; - constexpr uint8_t GRAPHIC_CONTROL = 0xF9; - constexpr uint8_t APPLICATION_EXTENSION = 0xFF; - constexpr uint8_t COMMENT_EXTENSION = 0xFE; - constexpr uint8_t PLAINTEXT_EXTENSION = 0x01; +// Constantes definidas con constexpr, en lugar de macros +constexpr uint8_t EXTENSION_INTRODUCER = 0x21; +constexpr uint8_t IMAGE_DESCRIPTOR = 0x2C; +constexpr uint8_t TRAILER = 0x3B; +constexpr uint8_t GRAPHIC_CONTROL = 0xF9; +constexpr uint8_t APPLICATION_EXTENSION = 0xFF; +constexpr uint8_t COMMENT_EXTENSION = 0xFE; +constexpr uint8_t PLAINTEXT_EXTENSION = 0x01; #pragma pack(push, 1) - struct ScreenDescriptor - { - uint16_t width; - uint16_t height; - uint8_t fields; - uint8_t background_color_index; - uint8_t pixel_aspect_ratio; - }; +struct ScreenDescriptor { + uint16_t width; + uint16_t height; + uint8_t fields; + uint8_t background_color_index; + uint8_t pixel_aspect_ratio; +}; - struct RGB - { - uint8_t r, g, b; - }; +struct RGB { + uint8_t r, g, b; +}; - struct ImageDescriptor - { - uint16_t image_left_position; - uint16_t image_top_position; - uint16_t image_width; - uint16_t image_height; - uint8_t fields; - }; +struct ImageDescriptor { + uint16_t image_left_position; + uint16_t image_top_position; + uint16_t image_width; + uint16_t image_height; + uint8_t fields; +}; #pragma pack(pop) - struct DictionaryEntry - { - uint8_t byte; - int prev; - int len; - }; +struct DictionaryEntry { + uint8_t byte; + int prev; + int len; +}; - struct Extension - { - uint8_t extension_code; - uint8_t block_size; - }; +struct Extension { + uint8_t extension_code; + uint8_t block_size; +}; - struct GraphicControlExtension - { - uint8_t fields; - uint16_t delay_time; - uint8_t transparent_color_index; - }; +struct GraphicControlExtension { + uint8_t fields; + uint16_t delay_time; + uint8_t transparent_color_index; +}; - struct ApplicationExtension - { - uint8_t application_id[8]; - uint8_t version[3]; - }; +struct ApplicationExtension { + uint8_t application_id[8]; + uint8_t version[3]; +}; - struct PlaintextExtension - { - uint16_t left, top, width, height; - uint8_t cell_width, cell_height; - uint8_t foreground_color, background_color; - }; +struct PlaintextExtension { + uint16_t left, top, width, height; + uint8_t cell_width, cell_height; + 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); +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, - // 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); + // 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). + std::vector<uint32_t> loadPalette(const uint8_t *buffer); - // Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y - // asigna el ancho y alto mediante referencias. - std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h); + // Carga el stream GIF; devuelve un vector con los datos de imagen sin comprimir y + // asigna el ancho y alto mediante referencias. + std::vector<uint8_t> loadGif(const uint8_t *buffer, uint16_t &w, uint16_t &h); - 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); + 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 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); - }; + // 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 diff --git a/source/external/jail_audio.cpp b/source/external/jail_audio.cpp index 1314003..3f2f33a 100644 --- a/source/external/jail_audio.cpp +++ b/source/external/jail_audio.cpp @@ -1,26 +1,24 @@ #ifndef JA_USESDLMIXER #include "jail_audio.h" -#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_Se... -#include <stdint.h> // Para uint32_t, uint8_t -#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread -#include <stdlib.h> // Para free, malloc -#include <string.h> // Para strcpy, strlen +#include <SDL3/SDL.h> // Para SDL_AudioFormat, SDL_BindAudioStream, SDL_Se... +#include <stdint.h> // Para uint32_t, uint8_t +#include <stdio.h> // Para NULL, fseek, printf, fclose, fopen, fread +#include <stdlib.h> // Para free, malloc +#include <string.h> // Para strcpy, strlen -#include "stb_vorbis.h" // Para stb_vorbis_decode_memory +#include "stb_vorbis.h" // Para stb_vorbis_decode_memory #define JA_MAX_SIMULTANEOUS_CHANNELS 20 #define JA_MAX_GROUPS 2 -struct JA_Sound_t -{ +struct JA_Sound_t { SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000}; Uint32 length{0}; Uint8 *buffer{NULL}; }; -struct JA_Channel_t -{ +struct JA_Channel_t { JA_Sound_t *sound{nullptr}; int pos{0}; int times{0}; @@ -29,8 +27,7 @@ struct JA_Channel_t JA_Channel_state state{JA_CHANNEL_FREE}; }; -struct JA_Music_t -{ +struct JA_Music_t { SDL_AudioSpec spec{SDL_AUDIO_S16, 2, 48000}; Uint32 length{0}; Uint8 *buffer{nullptr}; @@ -58,59 +55,43 @@ int fade_start_time; int fade_duration; int fade_initial_volume; -Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval) -{ - if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) - { - if (fading) - { +Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval) { + if (JA_musicEnabled && current_music && current_music->state == JA_MUSIC_PLAYING) { + if (fading) { int time = SDL_GetTicks(); - if (time > (fade_start_time + fade_duration)) - { + if (time > (fade_start_time + fade_duration)) { fading = false; JA_StopMusic(); return 30; - } - else - { + } else { const int time_passed = time - fade_start_time; const float percent = (float)time_passed / (float)fade_duration; SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume * (1.0 - percent)); } } - if (current_music->times != 0) - { - if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length / 2)) - { + if (current_music->times != 0) { + if ((Uint32)SDL_GetAudioStreamAvailable(current_music->stream) < (current_music->length / 2)) { SDL_PutAudioStreamData(current_music->stream, current_music->buffer, current_music->length); } if (current_music->times > 0) current_music->times--; - } - else - { + } else { if (SDL_GetAudioStreamAvailable(current_music->stream) == 0) JA_StopMusic(); } } - if (JA_soundEnabled) - { + if (JA_soundEnabled) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; ++i) - if (channels[i].state == JA_CHANNEL_PLAYING) - { - if (channels[i].times != 0) - { - if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) - { + if (channels[i].state == JA_CHANNEL_PLAYING) { + if (channels[i].times != 0) { + if ((Uint32)SDL_GetAudioStreamAvailable(channels[i].stream) < (channels[i].sound->length / 2)) { SDL_PutAudioStreamData(channels[i].stream, channels[i].sound->buffer, channels[i].sound->length); if (channels[i].times > 0) channels[i].times--; } - } - else - { + } else { if (SDL_GetAudioStreamAvailable(channels[i].stream) == 0) JA_StopChannel(i); } @@ -120,8 +101,7 @@ Uint32 JA_UpdateCallback(void *userdata, SDL_TimerID timerID, Uint32 interval) return 30; } -void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) -{ +void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channels) { #ifdef DEBUG SDL_SetLogPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_DEBUG); #endif @@ -140,8 +120,7 @@ void JA_Init(const int freq, const SDL_AudioFormat format, const int num_channel JA_timerID = SDL_AddTimer(30, JA_UpdateCallback, nullptr); } -void JA_Quit() -{ +void JA_Quit() { if (JA_timerID) SDL_RemoveTimer(JA_timerID); @@ -150,8 +129,7 @@ void JA_Quit() sdlAudioDevice = 0; } -JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length) -{ +JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length) { JA_Music_t *music = new JA_Music_t(); int chan, samplerate; @@ -170,8 +148,7 @@ JA_Music_t *JA_LoadMusic(Uint8 *buffer, Uint32 length) return music; } -JA_Music_t *JA_LoadMusic(const char *filename) -{ +JA_Music_t *JA_LoadMusic(const char *filename) { // [RZC 28/08/22] Carreguem primer el arxiu en memòria i després el descomprimim. Es algo més rapid. FILE *f = fopen(filename, "rb"); fseek(f, 0, SEEK_END); @@ -191,8 +168,7 @@ JA_Music_t *JA_LoadMusic(const char *filename) return music; } -void JA_PlayMusic(JA_Music_t *music, const int loop) -{ +void JA_PlayMusic(JA_Music_t *music, const int loop) { if (!JA_musicEnabled) return; @@ -212,15 +188,13 @@ void JA_PlayMusic(JA_Music_t *music, const int loop) // SDL_ResumeAudioStreamDevice(current_music->stream); } -char *JA_GetMusicFilename(JA_Music_t *music) -{ +char *JA_GetMusicFilename(JA_Music_t *music) { if (!music) music = current_music; return music->filename; } -void JA_PauseMusic() -{ +void JA_PauseMusic() { if (!JA_musicEnabled) return; if (!current_music || current_music->state == JA_MUSIC_INVALID) @@ -231,8 +205,7 @@ void JA_PauseMusic() SDL_UnbindAudioStream(current_music->stream); } -void JA_ResumeMusic() -{ +void JA_ResumeMusic() { if (!JA_musicEnabled) return; if (!current_music || current_music->state == JA_MUSIC_INVALID) @@ -243,8 +216,7 @@ void JA_ResumeMusic() SDL_BindAudioStream(sdlAudioDevice, current_music->stream); } -void JA_StopMusic() -{ +void JA_StopMusic() { if (!JA_musicEnabled) return; if (!current_music || current_music->state == JA_MUSIC_INVALID) @@ -259,8 +231,7 @@ void JA_StopMusic() current_music->filename = nullptr; } -void JA_FadeOutMusic(const int milliseconds) -{ +void JA_FadeOutMusic(const int milliseconds) { if (!JA_musicEnabled) return; if (current_music == NULL || current_music->state == JA_MUSIC_INVALID) @@ -272,8 +243,7 @@ void JA_FadeOutMusic(const int milliseconds) fade_initial_volume = JA_musicVolume; } -JA_Music_state JA_GetMusicState() -{ +JA_Music_state JA_GetMusicState() { if (!JA_musicEnabled) return JA_MUSIC_DISABLED; if (!current_music) @@ -282,8 +252,7 @@ JA_Music_state JA_GetMusicState() return current_music->state; } -void JA_DeleteMusic(JA_Music_t *music) -{ +void JA_DeleteMusic(JA_Music_t *music) { if (current_music == music) current_music = nullptr; SDL_free(music->buffer); @@ -292,68 +261,59 @@ void JA_DeleteMusic(JA_Music_t *music) delete music; } -float JA_SetMusicVolume(float volume) -{ +float JA_SetMusicVolume(float volume) { JA_musicVolume = SDL_clamp(volume, 0.0f, 1.0f); if (current_music) SDL_SetAudioStreamGain(current_music->stream, JA_musicVolume); return JA_musicVolume; } -void JA_SetMusicPosition(float value) -{ +void JA_SetMusicPosition(float value) { if (!current_music) return; current_music->pos = value * current_music->spec.freq; } -float JA_GetMusicPosition() -{ +float JA_GetMusicPosition() { if (!current_music) return 0; return float(current_music->pos) / float(current_music->spec.freq); } -void JA_EnableMusic(const bool value) -{ +void JA_EnableMusic(const bool value) { if (!value && current_music && (current_music->state == JA_MUSIC_PLAYING)) JA_StopMusic(); JA_musicEnabled = value; } -JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length) -{ +JA_Sound_t *JA_NewSound(Uint8 *buffer, Uint32 length) { JA_Sound_t *sound = new JA_Sound_t(); sound->buffer = buffer; sound->length = length; return sound; } -JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size) -{ +JA_Sound_t *JA_LoadSound(uint8_t *buffer, uint32_t size) { JA_Sound_t *sound = new JA_Sound_t(); SDL_LoadWAV_IO(SDL_IOFromMem(buffer, size), 1, &sound->spec, &sound->buffer, &sound->length); return sound; } -JA_Sound_t *JA_LoadSound(const char *filename) -{ +JA_Sound_t *JA_LoadSound(const char *filename) { JA_Sound_t *sound = new JA_Sound_t(); SDL_LoadWAV(filename, &sound->spec, &sound->buffer, &sound->length); return sound; } -int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group) -{ +int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group) { if (!JA_soundEnabled) return -1; int channel = 0; - while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) - { + while (channel < JA_MAX_SIMULTANEOUS_CHANNELS && channels[channel].state != JA_CHANNEL_FREE) { channel++; } if (channel == JA_MAX_SIMULTANEOUS_CHANNELS) @@ -372,8 +332,7 @@ int JA_PlaySound(JA_Sound_t *sound, const int loop, const int group) return channel; } -int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group) -{ +int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, const int group) { if (!JA_soundEnabled) return -1; @@ -393,10 +352,8 @@ int JA_PlaySoundOnChannel(JA_Sound_t *sound, const int channel, const int loop, return channel; } -void JA_DeleteSound(JA_Sound_t *sound) -{ - for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) - { +void JA_DeleteSound(JA_Sound_t *sound) { + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].sound == sound) JA_StopChannel(i); } @@ -404,25 +361,19 @@ void JA_DeleteSound(JA_Sound_t *sound) delete sound; } -void JA_PauseChannel(const int channel) -{ +void JA_PauseChannel(const int channel) { if (!JA_soundEnabled) return; - if (channel == -1) - { + if (channel == -1) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) - if (channels[i].state == JA_CHANNEL_PLAYING) - { + if (channels[i].state == JA_CHANNEL_PLAYING) { channels[i].state = JA_CHANNEL_PAUSED; // SDL_PauseAudioStreamDevice(channels[i].stream); SDL_UnbindAudioStream(channels[i].stream); } - } - else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) - { - if (channels[channel].state == JA_CHANNEL_PLAYING) - { + } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { + if (channels[channel].state == JA_CHANNEL_PLAYING) { channels[channel].state = JA_CHANNEL_PAUSED; // SDL_PauseAudioStreamDevice(channels[channel].stream); SDL_UnbindAudioStream(channels[channel].stream); @@ -430,25 +381,19 @@ void JA_PauseChannel(const int channel) } } -void JA_ResumeChannel(const int channel) -{ +void JA_ResumeChannel(const int channel) { if (!JA_soundEnabled) return; - if (channel == -1) - { + if (channel == -1) { for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) - if (channels[i].state == JA_CHANNEL_PAUSED) - { + if (channels[i].state == JA_CHANNEL_PAUSED) { channels[i].state = JA_CHANNEL_PLAYING; // SDL_ResumeAudioStreamDevice(channels[i].stream); SDL_BindAudioStream(sdlAudioDevice, channels[i].stream); } - } - else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) - { - if (channels[channel].state == JA_CHANNEL_PAUSED) - { + } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { + if (channels[channel].state == JA_CHANNEL_PAUSED) { channels[channel].state = JA_CHANNEL_PLAYING; // SDL_ResumeAudioStreamDevice(channels[channel].stream); SDL_BindAudioStream(sdlAudioDevice, channels[channel].stream); @@ -456,15 +401,12 @@ void JA_ResumeChannel(const int channel) } } -void JA_StopChannel(const int channel) -{ +void JA_StopChannel(const int channel) { if (!JA_soundEnabled) return; - if (channel == -1) - { - for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) - { + if (channel == -1) { + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[i].stream); channels[i].stream = nullptr; @@ -472,9 +414,7 @@ void JA_StopChannel(const int channel) channels[i].pos = 0; channels[i].sound = NULL; } - } - else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) - { + } else if (channel >= 0 && channel < JA_MAX_SIMULTANEOUS_CHANNELS) { if (channels[channel].state != JA_CHANNEL_FREE) SDL_DestroyAudioStream(channels[channel].stream); channels[channel].stream = nullptr; @@ -484,8 +424,7 @@ void JA_StopChannel(const int channel) } } -JA_Channel_state JA_GetChannelState(const int channel) -{ +JA_Channel_state JA_GetChannelState(const int channel) { if (!JA_soundEnabled) return JA_SOUND_DISABLED; @@ -495,11 +434,9 @@ JA_Channel_state JA_GetChannelState(const int channel) return channels[channel].state; } -float JA_SetSoundVolume(float volume, const int group) -{ +float JA_SetSoundVolume(float volume, const int group) { const float v = SDL_clamp(volume, 0.0f, 1.0f); - for (int i = 0; i < JA_MAX_GROUPS; ++i) - { + for (int i = 0; i < JA_MAX_GROUPS; ++i) { if (group == -1 || group == i) JA_soundVolume[i] = v; } @@ -512,18 +449,15 @@ float JA_SetSoundVolume(float volume, const int group) return v; } -void JA_EnableSound(const bool value) -{ - for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) - { +void JA_EnableSound(const bool value) { + for (int i = 0; i < JA_MAX_SIMULTANEOUS_CHANNELS; i++) { if (channels[i].state == JA_CHANNEL_PLAYING) JA_StopChannel(i); } JA_soundEnabled = value; } -float JA_SetVolume(float volume) -{ +float JA_SetVolume(float volume) { JA_SetSoundVolume(JA_SetMusicVolume(volume) / 2.0f); return JA_musicVolume; diff --git a/source/external/jail_audio.h b/source/external/jail_audio.h index 7bbb82f..42a89cd 100644 --- a/source/external/jail_audio.h +++ b/source/external/jail_audio.h @@ -1,16 +1,14 @@ #pragma once #include <SDL3/SDL.h> -enum JA_Channel_state -{ +enum JA_Channel_state { JA_CHANNEL_INVALID, JA_CHANNEL_FREE, JA_CHANNEL_PLAYING, JA_CHANNEL_PAUSED, JA_SOUND_DISABLED }; -enum JA_Music_state -{ +enum JA_Music_state { JA_MUSIC_INVALID, JA_MUSIC_PLAYING, JA_MUSIC_PAUSED, diff --git a/source/external/jail_shader.cpp b/source/external/jail_shader.cpp index bc502d6..3cdae9a 100644 --- a/source/external/jail_shader.cpp +++ b/source/external/jail_shader.cpp @@ -1,461 +1,416 @@ #include "jail_shader.h" -#include <SDL3/SDL.h> // Para SDL_GL_GetProcAddress, SDL_LogError -#include <stdint.h> // Para uintptr_t +#include <SDL3/SDL.h> // Para SDL_GL_GetProcAddress, SDL_LogError +#include <stdint.h> // Para uintptr_t + #include <cstring> // Para strncmp #include <stdexcept> // Para runtime_error #include <vector> // Para vector #ifdef __APPLE__ -#include <OpenGL/OpenGL.h> // Para OpenGL en macOS +#include <OpenGL/OpenGL.h> // Para OpenGL en macOS -#include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS +#include "CoreFoundation/CoreFoundation.h" // Para Core Foundation en macOS #if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 -#include <OpenGL/gl3.h> // Para OpenGL 3 en macOS -#else // NO ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 -#include <OpenGL/gl.h> // Para OpenGL (compatibilidad) en macOS -#endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 -#else // SI NO ES __APPLE__ -#include <SDL3/SDL_opengl.h> // Para GLuint, GLint, glTexCoord2f, glVertex2f -#endif // __APPLE__ +#include <OpenGL/gl3.h> // Para OpenGL 3 en macOS +#else // NO ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#include <OpenGL/gl.h> // Para OpenGL (compatibilidad) en macOS +#endif // ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 +#else // SI NO ES __APPLE__ +#include <SDL3/SDL_opengl.h> // Para GLuint, GLint, glTexCoord2f, glVertex2f +#endif // __APPLE__ -namespace shader -{ - // Constantes - const GLuint INVALID_SHADER_ID = 0; - const GLuint INVALID_PROGRAM_ID = 0; - const GLuint DEFAULT_TEXTURE_ID = 1; +namespace shader { +// Constantes +const GLuint INVALID_SHADER_ID = 0; +const GLuint INVALID_PROGRAM_ID = 0; +const GLuint DEFAULT_TEXTURE_ID = 1; - // Variables globales - SDL_Window *win = nullptr; - SDL_Renderer *renderer = nullptr; - GLuint programId = 0; - SDL_Texture *backBuffer = nullptr; - SDL_Point win_size = {320 * 4, 256 * 4}; - SDL_FPoint tex_size = {320, 256}; - bool usingOpenGL = false; +// Variables globales +SDL_Window *win = nullptr; +SDL_Renderer *renderer = nullptr; +GLuint programId = 0; +SDL_Texture *backBuffer = nullptr; +SDL_Point win_size = {320 * 4, 256 * 4}; +SDL_FPoint tex_size = {320, 256}; +bool usingOpenGL = false; #ifndef __APPLE__ - // Declaración de funciones de extensión de OpenGL (evitando GLEW) - PFNGLCREATESHADERPROC glCreateShader; - PFNGLSHADERSOURCEPROC glShaderSource; - PFNGLCOMPILESHADERPROC glCompileShader; - PFNGLGETSHADERIVPROC glGetShaderiv; - PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; - PFNGLDELETESHADERPROC glDeleteShader; - PFNGLATTACHSHADERPROC glAttachShader; - PFNGLCREATEPROGRAMPROC glCreateProgram; - PFNGLLINKPROGRAMPROC glLinkProgram; - PFNGLVALIDATEPROGRAMPROC glValidateProgram; - PFNGLGETPROGRAMIVPROC glGetProgramiv; - PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; - PFNGLUSEPROGRAMPROC glUseProgram; - PFNGLDELETEPROGRAMPROC glDeleteProgram; +// Declaración de funciones de extensión de OpenGL (evitando GLEW) +PFNGLCREATESHADERPROC glCreateShader; +PFNGLSHADERSOURCEPROC glShaderSource; +PFNGLCOMPILESHADERPROC glCompileShader; +PFNGLGETSHADERIVPROC glGetShaderiv; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +PFNGLDELETESHADERPROC glDeleteShader; +PFNGLATTACHSHADERPROC glAttachShader; +PFNGLCREATEPROGRAMPROC glCreateProgram; +PFNGLLINKPROGRAMPROC glLinkProgram; +PFNGLVALIDATEPROGRAMPROC glValidateProgram; +PFNGLGETPROGRAMIVPROC glGetProgramiv; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +PFNGLUSEPROGRAMPROC glUseProgram; +PFNGLDELETEPROGRAMPROC glDeleteProgram; - bool initGLExtensions() - { - glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); - glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); - glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); - glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); - glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); - glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); - glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); - glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); - glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); - glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); - glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); - glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); - glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); - glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); +bool initGLExtensions() { + glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader"); + glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog"); + glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader"); + glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader"); + glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram"); + glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram"); + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv"); + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog"); + glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram"); + glDeleteProgram = (PFNGLDELETEPROGRAMPROC)SDL_GL_GetProcAddress("glDeleteProgram"); - return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && - glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && - glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && - glUseProgram && glDeleteProgram; - } + return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv && + glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram && + glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog && + glUseProgram && glDeleteProgram; +} #endif - // Función para verificar errores de OpenGL - void checkGLError(const char *operation) - { - GLenum error = glGetError(); - if (error != GL_NO_ERROR) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "Error OpenGL en %s: 0x%x", operation, error); - } +// Función para verificar errores de OpenGL +void checkGLError(const char *operation) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Error OpenGL en %s: 0x%x", + operation, + error); + } +} + +// Función para compilar un shader a partir de un std::string +GLuint compileShader(const std::string &source, GLuint shader_type) { + if (source.empty()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR FATAL: El código fuente del shader está vacío."); + throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío."); } - // Función para compilar un shader a partir de un std::string - GLuint compileShader(const std::string &source, GLuint shader_type) - { - if (source.empty()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR FATAL: El código fuente del shader está vacío."); - throw std::runtime_error("ERROR FATAL: El código fuente del shader está vacío."); + // Crear identificador del shader + GLuint shader_id = glCreateShader(shader_type); + if (shader_id == INVALID_SHADER_ID) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el shader."); + checkGLError("glCreateShader"); + return INVALID_SHADER_ID; + } + + // Agregar una directiva según el tipo de shader + std::string directive = (shader_type == GL_VERTEX_SHADER) + ? "#define VERTEX\n" + : "#define FRAGMENT\n"; + + const char *sources[2] = {directive.c_str(), source.c_str()}; + + // Especificar el código fuente del shader + glShaderSource(shader_id, 2, sources, nullptr); + checkGLError("glShaderSource"); + + // Compilar el shader + glCompileShader(shader_id); + checkGLError("glCompileShader"); + + // Verificar si la compilación fue exitosa + GLint compiled_ok = GL_FALSE; + glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled_ok); + if (compiled_ok != GL_TRUE) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en la compilación del shader (%d)!", shader_id); + GLint log_length; + glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) { + std::vector<GLchar> log(log_length); + glGetShaderInfoLog(shader_id, log_length, &log_length, log.data()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de compilación del shader: %s", log.data()); } + glDeleteShader(shader_id); + return INVALID_SHADER_ID; + } + return shader_id; +} - // Crear identificador del shader - GLuint shader_id = glCreateShader(shader_type); - if (shader_id == INVALID_SHADER_ID) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el shader."); - checkGLError("glCreateShader"); - return INVALID_SHADER_ID; - } +// Función para compilar un programa de shaders (vertex y fragment) a partir de std::string +GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source) { + GLuint program_id = glCreateProgram(); + if (program_id == INVALID_PROGRAM_ID) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el programa de shaders."); + checkGLError("glCreateProgram"); + return INVALID_PROGRAM_ID; + } - // Agregar una directiva según el tipo de shader - std::string directive = (shader_type == GL_VERTEX_SHADER) - ? "#define VERTEX\n" - : "#define FRAGMENT\n"; + // Si el fragment shader está vacío, reutilizamos el código del vertex shader + GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER); + GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER); - const char *sources[2] = {directive.c_str(), source.c_str()}; + if (vertex_shader_id != INVALID_SHADER_ID && fragment_shader_id != INVALID_SHADER_ID) { + // Asociar los shaders al programa + glAttachShader(program_id, vertex_shader_id); + checkGLError("glAttachShader vertex"); + glAttachShader(program_id, fragment_shader_id); + checkGLError("glAttachShader fragment"); - // Especificar el código fuente del shader - glShaderSource(shader_id, 2, sources, nullptr); - checkGLError("glShaderSource"); + glLinkProgram(program_id); + checkGLError("glLinkProgram"); - // Compilar el shader - glCompileShader(shader_id); - checkGLError("glCompileShader"); - - // Verificar si la compilación fue exitosa - GLint compiled_ok = GL_FALSE; - glGetShaderiv(shader_id, GL_COMPILE_STATUS, &compiled_ok); - if (compiled_ok != GL_TRUE) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error en la compilación del shader (%d)!", shader_id); + // Verificar el estado del enlace + GLint isLinked = GL_FALSE; + glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked); + if (isLinked == GL_FALSE) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar el programa de shaders."); GLint log_length; - glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) - { - std::vector<GLchar> log(log_length); - glGetShaderInfoLog(shader_id, log_length, &log_length, log.data()); - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de compilación del shader: %s", log.data()); + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 0) { + std::vector<char> log(log_length); + glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de enlace del programa: %s", log.data()); } - glDeleteShader(shader_id); - return INVALID_SHADER_ID; - } - return shader_id; - } - - // Función para compilar un programa de shaders (vertex y fragment) a partir de std::string - GLuint compileProgram(const std::string &vertex_shader_source, const std::string &fragment_shader_source) - { - GLuint program_id = glCreateProgram(); - if (program_id == INVALID_PROGRAM_ID) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al crear el programa de shaders."); - checkGLError("glCreateProgram"); - return INVALID_PROGRAM_ID; - } - - // Si el fragment shader está vacío, reutilizamos el código del vertex shader - GLuint vertex_shader_id = compileShader(vertex_shader_source, GL_VERTEX_SHADER); - GLuint fragment_shader_id = compileShader(fragment_shader_source.empty() ? vertex_shader_source : fragment_shader_source, GL_FRAGMENT_SHADER); - - if (vertex_shader_id != INVALID_SHADER_ID && fragment_shader_id != INVALID_SHADER_ID) - { - // Asociar los shaders al programa - glAttachShader(program_id, vertex_shader_id); - checkGLError("glAttachShader vertex"); - glAttachShader(program_id, fragment_shader_id); - checkGLError("glAttachShader fragment"); - - glLinkProgram(program_id); - checkGLError("glLinkProgram"); - - // Verificar el estado del enlace - GLint isLinked = GL_FALSE; - glGetProgramiv(program_id, GL_LINK_STATUS, &isLinked); - if (isLinked == GL_FALSE) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al enlazar el programa de shaders."); - GLint log_length; - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 0) - { - std::vector<char> log(log_length); - glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Registro de enlace del programa: %s", log.data()); - } - glDeleteProgram(program_id); - program_id = INVALID_PROGRAM_ID; - } - else - { - glValidateProgram(program_id); - checkGLError("glValidateProgram"); - - // Log de información del programa (solo si hay información) - GLint log_length; - glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); - if (log_length > 1) // > 1 porque algunos drivers devuelven 1 para cadena vacía - { - std::vector<char> log(log_length); - glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data()); - } - } - } - else - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudieron compilar los shaders."); glDeleteProgram(program_id); program_id = INVALID_PROGRAM_ID; - } + } else { + glValidateProgram(program_id); + checkGLError("glValidateProgram"); - // Limpiar los shaders (ya no son necesarios después del enlace) - if (vertex_shader_id != INVALID_SHADER_ID) - { - glDeleteShader(vertex_shader_id); + // Log de información del programa (solo si hay información) + GLint log_length; + glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length); + if (log_length > 1) // > 1 porque algunos drivers devuelven 1 para cadena vacía + { + std::vector<char> log(log_length); + glGetProgramInfoLog(program_id, log_length, &log_length, log.data()); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Registro de información del programa:\n%s", log.data()); + } } - if (fragment_shader_id != INVALID_SHADER_ID) - { - glDeleteShader(fragment_shader_id); - } - - return program_id; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudieron compilar los shaders."); + glDeleteProgram(program_id); + program_id = INVALID_PROGRAM_ID; } - // Función para obtener el ID de textura OpenGL desde SDL3 - GLuint getTextureID(SDL_Texture *texture) - { - if (!texture) - return DEFAULT_TEXTURE_ID; - - // Intentar obtener el ID de textura OpenGL desde las propiedades de SDL3 - SDL_PropertiesID props = SDL_GetTextureProperties(texture); - GLuint textureId = 0; - - // Intentar diferentes nombres de propiedades según la versión de SDL3 - textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr); - - // Si la primera no funciona, intentar con el nombre alternativo - if (textureId == 0) - { - textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr); - } - - // Si aún no funciona, intentar obtener como número - if (textureId == 0) - { - textureId = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", DEFAULT_TEXTURE_ID); - } - - // Si ninguna funciona, usar el método manual de bindeo de textura - if (textureId == 0) - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "No se pudo obtener el ID de textura OpenGL, usando ID por defecto (%d)", DEFAULT_TEXTURE_ID); - textureId = DEFAULT_TEXTURE_ID; - } - - return textureId; + // Limpiar los shaders (ya no son necesarios después del enlace) + if (vertex_shader_id != INVALID_SHADER_ID) { + glDeleteShader(vertex_shader_id); + } + if (fragment_shader_id != INVALID_SHADER_ID) { + glDeleteShader(fragment_shader_id); } - bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) - { - shader::win = window; - shader::renderer = SDL_GetRenderer(window); - shader::backBuffer = back_buffer_texture; + return program_id; +} - if (!shader::renderer) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer de la ventana."); - return false; - } +// Función para obtener el ID de textura OpenGL desde SDL3 +GLuint getTextureID(SDL_Texture *texture) { + if (!texture) + return DEFAULT_TEXTURE_ID; - SDL_GetWindowSize(window, &win_size.x, &win_size.y); - SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y); + // Intentar obtener el ID de textura OpenGL desde las propiedades de SDL3 + SDL_PropertiesID props = SDL_GetTextureProperties(texture); + GLuint textureId = 0; - const auto render_name = SDL_GetRendererName(renderer); - if (!render_name) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el nombre del renderer."); - return false; - } + // Intentar diferentes nombres de propiedades según la versión de SDL3 + textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "SDL.texture.opengl.texture", nullptr); - // Verificar que el renderer sea OpenGL - if (!strncmp(render_name, "opengl", 6)) - { + // Si la primera no funciona, intentar con el nombre alternativo + if (textureId == 0) { + textureId = (GLuint)(uintptr_t)SDL_GetPointerProperty(props, "texture.opengl.texture", nullptr); + } + + // Si aún no funciona, intentar obtener como número + if (textureId == 0) { + textureId = (GLuint)SDL_GetNumberProperty(props, "SDL.texture.opengl.texture", DEFAULT_TEXTURE_ID); + } + + // Si ninguna funciona, usar el método manual de bindeo de textura + if (textureId == 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "No se pudo obtener el ID de textura OpenGL, usando ID por defecto (%d)", + DEFAULT_TEXTURE_ID); + textureId = DEFAULT_TEXTURE_ID; + } + + return textureId; +} + +bool init(SDL_Window *window, SDL_Texture *back_buffer_texture, const std::string &vertex_shader, const std::string &fragment_shader) { + shader::win = window; + shader::renderer = SDL_GetRenderer(window); + shader::backBuffer = back_buffer_texture; + + if (!shader::renderer) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el renderer de la ventana."); + return false; + } + + SDL_GetWindowSize(window, &win_size.x, &win_size.y); + SDL_GetTextureSize(back_buffer_texture, &tex_size.x, &tex_size.y); + + const auto render_name = SDL_GetRendererName(renderer); + if (!render_name) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo obtener el nombre del renderer."); + return false; + } + + // Verificar que el renderer sea OpenGL + if (!strncmp(render_name, "opengl", 6)) { #ifndef __APPLE__ - if (!initGLExtensions()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se han podido inicializar las extensiones de OpenGL."); - usingOpenGL = false; - return false; - } -#endif - // Compilar el programa de shaders utilizando std::string - programId = compileProgram(vertex_shader, fragment_shader); - if (programId == INVALID_PROGRAM_ID) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders."); - usingOpenGL = false; - return false; - } - } - else - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name); + if (!initGLExtensions()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se han podido inicializar las extensiones de OpenGL."); usingOpenGL = false; return false; } - - usingOpenGL = true; - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Shader system initialized successfully."); - return true; +#endif + // Compilar el programa de shaders utilizando std::string + programId = compileProgram(vertex_shader, fragment_shader); + if (programId == INVALID_PROGRAM_ID) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ERROR: No se pudo compilar el programa de shaders."); + usingOpenGL = false; + return false; + } + } else { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "ADVERTENCIA: El driver del renderer no es OpenGL (%s).", render_name); + usingOpenGL = false; + return false; } - void render() - { - // Establece el color de fondo - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_SetRenderTarget(renderer, nullptr); - SDL_RenderClear(renderer); + usingOpenGL = true; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Shader system initialized successfully."); + return true; +} - if (usingOpenGL && programId != INVALID_PROGRAM_ID) - { - // Guardar estados de OpenGL - GLint oldProgramId; - glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); +void render() { + // Establece el color de fondo + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_SetRenderTarget(renderer, nullptr); + SDL_RenderClear(renderer); - GLint oldViewport[4]; - glGetIntegerv(GL_VIEWPORT, oldViewport); + if (usingOpenGL && programId != INVALID_PROGRAM_ID) { + // Guardar estados de OpenGL + GLint oldProgramId; + glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId); - GLboolean wasTextureEnabled = glIsEnabled(GL_TEXTURE_2D); - GLint oldTextureId; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId); + GLint oldViewport[4]; + glGetIntegerv(GL_VIEWPORT, oldViewport); - // Obtener y bindear la textura - GLuint textureId = getTextureID(backBuffer); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textureId); - checkGLError("glBindTexture"); + GLboolean wasTextureEnabled = glIsEnabled(GL_TEXTURE_2D); + GLint oldTextureId; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureId); - // Usar nuestro programa de shaders - glUseProgram(programId); - checkGLError("glUseProgram"); + // Obtener y bindear la textura + GLuint textureId = getTextureID(backBuffer); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textureId); + checkGLError("glBindTexture"); - // Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize - int logicalW, logicalH; - SDL_RendererLogicalPresentation mode; - SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH, &mode); - if (logicalW == 0 || logicalH == 0) - { - logicalW = win_size.x; - logicalH = win_size.y; + // Usar nuestro programa de shaders + glUseProgram(programId); + checkGLError("glUseProgram"); + + // Recupera el tamaño lógico configurado con SDL_RenderSetLogicalSize + int logicalW, logicalH; + SDL_RendererLogicalPresentation mode; + SDL_GetRenderLogicalPresentation(renderer, &logicalW, &logicalH, &mode); + if (logicalW == 0 || logicalH == 0) { + logicalW = win_size.x; + logicalH = win_size.y; + } + + // Cálculo del viewport + int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y; + const bool USE_INTEGER_SCALE = mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; + if (USE_INTEGER_SCALE) { + // Calcula el factor de escalado entero máximo que se puede aplicar + int scaleX = win_size.x / logicalW; + int scaleY = win_size.y / logicalH; + int scale = (scaleX < scaleY ? scaleX : scaleY); + if (scale < 1) { + scale = 1; } - - // Cálculo del viewport - int viewportX = 0, viewportY = 0, viewportW = win_size.x, viewportH = win_size.y; - const bool USE_INTEGER_SCALE = mode == SDL_LOGICAL_PRESENTATION_INTEGER_SCALE; - if (USE_INTEGER_SCALE) - { - // Calcula el factor de escalado entero máximo que se puede aplicar - int scaleX = win_size.x / logicalW; - int scaleY = win_size.y / logicalH; - int scale = (scaleX < scaleY ? scaleX : scaleY); - if (scale < 1) - { - scale = 1; - } - viewportW = logicalW * scale; - viewportH = logicalH * scale; + viewportW = logicalW * scale; + viewportH = logicalH * scale; + viewportX = (win_size.x - viewportW) / 2; + viewportY = (win_size.y - viewportH) / 2; + } else { + // Letterboxing: preserva la relación de aspecto usando una escala flotante + float windowAspect = static_cast<float>(win_size.x) / win_size.y; + float logicalAspect = static_cast<float>(logicalW) / logicalH; + if (windowAspect > logicalAspect) { + viewportW = static_cast<int>(logicalAspect * win_size.y); viewportX = (win_size.x - viewportW) / 2; + } else { + viewportH = static_cast<int>(win_size.x / logicalAspect); viewportY = (win_size.y - viewportH) / 2; } - else - { - // Letterboxing: preserva la relación de aspecto usando una escala flotante - float windowAspect = static_cast<float>(win_size.x) / win_size.y; - float logicalAspect = static_cast<float>(logicalW) / logicalH; - if (windowAspect > logicalAspect) - { - viewportW = static_cast<int>(logicalAspect * win_size.y); - viewportX = (win_size.x - viewportW) / 2; - } - else - { - viewportH = static_cast<int>(win_size.x / logicalAspect); - viewportY = (win_size.y - viewportH) / 2; - } - } - glViewport(viewportX, viewportY, viewportW, viewportH); - checkGLError("glViewport"); - - // Configurar la proyección ortográfica usando el espacio lógico - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // Queremos que el origen esté en la esquina superior izquierda del espacio lógico. - glOrtho(0, static_cast<GLdouble>(logicalW), static_cast<GLdouble>(logicalH), 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // Dibuja el quad con las coordenadas ajustadas. - // Se asignan las coordenadas de textura "normales" para que no quede espejado horizontalmente, - // y se mantiene el flip vertical para que la imagen no aparezca volteada. - glBegin(GL_TRIANGLE_STRIP); - // Vértice superior izquierdo - glTexCoord2f(0.0f, 1.0f); - glVertex2f(0.0f, 0.0f); - // Vértice superior derecho - glTexCoord2f(1.0f, 1.0f); - glVertex2f(static_cast<GLfloat>(logicalW), 0.0f); - // Vértice inferior izquierdo - glTexCoord2f(0.0f, 0.0f); - glVertex2f(0.0f, static_cast<GLfloat>(logicalH)); - // Vértice inferior derecho - glTexCoord2f(1.0f, 0.0f); - glVertex2f(static_cast<GLfloat>(logicalW), static_cast<GLfloat>(logicalH)); - glEnd(); - checkGLError("render quad"); - - SDL_GL_SwapWindow(win); - - // Restaurar estados de OpenGL - glUseProgram(oldProgramId); - glBindTexture(GL_TEXTURE_2D, oldTextureId); - if (!wasTextureEnabled) - { - glDisable(GL_TEXTURE_2D); - } - glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); } - else - { - // Fallback a renderizado normal de SDL - SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr); - SDL_RenderPresent(renderer); + glViewport(viewportX, viewportY, viewportW, viewportH); + checkGLError("glViewport"); + + // Configurar la proyección ortográfica usando el espacio lógico + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + // Queremos que el origen esté en la esquina superior izquierda del espacio lógico. + glOrtho(0, static_cast<GLdouble>(logicalW), static_cast<GLdouble>(logicalH), 0, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Dibuja el quad con las coordenadas ajustadas. + // Se asignan las coordenadas de textura "normales" para que no quede espejado horizontalmente, + // y se mantiene el flip vertical para que la imagen no aparezca volteada. + glBegin(GL_TRIANGLE_STRIP); + // Vértice superior izquierdo + glTexCoord2f(0.0f, 1.0f); + glVertex2f(0.0f, 0.0f); + // Vértice superior derecho + glTexCoord2f(1.0f, 1.0f); + glVertex2f(static_cast<GLfloat>(logicalW), 0.0f); + // Vértice inferior izquierdo + glTexCoord2f(0.0f, 0.0f); + glVertex2f(0.0f, static_cast<GLfloat>(logicalH)); + // Vértice inferior derecho + glTexCoord2f(1.0f, 0.0f); + glVertex2f(static_cast<GLfloat>(logicalW), static_cast<GLfloat>(logicalH)); + glEnd(); + checkGLError("render quad"); + + SDL_GL_SwapWindow(win); + + // Restaurar estados de OpenGL + glUseProgram(oldProgramId); + glBindTexture(GL_TEXTURE_2D, oldTextureId); + if (!wasTextureEnabled) { + glDisable(GL_TEXTURE_2D); } + glViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); + } else { + // Fallback a renderizado normal de SDL + SDL_RenderTexture(renderer, backBuffer, nullptr, nullptr); + SDL_RenderPresent(renderer); + } +} + +void cleanup() { + if (programId != INVALID_PROGRAM_ID) { + glDeleteProgram(programId); + programId = INVALID_PROGRAM_ID; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Programa de shaders liberado."); } - void cleanup() - { - if (programId != INVALID_PROGRAM_ID) - { - glDeleteProgram(programId); - programId = INVALID_PROGRAM_ID; - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Programa de shaders liberado."); - } + // Reinicializar variables + win = nullptr; + renderer = nullptr; + backBuffer = nullptr; + usingOpenGL = false; +} - // Reinicializar variables - win = nullptr; - renderer = nullptr; - backBuffer = nullptr; - usingOpenGL = false; - } +bool isUsingOpenGL() { + return usingOpenGL; +} - bool isUsingOpenGL() - { - return usingOpenGL; - } - - GLuint getProgramId() - { - return programId; - } -} \ No newline at end of file +GLuint getProgramId() { + return programId; +} +} // namespace shader \ No newline at end of file diff --git a/source/external/jail_shader.h b/source/external/jail_shader.h index 6247dcb..d03b05d 100644 --- a/source/external/jail_shader.h +++ b/source/external/jail_shader.h @@ -1,10 +1,10 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_Texture, SDL_Window -#include <string> // Para basic_string, string +#include <SDL3/SDL.h> // Para SDL_Texture, SDL_Window -namespace shader -{ - bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = ""); - void render(); -} \ No newline at end of file +#include <string> // Para basic_string, string + +namespace shader { +bool init(SDL_Window *ventana, SDL_Texture *texturaBackBuffer, const std::string &vertexShader, const std::string &fragmentShader = ""); +void render(); +} // namespace shader \ No newline at end of file diff --git a/source/external/stb_image.h b/source/external/stb_image.h index 7df4224..076a4e0 100644 --- a/source/external/stb_image.h +++ b/source/external/stb_image.h @@ -369,18 +369,17 @@ RECENT REVISION HISTORY: #ifndef STBI_NO_STDIO #include <stdio.h> -#endif // STBI_NO_STDIO +#endif // STBI_NO_STDIO #define STBI_VERSION 1 -enum -{ - STBI_default = 0, // only used for desired_channels +enum { + STBI_default = 0, // only used for desired_channels - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 }; #include <stdlib.h> @@ -388,8 +387,7 @@ typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif #ifndef STBIDEF @@ -400,55 +398,55 @@ extern "C" #endif #endif - ////////////////////////////////////////////////////////////////////////////// - // - // PRIMARY API - works on images of any type - // +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// - // - // load image by filename, open file, or memory buffer - // +// +// load image by filename, open file, or memory buffer +// - typedef struct - { - int (*read)(void *user, char *data, int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof)(void *user); // returns nonzero if we are at end of file/data - } stbi_io_callbacks; +typedef struct +{ + int (*read)(void *user, char *data, int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip)(void *user, int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof)(void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; - //////////////////////////////////// - // - // 8-bits-per-channel interface - // +//////////////////////////////////// +// +// 8-bits-per-channel interface +// - STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // Para stbi_load_from_file, file pointer is left pointing immediately after image #endif #ifndef STBI_NO_GIF - STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); #endif #ifdef STBI_WINDOWS_UTF8 - STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input); +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input); #endif - //////////////////////////////////// - // - // 16-bits-per-channel interface - // +//////////////////////////////////// +// +// 16-bits-per-channel interface +// - STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif //////////////////////////////////// @@ -456,81 +454,81 @@ extern "C" // float-per-channel interface // #ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); +STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); +STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR - // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR - STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); - STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO - STBIDEF int stbi_is_hdr(char const *filename); - STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO +STBIDEF int stbi_is_hdr(char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO - // get a VERY brief reason for failure - // on most compilers (and ALL modern mainstream compilers) this is threadsafe - STBIDEF const char *stbi_failure_reason(void); +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason(void); - // free the loaded image -- this is just free() - STBIDEF void stbi_image_free(void *retval_from_stbi_load); +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free(void *retval_from_stbi_load); - // get image dimensions & components without fully decoding - STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); - STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); - STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO - STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp); - STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp); - STBIDEF int stbi_is_16_bit(char const *filename); - STBIDEF int stbi_is_16_bit_from_file(FILE *f); +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit(char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif - // Para image formats that explicitly notate that they have premultiplied alpha, - // we just return the colors as stored in the file. set this flag to force - // unpremultiplication. results are undefined if the unpremultiply overflow. - STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); +// Para image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - // indicate whether we should process iphone images back to canonical format, - // or just pass them through "as-is" - STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - // flip the image vertically, so the first pixel in the output array is the bottom left - STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - // as above, but only applies to images loaded on the thread that calls the function - // this function is only available if your compiler supports thread-local variables; - // calling it will fail to link if your compiler doesn't - STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); - STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); - STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - // ZLIB client - used by PNG, available for other purposes +// ZLIB client - used by PNG, available for other purposes - STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); - STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); - STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); - STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); - STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus } @@ -539,7 +537,7 @@ extern "C" // // //// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H +#endif // STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION @@ -577,14 +575,14 @@ extern "C" #define STBI_NO_ZLIB #endif +#include <limits.h> #include <stdarg.h> -#include <stddef.h> // ptrdiff_t on osx +#include <stddef.h> // ptrdiff_t on osx #include <stdlib.h> #include <string.h> -#include <limits.h> #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include <math.h> // ldexp, pow +#include <math.h> // ldexp, pow #endif #ifndef STBI_NO_STDIO @@ -719,47 +717,43 @@ typedef unsigned char validate_uint32[sizeof(stbi__uint32) == 4 ? 1 : -1]; #ifdef _MSC_VER -#if _MSC_VER >= 1400 // not VC6 -#include <intrin.h> // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info, 1); - return info[3]; +#if _MSC_VER >= 1400 // not VC6 +#include <intrin.h> // __cpuid +static int stbi__cpuid3(void) { + int info[4]; + __cpuid(info, 1); + return info[3]; } #else -static int stbi__cpuid3(void) -{ - int res; - __asm { +static int stbi__cpuid3(void) { + int res; + __asm { mov eax,1 cpuid mov res,edx - } - return res; + } + return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; +static int stbi__sse2_available(void) { + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; } #endif -#else // assume GCC-style if not VC++ +#else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; +static int stbi__sse2_available(void) { + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; } #endif @@ -796,67 +790,61 @@ static int stbi__sse2_available(void) // contains all the IO context, plus some basic image information typedef struct { - stbi__uint32 img_x, img_y; - int img_n, img_out_n; + stbi__uint32 img_x, img_y; + int img_n, img_out_n; - stbi_io_callbacks io; - void *io_user_data; + stbi_io_callbacks io; + void *io_user_data; - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *)buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *)buffer + len; +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *)buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *)buffer + len; } // initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int)fread(data, 1, size, (FILE *)user); +static int stbi__stdio_read(void *user, char *data, int size) { + return (int)fread(data, 1, size, (FILE *)user); } -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE *)user, n, SEEK_CUR); - ch = fgetc((FILE *)user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) - { - ungetc(ch, (FILE *)user); /* push byte back onto stream if valid. */ - } +static void stbi__stdio_skip(void *user, int n) { + int ch; + fseek((FILE *)user, n, SEEK_CUR); + ch = fgetc((FILE *)user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *)user); /* push byte back onto stream if valid. */ + } } -static int stbi__stdio_eof(void *user) -{ - return feof((FILE *)user) || ferror((FILE *)user); +static int stbi__stdio_eof(void *user) { + return feof((FILE *)user) || ferror((FILE *)user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -866,35 +854,32 @@ static stbi_io_callbacks stbi__stdio_callbacks = stbi__stdio_eof, }; -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *)f); +static void stbi__start_file(stbi__context *s, FILE *f) { + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *)f); } // static void stop_file(stbi__context *s) { } -#endif // !STBI_NO_STDIO +#endif // !STBI_NO_STDIO -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; +static void stbi__rewind(stbi__context *s) { + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; } -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR +enum { + STBI_ORDER_RGB, + STBI_ORDER_BGR }; typedef struct { - int bits_per_channel; - int num_channels; - int channel_order; + int bits_per_channel; + int num_channels; + int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG @@ -961,22 +946,19 @@ static #endif const char *stbi__g_failure_reason; -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; +STBIDEF const char *stbi_failure_reason(void) { + return stbi__g_failure_reason; } #ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; +static int stbi__err(const char *str) { + stbi__g_failure_reason = str; + return 0; } #endif -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); +static void *stbi__malloc(size_t size) { + return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. @@ -991,99 +973,89 @@ static void *stbi__malloc(size_t size) // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) - return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; +static int stbi__addsizes_valid(int a, int b) { + if (b < 0) + return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) - return 0; - if (b == 0) - return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX / b; +static int stbi__mul2sizes_valid(int a, int b) { + if (a < 0 || b < 0) + return 0; + if (b == 0) + return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX / b; } #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add); +static int stbi__mad2sizes_valid(int a, int b, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a * b, add); } #endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && - stbi__addsizes_valid(a * b * c, add); +static int stbi__mad3sizes_valid(int a, int b, int c, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && + stbi__addsizes_valid(a * b * c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && - stbi__mul2sizes_valid(a * b * c, d) && stbi__addsizes_valid(a * b * c * d, add); +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a * b, c) && + stbi__mul2sizes_valid(a * b * c, d) && stbi__addsizes_valid(a * b * c * d, add); } #endif #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) - return NULL; - return stbi__malloc(a * b + add); +static void *stbi__malloc_mad2(int a, int b, int add) { + if (!stbi__mad2sizes_valid(a, b, add)) + return NULL; + return stbi__malloc(a * b + add); } #endif -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) - return NULL; - return stbi__malloc(a * b * c + add); +static void *stbi__malloc_mad3(int a, int b, int c, int add) { + if (!stbi__mad3sizes_valid(a, b, c, add)) + return NULL; + return stbi__malloc(a * b * c + add); } #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) - return NULL; - return stbi__malloc(a * b * c * d + add); +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { + if (!stbi__mad4sizes_valid(a, b, c, d, add)) + return NULL; + return stbi__malloc(a * b * c * d + add); } #endif // returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. -static int stbi__addints_valid(int a, int b) -{ - if ((a >= 0) != (b >= 0)) - return 1; // a and b have different signs, so no overflow - if (a < 0 && b < 0) - return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. - return a <= INT_MAX - b; +static int stbi__addints_valid(int a, int b) { + if ((a >= 0) != (b >= 0)) + return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) + return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; } // returns 1 if the product of two ints fits in a signed short, 0 on overflow. -static int stbi__mul2shorts_valid(int a, int b) -{ - if (b == 0 || b == -1) - return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow - if ((a >= 0) == (b >= 0)) - return a <= SHRT_MAX / b; // product is positive, so similar to mul2sizes_valid - if (b < 0) - return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN - return a >= SHRT_MIN / b; +static int stbi__mul2shorts_valid(int a, int b) { + if (b == 0 || b == -1) + return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) + return a <= SHRT_MAX / b; // product is positive, so similar to mul2sizes_valid + if (b < 0) + return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; } // stbi__err - error @@ -1101,9 +1073,8 @@ static int stbi__mul2shorts_valid(int a, int b) #define stbi__errpf(x, y) ((float *)(size_t)(stbi__err(x, y) ? NULL : NULL)) #define stbi__errpuc(x, y) ((unsigned char *)(size_t)(stbi__err(x, y) ? NULL : NULL)) -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); +STBIDEF void stbi_image_free(void *retval_from_stbi_load) { + STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR @@ -1116,9 +1087,8 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); static int stbi__vertically_flip_on_load_global = 0; -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } #ifndef STBI_THREAD_LOCAL @@ -1126,218 +1096,200 @@ STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) #else static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) { + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; } -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; // test the formats with a very explicit header first (at least a FOURCC // or distinctive magic number first) #ifndef STBI_NO_PNG - if (stbi__png_test(s)) - return stbi__png_load(s, x, y, comp, req_comp, ri); + if (stbi__png_test(s)) + return stbi__png_load(s, x, y, comp, req_comp, ri); #endif #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) - return stbi__bmp_load(s, x, y, comp, req_comp, ri); + if (stbi__bmp_test(s)) + return stbi__bmp_load(s, x, y, comp, req_comp, ri); #endif #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) - return stbi__gif_load(s, x, y, comp, req_comp, ri); + if (stbi__gif_test(s)) + return stbi__gif_load(s, x, y, comp, req_comp, ri); #endif #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) - return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc); + if (stbi__psd_test(s)) + return stbi__psd_load(s, x, y, comp, req_comp, ri, bpc); #else - STBI_NOTUSED(bpc); + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) - return stbi__pic_load(s, x, y, comp, req_comp, ri); + if (stbi__pic_test(s)) + return stbi__pic_load(s, x, y, comp, req_comp, ri); #endif // then the formats that can end up attempting to load with just 1 or 2 // bytes matching expectations; these are prone to false positives, so // try them later #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) - return stbi__jpeg_load(s, x, y, comp, req_comp, ri); + if (stbi__jpeg_test(s)) + return stbi__jpeg_load(s, x, y, comp, req_comp, ri); #endif #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) - return stbi__pnm_load(s, x, y, comp, req_comp, ri); + if (stbi__pnm_test(s)) + return stbi__pnm_load(s, x, y, comp, req_comp, ri); #endif #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) - { - float *hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x, y, comp, req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } #endif #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s, x, y, comp, req_comp, ri); + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s, x, y, comp, req_comp, ri); #endif - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { + int i; + int img_len = w * h * channels; + stbi_uc *reduced; - reduced = (stbi_uc *)stbi__malloc(img_len); - if (reduced == NULL) - return stbi__errpuc("outofmem", "Out of memory"); + reduced = (stbi_uc *)stbi__malloc(img_len); + if (reduced == NULL) + return stbi__errpuc("outofmem", "Out of memory"); - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - STBI_FREE(orig); - return reduced; + STBI_FREE(orig); + return reduced; } -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; - enlarged = (stbi__uint16 *)stbi__malloc(img_len * 2); - if (enlarged == NULL) - return (stbi__uint16 *)stbi__errpuc("outofmem", "Out of memory"); + enlarged = (stbi__uint16 *)stbi__malloc(img_len * 2); + if (enlarged == NULL) + return (stbi__uint16 *)stbi__errpuc("outofmem", "Out of memory"); - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - STBI_FREE(orig); - return enlarged; + STBI_FREE(orig); + return enlarged; } -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) { + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; - for (row = 0; row < (h >> 1); row++) - { - stbi_uc *row0 = bytes + row * bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1) * bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) - { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } + for (row = 0; row < (h >> 1); row++) { + stbi_uc *row0 = bytes + row * bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1) * bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } } #ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { + int slice; + int slice_size = w * h * bytes_per_pixel; - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) - { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } } #endif -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - if (result == NULL) - return NULL; + if (result == NULL) + return NULL; - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - if (ri.bits_per_channel != 8) - { - result = stbi__convert_16_to_8((stbi__uint16 *)result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } - // @TODO: move stbi__convert_format to here + // @TODO: move stbi__convert_format to here - if (stbi__vertically_flip_on_load) - { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } - return (unsigned char *)result; + return (unsigned char *)result; } -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - if (result == NULL) - return NULL; + if (result == NULL) + return NULL; - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - if (ri.bits_per_channel != 16) - { - result = stbi__convert_8_to_16((stbi_uc *)result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *)result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - if (stbi__vertically_flip_on_load) - { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } - return (stbi__uint16 *)result; + return (stbi__uint16 *)result; } #if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) - { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } } #endif @@ -1349,250 +1301,225 @@ STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int #endif #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int)bufferlen, NULL, NULL); +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t *input) { + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int)bufferlen, NULL, NULL); } #endif -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; +static FILE *stbi__fopen(char const *filename, char const *mode) { + FILE *f; #if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename) / sizeof(*wFilename))) - return 0; + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename) / sizeof(*wFilename))) + return 0; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode) / sizeof(*wMode))) - return 0; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode) / sizeof(*wMode))) + return 0; #if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; #else - f = _wfopen(wFilename, wMode); + f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f = 0; + if (0 != fopen_s(&f, filename, mode)) + f = 0; #else - f = fopen(filename, mode); + f = fopen(filename, mode); #endif - return f; + return f; } -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) - return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f, x, y, comp, req_comp); - fclose(f); - return result; +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) + return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f, x, y, comp, req_comp); + fclose(f); + return result; } -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s, f); - result = stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); - if (result) - { - // need to 'unget' all the characters in the IO buffer - fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { + unsigned char *result; + stbi__context s; + stbi__start_file(&s, f); + result = stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; } -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s, f); - result = stbi__load_and_postprocess_16bit(&s, x, y, comp, req_comp); - if (result) - { - // need to 'unget' all the characters in the IO buffer - fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s, f); + result = stbi__load_and_postprocess_16bit(&s, x, y, comp, req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, -(int)(s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; } -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) - return (stbi_us *)stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f, x, y, comp, req_comp); - fclose(f); - return result; +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) + return (stbi_us *)stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f, x, y, comp, req_comp); + fclose(f); + return result; } -#endif //! STBI_NO_STDIO +#endif //! STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); } -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s, x, y, channels_in_file, desired_channels); } -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); } -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_8bit(&s, x, y, comp, req_comp); } #ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s, buffer, len); +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { + unsigned char *result; + stbi__context s; + stbi__start_mem(&s, buffer, len); - result = (unsigned char *)stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) - { - stbi__vertical_flip_slices(result, *x, *y, *z, *comp); - } + result = (unsigned char *)stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices(result, *x, *y, *z, *comp); + } - return result; + return result; } #endif #ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { + unsigned char *data; #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) - { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data, x, y, comp, req_comp); - return hdr_data; - } + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s, x, y, comp, req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data, x, y, comp, req_comp); + return hdr_data; + } #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__loadf_main(&s, x, y, comp, req_comp); +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__loadf_main(&s, x, y, comp, req_comp); } -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__loadf_main(&s, x, y, comp, req_comp); +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__loadf_main(&s, x, y, comp, req_comp); } #ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) - return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f, x, y, comp, req_comp); - fclose(f); - return result; +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) + return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f, x, y, comp, req_comp); + fclose(f); + return result; } -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s, f); - return stbi__loadf_main(&s, x, y, comp, req_comp); +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { + stbi__context s; + stbi__start_file(&s, f); + return stbi__loadf_main(&s, x, y, comp, req_comp); } -#endif // !STBI_NO_STDIO +#endif // !STBI_NO_STDIO -#endif // !STBI_NO_LINEAR +#endif // !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__hdr_test(&s); + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__hdr_test(&s); #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; #endif } #ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result = 0; - if (f) - { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; +STBIDEF int stbi_is_hdr(char const *filename) { + FILE *f = stbi__fopen(filename, "rb"); + int result = 0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; } -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ +STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s, f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s, f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; #else - STBI_NOTUSED(f); - return 0; + STBI_NOTUSED(f); + return 0; #endif } -#endif // !STBI_NO_STDIO +#endif // !STBI_NO_STDIO -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__hdr_test(&s); + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__hdr_test(&s); #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; #endif } @@ -1613,162 +1540,140 @@ STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1 / scale; // Common code used by all image loaders // -enum -{ - STBI__SCAN_load = 0, - STBI__SCAN_type, - STBI__SCAN_header +enum { + STBI__SCAN_load = 0, + STBI__SCAN_type, + STBI__SCAN_header }; -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data, (char *)s->buffer_start, s->buflen); - s->callback_already_read += (int)(s->img_buffer - s->img_buffer_original); - if (n == 0) - { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + 1; - *s->img_buffer = 0; - } - else - { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } +static void stbi__refill_buffer(stbi__context *s) { + int n = (s->io.read)(s->io_user_data, (char *)s->buffer_start, s->buflen); + s->callback_already_read += (int)(s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + 1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } } -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) - { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; +stbi_inline static stbi_uc stbi__get8(stbi__context *s) { + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; } #if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) - { - if (!(s->io.eof)(s->io_user_data)) - return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) - return 1; - } +stbi_inline static int stbi__at_eof(stbi__context *s) { + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) + return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) + return 1; + } - return s->img_buffer >= s->img_buffer_end; + return s->img_buffer >= s->img_buffer_end; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) // nothing #else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) - return; // already there! - if (n < 0) - { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) - { - int blen = (int)(s->img_buffer_end - s->img_buffer); - if (blen < n) - { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; +static void stbi__skip(stbi__context *s, int n) { + if (n == 0) + return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int)(s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) // nothing #else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) - { - int blen = (int)(s->img_buffer_end - s->img_buffer); - if (blen < n) - { - int res, count; +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { + if (s->io.read) { + int blen = (int)(s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; - memcpy(buffer, s->img_buffer, blen); + memcpy(buffer, s->img_buffer, blen); - count = (s->io.read)(s->io_user_data, (char *)buffer + blen, n - blen); - res = (count == (n - blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } + count = (s->io.read)(s->io_user_data, (char *)buffer + blen, n - blen); + res = (count == (n - blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } - if (s->img_buffer + n <= s->img_buffer_end) - { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } - else - return 0; + if (s->img_buffer + n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); +static int stbi__get16be(stbi__context *s) { + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); +static stbi__uint32 stbi__get32be(stbi__context *s) { + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); } #endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); +static int stbi__get16le(stbi__context *s) { + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - z += (stbi__uint32)stbi__get16le(s) << 16; - return z; +static stbi__uint32 stbi__get32le(stbi__context *s) { + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; } #endif -#define STBI__BYTECAST(x) ((stbi_uc)((x) & 255)) // truncate int to byte without warnings +#define STBI__BYTECAST(x) ((stbi_uc)((x) & 255)) // truncate int to byte without warnings #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing @@ -1784,302 +1689,267 @@ static stbi__uint32 stbi__get32le(stbi__context *s) // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc)(((r * 77) + (g * 150) + (29 * b)) >> 8); +static stbi_uc stbi__compute_y(int r, int g, int b) { + return (stbi_uc)(((r * 77) + (g * 150) + (29 * b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i, j; - unsigned char *good; +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { + int i, j; + unsigned char *good; - if (req_comp == img_n) - return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + if (req_comp == img_n) + return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (unsigned char *)stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) - { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } + good = (unsigned char *)stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } - for (j = 0; j < (int)y; ++j) - { - unsigned char *src = data + j * x * img_n; - unsigned char *dest = good + j * x * req_comp; + for (j = 0; j < (int)y; ++j) { + unsigned char *src = data + j * x * img_n; + unsigned char *dest = good + j * x * req_comp; #define STBI__COMBO(a, b) ((a) * 8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) - { - STBI__CASE(1, 2) - { - dest[0] = src[0]; - dest[1] = 255; - } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) - { - dest[0] = dest[1] = dest[2] = src[0]; - dest[3] = 255; - } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) - { - dest[0] = dest[1] = dest[2] = src[0]; - dest[3] = src[1]; - } - break; - STBI__CASE(3, 4) - { - dest[0] = src[0]; - dest[1] = src[1]; - dest[2] = src[2]; - dest[3] = 255; - } - break; - STBI__CASE(3, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) - { - dest[0] = stbi__compute_y(src[0], src[1], src[2]); - dest[1] = 255; - } - break; - STBI__CASE(4, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) - { - dest[0] = stbi__compute_y(src[0], src[1], src[2]); - dest[1] = src[3]; - } - break; - STBI__CASE(4, 3) - { - dest[0] = src[0]; - dest[1] = src[1]; - dest[2] = src[2]; - } - break; - default: - STBI_ASSERT(0); - STBI_FREE(data); - STBI_FREE(good); - return stbi__errpuc("unsupported", "Unsupported format conversion"); - } +#define STBI__CASE(a, b) \ + case STBI__COMBO(a, b): \ + for (i = x - 1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1, 2) { + dest[0] = src[0]; + dest[1] = 255; + } + break; + STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(1, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 255; + } + break; + STBI__CASE(2, 1) { dest[0] = src[0]; } + break; + STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(2, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + break; + STBI__CASE(3, 4) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 255; + } + break; + STBI__CASE(3, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } + break; + STBI__CASE(3, 2) { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = 255; + } + break; + STBI__CASE(4, 1) { dest[0] = stbi__compute_y(src[0], src[1], src[2]); } + break; + STBI__CASE(4, 2) { + dest[0] = stbi__compute_y(src[0], src[1], src[2]); + dest[1] = src[3]; + } + break; + STBI__CASE(4, 3) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + break; + default: + STBI_ASSERT(0); + STBI_FREE(data); + STBI_FREE(good); + return stbi__errpuc("unsupported", "Unsupported format conversion"); + } #undef STBI__CASE - } + } - STBI_FREE(data); - return good; + STBI_FREE(data); + return good; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { + return (stbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i, j; - stbi__uint16 *good; +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { + int i, j; + stbi__uint16 *good; - if (req_comp == img_n) - return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + if (req_comp == img_n) + return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - good = (stbi__uint16 *)stbi__malloc(req_comp * x * y * 2); - if (good == NULL) - { - STBI_FREE(data); - return (stbi__uint16 *)stbi__errpuc("outofmem", "Out of memory"); - } + good = (stbi__uint16 *)stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *)stbi__errpuc("outofmem", "Out of memory"); + } - for (j = 0; j < (int)y; ++j) - { - stbi__uint16 *src = data + j * x * img_n; - stbi__uint16 *dest = good + j * x * req_comp; + for (j = 0; j < (int)y; ++j) { + stbi__uint16 *src = data + j * x * img_n; + stbi__uint16 *dest = good + j * x * req_comp; #define STBI__COMBO(a, b) ((a) * 8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) - { - STBI__CASE(1, 2) - { - dest[0] = src[0]; - dest[1] = 0xffff; - } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) - { - dest[0] = dest[1] = dest[2] = src[0]; - dest[3] = 0xffff; - } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) - { - dest[0] = dest[1] = dest[2] = src[0]; - dest[3] = src[1]; - } - break; - STBI__CASE(3, 4) - { - dest[0] = src[0]; - dest[1] = src[1]; - dest[2] = src[2]; - dest[3] = 0xffff; - } - break; - STBI__CASE(3, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) - { - dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); - dest[1] = 0xffff; - } - break; - STBI__CASE(4, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) - { - dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); - dest[1] = src[3]; - } - break; - STBI__CASE(4, 3) - { - dest[0] = src[0]; - dest[1] = src[1]; - dest[2] = src[2]; - } - break; - default: - STBI_ASSERT(0); - STBI_FREE(data); - STBI_FREE(good); - return (stbi__uint16 *)stbi__errpuc("unsupported", "Unsupported format conversion"); - } +#define STBI__CASE(a, b) \ + case STBI__COMBO(a, b): \ + for (i = x - 1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1, 2) { + dest[0] = src[0]; + dest[1] = 0xffff; + } + break; + STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(1, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = 0xffff; + } + break; + STBI__CASE(2, 1) { dest[0] = src[0]; } + break; + STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } + break; + STBI__CASE(2, 4) { + dest[0] = dest[1] = dest[2] = src[0]; + dest[3] = src[1]; + } + break; + STBI__CASE(3, 4) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = 0xffff; + } + break; + STBI__CASE(3, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } + break; + STBI__CASE(3, 2) { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = 0xffff; + } + break; + STBI__CASE(4, 1) { dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); } + break; + STBI__CASE(4, 2) { + dest[0] = stbi__compute_y_16(src[0], src[1], src[2]); + dest[1] = src[3]; + } + break; + STBI__CASE(4, 3) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + } + break; + default: + STBI_ASSERT(0); + STBI_FREE(data); + STBI_FREE(good); + return (stbi__uint16 *)stbi__errpuc("unsupported", "Unsupported format conversion"); + } #undef STBI__CASE - } + } - STBI_FREE(data); - return good; + STBI_FREE(data); + return good; } #endif #ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i, k, n; - float *output; - if (!data) - return NULL; - output = (float *)stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) - { - STBI_FREE(data); - return stbi__errpf("outofmem", "Out of memory"); - } - // compute number of non-alpha components - if (comp & 1) - n = comp; - else - n = comp - 1; - for (i = 0; i < x * y; ++i) - { - for (k = 0; k < n; ++k) - { - output[i * comp + k] = (float)(pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) - { - for (i = 0; i < x * y; ++i) - { - output[i * comp + n] = data[i * comp + n] / 255.0f; - } - } - STBI_FREE(data); - return output; +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { + int i, k, n; + float *output; + if (!data) + return NULL; + output = (float *)stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { + STBI_FREE(data); + return stbi__errpf("outofmem", "Out of memory"); + } + // compute number of non-alpha components + if (comp & 1) + n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) { + for (k = 0; k < n; ++k) { + output[i * comp + k] = (float)(pow(data[i * comp + k] / 255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i = 0; i < x * y; ++i) { + output[i * comp + n] = data[i * comp + n] / 255.0f; + } + } + STBI_FREE(data); + return output; } #endif #ifndef STBI_NO_HDR #define stbi__float2int(x) ((int)(x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i, k, n; - stbi_uc *output; - if (!data) - return NULL; - output = (stbi_uc *)stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) - { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - // compute number of non-alpha components - if (comp & 1) - n = comp; - else - n = comp - 1; - for (i = 0; i < x * y; ++i) - { - for (k = 0; k < n; ++k) - { - float z = (float)pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) - z = 0; - if (z > 255) - z = 255; - output[i * comp + k] = (stbi_uc)stbi__float2int(z); - } - if (k < comp) - { - float z = data[i * comp + k] * 255 + 0.5f; - if (z < 0) - z = 0; - if (z > 255) - z = 255; - output[i * comp + k] = (stbi_uc)stbi__float2int(z); - } - } - STBI_FREE(data); - return output; +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { + int i, k, n; + stbi_uc *output; + if (!data) + return NULL; + output = (stbi_uc *)stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + // compute number of non-alpha components + if (comp & 1) + n = comp; + else + n = comp - 1; + for (i = 0; i < x * y; ++i) { + for (k = 0; k < n; ++k) { + float z = (float)pow(data[i * comp + k] * stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) + z = 0; + if (z > 255) + z = 255; + output[i * comp + k] = (stbi_uc)stbi__float2int(z); + } + if (k < comp) { + float z = data[i * comp + k] * 255 + 0.5f; + if (z < 0) + z = 0; + if (z > 255) + z = 255; + output[i * comp + k] = (stbi_uc)stbi__float2int(z); + } + } + STBI_FREE(data); + return output; } #endif @@ -2107,236 +1977,217 @@ static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) #ifndef STBI_NO_JPEG // huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache typedef struct { - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; - // sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; + // sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; - // definition of jpeg image component - struct - { - int id; - int h, v; - int tq; - int hd, ha; - int dc_pred; + // definition of jpeg image component + struct + { + int id; + int h, v; + int tq; + int hd, ha; + int dc_pred; - int x, y, w2, h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; + int x, y, w2, h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; - int scan_n, order[4]; - int restart_interval, todo; + int scan_n, order[4]; + int restart_interval, todo; - // kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); + // kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i, j, k = 0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i = 0; i < 16; ++i) - { - for (j = 0; j < count[i]; ++j) - { - h->size[k++] = (stbi_uc)(i + 1); - if (k >= 257) - return stbi__err("bad size list", "Corrupt JPEG"); - } - } - h->size[k] = 0; +static int stbi__build_huffman(stbi__huffman *h, int *count) { + int i, j, k = 0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i = 0; i < 16; ++i) { + for (j = 0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc)(i + 1); + if (k >= 257) + return stbi__err("bad size list", "Corrupt JPEG"); + } + } + h->size[k] = 0; - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for (j = 1; j <= 16; ++j) - { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) - { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16)(code++); - if (code - 1 >= (1u << j)) - return stbi__err("bad code lengths", "Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16 - j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for (j = 1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16)(code++); + if (code - 1 >= (1u << j)) + return stbi__err("bad code lengths", "Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16 - j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i = 0; i < k; ++i) - { - int s = h->size[i]; - if (s <= FAST_BITS) - { - int c = h->code[i] << (FAST_BITS - s); - int m = 1 << (FAST_BITS - s); - for (j = 0; j < m; ++j) - { - h->fast[c + j] = (stbi_uc)i; - } - } - } - return 1; + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i = 0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS - s); + int m = 1 << (FAST_BITS - s); + for (j = 0; j < m; ++j) { + h->fast[c + j] = (stbi_uc)i; + } + } + } + return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i = 0; i < (1 << FAST_BITS); ++i) - { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) - { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { + int i; + for (i = 0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; - if (magbits && len + magbits <= FAST_BITS) - { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) - k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16)((k * 256) + (run * 16) + (len + magbits)); - } - } - } + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) + k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16)((k * 256) + (run * 16) + (len + magbits)); + } + } + } } -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do - { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) - { - int c = stbi__get8(j->s); - while (c == 0xff) - c = stbi__get8(j->s); // consume fill bytes - if (c != 0) - { - j->marker = (unsigned char)c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) + c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char)c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); } // (1 << n) - 1 static const stbi__uint32 stbi__bmask[17] = {0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535}; // decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c, k; +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { + unsigned int temp; + int c, k; - if (j->code_bits < 16) - stbi__grow_buffer_unsafe(j); + if (j->code_bits < 16) + stbi__grow_buffer_unsafe(j); - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); - k = h->fast[c]; - if (k < 255) - { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k = FAST_BITS + 1;; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) - { - // error! code not found - j->code_bits -= 16; - return -1; - } + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k = FAST_BITS + 1;; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } - if (k > j->code_bits) - return -1; + if (k > j->code_bits) + return -1; - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - if (c < 0 || c >= 256) // symbol id out of bounds! - return -1; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if (c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; } // bias[n] = (-1<<n) + 1 @@ -2344,627 +2195,578 @@ static const int stbi__jbias[16] = {0, -1, -3, -7, -15, -31, -63, -127, -255, -5 // combined JPEG 'receive' and JPEG 'extend', since baseline // always extends everything it receives. -stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) -{ - unsigned int k; - int sgn; - if (j->code_bits < n) - stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) - return 0; // ran out of bits from stream, return 0s intead of continuing +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) { + unsigned int k; + int sgn; + if (j->code_bits < n) + stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) + return 0; // ran out of bits from stream, return 0s intead of continuing - sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & (sgn - 1)); + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); } // get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) - stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) - return 0; // ran out of bits from stream, return 0s intead of continuing - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { + unsigned int k; + if (j->code_bits < n) + stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) + return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; } -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) - stbi__grow_buffer_unsafe(j); - if (j->code_bits < 1) - return 0; // ran out of bits from stream, return 0s intead of continuing - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { + unsigned int k; + if (j->code_bits < 1) + stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) + return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? static const stbi_uc stbi__jpeg_dezigzag[64 + 15] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63}; + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63, + 63}; // decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff, dc, k; - int t; +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { + int diff, dc, k; + int t; - if (j->code_bits < 16) - stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) - return stbi__err("bad huffman code", "Corrupt JPEG"); + if (j->code_bits < 16) + stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) + return stbi__err("bad huffman code", "Corrupt JPEG"); - // 0 all the ac values now so we can do it 32-bits at a time - memset(data, 0, 64 * sizeof(data[0])); + // 0 all the ac values now so we can do it 32-bits at a time + memset(data, 0, 64 * sizeof(data[0])); - diff = t ? stbi__extend_receive(j, t) : 0; - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) - return stbi__err("bad delta", "Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, dequant[0])) - return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short)(dc * dequant[0]); + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) + return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short)(dc * dequant[0]); - // decode AC components, see JPEG spec - k = 1; - do - { - unsigned int zig; - int c, r, s; - if (j->code_bits < 16) - stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); - r = fac[c]; - if (r) - { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) - return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short)((r >> 8) * dequant[zig]); - } - else - { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) - return stbi__err("bad huffman code", "Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) - { - if (rs != 0xf0) - break; // end block - k += 16; - } - else - { - k += r; + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c, r, s; + if (j->code_bits < 16) + stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) + return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short)(stbi__extend_receive(j, s) * dequant[zig]); - } - } - } while (k < 64); - return 1; + data[zig] = (short)((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) + return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) + break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)(stbi__extend_receive(j, s) * dequant[zig]); + } + } + } while (k < 64); + return 1; } -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff, dc; - int t; - if (j->spec_end != 0) - return stbi__err("can't merge dc and ac", "Corrupt JPEG"); +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { + int diff, dc; + int t; + if (j->spec_end != 0) + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - if (j->code_bits < 16) - stbi__grow_buffer_unsafe(j); + if (j->code_bits < 16) + stbi__grow_buffer_unsafe(j); - if (j->succ_high == 0) - { - // first scan for DC coefficient, must be first - memset(data, 0, 64 * sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) - return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data, 0, 64 * sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) - return stbi__err("bad delta", "Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) - return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short)(dc * (1 << j->succ_low)); - } - else - { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short)(1 << j->succ_low); - } - return 1; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) + return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short)(dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short)(1 << j->succ_low); + } + return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) - return stbi__err("can't merge dc and ac", "Corrupt JPEG"); +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { + int k; + if (j->spec_start == 0) + return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - if (j->succ_high == 0) - { - int shift = j->succ_low; + if (j->succ_high == 0) { + int shift = j->succ_low; - if (j->eob_run) - { - --j->eob_run; - return 1; - } + if (j->eob_run) { + --j->eob_run; + return 1; + } - k = j->spec_start; - do - { - unsigned int zig; - int c, r, s; - if (j->code_bits < 16) - stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); - r = fac[c]; - if (r) - { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) - return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short)((r >> 8) * (1 << shift)); - } - else - { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) - return stbi__err("bad huffman code", "Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) - { - if (r < 15) - { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; + k = j->spec_start; + do { + unsigned int zig; + int c, r, s; + if (j->code_bits < 16) + stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS) - 1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) + return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) + return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short)(stbi__extend_receive(j, s) * (1 << shift)); + } } - else - { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short)(stbi__extend_receive(j, s) * (1 << shift)); - } - } - } while (k <= j->spec_end); - } - else - { - // refinement scan for these AC coefficients + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients - short bit = (short)(1 << j->succ_low); + short bit = (short)(1 << j->succ_low); - if (j->eob_run) - { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) - { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit) == 0) - { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } - else - { - k = j->spec_start; - do - { - int r, s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) - return stbi__err("bad huffman code", "Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) - { - if (r < 15) - { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } - else - { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } - else - { - if (s != 1) - return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit) == 0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } } + } else { + k = j->spec_start; + do { + int r, s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) + return stbi__err("bad huffman code", "Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) + return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } - // advance by r - while (k <= j->spec_end) - { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) - { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit) == 0) - { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - else - { - if (r == 0) - { - *p = (short)s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit) == 0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short)s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int)x > 255) - { - if (x < 0) - return 0; - if (x > 255) - return 255; - } - return (stbi_uc)x; +stbi_inline static stbi_uc stbi__clamp(int x) { + // trick to use a single test to catch both cases + if ((unsigned int)x > 255) { + if (x < 0) + return 0; + if (x > 255) + return 255; + } + return (stbi_uc)x; } #define stbi__f2f(x) ((int)(((x) * 4096 + 0.5))) #define stbi__fsh(x) ((x) * 4096) // derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0, s1, s2, s3, s4, s5, s6, s7) \ - int t0, t1, t2, t3, p1, p2, p3, p4, p5, x0, x1, x2, x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2 + p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3 * stbi__f2f(-1.847759065f); \ - t3 = p1 + p2 * stbi__f2f(0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2 + p3); \ - t1 = stbi__fsh(p2 - p3); \ - x0 = t0 + t3; \ - x3 = t0 - t3; \ - x1 = t1 + t2; \ - x2 = t1 - t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0 + t2; \ - p4 = t1 + t3; \ - p1 = t0 + t3; \ - p2 = t1 + t2; \ - p5 = (p3 + p4) * stbi__f2f(1.175875602f); \ - t0 = t0 * stbi__f2f(0.298631336f); \ - t1 = t1 * stbi__f2f(2.053119869f); \ - t2 = t2 * stbi__f2f(3.072711026f); \ - t3 = t3 * stbi__f2f(1.501321110f); \ - p1 = p5 + p1 * stbi__f2f(-0.899976223f); \ - p2 = p5 + p2 * stbi__f2f(-2.562915447f); \ - p3 = p3 * stbi__f2f(-1.961570560f); \ - p4 = p4 * stbi__f2f(-0.390180644f); \ - t3 += p1 + p4; \ - t2 += p2 + p3; \ - t1 += p2 + p4; \ - t0 += p1 + p3; +#define STBI__IDCT_1D(s0, s1, s2, s3, s4, s5, s6, s7) \ + int t0, t1, t2, t3, p1, p2, p3, p4, p5, x0, x1, x2, x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2 + p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3 * stbi__f2f(-1.847759065f); \ + t3 = p1 + p2 * stbi__f2f(0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2 + p3); \ + t1 = stbi__fsh(p2 - p3); \ + x0 = t0 + t3; \ + x3 = t0 - t3; \ + x1 = t1 + t2; \ + x2 = t1 - t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0 + t2; \ + p4 = t1 + t3; \ + p1 = t0 + t3; \ + p2 = t1 + t2; \ + p5 = (p3 + p4) * stbi__f2f(1.175875602f); \ + t0 = t0 * stbi__f2f(0.298631336f); \ + t1 = t1 * stbi__f2f(2.053119869f); \ + t2 = t2 * stbi__f2f(3.072711026f); \ + t3 = t3 * stbi__f2f(1.501321110f); \ + p1 = p5 + p1 * stbi__f2f(-0.899976223f); \ + p2 = p5 + p2 * stbi__f2f(-2.562915447f); \ + p3 = p3 * stbi__f2f(-1.961570560f); \ + p4 = p4 * stbi__f2f(-0.390180644f); \ + t3 += p1 + p4; \ + t2 += p2 + p3; \ + t1 += p2 + p4; \ + t0 += p1 + p3; -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i, val[64], *v = val; - stbi_uc *o; - short *d = data; +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { + int i, val[64], *v = val; + stbi_uc *o; + short *d = data; - // columns - for (i = 0; i < 8; ++i, ++d, ++v) - { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0 && d[40] == 0 && d[48] == 0 && d[56] == 0) - { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0] * 4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } - else - { - STBI__IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; - x1 += 512; - x2 += 512; - x3 += 512; - v[0] = (x0 + t3) >> 10; - v[56] = (x0 - t3) >> 10; - v[8] = (x1 + t2) >> 10; - v[48] = (x1 - t2) >> 10; - v[16] = (x2 + t1) >> 10; - v[40] = (x2 - t1) >> 10; - v[24] = (x3 + t0) >> 10; - v[32] = (x3 - t0) >> 10; - } - } + // columns + for (i = 0; i < 8; ++i, ++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[8] == 0 && d[16] == 0 && d[24] == 0 && d[32] == 0 && d[40] == 0 && d[48] == 0 && d[56] == 0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] * 4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[0], d[8], d[16], d[24], d[32], d[40], d[48], d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; + x1 += 512; + x2 += 512; + x3 += 512; + v[0] = (x0 + t3) >> 10; + v[56] = (x0 - t3) >> 10; + v[8] = (x1 + t2) >> 10; + v[48] = (x1 - t2) >> 10; + v[16] = (x2 + t1) >> 10; + v[40] = (x2 - t1) >> 10; + v[24] = (x3 + t0) >> 10; + v[32] = (x3 - t0) >> 10; + } + } - for (i = 0, v = val, o = out; i < 8; ++i, v += 8, o += out_stride) - { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128 << 17); - x1 += 65536 + (128 << 17); - x2 += 65536 + (128 << 17); - x3 += 65536 + (128 << 17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0 + t3) >> 17); - o[7] = stbi__clamp((x0 - t3) >> 17); - o[1] = stbi__clamp((x1 + t2) >> 17); - o[6] = stbi__clamp((x1 - t2) >> 17); - o[2] = stbi__clamp((x2 + t1) >> 17); - o[5] = stbi__clamp((x2 - t1) >> 17); - o[3] = stbi__clamp((x3 + t0) >> 17); - o[4] = stbi__clamp((x3 - t0) >> 17); - } + for (i = 0, v = val, o = out; i < 8; ++i, v += 8, o += out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128 << 17); + x1 += 65536 + (128 << 17); + x2 += 65536 + (128 << 17); + x3 += 65536 + (128 << 17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0 + t3) >> 17); + o[7] = stbi__clamp((x0 - t3) >> 17); + o[1] = stbi__clamp((x1 + t2) >> 17); + o[6] = stbi__clamp((x1 - t2) >> 17); + o[2] = stbi__clamp((x2 + t1) >> 17); + o[5] = stbi__clamp((x2 - t1) >> 17); + o[3] = stbi__clamp((x3 + t0) >> 17); + o[4] = stbi__clamp((x3 - t0) >> 17); + } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; // dot product constant: even elems=x, odd elems=y #define dct_const(x, y) _mm_setr_epi16((x), (y), (x), (y), (x), (y), (x), (y)) // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) // out(1) = c1[even]*x + c1[odd]*y -#define dct_rot(out0, out1, x, y, c0, c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x), (y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x), (y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) +#define dct_rot(out0, out1, x, y, c0, c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x), (y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x), (y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) // out = in << 12 (in 16-bit, out 32-bit) -#define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) +#define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) // wide add -#define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) +#define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) // wide sub -#define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) +#define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) // butterfly a/b, add bias, then shift by "s" and pack -#define dct_bfly32o(out0, out1, a, b, bias, s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } +#define dct_bfly32o(out0, out1, a, b, bias, s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } // 8-bit interleave step (for transposes) -#define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) +#define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) // 16-bit interleave step (for transposes) -#define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) +#define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) -#define dct_pass(bias, shift) \ - { \ - /* even part */ \ - dct_rot(t2e, t3e, row2, row6, rot0_0, rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o, y2o, row7, row3, rot2_0, rot2_1); \ - dct_rot(y1o, y3o, row5, row1, rot3_0, rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o, y5o, sum17, sum35, rot1_0, rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0, row7, x0, x7, bias, shift); \ - dct_bfly32o(row1, row6, x1, x6, bias, shift); \ - dct_bfly32o(row2, row5, x2, x5, bias, shift); \ - dct_bfly32o(row3, row4, x3, x4, bias, shift); \ - } +#define dct_pass(bias, shift) \ + { \ + /* even part */ \ + dct_rot(t2e, t3e, row2, row6, rot0_0, rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o, y2o, row7, row3, rot2_0, rot2_1); \ + dct_rot(y1o, y3o, row5, row1, rot3_0, rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o, y5o, sum17, sum35, rot1_0, rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0, row7, x0, x7, bias, shift); \ + dct_bfly32o(row1, row6, x1, x6, bias, shift); \ + dct_bfly32o(row2, row5, x2, x5, bias, shift); \ + dct_bfly32o(row3, row4, x3, x4, bias, shift); \ + } - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f(0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f(0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f(3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f(2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f(1.501321110f)); + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f(0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f(0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f(3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f(2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f(1.501321110f)); - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128 << 17)); + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128 << 17)); - // load - row0 = _mm_load_si128((const __m128i *)(data + 0 * 8)); - row1 = _mm_load_si128((const __m128i *)(data + 1 * 8)); - row2 = _mm_load_si128((const __m128i *)(data + 2 * 8)); - row3 = _mm_load_si128((const __m128i *)(data + 3 * 8)); - row4 = _mm_load_si128((const __m128i *)(data + 4 * 8)); - row5 = _mm_load_si128((const __m128i *)(data + 5 * 8)); - row6 = _mm_load_si128((const __m128i *)(data + 6 * 8)); - row7 = _mm_load_si128((const __m128i *)(data + 7 * 8)); + // load + row0 = _mm_load_si128((const __m128i *)(data + 0 * 8)); + row1 = _mm_load_si128((const __m128i *)(data + 1 * 8)); + row2 = _mm_load_si128((const __m128i *)(data + 2 * 8)); + row3 = _mm_load_si128((const __m128i *)(data + 3 * 8)); + row4 = _mm_load_si128((const __m128i *)(data + 4 * 8)); + row5 = _mm_load_si128((const __m128i *)(data + 5 * 8)); + row6 = _mm_load_si128((const __m128i *)(data + 6 * 8)); + row7 = _mm_load_si128((const __m128i *)(data + 7 * 8)); - // column pass - dct_pass(bias_0, 10); + // column pass + dct_pass(bias_0, 10); - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } - // row pass - dct_pass(bias_1, 17); + // row pass + dct_pass(bias_1, 17); - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... - // store - _mm_storel_epi64((__m128i *)out, p0); - out += out_stride; - _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p0, 0x4e)); - out += out_stride; - _mm_storel_epi64((__m128i *)out, p2); - out += out_stride; - _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p2, 0x4e)); - out += out_stride; - _mm_storel_epi64((__m128i *)out, p1); - out += out_stride; - _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p1, 0x4e)); - out += out_stride; - _mm_storel_epi64((__m128i *)out, p3); - out += out_stride; - _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p3, 0x4e)); - } + // store + _mm_storel_epi64((__m128i *)out, p0); + out += out_stride; + _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p0, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *)out, p2); + out += out_stride; + _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p2, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *)out, p1); + out += out_stride; + _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p1, 0x4e)); + out += out_stride; + _mm_storel_epi64((__m128i *)out, p3); + out += out_stride; + _mm_storel_epi64((__m128i *)out, _mm_shuffle_epi32(p3, 0x4e)); + } #undef dct_const #undef dct_rot @@ -2977,242 +2779,241 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) #undef dct_pass } -#endif // STBI_SSE2 +#endif // STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f(0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f(1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f(0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f(2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f(3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f(1.501321110f)); + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f(0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f(1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f(0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f(2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f(3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f(1.501321110f)); -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0, out1, a, b, shiftop, s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } +#define dct_bfly32o(out0, out1, a, b, shiftop, s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0, row7, x0, x7, shiftop, shift); \ - dct_bfly32o(row1, row6, x1, x6, shiftop, shift); \ - dct_bfly32o(row2, row5, x2, x5, shiftop, shift); \ - dct_bfly32o(row3, row4, x3, x4, shiftop, shift); \ - } +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0, row7, x0, x7, shiftop, shift); \ + dct_bfly32o(row1, row6, x1, x6, shiftop, shift); \ + dct_bfly32o(row2, row5, x2, x5, shiftop, shift); \ + dct_bfly32o(row3, row4, x3, x4, shiftop, shift); \ + } - // load - row0 = vld1q_s16(data + 0 * 8); - row1 = vld1q_s16(data + 1 * 8); - row2 = vld1q_s16(data + 2 * 8); - row3 = vld1q_s16(data + 3 * 8); - row4 = vld1q_s16(data + 4 * 8); - row5 = vld1q_s16(data + 5 * 8); - row6 = vld1q_s16(data + 6 * 8); - row7 = vld1q_s16(data + 7 * 8); + // load + row0 = vld1q_s16(data + 0 * 8); + row1 = vld1q_s16(data + 1 * 8); + row2 = vld1q_s16(data + 2 * 8); + row3 = vld1q_s16(data + 3 * 8); + row4 = vld1q_s16(data + 4 * 8); + row5 = vld1q_s16(data + 5 * 8); + row6 = vld1q_s16(data + 6 * 8); + row7 = vld1q_s16(data + 7 * 8); - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - // column pass - dct_pass(vrshrn_n_s32, 10); + // column pass + dct_pass(vrshrn_n_s32, 10); - // 16bit 8x8 transpose - { + // 16bit 8x8 transpose + { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) \ - { \ - int16x8x2_t t = vtrnq_s16(x, y); \ - x = t.val[0]; \ - y = t.val[1]; \ - } -#define dct_trn32(x, y) \ - { \ - int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); \ - x = vreinterpretq_s16_s32(t.val[0]); \ - y = vreinterpretq_s16_s32(t.val[1]); \ - } -#define dct_trn64(x, y) \ - { \ - int16x8_t x0 = x; \ - int16x8_t y0 = y; \ - x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); \ - y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); \ - } +#define dct_trn16(x, y) \ + { \ + int16x8x2_t t = vtrnq_s16(x, y); \ + x = t.val[0]; \ + y = t.val[1]; \ + } +#define dct_trn32(x, y) \ + { \ + int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); \ + x = vreinterpretq_s16_s32(t.val[0]); \ + y = vreinterpretq_s16_s32(t.val[1]); \ + } +#define dct_trn64(x, y) \ + { \ + int16x8_t x0 = x; \ + int16x8_t y0 = y; \ + x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); \ + y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); \ + } - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 - } + } - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) \ - { \ - uint8x8x2_t t = vtrn_u8(x, y); \ - x = t.val[0]; \ - y = t.val[1]; \ - } -#define dct_trn8_16(x, y) \ - { \ - uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); \ - x = vreinterpret_u8_u16(t.val[0]); \ - y = vreinterpret_u8_u16(t.val[1]); \ - } -#define dct_trn8_32(x, y) \ - { \ - uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); \ - x = vreinterpret_u8_u32(t.val[0]); \ - y = vreinterpret_u8_u32(t.val[1]); \ - } + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) \ + { \ + uint8x8x2_t t = vtrn_u8(x, y); \ + x = t.val[0]; \ + y = t.val[1]; \ + } +#define dct_trn8_16(x, y) \ + { \ + uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); \ + x = vreinterpret_u8_u16(t.val[0]); \ + y = vreinterpret_u8_u16(t.val[1]); \ + } +#define dct_trn8_32(x, y) \ + { \ + uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); \ + x = vreinterpret_u8_u32(t.val[0]); \ + y = vreinterpret_u8_u32(t.val[1]); \ + } - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); - // store - vst1_u8(out, p0); - out += out_stride; - vst1_u8(out, p1); - out += out_stride; - vst1_u8(out, p2); - out += out_stride; - vst1_u8(out, p3); - out += out_stride; - vst1_u8(out, p4); - out += out_stride; - vst1_u8(out, p5); - out += out_stride; - vst1_u8(out, p6); - out += out_stride; - vst1_u8(out, p7); + // store + vst1_u8(out, p0); + out += out_stride; + vst1_u8(out, p1); + out += out_stride; + vst1_u8(out, p2); + out += out_stride; + vst1_u8(out, p3); + out += out_stride; + vst1_u8(out, p4); + out += out_stride; + vst1_u8(out, p5); + out += out_stride; + vst1_u8(out, p6); + out += out_stride; + vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 - } + } #undef dct_long_mul #undef dct_long_mac @@ -3223,27 +3024,25 @@ static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) #undef dct_pass } -#endif // STBI_NEON +#endif // STBI_NEON #define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) - { - x = j->marker; - j->marker = STBI__MARKER_none; - return x; - } - x = stbi__get8(j->s); - if (x != 0xff) - return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; +static stbi_uc stbi__get_marker(stbi__jpeg *j) { + stbi_uc x; + if (j->marker != STBI__MARKER_none) { + x = j->marker; + j->marker = STBI__MARKER_none; + return x; + } + x = stbi__get8(j->s); + if (x != 0xff) + return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; } // in each scan, we'll have scan_n components, and the order @@ -3252,535 +3051,466 @@ static stbi_uc stbi__get_marker(stbi__jpeg *j) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels +static void stbi__jpeg_reset(stbi__jpeg *j) { + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels } -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) - { - if (z->scan_n == 1) - { - int i, j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x + 7) >> 3; - int h = (z->img_comp[n].y + 7) >> 3; - for (j = 0; j < h; ++j) - { - for (i = 0; i < w; ++i) - { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) - return 0; - z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) - { - if (z->code_bits < 24) - stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) - return 1; - stbi__jpeg_reset(z); - } +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i, j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) + return 0; + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) + stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) + return 1; + stbi__jpeg_reset(z); + } + } } - } - return 1; - } - else - { // interleaved - int i, j, k, x, y; - STBI_SIMD_ALIGN(short, data[64]); - for (j = 0; j < z->img_mcu_y; ++j) - { - for (i = 0; i < z->img_mcu_x; ++i) - { - // scan an interleaved mcu... process scan_n components in order - for (k = 0; k < z->scan_n; ++k) - { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y = 0; y < z->img_comp[n].v; ++y) - { - for (x = 0; x < z->img_comp[n].h; ++x) - { - int x2 = (i * z->img_comp[n].h + x) * 8; - int y2 = (j * z->img_comp[n].v + y) * 8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) - return 0; - z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * y2 + x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) - { - if (z->code_bits < 24) - stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) - return 1; - stbi__jpeg_reset(z); - } + return 1; + } else { // interleaved + int i, j, k, x, y; + STBI_SIMD_ALIGN(short, data[64]); + for (j = 0; j < z->img_mcu_y; ++j) { + for (i = 0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k = 0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y = 0; y < z->img_comp[n].v; ++y) { + for (x = 0; x < z->img_comp[n].h; ++x) { + int x2 = (i * z->img_comp[n].h + x) * 8; + int y2 = (j * z->img_comp[n].v + y) * 8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc + z->img_comp[n].hd, z->huff_ac + ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) + return 0; + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * y2 + x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) + return 1; + stbi__jpeg_reset(z); + } + } } - } - return 1; - } - } - else - { - if (z->scan_n == 1) - { - int i, j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x + 7) >> 3; - int h = (z->img_comp[n].y + 7) >> 3; - for (j = 0; j < h; ++j) - { - for (i = 0; i < w; ++i) - { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) - { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - else - { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) - { - if (z->code_bits < 24) - stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) - return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - else - { // interleaved - int i, j, k, x, y; - for (j = 0; j < z->img_mcu_y; ++j) - { - for (i = 0; i < z->img_mcu_x; ++i) - { - // scan an interleaved mcu... process scan_n components in order - for (k = 0; k < z->scan_n; ++k) - { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y = 0; y < z->img_comp[n].v; ++y) - { - for (x = 0; x < z->img_comp[n].h; ++x) - { - int x2 = (i * z->img_comp[n].h + x); - int y2 = (j * z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + return 1; + } + } else { + if (z->scan_n == 1) { + int i, j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) - { - if (z->code_bits < 24) - stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) - return 1; - stbi__jpeg_reset(z); - } + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) + return 1; + stbi__jpeg_reset(z); + } + } } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i = 0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) - { - // dequantize and idct the data - int i, j, n; - for (n = 0; n < z->s->img_n; ++n) - { - int w = (z->img_comp[n].x + 7) >> 3; - int h = (z->img_comp[n].y + 7) >> 3; - for (j = 0; j < h; ++j) - { - for (i = 0; i < w; ++i) - { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); + return 1; + } else { // interleaved + int i, j, k, x, y; + for (j = 0; j < z->img_mcu_y; ++j) { + for (i = 0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k = 0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y = 0; y < z->img_comp[n].v; ++y) { + for (x = 0; x < z->img_comp[n].h; ++x) { + int x2 = (i * z->img_comp[n].h + x); + int y2 = (j * z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) + stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) + return 1; + stbi__jpeg_reset(z); + } + } } - } - } - } + return 1; + } + } } -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) - { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker", "Corrupt JPEG"); +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { + int i; + for (i = 0; i < 64; ++i) + data[i] *= dequant[i]; +} - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) - return stbi__err("bad DRI len", "Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; +static void stbi__jpeg_finish(stbi__jpeg *z) { + if (z->progressive) { + // dequantize and idct the data + int i, j, n; + for (n = 0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x + 7) >> 3; + int h = (z->img_comp[n].y + 7) >> 3; + for (j = 0; j < h; ++j) { + for (i = 0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data + z->img_comp[n].w2 * j * 8 + i * 8, z->img_comp[n].w2, data); + } + } + } + } +} - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s) - 2; - while (L > 0) - { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15, i; - if (p != 0 && p != 1) - return stbi__err("bad DQT type", "Corrupt JPEG"); - if (t > 3) - return stbi__err("bad DQT table", "Corrupt JPEG"); +static int stbi__process_marker(stbi__jpeg *z, int m) { + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker", "Corrupt JPEG"); - for (i = 0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L == 0; + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) + return stbi__err("bad DRI len", "Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s) - 2; - while (L > 0) - { - stbi_uc *v; - int sizes[16], i, n = 0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) - return stbi__err("bad DHT header", "Corrupt JPEG"); - for (i = 0; i < 16; ++i) - { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - if (n > 256) - return stbi__err("bad DHT header", "Corrupt JPEG"); // Loop over i < n would write past end of values! - L -= 17; - if (tc == 0) - { - if (!stbi__build_huffman(z->huff_dc + th, sizes)) - return 0; - v = z->huff_dc[th].values; - } - else - { - if (!stbi__build_huffman(z->huff_ac + th, sizes)) - return 0; - v = z->huff_ac[th].values; - } - for (i = 0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L == 0; - } + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s) - 2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15, i; + if (p != 0 && p != 1) + return stbi__err("bad DQT type", "Corrupt JPEG"); + if (t > 3) + return stbi__err("bad DQT table", "Corrupt JPEG"); - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) - { - L = stbi__get16be(z->s); - if (L < 2) - { - if (m == 0xFE) - return stbi__err("bad COM len", "Corrupt JPEG"); - else - return stbi__err("bad APP len", "Corrupt JPEG"); - } - L -= 2; + for (i = 0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L == 0; - if (m == 0xE0 && L >= 5) - { // JFIF APP0 segment - static const unsigned char tag[5] = {'J', 'F', 'I', 'F', '\0'}; - int ok = 1; - int i; - for (i = 0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } - else if (m == 0xEE && L >= 12) - { // Adobe APP14 segment - static const unsigned char tag[6] = {'A', 'd', 'o', 'b', 'e', '\0'}; - int ok = 1; - int i; - for (i = 0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) - { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s) - 2; + while (L > 0) { + stbi_uc *v; + int sizes[16], i, n = 0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) + return stbi__err("bad DHT header", "Corrupt JPEG"); + for (i = 0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if (n > 256) + return stbi__err("bad DHT header", "Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc + th, sizes)) + return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac + th, sizes)) + return 0; + v = z->huff_ac[th].values; + } + for (i = 0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L == 0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len", "Corrupt JPEG"); + else + return stbi__err("bad APP len", "Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J', 'F', 'I', 'F', '\0'}; + int ok = 1; + int i; + for (i = 0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A', 'd', 'o', 'b', 'e', '\0'}; + int ok = 1; + int i; + for (i = 0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; L -= 6; - } - } + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } - stbi__skip(z->s, L); - return 1; - } + stbi__skip(z->s, L); + return 1; + } - return stbi__err("unknown marker", "Corrupt JPEG"); + return stbi__err("unknown marker", "Corrupt JPEG"); } // after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int)z->s->img_n) - return stbi__err("bad SOS component count", "Corrupt JPEG"); - if (Ls != 6 + 2 * z->scan_n) - return stbi__err("bad SOS len", "Corrupt JPEG"); - for (i = 0; i < z->scan_n; ++i) - { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) - return 0; // no match - z->img_comp[which].hd = q >> 4; - if (z->img_comp[which].hd > 3) - return stbi__err("bad DC huff", "Corrupt JPEG"); - z->img_comp[which].ha = q & 15; - if (z->img_comp[which].ha > 3) - return stbi__err("bad AC huff", "Corrupt JPEG"); - z->order[i] = which; - } +static int stbi__process_scan_header(stbi__jpeg *z) { + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int)z->s->img_n) + return stbi__err("bad SOS component count", "Corrupt JPEG"); + if (Ls != 6 + 2 * z->scan_n) + return stbi__err("bad SOS len", "Corrupt JPEG"); + for (i = 0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) + return 0; // no match + z->img_comp[which].hd = q >> 4; + if (z->img_comp[which].hd > 3) + return stbi__err("bad DC huff", "Corrupt JPEG"); + z->img_comp[which].ha = q & 15; + if (z->img_comp[which].ha > 3) + return stbi__err("bad AC huff", "Corrupt JPEG"); + z->order[i] = which; + } - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) - { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } - else - { - if (z->spec_start != 0) - return stbi__err("bad SOS", "Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) - return stbi__err("bad SOS", "Corrupt JPEG"); - z->spec_end = 63; - } - } + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) + return stbi__err("bad SOS", "Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) + return stbi__err("bad SOS", "Corrupt JPEG"); + z->spec_end = 63; + } + } - return 1; + return 1; } -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i = 0; i < ncomp; ++i) - { - if (z->img_comp[i].raw_data) - { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) - { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) - { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { + int i; + for (i = 0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; } -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf, p, i, q, h_max = 1, v_max = 1, c; - Lf = stbi__get16be(s); - if (Lf < 11) - return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG - p = stbi__get8(s); - if (p != 8) - return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); - if (s->img_y == 0) - return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); - if (s->img_x == 0) - return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) - return stbi__err("bad component count", "Corrupt JPEG"); - s->img_n = c; - for (i = 0; i < c; ++i) - { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } +static int stbi__process_frame_header(stbi__jpeg *z, int scan) { + stbi__context *s = z->s; + int Lf, p, i, q, h_max = 1, v_max = 1, c; + Lf = stbi__get16be(s); + if (Lf < 11) + return stbi__err("bad SOF len", "Corrupt JPEG"); // JPEG + p = stbi__get8(s); + if (p != 8) + return stbi__err("only 8-bit", "JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); + if (s->img_y == 0) + return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); + if (s->img_x == 0) + return stbi__err("0 width", "Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) + return stbi__err("bad component count", "Corrupt JPEG"); + s->img_n = c; + for (i = 0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } - if (Lf != 8 + 3 * s->img_n) - return stbi__err("bad SOF len", "Corrupt JPEG"); + if (Lf != 8 + 3 * s->img_n) + return stbi__err("bad SOF len", "Corrupt JPEG"); - z->rgb = 0; - for (i = 0; i < s->img_n; ++i) - { - static const unsigned char rgb[3] = {'R', 'G', 'B'}; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); - if (!z->img_comp[i].h || z->img_comp[i].h > 4) - return stbi__err("bad H", "Corrupt JPEG"); - z->img_comp[i].v = q & 15; - if (!z->img_comp[i].v || z->img_comp[i].v > 4) - return stbi__err("bad V", "Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); - if (z->img_comp[i].tq > 3) - return stbi__err("bad TQ", "Corrupt JPEG"); - } + z->rgb = 0; + for (i = 0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = {'R', 'G', 'B'}; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); + if (!z->img_comp[i].h || z->img_comp[i].h > 4) + return stbi__err("bad H", "Corrupt JPEG"); + z->img_comp[i].v = q & 15; + if (!z->img_comp[i].v || z->img_comp[i].v > 4) + return stbi__err("bad V", "Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); + if (z->img_comp[i].tq > 3) + return stbi__err("bad TQ", "Corrupt JPEG"); + } - if (scan != STBI__SCAN_load) - return 1; + if (scan != STBI__SCAN_load) + return 1; - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) - return stbi__err("too large", "Image too large to decode"); + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) + return stbi__err("too large", "Image too large to decode"); - for (i = 0; i < s->img_n; ++i) - { - if (z->img_comp[i].h > h_max) - h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) - v_max = z->img_comp[i].v; - } + for (i = 0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) + h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) + v_max = z->img_comp[i].v; + } - // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios - // and I've never seen a non-corrupted JPEG file actually use them - for (i = 0; i < s->img_n; ++i) - { - if (h_max % z->img_comp[i].h != 0) - return stbi__err("bad H", "Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) - return stbi__err("bad V", "Corrupt JPEG"); - } + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i = 0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) + return stbi__err("bad H", "Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) + return stbi__err("bad V", "Corrupt JPEG"); + } - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w - 1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h - 1) / z->img_mcu_h; + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w - 1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h - 1) / z->img_mcu_h; - for (i = 0; i < s->img_n; ++i) - { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max - 1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max - 1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc *)(((size_t)z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) - { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) + for (i = 0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max - 1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max - 1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short *)(((size_t)z->img_comp[i].raw_coeff + 15) & ~15); - } - } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc *)(((size_t)z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i + 1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short *)(((size_t)z->img_comp[i].raw_coeff + 15) & ~15); + } + } - return 1; + return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) @@ -3792,864 +3522,772 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) #define stbi__SOF_progressive(x) ((x) == 0xc2) -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) - return stbi__err("no SOI", "Corrupt JPEG"); - if (scan == STBI__SCAN_type) - return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) - { - if (!stbi__process_marker(z, m)) - return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) - { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) - return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) - return 0; - return 1; +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) + return stbi__err("no SOI", "Corrupt JPEG"); + if (scan == STBI__SCAN_type) + return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z, m)) + return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) + return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) + return 0; + return 1; } -static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) -{ - // some JPEGs have junk at end, skip over it but if we find what looks - // like a valid marker, resume there - while (!stbi__at_eof(j->s)) - { - stbi_uc x = stbi__get8(j->s); - while (x == 0xff) - { // might be a marker - if (stbi__at_eof(j->s)) - return STBI__MARKER_none; - x = stbi__get8(j->s); - if (x != 0x00 && x != 0xff) - { - // not a stuffed zero or lead-in to another marker, looks - // like an actual marker, return it - return x; - } - // stuffed zero has x=0 now which ends the loop, meaning we go - // back to regular scan loop. - // repeated 0xff keeps trying to read the next byte of the marker. - } - } - return STBI__MARKER_none; +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) { + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) + return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; } // decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) - { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) - return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) - { - if (stbi__SOS(m)) - { - if (!stbi__process_scan_header(j)) - return 0; - if (!stbi__parse_entropy_coded_data(j)) - return 0; - if (j->marker == STBI__MARKER_none) - { - j->marker = stbi__skip_jpeg_junk_at_end(j); - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - m = stbi__get_marker(j); - if (STBI__RESTART(m)) +static int stbi__decode_jpeg_image(stbi__jpeg *j) { + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) + return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) + return 0; + if (!stbi__parse_entropy_coded_data(j)) + return 0; + if (j->marker == STBI__MARKER_none) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } m = stbi__get_marker(j); - } - else if (stbi__DNL(m)) - { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) - return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) - return stbi__err("bad DNL height", "Corrupt JPEG"); - m = stbi__get_marker(j); - } - else - { - if (!stbi__process_marker(j, m)) - return 1; - m = stbi__get_marker(j); - } - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) + return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) + return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) + return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; } // static jfif-centered resampling (across block boundaries) -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, int w, int hs); #define stbi__div4(x) ((stbi_uc)((x) >> 2)) -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; } -static stbi_uc *stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i = 0; i < w; ++i) - out[i] = stbi__div4(3 * in_near[i] + in_far[i] + 2); - return out; +static stbi_uc *stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i = 0; i < w; ++i) + out[i] = stbi__div4(3 * in_near[i] + in_far[i] + 2); + return out; } -static stbi_uc *stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; +static stbi_uc *stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; - if (w == 1) - { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } - out[0] = input[0]; - out[1] = stbi__div4(input[0] * 3 + input[1] + 2); - for (i = 1; i < w - 1; ++i) - { - int n = 3 * input[i] + 2; - out[i * 2 + 0] = stbi__div4(n + input[i - 1]); - out[i * 2 + 1] = stbi__div4(n + input[i + 1]); - } - out[i * 2 + 0] = stbi__div4(input[w - 2] * 3 + input[w - 1] + 2); - out[i * 2 + 1] = input[w - 1]; + out[0] = input[0]; + out[1] = stbi__div4(input[0] * 3 + input[1] + 2); + for (i = 1; i < w - 1; ++i) { + int n = 3 * input[i] + 2; + out[i * 2 + 0] = stbi__div4(n + input[i - 1]); + out[i * 2 + 1] = stbi__div4(n + input[i + 1]); + } + out[i * 2 + 0] = stbi__div4(input[w - 2] * 3 + input[w - 1] + 2); + out[i * 2 + 1] = input[w - 1]; - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); - return out; + return out; } #define stbi__div16(x) ((stbi_uc)((x) >> 4)) -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i, t0, t1; - if (w == 1) - { - out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); - return out; - } +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate 2x2 samples for every one in input + int i, t0, t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); + return out; + } - t1 = 3 * in_near[0] + in_far[0]; - out[0] = stbi__div4(t1 + 2); - for (i = 1; i < w; ++i) - { - t0 = t1; - t1 = 3 * in_near[i] + in_far[i]; - out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); - out[i * 2] = stbi__div16(3 * t1 + t0 + 8); - } - out[w * 2 - 1] = stbi__div4(t1 + 2); + t1 = 3 * in_near[0] + in_far[0]; + out[0] = stbi__div4(t1 + 2); + for (i = 1; i < w; ++i) { + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); + } + out[w * 2 - 1] = stbi__div4(t1 + 2); - STBI_NOTUSED(hs); + STBI_NOTUSED(hs); - return out; + return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i = 0, t0, t1; +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // need to generate 2x2 samples for every one in input + int i = 0, t0, t1; - if (w == 1) - { - out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); - return out; - } + if (w == 1) { + out[0] = out[1] = stbi__div4(3 * in_near[0] + in_far[0] + 2); + return out; + } - t1 = 3 * in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w - 1) & ~7); i += 8) - { + t1 = 3 * in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w - 1) & ~7); i += 8) { #if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *)(in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *)(in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *)(in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *)(in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3 * in_near[i + 8] + in_far[i + 8], 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3 * in_near[i + 8] + in_far[i + 8], 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *)(out + i * 2), outv); + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *)(out + i * 2), outv); #elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3 * in_near[i + 8] + in_far[i + 8], nxt0, 7); + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3 * in_near[i + 8] + in_far[i + 8], nxt0, 7); - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i * 2, o); + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i * 2, o); #endif - // "previous" value for next iter - t1 = 3 * in_near[i + 7] + in_far[i + 7]; - } + // "previous" value for next iter + t1 = 3 * in_near[i + 7] + in_far[i + 7]; + } - t0 = t1; - t1 = 3 * in_near[i] + in_far[i]; - out[i * 2] = stbi__div16(3 * t1 + t0 + 8); + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); - for (++i; i < w; ++i) - { - t0 = t1; - t1 = 3 * in_near[i] + in_far[i]; - out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); - out[i * 2] = stbi__div16(3 * t1 + t0 + 8); - } - out[w * 2 - 1] = stbi__div4(t1 + 2); + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3 * in_near[i] + in_far[i]; + out[i * 2 - 1] = stbi__div16(3 * t0 + t1 + 8); + out[i * 2] = stbi__div16(3 * t1 + t0 + 8); + } + out[w * 2 - 1] = stbi__div4(t1 + 2); - STBI_NOTUSED(hs); + STBI_NOTUSED(hs); - return out; + return out; } #endif -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i, j; - STBI_NOTUSED(in_far); - for (i = 0; i < w; ++i) - for (j = 0; j < hs; ++j) - out[i * hs + j] = in_near[i]; - return out; +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { + // resample with nearest-neighbor + int i, j; + STBI_NOTUSED(in_far); + for (i = 0; i < w; ++i) + for (j = 0; j < hs; ++j) + out[i * hs + j] = in_near[i]; + return out; } // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int)((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i = 0; i < count; ++i) - { - int y_fixed = (y[i] << 20) + (1 << 19); // rounding - int r, g, b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr * stbi__float2fixed(1.40200f); - g = y_fixed + (cr * -stbi__float2fixed(0.71414f)) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb * stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned)r > 255) - { - if (r < 0) - r = 0; - else - r = 255; - } - if ((unsigned)g > 255) - { - if (g < 0) - g = 0; - else - g = 255; - } - if ((unsigned)b > 255) - { - if (b < 0) - b = 0; - else - b = 255; - } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { + int i; + for (i = 0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1 << 19); // rounding + int r, g, b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr * stbi__float2fixed(1.40200f); + g = y_fixed + (cr * -stbi__float2fixed(0.71414f)) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb * stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned)r > 255) { + if (r < 0) + r = 0; + else + r = 255; + } + if ((unsigned)g > 255) { + if (g < 0) + g = 0; + else + g = 255; + } + if ((unsigned)b > 255) { + if (b < 0) + b = 0; + else + b = 255; + } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } } #if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { + int i = 0; #ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) - { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16((short)(1.40200f * 4096.0f + 0.5f)); - __m128i cr_const1 = _mm_set1_epi16(-(short)(0.71414f * 4096.0f + 0.5f)); - __m128i cb_const0 = _mm_set1_epi16(-(short)(0.34414f * 4096.0f + 0.5f)); - __m128i cb_const1 = _mm_set1_epi16((short)(1.77200f * 4096.0f + 0.5f)); - __m128i y_bias = _mm_set1_epi8((char)(unsigned char)128); - __m128i xw = _mm_set1_epi16(255); // alpha channel + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16((short)(1.40200f * 4096.0f + 0.5f)); + __m128i cr_const1 = _mm_set1_epi16(-(short)(0.71414f * 4096.0f + 0.5f)); + __m128i cb_const0 = _mm_set1_epi16(-(short)(0.34414f * 4096.0f + 0.5f)); + __m128i cb_const1 = _mm_set1_epi16((short)(1.77200f * 4096.0f + 0.5f)); + __m128i y_bias = _mm_set1_epi8((char)(unsigned char)128); + __m128i xw = _mm_set1_epi16(255); // alpha channel - for (; i + 7 < count; i += 8) - { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *)(y + i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *)(pcr + i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *)(pcb + i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + for (; i + 7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *)(y + i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *)(pcr + i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *)(pcb + i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); - // store - _mm_storeu_si128((__m128i *)(out + 0), o0); - _mm_storeu_si128((__m128i *)(out + 16), o1); - out += 32; - } - } + // store + _mm_storeu_si128((__m128i *)(out + 0), o0); + _mm_storeu_si128((__m128i *)(out + 16), o1); + out += 32; + } + } #endif #ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) - { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16((short)(1.40200f * 4096.0f + 0.5f)); - int16x8_t cr_const1 = vdupq_n_s16(-(short)(0.71414f * 4096.0f + 0.5f)); - int16x8_t cb_const0 = vdupq_n_s16(-(short)(0.34414f * 4096.0f + 0.5f)); - int16x8_t cb_const1 = vdupq_n_s16((short)(1.77200f * 4096.0f + 0.5f)); + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16((short)(1.40200f * 4096.0f + 0.5f)); + int16x8_t cr_const1 = vdupq_n_s16(-(short)(0.71414f * 4096.0f + 0.5f)); + int16x8_t cb_const0 = vdupq_n_s16(-(short)(0.34414f * 4096.0f + 0.5f)); + int16x8_t cb_const1 = vdupq_n_s16((short)(1.77200f * 4096.0f + 0.5f)); - for (; i + 7 < count; i += 8) - { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + for (; i + 7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8 * 4; - } - } + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8 * 4; + } + } #endif - for (; i < count; ++i) - { - int y_fixed = (y[i] << 20) + (1 << 19); // rounding - int r, g, b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr * stbi__float2fixed(1.40200f); - g = y_fixed + cr * -stbi__float2fixed(0.71414f) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb * stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned)r > 255) - { - if (r < 0) - r = 0; - else - r = 255; - } - if ((unsigned)g > 255) - { - if (g < 0) - g = 0; - else - g = 255; - } - if ((unsigned)b > 255) - { - if (b < 0) - b = 0; - else - b = 255; - } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1 << 19); // rounding + int r, g, b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr * stbi__float2fixed(1.40200f); + g = y_fixed + cr * -stbi__float2fixed(0.71414f) + ((cb * -stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb * stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned)r > 255) { + if (r < 0) + r = 0; + else + r = 255; + } + if ((unsigned)g > 255) { + if (g < 0) + g = 0; + else + g = 255; + } + if ((unsigned)b > 255) { + if (b < 0) + b = 0; + else + b = 255; + } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } } #endif // set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; +static void stbi__setup_jpeg(stbi__jpeg *j) { + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 - if (stbi__sse2_available()) - { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } #endif #ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); +static void stbi__cleanup_jpeg(stbi__jpeg *j) { + stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct { - resample_row_func resample; - stbi_uc *line0, *line1; - int hs, vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on + resample_row_func resample; + stbi_uc *line0, *line1; + int hs, vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on } stbi__resample; // fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x * y + 128; - return (stbi_uc)((t + (t >> 8)) >> 8); +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) { + unsigned int t = x * y + 128; + return (stbi_uc)((t + (t >> 8)) >> 8); } -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe - // validate req_comp - if (req_comp < 0 || req_comp > 4) - return stbi__errpuc("bad req_comp", "Internal error"); + // validate req_comp + if (req_comp < 0 || req_comp > 4) + return stbi__errpuc("bad req_comp", "Internal error"); - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) - { - stbi__cleanup_jpeg(z); - return NULL; - } + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { + stbi__cleanup_jpeg(z); + return NULL; + } - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 - : 1; + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 + : 1; - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; - // nothing to do if no components requested; check this now to avoid - // accessing uninitialized coutput[0] later - if (decode_n <= 0) - { - stbi__cleanup_jpeg(z); - return NULL; - } + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { + stbi__cleanup_jpeg(z); + return NULL; + } - // resample and color-convert - { - int k; - unsigned int i, j; - stbi_uc *output; - stbi_uc *coutput[4] = {NULL, NULL, NULL, NULL}; + // resample and color-convert + { + int k; + unsigned int i, j; + stbi_uc *output; + stbi_uc *coutput[4] = {NULL, NULL, NULL, NULL}; - stbi__resample res_comp[4]; + stbi__resample res_comp[4]; - for (k = 0; k < decode_n; ++k) - { - stbi__resample *r = &res_comp[k]; + for (k = 0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *)stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) - { + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *)stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { + stbi__cleanup_jpeg(z); + return stbi__errpuc("outofmem", "Out of memory"); + } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs - 1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) + r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) + r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) + r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) + r->resample = z->resample_row_hv_2_kernel; + else + r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *)stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); - } + } - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs - 1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) - r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) - r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) - r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) - r->resample = z->resample_row_hv_2_kernel; - else - r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *)stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) - { - stbi__cleanup_jpeg(z); - return stbi__errpuc("outofmem", "Out of memory"); - } - - // now go ahead and resample - for (j = 0; j < z->s->img_y; ++j) - { - stbi_uc *out = output + n * z->s->img_x * j; - for (k = 0; k < decode_n; ++k) - { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) - { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; + // now go ahead and resample + for (j = 0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k = 0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, + r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } } - } - if (n >= 3) - { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) - { - if (is_rgb) - { - for (i = 0; i < z->s->img_x; ++i) - { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } - else - { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i = 0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i = 0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i = 0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i = 0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i = 0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i = 0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i = 0; i < z->s->img_x; ++i) + out[i] = y[i]; + else + for (i = 0; i < z->s->img_x; ++i) { + *out++ = y[i]; + *out++ = 255; + } + } } - else if (z->s->img_n == 4) - { - if (z->app14_color_transform == 0) - { // CMYK - for (i = 0; i < z->s->img_x; ++i) - { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } - else if (z->app14_color_transform == 2) - { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i = 0; i < z->s->img_x; ++i) - { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } - else - { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } - else - for (i = 0; i < z->s->img_x; ++i) - { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } - else - { - if (is_rgb) - { - if (n == 1) - for (i = 0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else - { - for (i = 0; i < z->s->img_x; ++i, out += 2) - { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } - else if (z->s->img_n == 4 && z->app14_color_transform == 0) - { - for (i = 0; i < z->s->img_x; ++i) - { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } - else if (z->s->img_n == 4 && z->app14_color_transform == 2) - { - for (i = 0; i < z->s->img_x; ++i) - { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } - else - { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i = 0; i < z->s->img_x; ++i) - out[i] = y[i]; - else - for (i = 0; i < z->s->img_x; ++i) - { - *out++ = y[i]; - *out++ = 255; - } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) - *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) + *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } } -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char *result; - stbi__jpeg *j = (stbi__jpeg *)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) - return stbi__errpuc("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x, y, comp, req_comp); - STBI_FREE(j); - return result; +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + unsigned char *result; + stbi__jpeg *j = (stbi__jpeg *)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) + return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x, y, comp, req_comp); + STBI_FREE(j); + return result; } -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg *j = (stbi__jpeg *)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) - return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; +static int stbi__jpeg_test(stbi__context *s) { + int r; + stbi__jpeg *j = (stbi__jpeg *)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) + return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; } -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) - { - stbi__rewind(j->s); - return 0; - } - if (x) - *x = j->s->img_x; - if (y) - *y = j->s->img_y; - if (comp) - *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind(j->s); + return 0; + } + if (x) + *x = j->s->img_x; + if (y) + *y = j->s->img_y; + if (comp) + *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; } -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg *j = (stbi__jpeg *)(stbi__malloc(sizeof(stbi__jpeg))); - if (!j) - return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { + int result; + stbi__jpeg *j = (stbi__jpeg *)(stbi__malloc(sizeof(stbi__jpeg))); + if (!j) + return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; } #endif @@ -4663,90 +4301,82 @@ static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) -#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[STBI__ZNSYMS]; - stbi__uint16 value[STBI__ZNSYMS]; + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; } stbi__zhuffman; -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; +stbi_inline static int stbi__bitreverse16(int n) { + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; } -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16 - bits); +stbi_inline static int stbi__bit_reverse(int v, int bits) { + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16 - bits); } -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i, k = 0; - int code, next_code[16], sizes[17]; +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { + int i, k = 0; + int code, next_code[16], sizes[17]; - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i = 0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i = 1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i = 1; i < 16; ++i) - { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16)code; - z->firstsymbol[i] = (stbi__uint16)k; - code = (code + sizes[i]); - if (sizes[i]) - if (code - 1 >= (1 << i)) - return stbi__err("bad codelengths", "Corrupt PNG"); - z->maxcode[i] = code << (16 - i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i = 0; i < num; ++i) - { - int s = sizelist[i]; - if (s) - { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16)((s << 9) | i); - z->size[c] = (stbi_uc)s; - z->value[c] = (stbi__uint16)i; - if (s <= STBI__ZFAST_BITS) - { - int j = stbi__bit_reverse(next_code[s], s); - while (j < (1 << STBI__ZFAST_BITS)) - { - z->fast[j] = fastv; - j += (1 << s); + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i = 0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i = 1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i = 1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16)code; + z->firstsymbol[i] = (stbi__uint16)k; + code = (code + sizes[i]); + if (sizes[i]) + if (code - 1 >= (1 << i)) + return stbi__err("bad codelengths", "Corrupt PNG"); + z->maxcode[i] = code << (16 - i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i = 0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16)((s << 9) | i); + z->size[c] = (stbi_uc)s; + z->value[c] = (stbi__uint16)i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s], s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } } - } - ++next_code[s]; - } - } - return 1; + ++next_code[s]; + } + } + return 1; } // zlib-from-memory implementation for PNG reading @@ -4757,364 +4387,308 @@ static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int typedef struct { - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - int hit_zeof_once; - stbi__uint32 code_buffer; + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; - stbi__zhuffman z_length, z_distance; + stbi__zhuffman z_length, z_distance; } stbi__zbuf; -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); +stbi_inline static int stbi__zeof(stbi__zbuf *z) { + return (z->zbuffer >= z->zbuffer_end); } -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { + return stbi__zeof(z) ? 0 : *z->zbuffer++; } -static void stbi__fill_bits(stbi__zbuf *z) -{ - do - { - if (z->code_buffer >= (1U << z->num_bits)) - { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int)stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); +static void stbi__fill_bits(stbi__zbuf *z) { + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int)stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); } -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) - stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { + unsigned int k; + if (z->num_bits < n) + stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; } -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b, s, k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s = STBI__ZFAST_BITS + 1;; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) - return -1; // invalid code! - // code size is s, so: - b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= STBI__ZNSYMS) - return -1; // some data was corrupt somewhere! - if (z->size[b] != s) - return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { + int b, s, k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s = STBI__ZFAST_BITS + 1;; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) + return -1; // invalid code! + // code size is s, so: + b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) + return -1; // some data was corrupt somewhere! + if (z->size[b] != s) + return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; } -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b, s; - if (a->num_bits < 16) - { - if (stbi__zeof(a)) - { - if (!a->hit_zeof_once) - { - // This is the first time we hit eof, insert 16 extra padding btis - // to allow us to keep going; if we actually consume any of them - // though, that is invalid data. This is caught later. - a->hit_zeof_once = 1; - a->num_bits += 16; // add 16 implicit zero bits - } - else - { - // We already inserted our extra 16 padding bits and are again - // out, this stream is actually prematurely terminated. - return -1; - } - } - else - { - stbi__fill_bits(a); - } - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) - { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { + int b, s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); } -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) - return stbi__err("output buffer limit", "Corrupt PNG"); - cur = (unsigned int)(z->zout - z->zout_start); - limit = old_limit = (unsigned)(z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned)n) - return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) - { - if (limit > UINT_MAX / 2) - return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) - return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) + return stbi__err("output buffer limit", "Corrupt PNG"); + cur = (unsigned int)(z->zout - z->zout_start); + limit = old_limit = (unsigned)(z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned)n) + return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if (limit > UINT_MAX / 2) + return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) + return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; } static const int stbi__zlength_base[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const int stbi__zlength_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0}; -static const int stbi__zdist_base[32] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; +static const int stbi__zdist_base[32] = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; static const int stbi__zdist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for (;;) - { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) - { - if (z < 0) - return stbi__err("bad huffman code", "Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) - { - if (!stbi__zexpand(a, zout, 1)) - return 0; - zout = a->zout; - } - *zout++ = (char)z; - } - else - { - stbi_uc *p; - int len, dist; - if (z == 256) - { - a->zout = zout; - if (a->hit_zeof_once && a->num_bits < 16) - { - // The first time we hit zeof, we inserted 16 extra zero bits into our bit - // buffer so the decoder can just do its speculative decoding. But if we - // actually consumed any of those bits (which is the case when num_bits < 16), - // the stream actually read past the end so it is malformed. - return stbi__err("unexpected end", "Corrupt PNG"); +static int stbi__parse_huffman_block(stbi__zbuf *a) { + char *zout = a->zout; + for (;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) + return stbi__err("bad huffman code", "Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) + return 0; + zout = a->zout; } - return 1; - } - if (z >= 286) - return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) - len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0 || z >= 30) - return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) - dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) - return stbi__err("bad dist", "Corrupt PNG"); - if (len > a->zout_end - zout) - { - if (!stbi__zexpand(a, zout, len)) - return 0; - zout = a->zout; - } - p = (stbi_uc *)(zout - dist); - if (dist == 1) - { // run of one byte; common in images. - stbi_uc v = *p; - if (len) - { - do - *zout++ = v; - while (--len); + *zout++ = (char)z; + } else { + stbi_uc *p; + int len, dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end", "Corrupt PNG"); + } + return 1; } - } - else - { - if (len) - { - do - *zout++ = *p++; - while (--len); + if (z >= 286) + return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) + len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) + return stbi__err("bad huffman code", "Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) + dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) + return stbi__err("bad dist", "Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) + return 0; + zout = a->zout; } - } - } - } + p = (stbi_uc *)(zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { + do + *zout++ = v; + while (--len); + } + } else { + if (len) { + do + *zout++ = *p++; + while (--len); + } + } + } + } } -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286 + 32 + 137]; // padding for maximum single op - stbi_uc codelength_sizes[19]; - int i, n; +static int stbi__compute_huffman_codes(stbi__zbuf *a) { + static const stbi_uc length_dezigzag[19] = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286 + 32 + 137]; // padding for maximum single op + stbi_uc codelength_sizes[19]; + int i, n; - int hlit = stbi__zreceive(a, 5) + 257; - int hdist = stbi__zreceive(a, 5) + 1; - int hclen = stbi__zreceive(a, 4) + 4; - int ntot = hlit + hdist; + int hlit = stbi__zreceive(a, 5) + 257; + int hdist = stbi__zreceive(a, 5) + 1; + int hclen = stbi__zreceive(a, 4) + 4; + int ntot = hlit + hdist; - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i = 0; i < hclen; ++i) - { - int s = stbi__zreceive(a, 3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc)s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) - return 0; + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i = 0; i < hclen; ++i) { + int s = stbi__zreceive(a, 3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc)s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) + return 0; - n = 0; - while (n < ntot) - { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) - return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc)c; - else - { - stbi_uc fill = 0; - if (c == 16) - { - c = stbi__zreceive(a, 2) + 3; - if (n == 0) - return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n - 1]; - } - else if (c == 17) - { - c = stbi__zreceive(a, 3) + 3; - } - else if (c == 18) - { - c = stbi__zreceive(a, 7) + 11; - } - else - { + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) - return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes + n, fill, c); - n += c; - } - } - if (n != ntot) - return stbi__err("bad codelengths", "Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) - return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) - return 0; - return 1; + if (c < 16) + lencodes[n++] = (stbi_uc)c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a, 2) + 3; + if (n == 0) + return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n - 1]; + } else if (c == 17) { + c = stbi__zreceive(a, 3) + 3; + } else if (c == 18) { + c = stbi__zreceive(a, 7) + 11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) + return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes + n, fill, c); + n += c; + } + } + if (n != ntot) + return stbi__err("bad codelengths", "Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) + return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) + return 0; + return 1; } -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len, nlen, k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) - { - header[k++] = (stbi_uc)(a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) - return stbi__err("zlib corrupt", "Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) - return stbi__err("zlib corrupt", "Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) - return stbi__err("read past buffer", "Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) - return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; +static int stbi__parse_uncompressed_block(stbi__zbuf *a) { + stbi_uc header[4]; + int len, nlen, k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc)(a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) + return stbi__err("zlib corrupt", "Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) + return stbi__err("zlib corrupt", "Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) + return stbi__err("read past buffer", "Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) + return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; } -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) - return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec - if ((cmf * 256 + flg) % 31 != 0) - return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec - if (flg & 32) - return stbi__err("no preset dict", "Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) - return stbi__err("bad compression", "Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; +static int stbi__parse_zlib_header(stbi__zbuf *a) { + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) + return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec + if ((cmf * 256 + flg) % 31 != 0) + return stbi__err("bad zlib header", "Corrupt PNG"); // zlib spec + if (flg & 32) + return stbi__err("no preset dict", "Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) + return stbi__err("bad compression", "Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; } static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = { - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8}; + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8}; static const stbi_uc stbi__zdefault_distance[32] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; @@ -5131,148 +4705,122 @@ Init algorithm: } */ -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) - return 0; - a->num_bits = 0; - a->code_buffer = 0; - a->hit_zeof_once = 0; - do - { - final = stbi__zreceive(a, 1); - type = stbi__zreceive(a, 2); - if (type == 0) - { - if (!stbi__parse_uncompressed_block(a)) +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; - } - else if (type == 3) - { - return 0; - } - else - { - if (type == 1) - { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, STBI__ZNSYMS)) - return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) - return 0; - } - else - { - if (!stbi__compute_huffman_codes(a)) - return 0; - } - if (!stbi__parse_huffman_block(a)) + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a, 1); + type = stbi__zreceive(a, 2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) + return 0; + } else if (type == 3) { return 0; - } - } while (!final); - return 1; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length, stbi__zdefault_length, STBI__ZNSYMS)) + return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) + return 0; + } else { + if (!stbi__compute_huffman_codes(a)) + return 0; + } + if (!stbi__parse_huffman_block(a)) + return 0; + } + } while (!final); + return 1; } -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; - return stbi__parse_zlib(a, parse_header); + return stbi__parse_zlib(a, parse_header); } -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *)stbi__malloc(initial_size); - if (p == NULL) - return NULL; - a.zbuffer = (stbi_uc *)buffer; - a.zbuffer_end = (stbi_uc *)buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) - { - if (outlen) - *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } - else - { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { + stbi__zbuf a; + char *p = (char *)stbi__malloc(initial_size); + if (p == NULL) + return NULL; + a.zbuffer = (stbi_uc *)buffer; + a.zbuffer_end = (stbi_uc *)buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *)stbi__malloc(initial_size); - if (p == NULL) - return NULL; - a.zbuffer = (stbi_uc *)buffer; - a.zbuffer_end = (stbi_uc *)buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) - { - if (outlen) - *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } - else - { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { + stbi__zbuf a; + char *p = (char *)stbi__malloc(initial_size); + if (p == NULL) + return NULL; + a.zbuffer = (stbi_uc *)buffer; + a.zbuffer_end = (stbi_uc *)buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *)ibuffer; - a.zbuffer_end = (stbi_uc *)ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int)(a.zout - a.zout_start); - else - return -1; +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { + stbi__zbuf a; + a.zbuffer = (stbi_uc *)ibuffer; + a.zbuffer_end = (stbi_uc *)ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int)(a.zout - a.zout_start); + else + return -1; } -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *)stbi__malloc(16384); - if (p == NULL) - return NULL; - a.zbuffer = (stbi_uc *)buffer; - a.zbuffer_end = (stbi_uc *)buffer + len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) - { - if (outlen) - *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } - else - { - STBI_FREE(a.zout_start); - return NULL; - } +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { + stbi__zbuf a; + char *p = (char *)stbi__malloc(16384); + if (p == NULL) + return NULL; + a.zbuffer = (stbi_uc *)buffer; + a.zbuffer_end = (stbi_uc *)buffer + len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) + *outlen = (int)(a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } } -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *)ibuffer; - a.zbuffer_end = (stbi_uc *)ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int)(a.zout - a.zout_start); - else - return -1; +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { + stbi__zbuf a; + a.zbuffer = (stbi_uc *)ibuffer; + a.zbuffer_end = (stbi_uc *)ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int)(a.zout - a.zout_start); + else + return -1; } #endif @@ -5289,44 +4837,41 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char #ifndef STBI_NO_PNG typedef struct { - stbi__uint32 length; - stbi__uint32 type; + stbi__uint32 length; + stbi__uint32 type; } stbi__pngchunk; -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; } -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - int i; - for (i = 0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) - return stbi__err("bad png sig", "Not a PNG"); - return 1; +static int stbi__check_png_header(stbi__context *s) { + static const stbi_uc png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + int i; + for (i = 0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) + return stbi__err("bad png sig", "Not a PNG"); + return 1; } typedef struct { - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; } stbi__png; -enum -{ - STBI__F_none = 0, - STBI__F_sub = 1, - STBI__F_up = 2, - STBI__F_avg = 3, - STBI__F_paeth = 4, - // synthetic filter used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first +enum { + STBI__F_none = 0, + STBI__F_sub = 1, + STBI__F_up = 2, + STBI__F_avg = 3, + STBI__F_paeth = 4, + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first }; static stbi_uc first_row_filter[5] = @@ -5335,20 +4880,19 @@ static stbi_uc first_row_filter[5] = STBI__F_sub, STBI__F_none, STBI__F_avg_first, - STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub }; -static int stbi__paeth(int a, int b, int c) -{ - // This formulation looks very different from the reference in the PNG spec, but is - // actually equivalent and has favorable data dependencies and admits straightforward - // generation of branch-free code, which helps performance significantly. - int thresh = c * 3 - (a + b); - int lo = a < b ? a : b; - int hi = a < b ? b : a; - int t0 = (hi <= thresh) ? lo : c; - int t1 = (thresh <= lo) ? hi : t0; - return t1; +static int stbi__paeth(int a, int b, int c) { + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c * 3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; } static const stbi_uc stbi__depth_scale_table[9] = {0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01}; @@ -5356,397 +4900,340 @@ static const stbi_uc stbi__depth_scale_table[9] = {0, 0xff, 0x55, 0, 0x11, 0, 0, // adds an extra all-255 alpha channel // dest == src is legal // img_n must be 1 or 3 -static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) -{ - int i; - // must process data backwards since we allow dest==src - if (img_n == 1) - { - for (i = x - 1; i >= 0; --i) - { - dest[i * 2 + 1] = 255; - dest[i * 2 + 0] = src[i]; - } - } - else - { - STBI_ASSERT(img_n == 3); - for (i = x - 1; i >= 0; --i) - { - dest[i * 4 + 3] = 255; - dest[i * 4 + 2] = src[i * 3 + 2]; - dest[i * 4 + 1] = src[i * 3 + 1]; - dest[i * 4 + 0] = src[i * 3 + 0]; - } - } +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) { + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i = x - 1; i >= 0; --i) { + dest[i * 2 + 1] = 255; + dest[i * 2 + 0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = x - 1; i >= 0; --i) { + dest[i * 4 + 3] = 255; + dest[i * 4 + 2] = src[i * 3 + 2]; + dest[i * 4 + 1] = src[i * 3 + 1]; + dest[i * 4 + 0] = src[i * 3 + 0]; + } + } } // create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16 ? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i, j, stride = x * out_n * bytes; - stbi__uint32 img_len, img_width_bytes; - stbi_uc *filter_buf; - int all_ok = 1; - int k; - int img_n = s->img_n; // copy it into a local for later +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i, j, stride = x * out_n * bytes; + stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; + int k; + int img_n = s->img_n; // copy it into a local for later - int output_bytes = out_n * bytes; - int filter_bytes = img_n * bytes; - int width = x; + int output_bytes = out_n * bytes; + int filter_bytes = img_n * bytes; + int width = x; - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); - a->out = (stbi_uc *)stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) - return stbi__err("outofmem", "Out of memory"); + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); + a->out = (stbi_uc *)stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) + return stbi__err("outofmem", "Out of memory"); - // note: error exits here don't need to clean up a->out individually, - // stbi__do_png always does on error. - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) - return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) - return stbi__err("too large", "Corrupt PNG"); - img_len = (img_width_bytes + 1) * y; + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) + return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) + return stbi__err("too large", "Corrupt PNG"); + img_len = (img_width_bytes + 1) * y; - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) - return stbi__err("not enough pixels", "Corrupt PNG"); + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) + return stbi__err("not enough pixels", "Corrupt PNG"); - // Allocate two scan lines worth of filter workspace buffer. - filter_buf = (stbi_uc *)stbi__malloc_mad2(img_width_bytes, 2, 0); - if (!filter_buf) - return stbi__err("outofmem", "Out of memory"); + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *)stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) + return stbi__err("outofmem", "Out of memory"); - // Filtering for low-bit-depth images - if (depth < 8) - { - filter_bytes = 1; - width = img_width_bytes; - } + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } - for (j = 0; j < y; ++j) - { - // cur/prior filter buffers alternate - stbi_uc *cur = filter_buf + (j & 1) * img_width_bytes; - stbi_uc *prior = filter_buf + (~j & 1) * img_width_bytes; - stbi_uc *dest = a->out + stride * j; - int nk = width * filter_bytes; - int filter = *raw++; + for (j = 0; j < y; ++j) { + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1) * img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1) * img_width_bytes; + stbi_uc *dest = a->out + stride * j; + int nk = width * filter_bytes; + int filter = *raw++; - // check filter type - if (filter > 4) - { - all_ok = stbi__err("invalid filter", "Corrupt PNG"); - break; - } + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter", "Corrupt PNG"); + break; + } - // if first row, use special filter that doesn't sample previous row - if (j == 0) - filter = first_row_filter[filter]; + // if first row, use special filter that doesn't sample previous row + if (j == 0) + filter = first_row_filter[filter]; - // perform actual filtering - switch (filter) - { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - case STBI__F_sub: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); - break; - case STBI__F_up: - for (k = 0; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - break; - case STBI__F_avg: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); - break; - case STBI__F_paeth: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); - break; - case STBI__F_avg_first: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); - break; - } + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); + break; + } - raw += nk; + raw += nk; - // expand decoded bits in cur to dest, also adding an extra alpha channel if desired - if (depth < 8) - { - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - stbi_uc *in = cur; - stbi_uc *out = dest; - stbi_uc inb = 0; - stbi__uint32 nsmp = x * img_n; + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x * img_n; - // expand bits to bytes first - if (depth == 4) - { - for (i = 0; i < nsmp; ++i) - { - if ((i & 1) == 0) - inb = *in++; - *out++ = scale * (inb >> 4); - inb <<= 4; + // expand bits to bytes first + if (depth == 4) { + for (i = 0; i < nsmp; ++i) { + if ((i & 1) == 0) + inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; + } + } else if (depth == 2) { + for (i = 0; i < nsmp; ++i) { + if ((i & 3) == 0) + inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; + } + } else { + STBI_ASSERT(depth == 1); + for (i = 0; i < nsmp; ++i) { + if ((i & 7) == 0) + inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; + } } - } - else if (depth == 2) - { - for (i = 0; i < nsmp; ++i) - { - if ((i & 3) == 0) - inb = *in++; - *out++ = scale * (inb >> 6); - inb <<= 2; - } - } - else - { - STBI_ASSERT(depth == 1); - for (i = 0; i < nsmp; ++i) - { - if ((i & 7) == 0) - inb = *in++; - *out++ = scale * (inb >> 7); - inb <<= 1; - } - } - // insert alpha=255 values if desired - if (img_n != out_n) - stbi__create_png_alpha_expand8(dest, dest, x, img_n); - } - else if (depth == 8) - { - if (img_n == out_n) - memcpy(dest, cur, x * img_n); - else - stbi__create_png_alpha_expand8(dest, cur, x, img_n); - } - else if (depth == 16) - { - // convert the image data from big-endian to platform-native - stbi__uint16 *dest16 = (stbi__uint16 *)dest; - stbi__uint32 nsmp = x * img_n; - - if (img_n == out_n) - { - for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) - *dest16 = (cur[0] << 8) | cur[1]; - } - else - { - STBI_ASSERT(img_n + 1 == out_n); - if (img_n == 1) - { - for (i = 0; i < x; ++i, dest16 += 2, cur += 2) - { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = 0xffff; - } - } + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x * img_n); else - { - STBI_ASSERT(img_n == 3); - for (i = 0; i < x; ++i, dest16 += 4, cur += 6) - { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = (cur[2] << 8) | cur[3]; - dest16[2] = (cur[4] << 8) | cur[5]; - dest16[3] = 0xffff; - } + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16 *)dest; + stbi__uint32 nsmp = x * img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n + 1 == out_n); + if (img_n == 1) { + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; + } + } } - } - } - } + } + } - STBI_FREE(filter_buf); - if (!all_ok) - return 0; + STBI_FREE(filter_buf); + if (!all_ok) + return 0; - return 1; + return 1; } -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - // de-interlacing - final = (stbi_uc *)stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - if (!final) - return stbi__err("outofmem", "Out of memory"); - for (p = 0; p < 7; ++p) - { - int xorig[] = {0, 4, 0, 2, 0, 1, 0}; - int yorig[] = {0, 0, 4, 0, 2, 0, 1}; - int xspc[] = {8, 8, 4, 4, 2, 2, 1}; - int yspc[] = {8, 8, 8, 4, 4, 2, 2}; - int i, j, x, y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; - if (x && y) - { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) - { - STBI_FREE(final); - return 0; - } - for (j = 0; j < y; ++j) - { - for (i = 0; i < x; ++i) - { - int out_y = j * yspc[p] + yorig[p]; - int out_x = i * xspc[p] + xorig[p]; - memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, - a->out + (j * x + i) * out_bytes, out_bytes); + // de-interlacing + final = (stbi_uc *)stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) + return stbi__err("outofmem", "Out of memory"); + for (p = 0; p < 7; ++p) { + int xorig[] = {0, 4, 0, 2, 0, 1, 0}; + int yorig[] = {0, 0, 4, 0, 2, 0, 1}; + int xspc[] = {8, 8, 4, 4, 2, 2, 1}; + int yspc[] = {8, 8, 8, 4, 4, 2, 2}; + int i, j, x, y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; + for (j = 0; j < y; ++j) { + for (i = 0; i < x; ++i) { + int out_y = j * yspc[p] + yorig[p]; + int out_x = i * xspc[p] + xorig[p]; + memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, + a->out + (j * x + i) * out_bytes, + out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; - return 1; + return 1; } -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); - if (out_n == 2) - { - for (i = 0; i < pixel_count; ++i) - { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } - else - { - for (i = 0; i < pixel_count; ++i) - { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; } -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16 *)z->out; +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16 *)z->out; - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); - if (out_n == 2) - { - for (i = 0; i < pixel_count; ++i) - { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } - else - { - for (i = 0; i < pixel_count; ++i) - { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; } -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; - p = (stbi_uc *)stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) - return stbi__err("outofmem", "Out of memory"); + p = (stbi_uc *)stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) + return stbi__err("outofmem", "Out of memory"); - // between here and free(out) below, exitting would leak - temp_out = p; + // between here and free(out) below, exitting would leak + temp_out = p; - if (pal_img_n == 3) - { - for (i = 0; i < pixel_count; ++i) - { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p += 3; - } - } - else - { - for (i = 0; i < pixel_count; ++i) - { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p[3] = palette[n + 3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; + if (pal_img_n == 3) { + for (i = 0; i < pixel_count; ++i) { + int n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p += 3; + } + } else { + for (i = 0; i < pixel_count; ++i) { + int n = orig[i] * 4; + p[0] = palette[n]; + p[1] = palette[n + 1]; + p[2] = palette[n + 2]; + p[3] = palette[n + 3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; - STBI_NOTUSED(len); + STBI_NOTUSED(len); - return 1; + return 1; } static int stbi__unpremultiply_on_load_global = 0; static int stbi__de_iphone_flag_global = 0; -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; } -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_global = flag_true_if_should_convert; +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { + stbi__de_iphone_flag_global = flag_true_if_should_convert; } #ifndef STBI_THREAD_LOCAL @@ -5756,974 +5243,846 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; -STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; - stbi__unpremultiply_on_load_set = 1; +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) { + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; } -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_local = flag_true_if_should_convert; - stbi__de_iphone_flag_set = 1; +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) { + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; } -#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ - ? stbi__unpremultiply_on_load_local \ - : stbi__unpremultiply_on_load_global) -#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ - ? stbi__de_iphone_flag_local \ - : stbi__de_iphone_flag_global) -#endif // STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ + ? stbi__unpremultiply_on_load_local \ + : stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ + ? stbi__de_iphone_flag_local \ + : stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; +static void stbi__de_iphone(stbi__png *z) { + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; - if (s->img_out_n == 3) - { // convert bgr to rgb - for (i = 0; i < pixel_count; ++i) - { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } - else - { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) - { - // convert bgr to rgb and unpremultiply - for (i = 0; i < pixel_count; ++i) - { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) - { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = (t * 255 + half) / a; - } - else - { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } - else - { - // convert bgr to rgb - for (i = 0; i < pixel_count; ++i) - { + if (s->img_out_n == 3) { // convert bgr to rgb + for (i = 0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; - p += 4; - } - } - } + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i = 0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = (t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i = 0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } } #define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d)) -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n = 0; - stbi_uc has_trans = 0, tc[3] = {0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; - int first = 1, k, interlace = 0, color = 0, is_iphone = 0; - stbi__context *s = z->s; +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { + stbi_uc palette[1024], pal_img_n = 0; + stbi_uc has_trans = 0, tc[3] = {0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; + int first = 1, k, interlace = 0, color = 0, is_iphone = 0; + stbi__context *s = z->s; - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; - if (!stbi__check_png_header(s)) - return 0; + if (!stbi__check_png_header(s)) + return 0; - if (scan == STBI__SCAN_type) - return 1; + if (scan == STBI__SCAN_type) + return 1; - for (;;) - { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) - { - case STBI__PNG_TYPE('C', 'g', 'B', 'I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I', 'H', 'D', 'R'): - { - int comp, filter; - if (!first) - return stbi__err("multiple IHDR", "Corrupt PNG"); - first = 0; - if (c.length != 13) - return stbi__err("bad IHDR len", "Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); - z->depth = stbi__get8(s); - if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) - return stbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); - if (color > 6) - return stbi__err("bad ctype", "Corrupt PNG"); - if (color == 3 && z->depth == 16) - return stbi__err("bad ctype", "Corrupt PNG"); - if (color == 3) - pal_img_n = 3; - else if (color & 1) - return stbi__err("bad ctype", "Corrupt PNG"); - comp = stbi__get8(s); - if (comp) - return stbi__err("bad comp method", "Corrupt PNG"); - filter = stbi__get8(s); - if (filter) - return stbi__err("bad filter method", "Corrupt PNG"); - interlace = stbi__get8(s); - if (interlace > 1) - return stbi__err("bad interlace method", "Corrupt PNG"); - if (!s->img_x || !s->img_y) - return stbi__err("0-pixel image", "Corrupt PNG"); - if (!pal_img_n) - { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) - return stbi__err("too large", "Image too large to decode"); - } - else - { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) - return stbi__err("too large", "Corrupt PNG"); - } - // even with SCAN_header, have to scan to see if we have a tRNS - break; - } - - case STBI__PNG_TYPE('P', 'L', 'T', 'E'): - { - if (first) - return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256 * 3) - return stbi__err("invalid PLTE", "Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) - return stbi__err("invalid PLTE", "Corrupt PNG"); - for (i = 0; i < pal_len; ++i) - { - palette[i * 4 + 0] = stbi__get8(s); - palette[i * 4 + 1] = stbi__get8(s); - palette[i * 4 + 2] = stbi__get8(s); - palette[i * 4 + 3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t', 'R', 'N', 'S'): - { - if (first) - return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) - return stbi__err("tRNS after IDAT", "Corrupt PNG"); - if (pal_img_n) - { - if (scan == STBI__SCAN_header) - { - s->img_n = 4; - return 1; + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C', 'g', 'B', 'I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { + int comp, filter; + if (!first) + return stbi__err("multiple IHDR", "Corrupt PNG"); + first = 0; + if (c.length != 13) + return stbi__err("bad IHDR len", "Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + z->depth = stbi__get8(s); + if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) + return stbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); + if (color > 6) + return stbi__err("bad ctype", "Corrupt PNG"); + if (color == 3 && z->depth == 16) + return stbi__err("bad ctype", "Corrupt PNG"); + if (color == 3) + pal_img_n = 3; + else if (color & 1) + return stbi__err("bad ctype", "Corrupt PNG"); + comp = stbi__get8(s); + if (comp) + return stbi__err("bad comp method", "Corrupt PNG"); + filter = stbi__get8(s); + if (filter) + return stbi__err("bad filter method", "Corrupt PNG"); + interlace = stbi__get8(s); + if (interlace > 1) + return stbi__err("bad interlace method", "Corrupt PNG"); + if (!s->img_x || !s->img_y) + return stbi__err("0-pixel image", "Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) + return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) + return stbi__err("too large", "Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; } - if (pal_len == 0) - return stbi__err("tRNS before PLTE", "Corrupt PNG"); - if (c.length > pal_len) - return stbi__err("bad tRNS len", "Corrupt PNG"); - pal_img_n = 4; - for (i = 0; i < c.length; ++i) - palette[i * 4 + 3] = stbi__get8(s); - } - else - { - if (!(s->img_n & 1)) - return stbi__err("tRNS with alpha", "Corrupt PNG"); - if (c.length != (stbi__uint32)s->img_n * 2) - return stbi__err("bad tRNS len", "Corrupt PNG"); - has_trans = 1; - // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. - if (scan == STBI__SCAN_header) - { - ++s->img_n; - return 1; - } - if (z->depth == 16) - { - for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning - tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } - else - { - for (k = 0; k < s->img_n && k < 3; ++k) - tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - case STBI__PNG_TYPE('I', 'D', 'A', 'T'): - { - if (first) - return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) - return stbi__err("no PLTE", "Corrupt PNG"); - if (scan == STBI__SCAN_header) - { - // header scan definitely stops at first IDAT - if (pal_img_n) - s->img_n = pal_img_n; - return 1; - } - if (c.length > (1u << 30)) - return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); - if ((int)(ioff + c.length) < (int)ioff) - return 0; - if (ioff + c.length > idata_limit) - { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) - idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); - if (p == NULL) - return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata + ioff, c.length)) - return stbi__err("outofdata", "Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I', 'E', 'N', 'D'): - { - stbi__uint32 raw_len, bpl; - if (first) - return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) - return 1; - if (z->idata == NULL) - return stbi__err("no IDAT", "Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *)stbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, !is_iphone); - if (z->expanded == NULL) - return 0; // zlib should set error - STBI_FREE(z->idata); - z->idata = NULL; - if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n + 1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) - return 0; - if (has_trans) - { - if (z->depth == 16) - { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) - return 0; + case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { + if (first) + return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256 * 3) + return stbi__err("invalid PLTE", "Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) + return stbi__err("invalid PLTE", "Corrupt PNG"); + for (i = 0; i < pal_len; ++i) { + palette[i * 4 + 0] = stbi__get8(s); + palette[i * 4 + 1] = stbi__get8(s); + palette[i * 4 + 2] = stbi__get8(s); + palette[i * 4 + 3] = 255; + } + break; } - else - { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) - return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) - { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) - s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } - else if (has_trans) - { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); - z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - default: - // if critical, fail - if (first) - return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) - { + case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { + if (first) + return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) + return stbi__err("tRNS after IDAT", "Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { + s->img_n = 4; + return 1; + } + if (pal_len == 0) + return stbi__err("tRNS before PLTE", "Corrupt PNG"); + if (c.length > pal_len) + return stbi__err("bad tRNS len", "Corrupt PNG"); + pal_img_n = 4; + for (i = 0; i < c.length; ++i) + palette[i * 4 + 3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) + return stbi__err("tRNS with alpha", "Corrupt PNG"); + if (c.length != (stbi__uint32)s->img_n * 2) + return stbi__err("bad tRNS len", "Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { + ++s->img_n; + return 1; + } + if (z->depth == 16) { + for (k = 0; k < s->img_n && k < 3; ++k) // extra loop test to suppress false GCC warning + tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n && k < 3; ++k) + tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { + if (first) + return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) + return stbi__err("no PLTE", "Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) + return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) + return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) + idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); + if (p == NULL) + return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata + ioff, c.length)) + return stbi__err("outofdata", "Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { + stbi__uint32 raw_len, bpl; + if (first) + return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) + return 1; + if (z->idata == NULL) + return stbi__err("no IDAT", "Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *)stbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, !is_iphone); + if (z->expanded == NULL) + return 0; // zlib should set error + STBI_FREE(z->idata); + z->idata = NULL; + if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n + 1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) + return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) + return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) + return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) + s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); + z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) + return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } } -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result = NULL; - if (req_comp < 0 || req_comp > 4) - return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) - { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) - { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) - return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) - *n = p->s->img_n; - } - STBI_FREE(p->out); - p->out = NULL; - STBI_FREE(p->expanded); - p->expanded = NULL; - STBI_FREE(p->idata); - p->idata = NULL; +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { + void *result = NULL; + if (req_comp < 0 || req_comp > 4) + return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) + return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) + *n = p->s->img_n; + } + STBI_FREE(p->out); + p->out = NULL; + STBI_FREE(p->expanded); + p->expanded = NULL; + STBI_FREE(p->idata); + p->idata = NULL; - return result; + return result; } -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x, y, comp, req_comp, ri); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi__png p; + p.s = s; + return stbi__do_png(&p, x, y, comp, req_comp, ri); } -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; +static int stbi__png_test(stbi__context *s) { + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; } -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) - { - stbi__rewind(p->s); - return 0; - } - if (x) - *x = p->s->img_x; - if (y) - *y = p->s->img_y; - if (comp) - *comp = p->s->img_n; - return 1; +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind(p->s); + return 0; + } + if (x) + *x = p->s->img_x; + if (y) + *y = p->s->img_y; + if (comp) + *comp = p->s->img_n; + return 1; } -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); } -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) - { - stbi__rewind(p.s); - return 0; - } - return 1; +static int stbi__png_is16(stbi__context *s) { + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') - return 0; - if (stbi__get8(s) != 'M') - return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; +static int stbi__bmp_test_raw(stbi__context *s) { + int r; + int sz; + if (stbi__get8(s) != 'B') + return 0; + if (stbi__get8(s) != 'M') + return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; } -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; +static int stbi__bmp_test(stbi__context *s) { + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; } // returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n = 0; - if (z == 0) - return -1; - if (z >= 0x10000) - { - n += 16; - z >>= 16; - } - if (z >= 0x00100) - { - n += 8; - z >>= 8; - } - if (z >= 0x00010) - { - n += 4; - z >>= 4; - } - if (z >= 0x00004) - { - n += 2; - z >>= 2; - } - if (z >= 0x00002) - { - n += 1; /* >>= 1;*/ - } - return n; +static int stbi__high_bit(unsigned int z) { + int n = 0; + if (z == 0) + return -1; + if (z >= 0x10000) { + n += 16; + z >>= 16; + } + if (z >= 0x00100) { + n += 8; + z >>= 8; + } + if (z >= 0x00010) { + n += 4; + z >>= 4; + } + if (z >= 0x00004) { + n += 2; + z >>= 2; + } + if (z >= 0x00002) { + n += 1; /* >>= 1;*/ + } + return n; } -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; +static int stbi__bitcount(unsigned int a) { + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; } // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff /*0b11111111*/, - 0x55 /*0b01010101*/, - 0x49 /*0b01001001*/, - 0x11 /*0b00010001*/, - 0x21 /*0b00100001*/, - 0x41 /*0b01000001*/, - 0x81 /*0b10000001*/, - 0x01 /*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, - 0, - 0, - 1, - 0, - 2, - 4, - 6, - 0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8 - bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int)((unsigned)v * mul_table[bits]) >> shift_table[bits]; +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { + static unsigned int mul_table[9] = { + 0, + 0xff /*0b11111111*/, + 0x55 /*0b01010101*/, + 0x49 /*0b01001001*/, + 0x11 /*0b00010001*/, + 0x21 /*0b00100001*/, + 0x41 /*0b01000001*/, + 0x81 /*0b10000001*/, + 0x01 /*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, + 0, + 0, + 1, + 0, + 2, + 4, + 6, + 0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8 - bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int)((unsigned)v * mul_table[bits]) >> shift_table[bits]; } typedef struct { - int bpp, offset, hsz; - unsigned int mr, mg, mb, ma, all_a; - int extra_read; + int bpp, offset, hsz; + unsigned int mr, mg, mb, ma, all_a; + int extra_read; } stbi__bmp_data; -static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) -{ - // BI_BITFIELDS specifies masks explicitly, don't override - if (compress == 3) - return 1; +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) { + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; - if (compress == 0) - { - if (info->bpp == 16) - { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - else if (info->bpp == 32) - { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } - else - { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; - } - return 0; // error + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error } -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') - return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') + return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; - if (info->offset < 0) - return stbi__errpuc("bad BMP", "bad BMP"); + if (info->offset < 0) + return stbi__errpuc("bad BMP", "bad BMP"); - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) - return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) - { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } - else - { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) - return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) - { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) - return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) - return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) - return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) - { - if (hsz == 56) - { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) - { - if (compress == 0) - { - stbi__bmp_set_mask_defaults(info, compress); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) + return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) + return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) + return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) + return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) + return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); } - else if (compress == 3) - { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) - { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); } - else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } - else - { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i = 0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) - { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *)1; + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i = 0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *)1; } -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr = 0, mg = 0, mb = 0, ma = 0, all_a; - stbi_uc pal[256][4]; - int psize = 0, i, j, width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *out; + unsigned int mr = 0, mg = 0, mb = 0, ma = 0, all_a; + stbi_uc pal[256][4]; + int psize = 0, i, j, width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set - flip_vertically = ((int)s->img_y) > 0; - s->img_y = abs((int)s->img_y); + flip_vertically = ((int)s->img_y) > 0; + s->img_y = abs((int)s->img_y); - if (s->img_y > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_y > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; - if (info.hsz == 12) - { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } - else - { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) - { - // accept some number of extra bytes after the header, but if the offset points either to before - // the header ends or implies a large amount of extra data, reject the file as malformed - int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); - int header_limit = 1024; // max we actually read is below 256 bytes currently. - int extra_data_limit = 256 * 4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. - if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) - { - return stbi__errpuc("bad header", "Corrupt BMP"); - } - // we established that bytes_read_so_far is positive and sensible. - // the first half of this test rejects offsets that are either too small positives, or - // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn - // ensures the number computed in the second half of the test can't overflow. - if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) - { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - else - { - stbi__skip(s, info.offset - bytes_read_so_far); - } - } + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256 * 4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); - out = (stbi_uc *)stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) - return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) - { - int z = 0; - if (psize == 0 || psize > 256) - { - STBI_FREE(out); - return stbi__errpuc("invalid", "Corrupt BMP"); - } - for (i = 0; i < psize; ++i) - { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) - stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) - width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) - width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) - width = s->img_x; - else - { - STBI_FREE(out); - return stbi__errpuc("bad bpp", "Corrupt BMP"); - } - pad = (-width) & 3; - if (info.bpp == 1) - { - for (j = 0; j < (int)s->img_y; ++j) - { - int bit_offset = 7, v = stbi__get8(s); - for (i = 0; i < (int)s->img_x; ++i) - { - int color = (v >> bit_offset) & 0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) - out[z++] = 255; - if (i + 1 == (int)s->img_x) - break; - if ((--bit_offset) < 0) - { - bit_offset = 7; - v = stbi__get8(s); - } + out = (stbi_uc *)stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) + return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z = 0; + if (psize == 0 || psize > 256) { + STBI_FREE(out); + return stbi__errpuc("invalid", "Corrupt BMP"); + } + for (i = 0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) + stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) + width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) + width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) + width = s->img_x; + else { + STBI_FREE(out); + return stbi__errpuc("bad bpp", "Corrupt BMP"); + } + pad = (-width) & 3; + if (info.bpp == 1) { + for (j = 0; j < (int)s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i = 0; i < (int)s->img_x; ++i) { + int color = (v >> bit_offset) & 0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) + out[z++] = 255; + if (i + 1 == (int)s->img_x) + break; + if ((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j = 0; j < (int)s->img_y; ++j) { + for (i = 0; i < (int)s->img_x; i += 2) { + int v = stbi__get8(s), v2 = 0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) + out[z++] = 255; + if (i + 1 == (int)s->img_x) + break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) + out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift = 0, gshift = 0, bshift = 0, ashift = 0, rcount = 0, gcount = 0, bcount = 0, acount = 0; + int z = 0; + int easy = 0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) + width = 3 * s->img_x; + else if (info.bpp == 16) + width = 2 * s->img_x; + else /* bpp = 32 and pad = 0 */ + width = 0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { + STBI_FREE(out); + return stbi__errpuc("bad masks", "Corrupt BMP"); + } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr) - 7; + rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg) - 7; + gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb) - 7; + bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma) - 7; + acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { + STBI_FREE(out); + return stbi__errpuc("bad masks", "Corrupt BMP"); + } + } + for (j = 0; j < (int)s->img_y; ++j) { + if (easy) { + for (i = 0; i < (int)s->img_x; ++i) { + unsigned char a; + out[z + 2] = stbi__get8(s); + out[z + 1] = stbi__get8(s); + out[z + 0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) + out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i = 0; i < (int)s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32)stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) + out[z++] = STBI__BYTECAST(a); + } } stbi__skip(s, pad); - } - } - else - { - for (j = 0; j < (int)s->img_y; ++j) - { - for (i = 0; i < (int)s->img_x; i += 2) - { - int v = stbi__get8(s), v2 = 0; - if (info.bpp == 4) - { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) - out[z++] = 255; - if (i + 1 == (int)s->img_x) - break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) - out[z++] = 255; + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i = 4 * s->img_x * s->img_y - 1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j = 0; j < (int)s->img_y >> 1; ++j) { + stbi_uc *p1 = out + j * s->img_x * target; + stbi_uc *p2 = out + (s->img_y - 1 - j) * s->img_x * target; + for (i = 0; i < (int)s->img_x * target; ++i) { + t = p1[i]; + p1[i] = p2[i]; + p2[i] = t; } - stbi__skip(s, pad); - } - } - } - else - { - int rshift = 0, gshift = 0, bshift = 0, ashift = 0, rcount = 0, gcount = 0, bcount = 0, acount = 0; - int z = 0; - int easy = 0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) - width = 3 * s->img_x; - else if (info.bpp == 16) - width = 2 * s->img_x; - else /* bpp = 32 and pad = 0 */ - width = 0; - pad = (-width) & 3; - if (info.bpp == 24) - { - easy = 1; - } - else if (info.bpp == 32) - { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) - { - if (!mr || !mg || !mb) - { - STBI_FREE(out); - return stbi__errpuc("bad masks", "Corrupt BMP"); - } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr) - 7; - rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg) - 7; - gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb) - 7; - bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma) - 7; - acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) - { - STBI_FREE(out); - return stbi__errpuc("bad masks", "Corrupt BMP"); - } - } - for (j = 0; j < (int)s->img_y; ++j) - { - if (easy) - { - for (i = 0; i < (int)s->img_x; ++i) - { - unsigned char a; - out[z + 2] = stbi__get8(s); - out[z + 1] = stbi__get8(s); - out[z + 0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) - out[z++] = a; - } - } - else - { - int bpp = info.bpp; - for (i = 0; i < (int)s->img_x; ++i) - { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32)stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) - out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } + } + } - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i = 4 * s->img_x * s->img_y - 1; i >= 0; i -= 4) - out[i] = 255; + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) + return out; // stbi__convert_format frees input on failure + } - if (flip_vertically) - { - stbi_uc t; - for (j = 0; j < (int)s->img_y >> 1; ++j) - { - stbi_uc *p1 = out + j * s->img_x * target; - stbi_uc *p2 = out + (s->img_y - 1 - j) * s->img_x * target; - for (i = 0; i < (int)s->img_x * target; ++i) - { - t = p1[i]; - p1[i] = p2[i]; - p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) - { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) - return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) - *comp = s->img_n; - return out; + *x = s->img_x; + *y = s->img_y; + if (comp) + *comp = s->img_n; + return out; } #endif @@ -6731,403 +6090,346 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req // by Jonathan Dummer #ifndef STBI_NO_TGA // returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int *is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) - *is_rgb16 = 0; - switch (bits_per_pixel) - { - case 8: - return STBI_grey; - case 16: - if (is_grey) - return STBI_grey_alpha; - // fallthrough - case 15: - if (is_rgb16) - *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: - return bits_per_pixel / 8; - default: - return 0; - } +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int *is_rgb16) { + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) + *is_rgb16 = 0; + switch (bits_per_pixel) { + case 8: + return STBI_grey; + case 16: + if (is_grey) + return STBI_grey_alpha; + // fallthrough + case 15: + if (is_rgb16) + *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: + return bits_per_pixel / 8; + default: + return 0; + } } -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if (tga_colormap_type > 1) - { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if (tga_colormap_type == 1) - { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) - { - stbi__rewind(s); - return 0; - } - stbi__skip(s, 4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) - { - stbi__rewind(s); - return 0; - } - stbi__skip(s, 4); // skip image x and y origin - tga_colormap_bpp = sz; - } - else - { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ((tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11)) - { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s, 9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if (tga_w < 1) - { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if (tga_h < 1) - { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) - { - if ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) - { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } - else - { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if (!tga_comp) - { - stbi__rewind(s); - return 0; - } - if (x) - *x = tga_w; - if (y) - *y = tga_h; - if (comp) - *comp = tga_comp; - return 1; // seems to have passed everything +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if (tga_colormap_type > 1) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if (tga_colormap_type == 1) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ((tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11)) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s, 9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if (tga_w < 1) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if (tga_h < 1) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if ((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if (!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) + *x = tga_w; + if (y) + *y = tga_h; + if (comp) + *comp = tga_comp; + return 1; // seems to have passed everything } -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if (tga_color_type > 1) - goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if (tga_color_type == 1) - { // colormapped (paletted) image - if (sz != 1 && sz != 9) - goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s, 4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) - goto errorEnd; - stbi__skip(s, 4); // skip image x and y origin - } - else - { // "normal" image w/o colormap - if ((sz != 2) && (sz != 3) && (sz != 10) && (sz != 11)) - goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s, 9); // skip colormap specification and image x/y origin - } - if (stbi__get16le(s) < 1) - goto errorEnd; // test width - if (stbi__get16le(s) < 1) - goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ((tga_color_type == 1) && (sz != 8) && (sz != 16)) - goto errorEnd; // Para colormapped images, bpp is size of an index - if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) - goto errorEnd; +static int stbi__tga_test(stbi__context *s) { + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if (tga_color_type > 1) + goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if (tga_color_type == 1) { // colormapped (paletted) image + if (sz != 1 && sz != 9) + goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s, 4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) + goto errorEnd; + stbi__skip(s, 4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ((sz != 2) && (sz != 3) && (sz != 10) && (sz != 11)) + goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s, 9); // skip colormap specification and image x/y origin + } + if (stbi__get16le(s) < 1) + goto errorEnd; // test width + if (stbi__get16le(s) < 1) + goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ((tga_color_type == 1) && (sz != 8) && (sz != 16)) + goto errorEnd; // Para colormapped images, bpp is size of an index + if ((sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32)) + goto errorEnd; - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 errorEnd: - stbi__rewind(s); - return res; + stbi__rewind(s); + return res; } // read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc *out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255) / 31); - out[1] = (stbi_uc)((g * 255) / 31); - out[2] = (stbi_uc)((b * 255) / 31); +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc *out) { + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255) / 31); + out[1] = (stbi_uc)((g * 255) / 31); + out[2] = (stbi_uc)((b * 255) / 31); - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16 = 0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16 = 0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO - if (tga_height > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (tga_height > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); - // do a tiny bit of precessing - if (tga_image_type >= 8) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); + // do a tiny bit of precessing + if (tga_image_type >= 8) { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); - // If I'm paletted, then I'll use the number of bits from the palette - if (tga_indexed) - tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + // If I'm paletted, then I'll use the number of bits from the palette + if (tga_indexed) + tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - if (!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + if (!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - // tga info - *x = tga_width; - *y = tga_height; - if (comp) - *comp = tga_comp; + // tga info + *x = tga_width; + *y = tga_height; + if (comp) + *comp = tga_comp; - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); - tga_data = (unsigned char *)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) - return stbi__errpuc("outofmem", "Out of memory"); + tga_data = (unsigned char *)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) + return stbi__errpuc("outofmem", "Out of memory"); - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset); + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset); - if (!tga_indexed && !tga_is_RLE && !tga_rgb16) - { - for (i = 0; i < tga_height; ++i) - { - int row = tga_inverted ? tga_height - i - 1 : i; - stbi_uc *tga_row = tga_data + row * tga_width * tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } - else - { - // do I need to load a palette? - if (tga_indexed) - { - if (tga_palette_len == 0) - { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start); - // load the palette - tga_palette = (unsigned char *)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) - { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) - { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i = 0; i < tga_palette_len; ++i) - { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; + if (!tga_indexed && !tga_is_RLE && !tga_rgb16) { + for (i = 0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height - i - 1 : i; + stbi_uc *tga_row = tga_data + row * tga_width * tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if (tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); } - } - else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) - { - STBI_FREE(tga_data); + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start); + // load the palette + tga_palette = (unsigned char *)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i = 0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i = 0; i < tga_width * tga_height; ++i) { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if (tga_is_RLE) { + if (RLE_count == 0) { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if (!RLE_repeating) { + read_next_pixel = 1; + } + } else { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if (read_next_pixel) { + // load however much data we did have + if (tga_indexed) { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if (pal_idx >= tga_palette_len) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx + j]; + } + } else if (tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i * tga_comp + j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if (tga_inverted) { + for (j = 0; j * 2 < tga_height; ++j) { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if (tga_palette != NULL) { STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i = 0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if (tga_is_RLE) - { - if (RLE_count == 0) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } - else if (!RLE_repeating) - { - read_next_pixel = 1; - } - } - else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if (read_next_pixel) - { - // load however much data we did have - if (tga_indexed) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if (pal_idx >= tga_palette_len) - { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) - { - raw_data[j] = tga_palette[pal_idx + j]; - } - } - else if (tga_rgb16) - { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } - else - { - // read in the data raw - for (j = 0; j < tga_comp; ++j) - { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel + } + } - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i * tga_comp + j] = raw_data[j]; + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) { + unsigned char *tga_pixel = tga_data; + for (i = 0; i < tga_width * tga_height; ++i) { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if (tga_inverted) - { - for (j = 0; j * 2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if (tga_palette != NULL) - { - STBI_FREE(tga_palette); - } - } + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char *tga_pixel = tga_data; - for (i = 0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; } #endif @@ -7135,296 +6437,253 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; +static int stbi__psd_test(stbi__context *s) { + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; } -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { + int count, nleft, len; - count = 0; - while ((nleft = pixelCount - count) > 0) - { - len = stbi__get8(s); - if (len == 128) - { - // No-op. - } - else if (len < 128) - { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) - return 0; // corrupt data - count += len; - while (len) - { - *p = stbi__get8(s); - p += 4; - len--; - } - } - else if (len > 128) - { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) - return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) - { - *p = val; - p += 4; - len--; - } - } - } + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) + return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) + return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } - return 1; + return 1; } -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w, h; - stbi_uc *out; - STBI_NOTUSED(ri); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w, h; + stbi_uc *out; + STBI_NOTUSED(ri); - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - // Skip 6 reserved bytes. - stbi__skip(s, 6); + // Skip 6 reserved bytes. + stbi__skip(s, 6); - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); - if (h > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (h > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s, stbi__get32be(s)); + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s, stbi__get32be(s)); - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s)); + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s)); - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s)); + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s)); - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); - // Create the destination image. + // Create the destination image. - if (!compression && bitdepth == 16 && bpc == 16) - { - out = (stbi_uc *)stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } - else - out = (stbi_uc *)stbi__malloc(4 * w * h); + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *)stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *)stbi__malloc(4 * w * h); - if (!out) - return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w * h; + if (!out) + return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w * h; - // Initialize the data to zero. - // memset( out, 0, pixelCount * 4 ); + // Initialize the data to zero. + // memset( out, 0, pixelCount * 4 ); - // Finally, the image data. - if (compression) - { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2); + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2); - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) - { - stbi_uc *p; + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; - p = out + channel; - if (channel >= channelCount) - { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } - else - { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) - { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } } - } - } - } - else - { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + } + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - // Read the data by channel. - for (channel = 0; channel < 4; channel++) - { - if (channel >= channelCount) - { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) - { - stbi__uint16 *q = ((stbi__uint16 *)out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *)out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out + channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *)out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16)stbi__get16be(s); + } else { + stbi_uc *p = out + channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc)(stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } } - else - { - stbi_uc *p = out + channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } - else - { - if (ri->bits_per_channel == 16) - { // output bpc - stbi__uint16 *q = ((stbi__uint16 *)out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16)stbi__get16be(s); - } - else - { - stbi_uc *p = out + channel; - if (bitdepth == 16) - { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc)(stbi__get16be(s) >> 8); - } - else - { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } + } + } - // remove weird white matte from PSD - if (channelCount >= 4) - { - if (ri->bits_per_channel == 16) - { - for (i = 0; i < w * h; ++i) - { - stbi__uint16 *pixel = (stbi__uint16 *)out + 4 * i; - if (pixel[3] != 0 && pixel[3] != 65535) - { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16)(pixel[0] * ra + inv_a); - pixel[1] = (stbi__uint16)(pixel[1] * ra + inv_a); - pixel[2] = (stbi__uint16)(pixel[2] * ra + inv_a); + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i = 0; i < w * h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *)out + 4 * i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16)(pixel[0] * ra + inv_a); + pixel[1] = (stbi__uint16)(pixel[1] * ra + inv_a); + pixel[2] = (stbi__uint16)(pixel[2] * ra + inv_a); + } } - } - } - else - { - for (i = 0; i < w * h; ++i) - { - unsigned char *pixel = out + 4 * i; - if (pixel[3] != 0 && pixel[3] != 255) - { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char)(pixel[0] * ra + inv_a); - pixel[1] = (unsigned char)(pixel[1] * ra + inv_a); - pixel[2] = (unsigned char)(pixel[2] * ra + inv_a); + } else { + for (i = 0; i < w * h; ++i) { + unsigned char *pixel = out + 4 * i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char)(pixel[0] * ra + inv_a); + pixel[1] = (unsigned char)(pixel[1] * ra + inv_a); + pixel[2] = (unsigned char)(pixel[2] * ra + inv_a); + } } - } - } - } + } + } - // convert to desired output format - if (req_comp && req_comp != 4) - { - if (ri->bits_per_channel == 16) - out = (stbi_uc *)stbi__convert_format16((stbi__uint16 *)out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) - return out; // stbi__convert_format frees input on failure - } + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *)stbi__convert_format16((stbi__uint16 *)out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) + return out; // stbi__convert_format frees input on failure + } - if (comp) - *comp = 4; - *y = h; - *x = w; + if (comp) + *comp = 4; + *y = h; + *x = w; - return out; + return out; } #endif @@ -7436,244 +6695,222 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s, const char *str) -{ - int i; - for (i = 0; i < 4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; +static int stbi__pic_is4(stbi__context *s, const char *str) { + int i; + for (i = 0; i < 4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; - return 1; + return 1; } -static int stbi__pic_test_core(stbi__context *s) -{ - int i; +static int stbi__pic_test_core(stbi__context *s) { + int i; - if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) - return 0; + if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) + return 0; - for (i = 0; i < 84; ++i) - stbi__get8(s); + for (i = 0; i < 84; ++i) + stbi__get8(s); - if (!stbi__pic_is4(s, "PICT")) - return 0; + if (!stbi__pic_is4(s, "PICT")) + return 0; - return 1; + return 1; } typedef struct { - stbi_uc size, type, channel; + stbi_uc size, type, channel; } stbi__pic_packet; -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask = 0x80, i; +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { + int mask = 0x80, i; - for (i = 0; i < 4; ++i, mask >>= 1) - { - if (channel & mask) - { - if (stbi__at_eof(s)) - return stbi__errpuc("bad file", "PIC file too short"); - dest[i] = stbi__get8(s); - } - } + for (i = 0; i < 4; ++i, mask >>= 1) { + if (channel & mask) { + if (stbi__at_eof(s)) + return stbi__errpuc("bad file", "PIC file too short"); + dest[i] = stbi__get8(s); + } + } - return dest; + return dest; } -static void stbi__copyval(int channel, stbi_uc *dest, const stbi_uc *src) -{ - int mask = 0x80, i; +static void stbi__copyval(int channel, stbi_uc *dest, const stbi_uc *src) { + int mask = 0x80, i; - for (i = 0; i < 4; ++i, mask >>= 1) - if (channel & mask) - dest[i] = src[i]; + for (i = 0; i < 4; ++i, mask >>= 1) + if (channel & mask) + dest[i] = src[i]; } -static stbi_uc *stbi__pic_load_core(stbi__context *s, int width, int height, int *comp, stbi_uc *result) -{ - int act_comp = 0, num_packets = 0, y, chained; - stbi__pic_packet packets[10]; +static stbi_uc *stbi__pic_load_core(stbi__context *s, int width, int height, int *comp, stbi_uc *result) { + int act_comp = 0, num_packets = 0, y, chained; + stbi__pic_packet packets[10]; - // this will (should...) cater for even some bizarre stuff like having data - // Para the same channel in multiple packets. - do - { - stbi__pic_packet *packet; + // this will (should...) cater for even some bizarre stuff like having data + // Para the same channel in multiple packets. + do { + stbi__pic_packet *packet; - if (num_packets == sizeof(packets) / sizeof(packets[0])) - return stbi__errpuc("bad format", "too many packets"); + if (num_packets == sizeof(packets) / sizeof(packets[0])) + return stbi__errpuc("bad format", "too many packets"); - packet = &packets[num_packets++]; + packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); - act_comp |= packet->channel; + act_comp |= packet->channel; - if (stbi__at_eof(s)) - return stbi__errpuc("bad file", "file too short (reading packets)"); - if (packet->size != 8) - return stbi__errpuc("bad format", "packet isn't 8bpp"); - } while (chained); + if (stbi__at_eof(s)) + return stbi__errpuc("bad file", "file too short (reading packets)"); + if (packet->size != 8) + return stbi__errpuc("bad format", "packet isn't 8bpp"); + } while (chained); - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - for (y = 0; y < height; ++y) - { - int packet_idx; + for (y = 0; y < height; ++y) { + int packet_idx; - for (packet_idx = 0; packet_idx < num_packets; ++packet_idx) - { - stbi__pic_packet *packet = &packets[packet_idx]; - stbi_uc *dest = result + y * width * 4; + for (packet_idx = 0; packet_idx < num_packets; ++packet_idx) { + stbi__pic_packet *packet = &packets[packet_idx]; + stbi_uc *dest = result + y * width * 4; - switch (packet->type) - { - default: - return stbi__errpuc("bad format", "packet has bad compression type"); + switch (packet->type) { + default: + return stbi__errpuc("bad format", "packet has bad compression type"); - case 0: - { // uncompressed - int x; + case 0: { // uncompressed + int x; - for (x = 0; x < width; ++x, dest += 4) - if (!stbi__readval(s, packet->channel, dest)) - return 0; - break; - } + for (x = 0; x < width; ++x, dest += 4) + if (!stbi__readval(s, packet->channel, dest)) + return 0; + break; + } - case 1: // Pure RLE - { - int left = width, i; + case 1: // Pure RLE + { + int left = width, i; - while (left > 0) - { - stbi_uc count, value[4]; + while (left > 0) { + stbi_uc count, value[4]; - count = stbi__get8(s); - if (stbi__at_eof(s)) - return stbi__errpuc("bad file", "file too short (pure read count)"); + count = stbi__get8(s); + if (stbi__at_eof(s)) + return stbi__errpuc("bad file", "file too short (pure read count)"); - if (count > left) - count = (stbi_uc)left; + if (count > left) + count = (stbi_uc)left; - if (!stbi__readval(s, packet->channel, value)) - return 0; + if (!stbi__readval(s, packet->channel, value)) + return 0; - for (i = 0; i < count; ++i, dest += 4) - stbi__copyval(packet->channel, dest, value); - left -= count; + for (i = 0; i < count; ++i, dest += 4) + stbi__copyval(packet->channel, dest, value); + left -= count; + } + } break; + + case 2: { // Mixed RLE + int left = width; + while (left > 0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) + return stbi__errpuc("bad file", "file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count == 128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file", "scanline overrun"); + + if (!stbi__readval(s, packet->channel, value)) + return 0; + + for (i = 0; i < count; ++i, dest += 4) + stbi__copyval(packet->channel, dest, value); + } else { // Raw + ++count; + if (count > left) + return stbi__errpuc("bad file", "scanline overrun"); + + for (i = 0; i < count; ++i, dest += 4) + if (!stbi__readval(s, packet->channel, dest)) + return 0; + } + left -= count; + } + break; + } } - } - break; + } + } - case 2: - { // Mixed RLE - int left = width; - while (left > 0) - { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) - return stbi__errpuc("bad file", "file too short (mixed read count)"); - - if (count >= 128) - { // Repeated - stbi_uc value[4]; - - if (count == 128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file", "scanline overrun"); - - if (!stbi__readval(s, packet->channel, value)) - return 0; - - for (i = 0; i < count; ++i, dest += 4) - stbi__copyval(packet->channel, dest, value); - } - else - { // Raw - ++count; - if (count > left) - return stbi__errpuc("bad file", "scanline overrun"); - - for (i = 0; i < count; ++i, dest += 4) - if (!stbi__readval(s, packet->channel, dest)) - return 0; - } - left -= count; - } - break; - } - } - } - } - - return result; + return result; } -static void *stbi__pic_load(stbi__context *s, int *px, int *py, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x, y, internal_comp; - STBI_NOTUSED(ri); +static void *stbi__pic_load(stbi__context *s, int *px, int *py, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *result; + int i, x, y, internal_comp; + STBI_NOTUSED(ri); - if (!comp) - comp = &internal_comp; + if (!comp) + comp = &internal_comp; - for (i = 0; i < 92; ++i) - stbi__get8(s); + for (i = 0; i < 92; ++i) + stbi__get8(s); - x = stbi__get16be(s); - y = stbi__get16be(s); + x = stbi__get16be(s); + y = stbi__get16be(s); - if (y > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (y > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (stbi__at_eof(s)) - return stbi__errpuc("bad file", "file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) - return stbi__errpuc("too large", "PIC image too large to decode"); + if (stbi__at_eof(s)) + return stbi__errpuc("bad file", "file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) + return stbi__errpuc("too large", "PIC image too large to decode"); - stbi__get32be(s); // skip `ratio' - stbi__get16be(s); // skip `fields' - stbi__get16be(s); // skip `pad' + stbi__get32be(s); // skip `ratio' + stbi__get16be(s); // skip `fields' + stbi__get16be(s); // skip `pad' - // intermediate buffer is RGBA - result = (stbi_uc *)stbi__malloc_mad3(x, y, 4, 0); - if (!result) - return stbi__errpuc("outofmem", "Out of memory"); - memset(result, 0xff, x * y * 4); + // intermediate buffer is RGBA + result = (stbi_uc *)stbi__malloc_mad3(x, y, 4, 0); + if (!result) + return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x * y * 4); - if (!stbi__pic_load_core(s, x, y, comp, result)) - { - STBI_FREE(result); - result = 0; - } - *px = x; - *py = y; - if (req_comp == 0) - req_comp = *comp; - result = stbi__convert_format(result, 4, req_comp, x, y); + if (!stbi__pic_load_core(s, x, y, comp, result)) { + STBI_FREE(result); + result = 0; + } + *px = x; + *py = y; + if (req_comp == 0) + req_comp = *comp; + result = stbi__convert_format(result, 4, req_comp, x, y); - return result; + return result; } -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; +static int stbi__pic_test(stbi__context *s) { + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; } #endif @@ -7683,626 +6920,539 @@ static int stbi__pic_test(stbi__context *s) #ifndef STBI_NO_GIF typedef struct { - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; } stbi__gif_lzw; typedef struct { - int w, h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; + int w, h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; } stbi__gif; -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') - return 0; - if (stbi__get8(s) != 'a') - return 0; - return 1; +static int stbi__gif_test_raw(stbi__context *s) { + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') + return 0; + if (stbi__get8(s) != 'a') + return 0; + return 1; } -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; +static int stbi__gif_test(stbi__context *s) { + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; } -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i = 0; i < num_entries; ++i) - { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { + int i; + for (i = 0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } } -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); - version = stbi__get8(s); - if (version != '7' && version != '9') - return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') - return stbi__err("not GIF", "Corrupt GIF"); + version = stbi__get8(s); + if (version != '7' && version != '9') + return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') + return stbi__err("not GIF", "Corrupt GIF"); - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; - if (g->w > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) - return stbi__err("too large", "Very large image (corrupt?)"); + if (g->w > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) + return stbi__err("too large", "Very large image (corrupt?)"); - if (comp != 0) - *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + if (comp != 0) + *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - if (is_info) - return 1; + if (is_info) + return 1; - if (g->flags & 0x80) - stbi__gif_parse_colortable(s, g->pal, 2 << (g->flags & 7), -1); + if (g->flags & 0x80) + stbi__gif_parse_colortable(s, g->pal, 2 << (g->flags & 7), -1); - return 1; + return 1; } -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif *g = (stbi__gif *)stbi__malloc(sizeof(stbi__gif)); - if (!g) - return stbi__err("outofmem", "Out of memory"); - if (!stbi__gif_header(s, g, comp, 1)) - { - STBI_FREE(g); - stbi__rewind(s); - return 0; - } - if (x) - *x = g->w; - if (y) - *y = g->h; - STBI_FREE(g); - return 1; +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { + stbi__gif *g = (stbi__gif *)stbi__malloc(sizeof(stbi__gif)); + if (!g) + return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind(s); + return 0; + } + if (x) + *x = g->w; + if (y) + *y = g->h; + STBI_FREE(g); + return 1; } -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { + stbi_uc *p, *c; + int idx; - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); - if (g->cur_y >= g->max_y) - return; + if (g->cur_y >= g->max_y) + return; - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) - { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; - if (g->cur_x >= g->max_x) - { - g->cur_x = g->start_x; - g->cur_y += g->step; + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; - while (g->cur_y >= g->max_y && g->parse > 0) - { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } } -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) - return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) - { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc)init_code; - g->codes[init_code].suffix = (stbi_uc)init_code; - } + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) + return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc)init_code; + g->codes[init_code].suffix = (stbi_uc)init_code; + } - // support no starting clear code - avail = clear + 2; - oldcode = -1; + // support no starting clear code + avail = clear + 2; + oldcode = -1; - len = 0; - for (;;) - { - if (valid_bits < codesize) - { - if (len == 0) - { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32)stbi__get8(s) << valid_bits; - valid_bits += 8; - } - else - { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) - { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } - else if (code == clear + 1) - { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s, len); - return g->out; - } - else if (code <= avail) - { - if (first) - { - return stbi__errpuc("no clear code", "Corrupt GIF"); + len = 0; + for (;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; } + --len; + bits |= (stbi__int32)stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s, len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } - if (oldcode >= 0) - { - p = &g->codes[avail++]; - if (avail > 8192) - { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } - p->prefix = (stbi__int16)oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; + p->prefix = (stbi__int16)oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16)code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); } - else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16)code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) - { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } - else - { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } + } + } } // this function is designed to support animated gifs, although stb_image doesn't support it // two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) - { - if (!stbi__gif_header(s, g, comp, 0)) - return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *)stbi__malloc(4 * pcount); - g->background = (stbi_uc *)stbi__malloc(4 * pcount); - g->history = (stbi_uc *)stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp, 0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *)stbi__malloc(4 * pcount); + g->background = (stbi_uc *)stbi__malloc(4 * pcount); + g->history = (stbi_uc *)stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } - else - { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; - if ((dispose == 3) && (two_back == 0)) - { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } - if (dispose == 3) - { // use previous graphic - for (pi = 0; pi < pcount; ++pi) - { - if (g->history[pi]) - { - memcpy(&g->out[pi * 4], &two_back[pi * 4], 4); + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy(&g->out[pi * 4], &two_back[pi * 4], 4); + } } - } - } - else if (dispose == 2) - { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) - { - if (g->history[pi]) - { - memcpy(&g->out[pi * 4], &g->background[pi * 4], 4); + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy(&g->out[pi * 4], &g->background[pi * 4], 4); + } } - } - } - else - { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } - // background is what out is after the undoing of the previou frame; - memcpy(g->background, g->out, 4 * g->w * g->h); - } + // background is what out is after the undoing of the previou frame; + memcpy(g->background, g->out, 4 * g->w * g->h); + } - // clear my history; - memset(g->history, 0x00, g->w * g->h); // pixels that were affected previous frame + // clear my history; + memset(g->history, 0x00, g->w * g->h); // pixels that were affected previous frame - for (;;) - { - int tag = stbi__get8(s); - switch (tag) - { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) - { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } - else - { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) - { - stbi__gif_parse_colortable(s, g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *)g->lpal; - } - else if (g->flags & 0x80) - { - g->color_table = (stbi_uc *)g->pal; - } - else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) - return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) - { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ { - if (g->history[pi] == 0) - { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy(&g->out[pi * 4], &g->pal[g->bgindex], 4); - } + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s, g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *)g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *)g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) + return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy(&g->out[pi * 4], &g->pal[g->bgindex], 4); + } + } + } + + return o; } - } - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) - { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) + case 0x21: // Comment Extension. { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - // unset old transparent - if (g->transparent >= 0) - { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) - { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) - { - g->pal[g->transparent][3] = 0; - } - } - else - { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; } - else - { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) - { - stbi__skip(s, len); - } - break; - } - case 0x3B: // gif stream termination code - return (stbi_uc *)s; // using '1' causes warning on some compilers + case 0x3B: // gif stream termination code + return (stbi_uc *)s; // using '1' causes warning on some compilers - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } } -static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) -{ - STBI_FREE(g->out); - STBI_FREE(g->history); - STBI_FREE(g->background); +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) { + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); - if (out) - STBI_FREE(out); - if (delays && *delays) - STBI_FREE(*delays); - return stbi__errpuc("outofmem", "Out of memory"); + if (out) + STBI_FREE(out); + if (delays && *delays) + STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); } -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) - { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); - memset(&g, 0, sizeof(g)); - if (delays) - { - *delays = 0; - } + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } - do - { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *)s) - u = 0; // end of animated gif marker + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *)s) + u = 0; // end of animated gif marker - if (u) - { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; - if (out) - { - void *tmp = (stbi_uc *)STBI_REALLOC_SIZED(out, out_size, layers * stride); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else - { - out = (stbi_uc *)tmp; - out_size = layers * stride; - } + if (out) { + void *tmp = (stbi_uc *)STBI_REALLOC_SIZED(out, out_size, layers * stride); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc *)tmp; + out_size = layers * stride; + } - if (delays) - { - int *new_delays = (int *)STBI_REALLOC_SIZED(*delays, delays_size, sizeof(int) * layers); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } - else - { - out = (stbi_uc *)stbi__malloc(layers * stride); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) - { - *delays = (int *)stbi__malloc(layers * sizeof(int)); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy(out + ((layers - 1) * stride), u, stride); - if (layers >= 2) - { - two_back = out - 2 * stride; + if (delays) { + int *new_delays = (int *)STBI_REALLOC_SIZED(*delays, delays_size, sizeof(int) * layers); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc *)stbi__malloc(layers * stride); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int *)stbi__malloc(layers * sizeof(int)); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy(out + ((layers - 1) * stride), u, stride); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } } + } while (u != 0); - if (delays) - { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } - else - { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } } -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *)s) - u = 0; // end of animated gif marker - if (u) - { - *x = g.w; - *y = g.h; + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *)s) + u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } - else if (g.out) - { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); - return u; + return u; } -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s, x, y, comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { + return stbi__gif_info_raw(s, x, y, comp); } #endif @@ -8310,487 +7460,428 @@ static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i = 0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; +static int stbi__hdr_test_core(stbi__context *s, const char *signature) { + int i; + for (i = 0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; } -static int stbi__hdr_test(stbi__context *s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if (!r) - { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; +static int stbi__hdr_test(stbi__context *s) { + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if (!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; } #define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len = 0; - char c = '\0'; +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { + int len = 0; + char c = '\0'; - c = (char)stbi__get8(z); + c = (char)stbi__get8(z); - while (!stbi__at_eof(z) && c != '\n') - { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN - 1) - { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char)stbi__get8(z); - } + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN - 1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n'); + break; + } + c = (char)stbi__get8(z); + } - buffer[len] = 0; - return buffer; + buffer[len] = 0; + return buffer; } -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if (input[3] != 0) - { - float f1; - // Exponent - f1 = (float)ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else - { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) - output[1] = 1; - if (req_comp == 4) - output[3] = 1; - } - else - { - switch (req_comp) - { - case 4: - output[3] = 1; /* fallthrough */ - case 3: - output[0] = output[1] = output[2] = 0; - break; - case 2: - output[1] = 1; /* fallthrough */ - case 1: - output[0] = 0; - break; - } - } +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { + if (input[3] != 0) { + float f1; + // Exponent + f1 = (float)ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) + output[1] = 1; + if (req_comp == 4) + output[3] = 1; + } else { + switch (req_comp) { + case 4: + output[3] = 1; /* fallthrough */ + case 3: + output[0] = output[1] = output[2] = 0; + break; + case 2: + output[1] = 1; /* fallthrough */ + case 1: + output[0] = 0; + break; + } + } } -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1, c2, z; - const char *headerToken; - STBI_NOTUSED(ri); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1, c2, z; + const char *headerToken; + STBI_NOTUSED(ri); - // Check identifier - headerToken = stbi__hdr_gettoken(s, buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); + // Check identifier + headerToken = stbi__hdr_gettoken(s, buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); - // Parse header - for (;;) - { - token = stbi__hdr_gettoken(s, buffer); - if (token[0] == 0) - break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) - valid = 1; - } + // Parse header + for (;;) { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) + break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) + valid = 1; + } - if (!valid) - return stbi__errpf("unsupported format", "Unsupported HDR format"); + if (!valid) + return stbi__errpf("unsupported format", "Unsupported HDR format"); - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s, buffer); - if (strncmp(token, "-Y ", 3)) - return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int)strtol(token, &token, 10); - while (*token == ' ') - ++token; - if (strncmp(token, "+X ", 3)) - return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int)strtol(token, NULL, 10); + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s, buffer); + if (strncmp(token, "-Y ", 3)) + return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int)strtol(token, &token, 10); + while (*token == ' ') + ++token; + if (strncmp(token, "+X ", 3)) + return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int)strtol(token, NULL, 10); - if (height > STBI_MAX_DIMENSIONS) - return stbi__errpf("too large", "Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) - return stbi__errpf("too large", "Very large image (corrupt?)"); + if (height > STBI_MAX_DIMENSIONS) + return stbi__errpf("too large", "Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) + return stbi__errpf("too large", "Very large image (corrupt?)"); - *x = width; - *y = height; + *x = width; + *y = height; - if (comp) - *comp = 3; - if (req_comp == 0) - req_comp = 3; + if (comp) + *comp = 3; + if (req_comp == 0) + req_comp = 3; - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); - // Read data - hdr_data = (float *)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); + // Read data + hdr_data = (float *)stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); - // Load image data - // image data is stored as some number of sca - if (width < 8 || width >= 32768) - { - // Read flat data - for (j = 0; j < height; ++j) - { - for (i = 0; i < width; ++i) - { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } - else - { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) - { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) - { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc)c1; - rgbe[1] = (stbi_uc)c2; - rgbe[2] = (stbi_uc)len; - rgbe[3] = (stbi_uc)stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) - { - STBI_FREE(hdr_data); - STBI_FREE(scanline); - return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); - } - if (scanline == NULL) - { - scanline = (stbi_uc *)stbi__malloc_mad2(width, 4, 0); - if (!scanline) - { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); + // Load image data + // image data is stored as some number of sca + if (width < 8 || width >= 32768) { + // Read flat data + for (j = 0; j < height; ++j) { + for (i = 0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } - } + } + } else { + // Read RLE-encoded data + scanline = NULL; - for (k = 0; k < 4; ++k) - { - int nleft; - i = 0; - while ((nleft = width - i) > 0) - { - count = stbi__get8(s); - if (count > 128) - { - // Run - value = stbi__get8(s); - count -= 128; - if ((count == 0) || (count > nleft)) - { - STBI_FREE(hdr_data); - STBI_FREE(scanline); - return stbi__errpf("corrupt", "bad RLE data in HDR"); - } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } - else - { - // Dump - if ((count == 0) || (count > nleft)) - { - STBI_FREE(hdr_data); - STBI_FREE(scanline); - return stbi__errpf("corrupt", "bad RLE data in HDR"); - } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc)c1; + rgbe[1] = (stbi_uc)c2; + rgbe[2] = (stbi_uc)len; + rgbe[3] = (stbi_uc)stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); + } + if (scanline == NULL) { + scanline = (stbi_uc *)stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } } - } - for (i = 0; i < width; ++i) - stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - return hdr_data; + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("corrupt", "bad RLE data in HDR"); + } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { + STBI_FREE(hdr_data); + STBI_FREE(scanline); + return stbi__errpf("corrupt", "bad RLE data in HDR"); + } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i = 0; i < width; ++i) + stbi__hdr_convert(hdr_data + (j * width + i) * req_comp, scanline + i * 4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; } -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; - if (!x) - x = &dummy; - if (!y) - y = &dummy; - if (!comp) - comp = &dummy; + if (!x) + x = &dummy; + if (!y) + y = &dummy; + if (!comp) + comp = &dummy; - if (stbi__hdr_test(s) == 0) - { - stbi__rewind(s); - return 0; - } + if (stbi__hdr_test(s) == 0) { + stbi__rewind(s); + return 0; + } - for (;;) - { - token = stbi__hdr_gettoken(s, buffer); - if (token[0] == 0) - break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) - valid = 1; - } + for (;;) { + token = stbi__hdr_gettoken(s, buffer); + if (token[0] == 0) + break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) + valid = 1; + } - if (!valid) - { - stbi__rewind(s); - return 0; - } - token = stbi__hdr_gettoken(s, buffer); - if (strncmp(token, "-Y ", 3)) - { - stbi__rewind(s); - return 0; - } - token += 3; - *y = (int)strtol(token, &token, 10); - while (*token == ' ') - ++token; - if (strncmp(token, "+X ", 3)) - { - stbi__rewind(s); - return 0; - } - token += 3; - *x = (int)strtol(token, NULL, 10); - *comp = 3; - return 1; + if (!valid) { + stbi__rewind(s); + return 0; + } + token = stbi__hdr_gettoken(s, buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind(s); + return 0; + } + token += 3; + *y = (int)strtol(token, &token, 10); + while (*token == ' ') + ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind(s); + return 0; + } + token += 3; + *x = (int)strtol(token, NULL, 10); + *comp = 3; + return 1; } -#endif // STBI_NO_HDR +#endif // STBI_NO_HDR #ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { + void *p; + stbi__bmp_data info; - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - if (p == NULL) - { - stbi__rewind(s); - return 0; - } - if (x) - *x = s->img_x; - if (y) - *y = s->img_y; - if (comp) - { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind(s); + return 0; + } + if (x) + *x = s->img_x; + if (y) + *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; } #endif #ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) - x = &dummy; - if (!y) - y = &dummy; - if (!comp) - comp = &dummy; - if (stbi__get32be(s) != 0x38425053) - { - stbi__rewind(s); - return 0; - } - if (stbi__get16be(s) != 1) - { - stbi__rewind(s); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - { - stbi__rewind(s); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) - { - stbi__rewind(s); - return 0; - } - if (stbi__get16be(s) != 3) - { - stbi__rewind(s); - return 0; - } - *comp = 4; - return 1; +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { + int channelCount, dummy, depth; + if (!x) + x = &dummy; + if (!y) + y = &dummy; + if (!comp) + comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind(s); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind(s); + return 0; + } + *comp = 4; + return 1; } -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) - { - stbi__rewind(s); - return 0; - } - if (stbi__get16be(s) != 1) - { - stbi__rewind(s); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - { - stbi__rewind(s); - return 0; - } - STBI_NOTUSED(stbi__get32be(s)); - STBI_NOTUSED(stbi__get32be(s)); - depth = stbi__get16be(s); - if (depth != 16) - { - stbi__rewind(s); - return 0; - } - return 1; +static int stbi__psd_is16(stbi__context *s) { + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind(s); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind(s); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind(s); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind(s); + return 0; + } + return 1; } #endif #ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp = 0, num_packets = 0, chained, dummy; - stbi__pic_packet packets[10]; +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { + int act_comp = 0, num_packets = 0, chained, dummy; + stbi__pic_packet packets[10]; - if (!x) - x = &dummy; - if (!y) - y = &dummy; - if (!comp) - comp = &dummy; + if (!x) + x = &dummy; + if (!y) + y = &dummy; + if (!comp) + comp = &dummy; - if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) - { - stbi__rewind(s); - return 0; - } + if (!stbi__pic_is4(s, "\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } - stbi__skip(s, 88); + stbi__skip(s, 88); - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) - { - stbi__rewind(s); - return 0; - } - if ((*x) != 0 && (1 << 28) / (*x) < (*y)) - { - stbi__rewind(s); - return 0; - } + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind(s); + return 0; + } + if ((*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind(s); + return 0; + } - stbi__skip(s, 8); + stbi__skip(s, 8); - do - { - stbi__pic_packet *packet; + do { + stbi__pic_packet *packet; - if (num_packets == sizeof(packets) / sizeof(packets[0])) - return 0; + if (num_packets == sizeof(packets) / sizeof(packets[0])) + return 0; - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; - if (stbi__at_eof(s)) - { - stbi__rewind(s); - return 0; - } - if (packet->size != 8) - { - stbi__rewind(s); - return 0; - } - } while (chained); + if (stbi__at_eof(s)) { + stbi__rewind(s); + return 0; + } + if (packet->size != 8) { + stbi__rewind(s); + return 0; + } + } while (chained); - *comp = (act_comp & 0x10 ? 4 : 3); + *comp = (act_comp & 0x10 ? 4 : 3); - return 1; + return 1; } #endif @@ -8807,305 +7898,278 @@ static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) #ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char)stbi__get8(s); - t = (char)stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) - { - stbi__rewind(s); - return 0; - } - return 1; +static int stbi__pnm_test(stbi__context *s) { + char p, t; + p = (char)stbi__get8(s); + t = (char)stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + return 1; } -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { + stbi_uc *out; + STBI_NOTUSED(ri); - ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); - if (ri->bits_per_channel == 0) - return 0; + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; - if (s->img_y > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) - return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_y > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) + return stbi__errpuc("too large", "Very large image (corrupt?)"); - *x = s->img_x; - *y = s->img_y; - if (comp) - *comp = s->img_n; + *x = s->img_x; + *y = s->img_y; + if (comp) + *comp = s->img_n; - if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); - out = (stbi_uc *)stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); - if (!out) - return stbi__errpuc("outofmem", "Out of memory"); - if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) - { - STBI_FREE(out); - return stbi__errpuc("bad PNM", "PNM file truncated"); - } + out = (stbi_uc *)stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) + return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } - if (req_comp && req_comp != s->img_n) - { - if (ri->bits_per_channel == 16) - { - out = (stbi_uc *)stbi__convert_format16((stbi__uint16 *)out, s->img_n, req_comp, s->img_x, s->img_y); - } - else - { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - } - if (out == NULL) - return out; // stbi__convert_format frees input on failure - } - return out; + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *)stbi__convert_format16((stbi__uint16 *)out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) + return out; // stbi__convert_format frees input on failure + } + return out; } -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +static int stbi__pnm_isspace(char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) - { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char)stbi__get8(s); +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char)stbi__get8(s); - if (stbi__at_eof(s) || *c != '#') - break; + if (stbi__at_eof(s) || *c != '#') + break; - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r') - *c = (char)stbi__get8(s); - } + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r') + *c = (char)stbi__get8(s); + } } -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; +static int stbi__pnm_isdigit(char c) { + return c >= '0' && c <= '9'; } -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; +static int stbi__pnm_getinteger(stbi__context *s, char *c) { + int value = 0; - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) - { - value = value * 10 + (*c - '0'); - *c = (char)stbi__get8(s); - if ((value > 214748364) || (value == 214748364 && *c > '7')) - return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); - } + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value * 10 + (*c - '0'); + *c = (char)stbi__get8(s); + if ((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } - return value; + return value; } -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { + int maxv, dummy; + char c, p, t; - if (!x) - x = &dummy; - if (!y) - y = &dummy; - if (!comp) - comp = &dummy; + if (!x) + x = &dummy; + if (!y) + y = &dummy; + if (!comp) + comp = &dummy; - stbi__rewind(s); + stbi__rewind(s); - // Get identifier - p = (char)stbi__get8(s); - t = (char)stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) - { - stbi__rewind(s); - return 0; - } + // Get identifier + p = (char)stbi__get8(s); + t = (char)stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - c = (char)stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); + c = (char)stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); - *x = stbi__pnm_getinteger(s, &c); // read width - if (*x == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); + *x = stbi__pnm_getinteger(s, &c); // read width + if (*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); - *y = stbi__pnm_getinteger(s, &c); // read height - if (*y == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); - maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); - else if (maxv > 255) - return 16; - else - return 8; + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; } -static int stbi__pnm_is16(stbi__context *s) -{ - if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) - return 1; - return 0; +static int stbi__pnm_is16(stbi__context *s) { + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; } #endif -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) - return 1; + if (stbi__jpeg_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) - return 1; + if (stbi__png_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) - return 1; + if (stbi__gif_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) - return 1; + if (stbi__bmp_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) - return 1; + if (stbi__psd_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) - return 1; + if (stbi__pic_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) - return 1; + if (stbi__pnm_info(s, x, y, comp)) + return 1; #endif #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) - return 1; + if (stbi__hdr_info(s, x, y, comp)) + return 1; #endif // test tga last because it's a crappy test! #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; + if (stbi__tga_info(s, x, y, comp)) + return 1; #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } -static int stbi__is_16_main(stbi__context *s) -{ +static int stbi__is_16_main(stbi__context *s) { #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) - return 1; + if (stbi__png_is16(s)) + return 1; #endif #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) - return 1; + if (stbi__psd_is16(s)) + return 1; #endif #ifndef STBI_NO_PNM - if (stbi__pnm_is16(s)) - return 1; + if (stbi__pnm_is16(s)) + return 1; #endif - return 0; + return 0; } #ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) - return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) + return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; } -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s, x, y, comp); - fseek(f, pos, SEEK_SET); - return r; +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s, x, y, comp); + fseek(f, pos, SEEK_SET); + return r; } -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) - return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; +STBIDEF int stbi_is_16_bit(char const *filename) { + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) + return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; } -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f, pos, SEEK_SET); - return r; +STBIDEF int stbi_is_16_bit_from_file(FILE *f) { + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f, pos, SEEK_SET); + return r; } -#endif // !STBI_NO_STDIO +#endif // !STBI_NO_STDIO -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__info_main(&s, x, y, comp); +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__info_main(&s, x, y, comp); } -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)c, user); - return stbi__info_main(&s, x, y, comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)c, user); + return stbi__info_main(&s, x, y, comp); } -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s, buffer, len); - return stbi__is_16_main(&s); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) { + stbi__context s; + stbi__start_mem(&s, buffer, len); + return stbi__is_16_main(&s); } -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)c, user); - return stbi__is_16_main(&s); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) { + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)c, user); + return stbi__is_16_main(&s); } -#endif // STB_IMAGE_IMPLEMENTATION +#endif // STB_IMAGE_IMPLEMENTATION /* revision history: diff --git a/source/external/stb_vorbis.h b/source/external/stb_vorbis.h index f372cc9..ee203aa 100644 --- a/source/external/stb_vorbis.h +++ b/source/external/stb_vorbis.h @@ -79,147 +79,144 @@ #endif #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - /////////// THREAD SAFETY +/////////// THREAD SAFETY - // Individual stb_vorbis* handles are not thread-safe; you cannot decode from - // them from multiple threads at the same time. However, you can have multiple - // stb_vorbis* handles and decode from them independently in multiple thrads. +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. - /////////// MEMORY ALLOCATION +/////////// MEMORY ALLOCATION - // normally stb_vorbis uses malloc() to allocate memory at startup, - // and alloca() to allocate temporary memory during a frame on the - // stack. (Memory consumption will depend on the amount of setup - // data in the file and how you set the compile flags for speed - // vs. size. In my test files the maximal-size usage is ~150KB.) - // - // You can modify the wrapper functions in the source (setup_malloc, - // setup_temp_malloc, temp_malloc) to change this behavior, or you - // can use a simpler allocation model: you pass in a buffer from - // which stb_vorbis will allocate _all_ its memory (including the - // temp memory). "open" may fail with a VORBIS_outofmem if you - // do not pass in enough data; there is no way to determine how - // much you do need except to succeed (at which point you can - // query get_info to find the exact amount required. yes I know - // this is lame). - // - // If you pass in a non-NULL buffer of the type below, allocation - // will occur from it as described above. Otherwise just pass NULL - // to use malloc()/alloca() +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() - typedef struct - { - char *alloc_buffer; - int alloc_buffer_length_in_bytes; - } stb_vorbis_alloc; +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; - /////////// FUNCTIONS USEABLE WITH ALL INPUT MODES +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES - typedef struct stb_vorbis stb_vorbis; +typedef struct stb_vorbis stb_vorbis; - typedef struct - { - unsigned int sample_rate; - int channels; +typedef struct +{ + unsigned int sample_rate; + int channels; - unsigned int setup_memory_required; - unsigned int setup_temp_memory_required; - unsigned int temp_memory_required; + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; - int max_frame_size; - } stb_vorbis_info; + int max_frame_size; +} stb_vorbis_info; - typedef struct - { - char *vendor; +typedef struct +{ + char *vendor; - int comment_list_length; - char **comment_list; - } stb_vorbis_comment; + int comment_list_length; + char **comment_list; +} stb_vorbis_comment; - // get general information about the file - extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); - // get ogg comments - extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); +// get ogg comments +extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); - // get the last error detected (clears it, too) - extern int stb_vorbis_get_error(stb_vorbis *f); +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); - // close an ogg vorbis file and free all memory in use - extern void stb_vorbis_close(stb_vorbis *f); +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); - // this function returns the offset (in samples) from the beginning of the - // file that will be returned by the next decode, if it is known, or -1 - // otherwise. after a flush_pushdata() call, this may take a while before - // it becomes valid again. - // NOT WORKING YET after a seek with PULLDATA API - extern int stb_vorbis_get_sample_offset(stb_vorbis *f); +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); - // returns the current seek point within the file, or offset from the beginning - // of the memory buffer. In pushdata mode it returns 0. - extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); - /////////// PUSHDATA API +/////////// PUSHDATA API #ifndef STB_VORBIS_NO_PUSHDATA_API - // this API allows you to get blocks of data from any source and hand - // them to stb_vorbis. you have to buffer them; stb_vorbis will tell - // you how much it used, and you have to give it the rest next time; - // and stb_vorbis may not have enough data to work with and you will - // need to give it the same data again PLUS more. Note that the Vorbis - // specification does not bound the size of an individual frame. +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. - extern stb_vorbis *stb_vorbis_open_pushdata( - const unsigned char *datablock, int datablock_length_in_bytes, - int *datablock_memory_consumed_in_bytes, - int *error, - const stb_vorbis_alloc *alloc_buffer); - // create a vorbis decoder by passing in the initial data block containing - // the ogg&vorbis headers (you don't need to do parse them, just provide - // the first N bytes of the file--you're told if it's not enough, see below) - // on success, returns an stb_vorbis *, does not set error, returns the amount of - // data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; - // on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed - // if returns NULL and *error is VORBIS_need_more_data, then the input block was - // incomplete and you need to pass in a larger block from the start of the file +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file - extern int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, - const unsigned char *datablock, int datablock_length_in_bytes, - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples - ); - // decode a frame of audio sample data if possible from the passed-in data block - // - // return value: number of bytes we used from datablock - // - // possible cases: - // 0 bytes used, 0 samples output (need more data) - // N bytes used, 0 samples output (resynching the stream, keep going) - // N bytes used, M samples output (one frame of data) - // note that after opening a file, you will ALWAYS get one N-bytes,0-sample - // frame, because Vorbis always "discards" the first frame. - // - // Note that on resynch, stb_vorbis will rarely consume all of the buffer, - // instead only datablock_length_in_bytes-3 or less. This is because it wants - // to avoid missing parts of a page header if they cross a datablock boundary, - // without writing state-machiney code to record a partial detection. - // - // The number of channels returned are stored in *channels (which can be - // NULL--it is always the same as the number of channels reported by - // get_info). *output will contain an array of float* buffers, one per - // channel. In other words, (*output)[0][0] contains the first sample from - // the first channel, and (*output)[1][0] contains the first sample from - // the second channel. +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, + int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples +); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. - extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and @@ -232,50 +229,48 @@ extern "C" // if you don't like the result, seek your file again and repeat. #endif - ////////// PULLING INPUT API +////////// PULLING INPUT API #ifndef STB_VORBIS_NO_PULLDATA_API - // This API assumes stb_vorbis is allowed to pull data from a source-- - // either a block of memory containing the _entire_ vorbis stream, or a - // FILE * that you or it create, or possibly some other reading mechanism - // if you go modify the source to replace the FILE * case with some kind - // of callback to your code. (But if you don't support seeking, you may - // just want to go ahead and use pushdata.) +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) - extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif #if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) - extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif - // decode an entire file and output the data interleaved into a malloc()ed - // buffer stored in *output. The return value is the number of samples - // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. - // When you're done with it, just free() the pointer returned in *output. +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. - extern stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, - int *error, const stb_vorbis_alloc *alloc_buffer); - // create an ogg vorbis decoder from an ogg vorbis stream in memory (note - // this must be the entire stream!). on failure, returns NULL and sets *error +extern stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO - extern stb_vorbis *stb_vorbis_open_filename(const char *filename, - int *error, const stb_vorbis_alloc *alloc_buffer); - // create an ogg vorbis decoder from a filename via fopen(). on failure, - // returns NULL and sets *error (possibly to VORBIS_file_open_failure). +extern stb_vorbis *stb_vorbis_open_filename(const char *filename, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). - extern stb_vorbis *stb_vorbis_open_file(FILE *f, int close_handle_on_close, - int *error, const stb_vorbis_alloc *alloc_buffer); - // create an ogg vorbis decoder from an open FILE *, looking for a stream at - // the _current_ seek point (ftell). on failure, returns NULL and sets *error. - // note that stb_vorbis must "own" this stream; if you seek it in between - // calls to stb_vorbis, it will become confused. Moreover, if you attempt to - // perform stb_vorbis_seek_*() operations on this file, it will assume it - // owns the _entire_ rest of the file after the start point. Use the next - // function, stb_vorbis_open_file_section(), to limit it. +extern stb_vorbis *stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Moreover, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. - extern stb_vorbis *stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, - int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +extern stb_vorbis *stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. // on failure, returns NULL and sets *error. note that stb_vorbis must "own" @@ -283,122 +278,121 @@ extern "C" // confused. #endif - extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); - extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); - // these functions seek in the Vorbis file to (approximately) 'sample_number'. - // after calling seek_frame(), the next call to get_frame_*() will include - // the specified sample. after calling stb_vorbis_seek(), the next call to - // stb_vorbis_get_samples_* will start with the specified sample. If you - // do not need to seek to EXACTLY the target sample when using get_samples_*, - // you can also use seek_frame(). +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). - extern int stb_vorbis_seek_start(stb_vorbis *f); - // this function is equivalent to stb_vorbis_seek(f,0) +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) - extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); - extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); - // these functions return the total length of the vorbis stream +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream - extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); - // decode the next frame and return the number of samples. the number of - // channels returned are stored in *channels (which can be NULL--it is always - // the same as the number of channels reported by get_info). *output will - // contain an array of float* buffers, one per channel. These outputs will - // be overwritten on the next call to stb_vorbis_get_frame_*. - // - // You generally should not intermix calls to stb_vorbis_get_frame_*() - // and stb_vorbis_get_samples_*(), since the latter calls the former. +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION - extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); - extern int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples); +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif - // decode the next frame and return the number of *samples* per channel. - // Note that for interleaved data, you pass in the number of shorts (the - // size of your array), but the return value is the number of samples per - // channel, not the total number of samples. - // - // The data is coerced to the number of channels you request according to the - // channel coercion rules (see below). You must pass in the size of your - // buffer(s) so that stb_vorbis will not overwrite the end of the buffer. - // The maximum buffer size needed can be gotten from get_info(); however, - // the Vorbis I specification implies an absolute maximum of 4096 samples - // per channel. +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. - // Channel coercion rules: - // Let M be the number of channels requested, and N the number of channels present, - // and Cn be the nth channel; let stereo L be the sum of all L and center channels, - // and stereo R be the sum of all R and center channels (channel assignment from the - // vorbis spec). - // M N output - // 1 k sum(Ck) for all k - // 2 * stereo L, stereo R - // k l k > l, the first l channels, then 0s - // k l k <= l, the first k channels - // Note that this is not _good_ surround etc. mixing at all! It's just so - // you get something useful. +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. - extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); - extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); - // gets num_samples samples, not necessarily on a frame boundary--this requires - // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. - // Returns the number of samples stored per channel; it may be less than requested - // at the end of the file. If there are no more samples in the file, returns 0. +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION - extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); - extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif - // gets num_samples samples, not necessarily on a frame boundary--this requires - // buffering so you have to supply the buffers. Applies the coercion rules above - // to produce 'channels' channels. Returns the number of samples stored per channel; - // it may be less than requested at the end of the file. If there are no more - // samples in the file, returns 0. +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. #endif - //////// ERROR CODES +//////// ERROR CODES - enum STBVorbisError - { - VORBIS__no_error, +enum STBVorbisError { + VORBIS__no_error, - VORBIS_need_more_data = 1, // not a real error + VORBIS_need_more_data = 1, // not a real error - VORBIS_invalid_api_mixing, // can't mix API modes - VORBIS_outofmem, // not enough memory - VORBIS_feature_not_supported, // uses floor 0 - VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small - VORBIS_file_open_failure, // fopen() failed - VORBIS_seek_without_length, // can't seek in unknown-length file + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file - VORBIS_unexpected_eof = 10, // file is truncated? - VORBIS_seek_invalid, // seek past EOF + VORBIS_unexpected_eof = 10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF - // decoding errors (corrupt/invalid stream) -- you probably - // don't care about the exact details of these + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these - // vorbis errors: - VORBIS_invalid_setup = 20, - VORBIS_invalid_stream, + // vorbis errors: + VORBIS_invalid_setup = 20, + VORBIS_invalid_stream, - // ogg errors: - VORBIS_missing_capture_pattern = 30, - VORBIS_invalid_stream_structure_version, - VORBIS_continued_packet_flag_invalid, - VORBIS_incorrect_stream_serial_number, - VORBIS_invalid_first_page, - VORBIS_bad_packet_type, - VORBIS_cant_find_last_page, - VORBIS_seek_failed, - VORBIS_ogg_skeleton_not_supported - }; + // ogg errors: + VORBIS_missing_capture_pattern = 30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed, + VORBIS_ogg_skeleton_not_supported +}; #ifdef __cplusplus } #endif -#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H // // HEADER ENDS HERE // @@ -444,7 +438,7 @@ extern "C" // I forgot to account for. Can probably go as low as 8 (7.1 audio), // 6 (5.1 audio), or 2 (stereo only). #ifndef STB_VORBIS_MAX_CHANNELS -#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? #endif // STB_VORBIS_PUSHDATA_CRC_COUNT [number] @@ -561,10 +555,10 @@ extern "C" #endif #ifndef STB_VORBIS_NO_CRT -#include <stdlib.h> -#include <string.h> #include <assert.h> #include <math.h> +#include <stdlib.h> +#include <string.h> // find definition of alloca if it's not in stdlib.h: #if defined(_MSC_VER) || defined(__MINGW32__) @@ -573,12 +567,12 @@ extern "C" #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include <alloca.h> #endif -#else // STB_VORBIS_NO_CRT +#else // STB_VORBIS_NO_CRT #define NULL 0 #define malloc(s) 0 #define free(s) ((void)0) #define realloc(s) 0 -#endif // STB_VORBIS_NO_CRT +#endif // STB_VORBIS_NO_CRT #include <limits.h> @@ -618,7 +612,7 @@ extern "C" #define CHECK(f) ((void)0) #endif -#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE_LOG 13 // from specification #define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) typedef unsigned char uint8; @@ -653,225 +647,223 @@ typedef float codetype; typedef struct { - int dimensions, entries; - uint8 *codeword_lengths; - float minimum_value; - float delta_value; - uint8 value_bits; - uint8 lookup_type; - uint8 sequence_p; - uint8 sparse; - uint32 lookup_values; - codetype *multiplicands; - uint32 *codewords; + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #else - int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #endif - uint32 *sorted_codewords; - int *sorted_values; - int sorted_entries; + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; } Codebook; typedef struct { - uint8 order; - uint16 rate; - uint16 bark_map_size; - uint8 amplitude_bits; - uint8 amplitude_offset; - uint8 number_of_books; - uint8 book_list[16]; // varies + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies } Floor0; typedef struct { - uint8 partitions; - uint8 partition_class_list[32]; // varies - uint8 class_dimensions[16]; // varies - uint8 class_subclasses[16]; // varies - uint8 class_masterbooks[16]; // varies - int16 subclass_books[16][8]; // varies - uint16 Xlist[31 * 8 + 2]; // varies - uint8 sorted_order[31 * 8 + 2]; - uint8 neighbors[31 * 8 + 2][2]; - uint8 floor1_multiplier; - uint8 rangebits; - int values; + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31 * 8 + 2]; // varies + uint8 sorted_order[31 * 8 + 2]; + uint8 neighbors[31 * 8 + 2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; } Floor1; -typedef union -{ - Floor0 floor0; - Floor1 floor1; +typedef union { + Floor0 floor0; + Floor1 floor1; } Floor; typedef struct { - uint32 begin, end; - uint32 part_size; - uint8 classifications; - uint8 classbook; - uint8 **classdata; - int16 (*residue_books)[8]; + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; } Residue; typedef struct { - uint8 magnitude; - uint8 angle; - uint8 mux; + uint8 magnitude; + uint8 angle; + uint8 mux; } MappingChannel; typedef struct { - uint16 coupling_steps; - MappingChannel *chan; - uint8 submaps; - uint8 submap_floor[15]; // varies - uint8 submap_residue[15]; // varies + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies } Mapping; typedef struct { - uint8 blockflag; - uint8 mapping; - uint16 windowtype; - uint16 transformtype; + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; } Mode; typedef struct { - uint32 goal_crc; // expected crc if match - int bytes_left; // bytes left in packet - uint32 crc_so_far; // running crc - int bytes_done; // bytes processed in _current_ chunk - uint32 sample_loc; // granule pos encoded in page + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page } CRCscan; typedef struct { - uint32 page_start, page_end; - uint32 last_decoded_sample; + uint32 page_start, page_end; + uint32 last_decoded_sample; } ProbedPage; -struct stb_vorbis -{ - // user-accessible info - unsigned int sample_rate; - int channels; +struct stb_vorbis { + // user-accessible info + unsigned int sample_rate; + int channels; - unsigned int setup_memory_required; - unsigned int temp_memory_required; - unsigned int setup_temp_memory_required; + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; - char *vendor; - int comment_list_length; - char **comment_list; + char *vendor; + int comment_list_length; + char **comment_list; - // input config + // input config #ifndef STB_VORBIS_NO_STDIO - FILE *f; - uint32 f_start; - int close_on_free; + FILE *f; + uint32 f_start; + int close_on_free; #endif - uint8 *stream; - uint8 *stream_start; - uint8 *stream_end; + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; - uint32 stream_len; + uint32 stream_len; - uint8 push_mode; + uint8 push_mode; - // the page to seek to when seeking to start, may be zero - uint32 first_audio_page_offset; + // the page to seek to when seeking to start, may be zero + uint32 first_audio_page_offset; - // p_first is the page on which the first audio packet ends - // (but not necessarily the page on which it starts) - ProbedPage p_first, p_last; + // p_first is the page on which the first audio packet ends + // (but not necessarily the page on which it starts) + ProbedPage p_first, p_last; - // memory management - stb_vorbis_alloc alloc; - int setup_offset; - int temp_offset; + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; - // run-time results - int eof; - enum STBVorbisError error; + // run-time results + int eof; + enum STBVorbisError error; - // user-useful data + // user-useful data - // header info - int blocksize[2]; - int blocksize_0, blocksize_1; - int codebook_count; - Codebook *codebooks; - int floor_count; - uint16 floor_types[64]; // varies - Floor *floor_config; - int residue_count; - uint16 residue_types[64]; // varies - Residue *residue_config; - int mapping_count; - Mapping *mapping; - int mode_count; - Mode mode_config[64]; // varies + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies - uint32 total_samples; + uint32 total_samples; - // decode buffer - float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; - float *outputs[STB_VORBIS_MAX_CHANNELS]; + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs[STB_VORBIS_MAX_CHANNELS]; - float *previous_window[STB_VORBIS_MAX_CHANNELS]; - int previous_length; + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; #ifndef STB_VORBIS_NO_DEFER_FLOOR - int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; #else - float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; #endif - uint32 current_loc; // sample location of next frame to decode - int current_loc_valid; + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; - // per-blocksize precomputed data + // per-blocksize precomputed data - // twiddle factors - float *A[2], *B[2], *C[2]; - float *window[2]; - uint16 *bit_reverse[2]; + // twiddle factors + float *A[2], *B[2], *C[2]; + float *window[2]; + uint16 *bit_reverse[2]; - // current page/packet/segment streaming info - uint32 serial; // stream serial number for verification - int last_page; - int segment_count; - uint8 segments[255]; - uint8 page_flag; - uint8 bytes_in_seg; - uint8 first_decode; - int next_seg; - int last_seg; // flag that we're on the last segment - int last_seg_which; // what was the segment number of the last seg? - uint32 acc; - int valid_bits; - int packet_bytes; - int end_seg_with_known_loc; - uint32 known_loc_for_packet; - int discard_samples_deferred; - uint32 samples_output; + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; - // push mode scanning - int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching #ifndef STB_VORBIS_NO_PUSHDATA_API - CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; #endif - // sample-access - int channel_buffer_start; - int channel_buffer_end; + // sample-access + int channel_buffer_start; + int channel_buffer_end; }; #if defined(STB_VORBIS_NO_PUSHDATA_API) @@ -884,14 +876,12 @@ struct stb_vorbis typedef struct stb_vorbis vorb; -static int error(vorb *f, enum STBVorbisError e) -{ - f->error = e; - if (!f->eof && e != VORBIS_need_more_data) - { - f->error = e; // breakpoint for debugging - } - return 0; +static int error(vorb *f, enum STBVorbisError e) { + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error = e; // breakpoint for debugging + } + return 0; } // these functions are used for allocating temporary memory @@ -909,130 +899,115 @@ static int error(vorb *f, enum STBVorbisError e) #define temp_block_array(f, count, size) make_block_array(temp_alloc(f, array_size_required(count, size)), count, size) // given a sufficiently large block of memory, make an array of pointers to subblocks of it -static void *make_block_array(void *mem, int count, int size) -{ - int i; - void **p = (void **)mem; - char *q = (char *)(p + count); - for (i = 0; i < count; ++i) - { - p[i] = q; - q += size; - } - return p; +static void *make_block_array(void *mem, int count, int size) { + int i; + void **p = (void **)mem; + char *q = (char *)(p + count); + for (i = 0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; } -static void *setup_malloc(vorb *f, int sz) -{ - sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. - f->setup_memory_required += sz; - if (f->alloc.alloc_buffer) - { - void *p = (char *)f->alloc.alloc_buffer + f->setup_offset; - if (f->setup_offset + sz > f->temp_offset) - return NULL; - f->setup_offset += sz; - return p; - } - return sz ? malloc(sz) : NULL; +static void *setup_malloc(vorb *f, int sz) { + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *)f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) + return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; } -static void setup_free(vorb *f, void *p) -{ - if (f->alloc.alloc_buffer) - return; // do nothing; setup mem is a stack - free(p); +static void setup_free(vorb *f, void *p) { + if (f->alloc.alloc_buffer) + return; // do nothing; setup mem is a stack + free(p); } -static void *setup_temp_malloc(vorb *f, int sz) -{ - sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. - if (f->alloc.alloc_buffer) - { - if (f->temp_offset - sz < f->setup_offset) - return NULL; - f->temp_offset -= sz; - return (char *)f->alloc.alloc_buffer + f->temp_offset; - } - return malloc(sz); +static void *setup_temp_malloc(vorb *f, int sz) { + sz = (sz + 7) & ~7; // round up to nearest 8 for alignment of future allocs. + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) + return NULL; + f->temp_offset -= sz; + return (char *)f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); } -static void setup_temp_free(vorb *f, void *p, int sz) -{ - if (f->alloc.alloc_buffer) - { - f->temp_offset += (sz + 7) & ~7; - return; - } - free(p); +static void setup_temp_free(vorb *f, void *p, int sz) { + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz + 7) & ~7; + return; + } + free(p); } -#define CRC32_POLY 0x04c11db7 // from spec +#define CRC32_POLY 0x04c11db7 // from spec static uint32 crc_table[256]; -static void crc32_init(void) -{ - int i, j; - uint32 s; - for (i = 0; i < 256; i++) - { - for (s = (uint32)i << 24, j = 0; j < 8; ++j) - s = (s << 1) ^ (s >= (1U << 31) ? CRC32_POLY : 0); - crc_table[i] = s; - } +static void crc32_init(void) { + int i, j; + uint32 s; + for (i = 0; i < 256; i++) { + for (s = (uint32)i << 24, j = 0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U << 31) ? CRC32_POLY : 0); + crc_table[i] = s; + } } -static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) -{ - return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) { + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; } // used in setup, and for huffman that doesn't go fast path -static unsigned int bit_reverse(unsigned int n) -{ - n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); - n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); - n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); - n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); - return (n >> 16) | (n << 16); +static unsigned int bit_reverse(unsigned int n) { + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); } -static float square(float x) -{ - return x * x; +static float square(float x) { + return x * x; } // this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 // as required by the specification. fast(?) implementation from stb.h // @OPTIMIZE: called multiple times per-packet with "constants"; move to setup -static int ilog(int32 n) -{ - static signed char log2_4[16] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4}; +static int ilog(int32 n) { + static signed char log2_4[16] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4}; - if (n < 0) - return 0; // signed n returns 0 + if (n < 0) + return 0; // signed n returns 0 - // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) - if (n < (1 << 14)) - if (n < (1 << 4)) - return 0 + log2_4[n]; - else if (n < (1 << 9)) - return 5 + log2_4[n >> 5]; - else - return 10 + log2_4[n >> 10]; - else if (n < (1 << 24)) - if (n < (1 << 19)) - return 15 + log2_4[n >> 15]; - else - return 20 + log2_4[n >> 20]; - else if (n < (1 << 29)) - return 25 + log2_4[n >> 25]; - else - return 30 + log2_4[n >> 30]; + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) + return 0 + log2_4[n]; + else if (n < (1 << 9)) + return 5 + log2_4[n >> 5]; + else + return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) + return 15 + log2_4[n >> 15]; + else + return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) + return 25 + log2_4[n >> 25]; + else + return 30 + log2_4[n >> 30]; } #ifndef M_PI -#define M_PI 3.14159265358979323846264f // from CRC +#define M_PI 3.14159265358979323846264f // from CRC #endif // code length assigned to a value with no huffman encoding @@ -1043,14 +1018,13 @@ static int ilog(int32 n) // these functions are only called at setup, and only a few times // per file -static float float32_unpack(uint32 x) -{ - // from the specification - uint32 mantissa = x & 0x1fffff; - uint32 sign = x & 0x80000000; - uint32 exp = (x & 0x7fe00000) >> 21; - double res = sign ? -(double)mantissa : (double)mantissa; - return (float)ldexp((float)res, exp - 788); +static float float32_unpack(uint32 x) { + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float)ldexp((float)res, exp - 788); } // zlib & jpeg huffman tables assume that the output symbols @@ -1060,106 +1034,92 @@ static float float32_unpack(uint32 x) // vorbis allows a huffman table with non-sorted lengths. This // requires a more sophisticated construction, since symbols in // order do not map to huffman codes "in order". -static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) -{ - if (!c->sparse) - { - c->codewords[symbol] = huff_code; - } - else - { - c->codewords[count] = huff_code; - c->codeword_lengths[count] = len; - values[count] = symbol; - } +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) { + if (!c->sparse) { + c->codewords[symbol] = huff_code; + } else { + c->codewords[count] = huff_code; + c->codeword_lengths[count] = len; + values[count] = symbol; + } } -static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) -{ - int i, k, m = 0; - uint32 available[32]; +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { + int i, k, m = 0; + uint32 available[32]; - memset(available, 0, sizeof(available)); - // find the first entry - for (k = 0; k < n; ++k) - if (len[k] < NO_CODE) - break; - if (k == n) - { - assert(c->sorted_entries == 0); - return TRUE; - } - // add to the list - add_entry(c, 0, k, m++, len[k], values); - // add all available leaves - for (i = 1; i <= len[k]; ++i) - available[i] = 1U << (32 - i); - // note that the above code treats the first case specially, - // but it's really the same as the following code, so they - // could probably be combined (except the initial code is 0, - // and I use 0 in available[] to mean 'empty') - for (i = k + 1; i < n; ++i) - { - uint32 res; - int z = len[i], y; - if (z == NO_CODE) - continue; - // find lowest available leaf (should always be earliest, - // which is what the specification calls for) - // note that this property, and the fact we can never have - // more than one free leaf at a given level, isn't totally - // trivial to prove, but it seems true and the assert never - // fires, so! - while (z > 0 && !available[z]) - --z; - if (z == 0) - { - return FALSE; - } - res = available[z]; - assert(z >= 0 && z < 32); - available[z] = 0; - add_entry(c, bit_reverse(res), i, m++, len[i], values); - // propagate availability up the tree - if (z != len[i]) - { - assert(len[i] >= 0 && len[i] < 32); - for (y = len[i]; y > z; --y) - { - assert(available[y] == 0); - available[y] = res + (1 << (32 - y)); - } - } - } - return TRUE; + memset(available, 0, sizeof(available)); + // find the first entry + for (k = 0; k < n; ++k) + if (len[k] < NO_CODE) + break; + if (k == n) { + assert(c->sorted_entries == 0); + return TRUE; + } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i = 1; i <= len[k]; ++i) + available[i] = 1U << (32 - i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i = k + 1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) + continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) + --z; + if (z == 0) { + return FALSE; + } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propagate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y = len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32 - y)); + } + } + } + return TRUE; } // accelerated huffman table allows fast O(1) match of all symbols // of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH -static void compute_accelerated_huffman(Codebook *c) -{ - int i, len; - for (i = 0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) - c->fast_huffman[i] = -1; +static void compute_accelerated_huffman(Codebook *c) { + int i, len; + for (i = 0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; - len = c->sparse ? c->sorted_entries : c->entries; + len = c->sparse ? c->sorted_entries : c->entries; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT - if (len > 32767) - len = 32767; // largest possible value we can encode! + if (len > 32767) + len = 32767; // largest possible value we can encode! #endif - for (i = 0; i < len; ++i) - { - if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) - { - uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; - // set table entries for all bit combinations in the higher bits - while (z < FAST_HUFFMAN_TABLE_SIZE) - { - c->fast_huffman[z] = i; - z += 1 << c->codeword_lengths[i]; - } - } - } + for (i = 0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } } #ifdef _MSC_VER @@ -1168,201 +1128,172 @@ static void compute_accelerated_huffman(Codebook *c) #define STBV_CDECL #endif -static int STBV_CDECL uint32_compare(const void *p, const void *q) -{ - uint32 x = *(uint32 *)p; - uint32 y = *(uint32 *)q; - return x < y ? -1 : x > y; +static int STBV_CDECL uint32_compare(const void *p, const void *q) { + uint32 x = *(uint32 *)p; + uint32 y = *(uint32 *)q; + return x < y ? -1 : x > y; } -static int include_in_sort(Codebook *c, uint8 len) -{ - if (c->sparse) - { - assert(len != NO_CODE); - return TRUE; - } - if (len == NO_CODE) - return FALSE; - if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) - return TRUE; - return FALSE; +static int include_in_sort(Codebook *c, uint8 len) { + if (c->sparse) { + assert(len != NO_CODE); + return TRUE; + } + if (len == NO_CODE) + return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) + return TRUE; + return FALSE; } // if the fast table above doesn't work, we want to binary // search them... need to reverse the bits -static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) -{ - int i, len; - // build a list of all the entries - // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. - // this is kind of a frivolous optimization--I don't see any performance improvement, - // but it's like 4 extra lines of code, so. - if (!c->sparse) - { - int k = 0; - for (i = 0; i < c->entries; ++i) - if (include_in_sort(c, lengths[i])) - c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); - assert(k == c->sorted_entries); - } - else - { - for (i = 0; i < c->sorted_entries; ++i) - c->sorted_codewords[i] = bit_reverse(c->codewords[i]); - } +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) { + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i = 0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i = 0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } - qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); - c->sorted_codewords[c->sorted_entries] = 0xffffffff; + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; - len = c->sparse ? c->sorted_entries : c->entries; - // now we need to indicate how they correspond; we could either - // #1: sort a different data structure that says who they correspond to - // #2: for each sorted entry, search the original list to find who corresponds - // #3: for each original entry, find the sorted entry - // #1 requires extra storage, #2 is slow, #3 can use binary search! - for (i = 0; i < len; ++i) - { - int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; - if (include_in_sort(c, huff_len)) - { - uint32 code = bit_reverse(c->codewords[i]); - int x = 0, n = c->sorted_entries; - while (n > 1) - { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) - { - x = m; - n -= (n >> 1); + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i = 0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c, huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x = 0, n = c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n >> 1); + } else { + n >>= 1; + } } - else - { - n >>= 1; + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; } - } - assert(c->sorted_codewords[x] == code); - if (c->sparse) - { - c->sorted_values[x] = values[i]; - c->codeword_lengths[x] = huff_len; - } - else - { - c->sorted_values[x] = i; - } - } - } + } + } } // only run while parsing the header (3 times) -static int vorbis_validate(uint8 *data) -{ - static uint8 vorbis[6] = {'v', 'o', 'r', 'b', 'i', 's'}; - return memcmp(data, vorbis, 6) == 0; +static int vorbis_validate(uint8 *data) { + static uint8 vorbis[6] = {'v', 'o', 'r', 'b', 'i', 's'}; + return memcmp(data, vorbis, 6) == 0; } // called from setup only, once per code book // (formula implied by specification) -static int lookup1_values(int entries, int dim) -{ - int r = (int)floor(exp((float)log((float)entries) / dim)); - if ((int)floor(pow((float)r + 1, dim)) <= entries) // (int) cast for MinGW warning; - ++r; // floor() to avoid _ftol() when non-CRT - if (pow((float)r + 1, dim) <= entries) - return -1; - if ((int)floor(pow((float)r, dim)) > entries) - return -1; - return r; +static int lookup1_values(int entries, int dim) { + int r = (int)floor(exp((float)log((float)entries) / dim)); + if ((int)floor(pow((float)r + 1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + if (pow((float)r + 1, dim) <= entries) + return -1; + if ((int)floor(pow((float)r, dim)) > entries) + return -1; + return r; } // called twice per file -static void compute_twiddle_factors(int n, float *A, float *B, float *C) -{ - int n4 = n >> 2, n8 = n >> 3; - int k, k2; +static void compute_twiddle_factors(int n, float *A, float *B, float *C) { + int n4 = n >> 2, n8 = n >> 3; + int k, k2; - for (k = k2 = 0; k < n4; ++k, k2 += 2) - { - A[k2] = (float)cos(4 * k * M_PI / n); - A[k2 + 1] = (float)-sin(4 * k * M_PI / n); - B[k2] = (float)cos((k2 + 1) * M_PI / n / 2) * 0.5f; - B[k2 + 1] = (float)sin((k2 + 1) * M_PI / n / 2) * 0.5f; - } - for (k = k2 = 0; k < n8; ++k, k2 += 2) - { - C[k2] = (float)cos(2 * (k2 + 1) * M_PI / n); - C[k2 + 1] = (float)-sin(2 * (k2 + 1) * M_PI / n); - } + for (k = k2 = 0; k < n4; ++k, k2 += 2) { + A[k2] = (float)cos(4 * k * M_PI / n); + A[k2 + 1] = (float)-sin(4 * k * M_PI / n); + B[k2] = (float)cos((k2 + 1) * M_PI / n / 2) * 0.5f; + B[k2 + 1] = (float)sin((k2 + 1) * M_PI / n / 2) * 0.5f; + } + for (k = k2 = 0; k < n8; ++k, k2 += 2) { + C[k2] = (float)cos(2 * (k2 + 1) * M_PI / n); + C[k2 + 1] = (float)-sin(2 * (k2 + 1) * M_PI / n); + } } -static void compute_window(int n, float *window) -{ - int n2 = n >> 1, i; - for (i = 0; i < n2; ++i) - window[i] = (float)sin(0.5 * M_PI * square((float)sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +static void compute_window(int n, float *window) { + int n2 = n >> 1, i; + for (i = 0; i < n2; ++i) + window[i] = (float)sin(0.5 * M_PI * square((float)sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); } -static void compute_bitreverse(int n, uint16 *rev) -{ - int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - int i, n8 = n >> 3; - for (i = 0; i < n8; ++i) - rev[i] = (bit_reverse(i) >> (32 - ld + 3)) << 2; +static void compute_bitreverse(int n, uint16 *rev) { + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i = 0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32 - ld + 3)) << 2; } -static int init_blocksize(vorb *f, int b, int n) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; - f->A[b] = (float *)setup_malloc(f, sizeof(float) * n2); - f->B[b] = (float *)setup_malloc(f, sizeof(float) * n2); - f->C[b] = (float *)setup_malloc(f, sizeof(float) * n4); - if (!f->A[b] || !f->B[b] || !f->C[b]) - return error(f, VORBIS_outofmem); - compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); - f->window[b] = (float *)setup_malloc(f, sizeof(float) * n2); - if (!f->window[b]) - return error(f, VORBIS_outofmem); - compute_window(n, f->window[b]); - f->bit_reverse[b] = (uint16 *)setup_malloc(f, sizeof(uint16) * n8); - if (!f->bit_reverse[b]) - return error(f, VORBIS_outofmem); - compute_bitreverse(n, f->bit_reverse[b]); - return TRUE; +static int init_blocksize(vorb *f, int b, int n) { + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *)setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *)setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *)setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) + return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *)setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) + return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *)setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) + return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; } -static void neighbors(uint16 *x, int n, int *plow, int *phigh) -{ - int low = -1; - int high = 65536; - int i; - for (i = 0; i < n; ++i) - { - if (x[i] > low && x[i] < x[n]) - { - *plow = i; - low = x[i]; - } - if (x[i] < high && x[i] > x[n]) - { - *phigh = i; - high = x[i]; - } - } +static void neighbors(uint16 *x, int n, int *plow, int *phigh) { + int low = -1; + int high = 65536; + int i; + for (i = 0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { + *plow = i; + low = x[i]; + } + if (x[i] < high && x[i] > x[n]) { + *phigh = i; + high = x[i]; + } + } } // this has been repurposed so y is now the original index instead of y typedef struct { - uint16 x, id; + uint16 x, id; } stbv__floor_ordering; -static int STBV_CDECL point_compare(const void *p, const void *q) -{ - stbv__floor_ordering *a = (stbv__floor_ordering *)p; - stbv__floor_ordering *b = (stbv__floor_ordering *)q; - return a->x < b->x ? -1 : a->x > b->x; +static int STBV_CDECL point_compare(const void *p, const void *q) { + stbv__floor_ordering *a = (stbv__floor_ordering *)p; + stbv__floor_ordering *b = (stbv__floor_ordering *)q; + return a->x < b->x ? -1 : a->x > b->x; } // @@ -1374,507 +1305,440 @@ static int STBV_CDECL point_compare(const void *p, const void *q) #define USE_MEMORY(z) ((z)->stream) #endif -static uint8 get8(vorb *z) -{ - if (USE_MEMORY(z)) - { - if (z->stream >= z->stream_end) - { - z->eof = TRUE; - return 0; - } - return *z->stream++; - } +static uint8 get8(vorb *z) { + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { + z->eof = TRUE; + return 0; + } + return *z->stream++; + } #ifndef STB_VORBIS_NO_STDIO - { - int c = fgetc(z->f); - if (c == EOF) - { - z->eof = TRUE; - return 0; - } - return c; - } + { + int c = fgetc(z->f); + if (c == EOF) { + z->eof = TRUE; + return 0; + } + return c; + } #endif } -static uint32 get32(vorb *f) -{ - uint32 x; - x = get8(f); - x += get8(f) << 8; - x += get8(f) << 16; - x += (uint32)get8(f) << 24; - return x; +static uint32 get32(vorb *f) { + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32)get8(f) << 24; + return x; } -static int getn(vorb *z, uint8 *data, int n) -{ - if (USE_MEMORY(z)) - { - if (z->stream + n > z->stream_end) - { - z->eof = 1; - return 0; - } - memcpy(data, z->stream, n); - z->stream += n; - return 1; - } +static int getn(vorb *z, uint8 *data, int n) { + if (USE_MEMORY(z)) { + if (z->stream + n > z->stream_end) { + z->eof = 1; + return 0; + } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } #ifndef STB_VORBIS_NO_STDIO - if (fread(data, n, 1, z->f) == 1) - return 1; - else - { - z->eof = 1; - return 0; - } + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } #endif } -static void skip(vorb *z, int n) -{ - if (USE_MEMORY(z)) - { - z->stream += n; - if (z->stream >= z->stream_end) - z->eof = 1; - return; - } +static void skip(vorb *z, int n) { + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) + z->eof = 1; + return; + } #ifndef STB_VORBIS_NO_STDIO - { - long x = ftell(z->f); - fseek(z->f, x + n, SEEK_SET); - } + { + long x = ftell(z->f); + fseek(z->f, x + n, SEEK_SET); + } #endif } -static int set_file_offset(stb_vorbis *f, unsigned int loc) -{ +static int set_file_offset(stb_vorbis *f, unsigned int loc) { #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) - return 0; + if (f->push_mode) + return 0; #endif - f->eof = 0; - if (USE_MEMORY(f)) - { - if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) - { - f->stream = f->stream_end; - f->eof = 1; - return 0; - } - else - { - f->stream = f->stream_start + loc; - return 1; - } - } + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } #ifndef STB_VORBIS_NO_STDIO - if (loc + f->f_start < loc || loc >= 0x80000000) - { - loc = 0x7fffffff; - f->eof = 1; - } - else - { - loc += f->f_start; - } - if (!fseek(f->f, loc, SEEK_SET)) - return 1; - f->eof = 1; - fseek(f->f, f->f_start, SEEK_END); - return 0; + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; #endif } static uint8 ogg_page_header[4] = {0x4f, 0x67, 0x67, 0x53}; -static int capture_pattern(vorb *f) -{ - if (0x4f != get8(f)) - return FALSE; - if (0x67 != get8(f)) - return FALSE; - if (0x67 != get8(f)) - return FALSE; - if (0x53 != get8(f)) - return FALSE; - return TRUE; +static int capture_pattern(vorb *f) { + if (0x4f != get8(f)) + return FALSE; + if (0x67 != get8(f)) + return FALSE; + if (0x67 != get8(f)) + return FALSE; + if (0x53 != get8(f)) + return FALSE; + return TRUE; } #define PAGEFLAG_continued_packet 1 #define PAGEFLAG_first_page 2 #define PAGEFLAG_last_page 4 -static int start_page_no_capturepattern(vorb *f) -{ - uint32 loc0, loc1, n; - if (f->first_decode && !IS_PUSH_MODE(f)) - { - f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; - } - // stream structure version - if (0 != get8(f)) - return error(f, VORBIS_invalid_stream_structure_version); - // header flag - f->page_flag = get8(f); - // absolute granule position - loc0 = get32(f); - loc1 = get32(f); - // @TODO: validate loc0,loc1 as valid positions? - // stream serial number -- vorbis doesn't interleave, so discard - get32(f); - // if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); - // page sequence number - n = get32(f); - f->last_page = n; - // CRC32 - get32(f); - // page_segments - f->segment_count = get8(f); - if (!getn(f, f->segments, f->segment_count)) - return error(f, VORBIS_unexpected_eof); - // assume we _don't_ know any the sample position of any segments - f->end_seg_with_known_loc = -2; - if (loc0 != ~0U || loc1 != ~0U) - { - int i; - // determine which packet is the last one that will complete - for (i = f->segment_count - 1; i >= 0; --i) - if (f->segments[i] < 255) - break; - // 'i' is now the index of the _last_ segment of a packet that ends - if (i >= 0) - { - f->end_seg_with_known_loc = i; - f->known_loc_for_packet = loc0; - } - } - if (f->first_decode) - { - int i, len; - len = 0; - for (i = 0; i < f->segment_count; ++i) - len += f->segments[i]; - len += 27 + f->segment_count; - f->p_first.page_end = f->p_first.page_start + len; - f->p_first.last_decoded_sample = loc0; - } - f->next_seg = 0; - return TRUE; +static int start_page_no_capturepattern(vorb *f) { + uint32 loc0, loc1, n; + if (f->first_decode && !IS_PUSH_MODE(f)) { + f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; + } + // stream structure version + if (0 != get8(f)) + return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + // if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i = f->segment_count - 1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i, len; + len = 0; + for (i = 0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + f->p_first.page_end = f->p_first.page_start + len; + f->p_first.last_decoded_sample = loc0; + } + f->next_seg = 0; + return TRUE; } -static int start_page(vorb *f) -{ - if (!capture_pattern(f)) - return error(f, VORBIS_missing_capture_pattern); - return start_page_no_capturepattern(f); +static int start_page(vorb *f) { + if (!capture_pattern(f)) + return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); } -static int start_packet(vorb *f) -{ - while (f->next_seg == -1) - { - if (!start_page(f)) - return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) - return error(f, VORBIS_continued_packet_flag_invalid); - } - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - // f->next_seg is now valid - return TRUE; +static int start_packet(vorb *f) { + while (f->next_seg == -1) { + if (!start_page(f)) + return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; } -static int maybe_start_packet(vorb *f) -{ - if (f->next_seg == -1) - { - int x = get8(f); - if (f->eof) - return FALSE; // EOF at page boundary is not an error! - if (0x4f != x) - return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) - return error(f, VORBIS_missing_capture_pattern); - if (0x67 != get8(f)) - return error(f, VORBIS_missing_capture_pattern); - if (0x53 != get8(f)) - return error(f, VORBIS_missing_capture_pattern); - if (!start_page_no_capturepattern(f)) - return FALSE; - if (f->page_flag & PAGEFLAG_continued_packet) - { - // set up enough state that we can read this packet if we want, - // e.g. during recovery - f->last_seg = FALSE; - f->bytes_in_seg = 0; - return error(f, VORBIS_continued_packet_flag_invalid); - } - } - return start_packet(f); +static int maybe_start_packet(vorb *f) { + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) + return FALSE; // EOF at page boundary is not an error! + if (0x4f != x) + return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) + return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) + return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) + return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) + return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); } -static int next_segment(vorb *f) -{ - int len; - if (f->last_seg) - return 0; - if (f->next_seg == -1) - { - f->last_seg_which = f->segment_count - 1; // in case start_page fails - if (!start_page(f)) - { - f->last_seg = 1; - return 0; - } - if (!(f->page_flag & PAGEFLAG_continued_packet)) - return error(f, VORBIS_continued_packet_flag_invalid); - } - len = f->segments[f->next_seg++]; - if (len < 255) - { - f->last_seg = TRUE; - f->last_seg_which = f->next_seg - 1; - } - if (f->next_seg >= f->segment_count) - f->next_seg = -1; - assert(f->bytes_in_seg == 0); - f->bytes_in_seg = len; - return len; +static int next_segment(vorb *f) { + int len; + if (f->last_seg) + return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count - 1; // in case start_page fails + if (!start_page(f)) { + f->last_seg = 1; + return 0; + } + if (!(f->page_flag & PAGEFLAG_continued_packet)) + return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg - 1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; } #define EOP (-1) #define INVALID_BITS (-1) -static int get8_packet_raw(vorb *f) -{ - if (!f->bytes_in_seg) - { // CLANG! - if (f->last_seg) - return EOP; - else if (!next_segment(f)) - return EOP; - } - assert(f->bytes_in_seg > 0); - --f->bytes_in_seg; - ++f->packet_bytes; - return get8(f); +static int get8_packet_raw(vorb *f) { + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) + return EOP; + else if (!next_segment(f)) + return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); } -static int get8_packet(vorb *f) -{ - int x = get8_packet_raw(f); - f->valid_bits = 0; - return x; +static int get8_packet(vorb *f) { + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; } -static int get32_packet(vorb *f) -{ - uint32 x; - x = get8_packet(f); - x += get8_packet(f) << 8; - x += get8_packet(f) << 16; - x += (uint32)get8_packet(f) << 24; - return x; +static int get32_packet(vorb *f) { + uint32 x; + x = get8_packet(f); + x += get8_packet(f) << 8; + x += get8_packet(f) << 16; + x += (uint32)get8_packet(f) << 24; + return x; } -static void flush_packet(vorb *f) -{ - while (get8_packet_raw(f) != EOP) - ; +static void flush_packet(vorb *f) { + while (get8_packet_raw(f) != EOP); } // @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important // as the huffman decoder? -static uint32 get_bits(vorb *f, int n) -{ - uint32 z; +static uint32 get_bits(vorb *f, int n) { + uint32 z; - if (f->valid_bits < 0) - return 0; - if (f->valid_bits < n) - { - if (n > 24) - { - // the accumulator technique below would not work correctly in this case - z = get_bits(f, 24); - z += get_bits(f, n - 24) << 24; - return z; - } - if (f->valid_bits == 0) - f->acc = 0; - while (f->valid_bits < n) - { - int z = get8_packet_raw(f); - if (z == EOP) - { - f->valid_bits = INVALID_BITS; - return 0; - } - f->acc += z << f->valid_bits; - f->valid_bits += 8; - } - } + if (f->valid_bits < 0) + return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n - 24) << 24; + return z; + } + if (f->valid_bits == 0) + f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } - assert(f->valid_bits >= n); - z = f->acc & ((1 << n) - 1); - f->acc >>= n; - f->valid_bits -= n; - return z; + assert(f->valid_bits >= n); + z = f->acc & ((1 << n) - 1); + f->acc >>= n; + f->valid_bits -= n; + return z; } // @OPTIMIZE: primary accumulator for huffman // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, // e.g. cache them locally and decode locally -static __forceinline void prep_huffman(vorb *f) -{ - if (f->valid_bits <= 24) - { - if (f->valid_bits == 0) - f->acc = 0; - do - { - int z; - if (f->last_seg && !f->bytes_in_seg) - return; - z = get8_packet_raw(f); - if (z == EOP) - return; - f->acc += (unsigned)z << f->valid_bits; - f->valid_bits += 8; - } while (f->valid_bits <= 24); - } +static __forceinline void prep_huffman(vorb *f) { + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) + f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) + return; + z = get8_packet_raw(f); + if (z == EOP) + return; + f->acc += (unsigned)z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } } -enum -{ - VORBIS_packet_id = 1, - VORBIS_packet_comment = 3, - VORBIS_packet_setup = 5 +enum { + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 }; -static int codebook_decode_scalar_raw(vorb *f, Codebook *c) -{ - int i; - prep_huffman(f); +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { + int i; + prep_huffman(f); - if (c->codewords == NULL && c->sorted_codewords == NULL) - return -1; + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; - // cases to use binary search: sorted_codewords && !c->codewords - // sorted_codewords && c->entries > 8 - if (c->entries > 8 ? c->sorted_codewords != NULL : !c->codewords) - { - // binary search - uint32 code = bit_reverse(f->acc); - int x = 0, n = c->sorted_entries, len; + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords != NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x = 0, n = c->sorted_entries, len; - while (n > 1) - { - // invariant: sc[x] <= code < sc[x+n] - int m = x + (n >> 1); - if (c->sorted_codewords[m] <= code) - { - x = m; - n -= (n >> 1); - } - else - { - n >>= 1; - } - } - // x is now the sorted index - if (!c->sparse) - x = c->sorted_values[x]; - // x is now sorted index if sparse, or symbol otherwise - len = c->codeword_lengths[x]; - if (f->valid_bits >= len) - { - f->acc >>= len; - f->valid_bits -= len; - return x; - } + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n >> 1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) + x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } - f->valid_bits = 0; - return -1; - } + f->valid_bits = 0; + return -1; + } - // if small, linear search - assert(!c->sparse); - for (i = 0; i < c->entries; ++i) - { - if (c->codeword_lengths[i] == NO_CODE) - continue; - if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i]) - 1))) - { - if (f->valid_bits >= c->codeword_lengths[i]) - { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - return i; - } - f->valid_bits = 0; - return -1; - } - } + // if small, linear search + assert(!c->sparse); + for (i = 0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) + continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i]) - 1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } - error(f, VORBIS_invalid_stream); - f->valid_bits = 0; - return -1; + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; } #ifndef STB_VORBIS_NO_INLINE_DECODE -#define DECODE_RAW(var, f, c) \ - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ - prep_huffman(f); \ - var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ - var = c->fast_huffman[var]; \ - if (var >= 0) \ - { \ - int n = c->codeword_lengths[var]; \ - f->acc >>= n; \ - f->valid_bits -= n; \ - if (f->valid_bits < 0) \ - { \ - f->valid_bits = 0; \ - var = -1; \ - } \ - } \ - else \ - { \ - var = codebook_decode_scalar_raw(f, c); \ - } +#define DECODE_RAW(var, f, c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { \ + f->valid_bits = 0; \ + var = -1; \ + } \ + } else { \ + var = codebook_decode_scalar_raw(f, c); \ + } #else -static int codebook_decode_scalar(vorb *f, Codebook *c) -{ - int i; - if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) - prep_huffman(f); - // fast huffman table lookup - i = f->acc & FAST_HUFFMAN_TABLE_MASK; - i = c->fast_huffman[i]; - if (i >= 0) - { - f->acc >>= c->codeword_lengths[i]; - f->valid_bits -= c->codeword_lengths[i]; - if (f->valid_bits < 0) - { - f->valid_bits = 0; - return -1; - } - return i; - } - return codebook_decode_scalar_raw(f, c); +static int codebook_decode_scalar(vorb *f, Codebook *c) { + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { + f->valid_bits = 0; + return -1; + } + return i; + } + return codebook_decode_scalar_raw(f, c); } #define DECODE_RAW(var, f, c) var = codebook_decode_scalar(f, c); @@ -1882,9 +1746,9 @@ static int codebook_decode_scalar(vorb *f, Codebook *c) #endif #define DECODE(var, f, c) \ - DECODE_RAW(var, f, c) \ - if (c->sparse) \ - var = c->sorted_values[var]; + DECODE_RAW(var, f, c) \ + if (c->sparse) \ + var = c->sorted_values[var]; #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK #define DECODE_VQ(var, f, c) DECODE_RAW(var, f, c) @@ -1898,289 +1762,195 @@ static int codebook_decode_scalar(vorb *f, Codebook *c) #define CODEBOOK_ELEMENT_FAST(c, off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_BASE(c) (0) -static int codebook_decode_start(vorb *f, Codebook *c) -{ - int z = -1; +static int codebook_decode_start(vorb *f, Codebook *c) { + int z = -1; - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) - error(f, VORBIS_invalid_stream); - else - { - DECODE_VQ(z, f, c); - if (c->sparse) - assert(z < c->sorted_entries); - if (z < 0) - { // check for EOP - if (!f->bytes_in_seg) - if (f->last_seg) - return z; - error(f, VORBIS_invalid_stream); - } - } - return z; + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z, f, c); + if (c->sparse) + assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; } -static int codebook_decode(vorb *f, Codebook *c, float *output, int len) -{ - int i, z = codebook_decode_start(f, c); - if (z < 0) - return FALSE; - if (len > c->dimensions) - len = c->dimensions; +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { + int i, z = codebook_decode_start(f, c); + if (z < 0) + return FALSE; + if (len > c->dimensions) + len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) - { - float last = CODEBOOK_ELEMENT_BASE(c); - int div = 1; - for (i = 0; i < len; ++i) - { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c, off) + last; - output[i] += val; - if (c->sequence_p) - last = val + c->minimum_value; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - if (c->sequence_p) - { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i = 0; i < len; ++i) - { - float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; - output[i] += val; - last = val + c->minimum_value; - } - } - else - { - float last = CODEBOOK_ELEMENT_BASE(c); - for (i = 0; i < len; ++i) - { - output[i] += CODEBOOK_ELEMENT_FAST(c, z + i) + last; - } - } - - return TRUE; -} - -static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) -{ - int i, z = codebook_decode_start(f, c); - float last = CODEBOOK_ELEMENT_BASE(c); - if (z < 0) - return FALSE; - if (len > c->dimensions) - len = c->dimensions; - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) - { - int div = 1; - for (i = 0; i < len; ++i) - { - int off = (z / div) % c->lookup_values; - float val = CODEBOOK_ELEMENT_FAST(c, off) + last; - output[i * step] += val; - if (c->sequence_p) - last = val; - div *= c->lookup_values; - } - return TRUE; - } -#endif - - z *= c->dimensions; - for (i = 0; i < len; ++i) - { - float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; - output[i * step] += val; - if (c->sequence_p) - last = val; - } - - return TRUE; -} - -static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) -{ - int c_inter = *c_inter_p; - int p_inter = *p_inter_p; - int i, z, effective = c->dimensions; - - // type 0 is only legal in a scalar context - if (c->lookup_type == 0) - return error(f, VORBIS_invalid_stream); - - while (total_decode > 0) - { - float last = CODEBOOK_ELEMENT_BASE(c); - DECODE_VQ(z, f, c); -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - assert(!c->sparse || z < c->sorted_entries); -#endif - if (z < 0) - { - if (!f->bytes_in_seg) - if (f->last_seg) - return FALSE; - return error(f, VORBIS_invalid_stream); - } - - // if this will take us off the end of the buffers, stop short! - // we check by computing the length of the virtual interleaved - // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), - // and the length we'll be using (effective) - if (c_inter + p_inter * ch + effective > len * ch) - { - effective = len * ch - (p_inter * ch - c_inter); - } - -#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) - { - int div = 1; - for (i = 0; i < effective; ++i) - { + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i = 0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c, off) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) - { - c_inter = 0; - ++p_inter; - } + output[i] += val; if (c->sequence_p) - last = val; + last = val + c->minimum_value; div *= c->lookup_values; - } - } - else + } + return TRUE; + } #endif - { - z *= c->dimensions; - if (c->sequence_p) - { - for (i = 0; i < effective; ++i) - { - float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) - { - c_inter = 0; - ++p_inter; - } - last = val; - } - } - else - { - for (i = 0; i < effective; ++i) - { - float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; - if (outputs[c_inter]) - outputs[c_inter][p_inter] += val; - if (++c_inter == ch) - { - c_inter = 0; - ++p_inter; - } - } - } - } - total_decode -= effective; - } - *c_inter_p = c_inter; - *p_inter_p = p_inter; - return TRUE; + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i = 0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i = 0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c, z + i) + last; + } + } + + return TRUE; } -static int predict_point(int x, int x0, int x1, int y0, int y1) -{ - int dy = y1 - y0; - int adx = x1 - x0; - // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? - int err = abs(dy) * (x - x0); - int off = err / adx; - return dy < 0 ? y0 - off : y0 + off; +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { + int i, z = codebook_decode_start(f, c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) + return FALSE; + if (len > c->dimensions) + len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i = 0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c, off) + last; + output[i * step] += val; + if (c->sequence_p) + last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i = 0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; + output[i * step] += val; + if (c->sequence_p) + last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) { + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i, z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z, f, c); +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); +#endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) + return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter * ch + effective > len * ch) { + effective = len * ch - (p_inter * ch - c_inter); + } + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i = 0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c, off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { + c_inter = 0; + ++p_inter; + } + if (c->sequence_p) + last = val; + div *= c->lookup_values; + } + } else +#endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i = 0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { + c_inter = 0; + ++p_inter; + } + last = val; + } + } else { + for (i = 0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c, z + i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { + c_inter = 0; + ++p_inter; + } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) { + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; } // the following table is block-copied from the specification static float inverse_db_table[256] = { - 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, - 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, - 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, - 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, - 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, - 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, - 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, - 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, - 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, - 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, - 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, - 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, - 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, - 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, - 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, - 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, - 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, - 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, - 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, - 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, - 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, - 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, - 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, - 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, - 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, - 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, - 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, - 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, - 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, - 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, - 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, - 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, - 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, - 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, - 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, - 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, - 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, - 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, - 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, - 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, - 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, - 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, - 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, - 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, - 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, - 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, - 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, - 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, - 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, - 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, - 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, - 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, - 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, - 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, - 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, - 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, - 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, - 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, - 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, - 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, - 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, - 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, - 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, - 0.82788260f, 0.88168307f, 0.9389798f, 1.0f}; + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f}; // @OPTIMIZE: if you want to replace this bresenham line-drawing routine, // note that you must produce bit-identical output to decode correctly; @@ -2198,306 +1968,260 @@ static float inverse_db_table[256] = #ifdef STB_VORBIS_DIVIDE_TABLE #define DIVTAB_NUMER 32 #define DIVTAB_DENOM 64 -int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB #endif -static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) -{ - int dy = y1 - y0; - int adx = x1 - x0; - int ady = abs(dy); - int base; - int x = x0, y = y0; - int err = 0; - int sy; +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) { + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x = x0, y = y0; + int err = 0; + int sy; #ifdef STB_VORBIS_DIVIDE_TABLE - if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) - { - if (dy < 0) - { - base = -integer_divide_table[ady][adx]; - sy = base - 1; - } - else - { - base = integer_divide_table[ady][adx]; - sy = base + 1; - } - } - else - { - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base + 1; - } + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base - 1; + } else { + base = integer_divide_table[ady][adx]; + sy = base + 1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base + 1; + } #else - base = dy / adx; - if (dy < 0) - sy = base - 1; - else - sy = base + 1; + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base + 1; #endif - ady -= abs(base) * adx; - if (x1 > n) - x1 = n; - if (x < x1) - { - LINE_OP(output[x], inverse_db_table[y & 255]); - for (++x; x < x1; ++x) - { - err += ady; - if (err >= adx) - { - err -= adx; - y += sy; - } - else - y += base; - LINE_OP(output[x], inverse_db_table[y & 255]); - } - } + ady -= abs(base) * adx; + if (x1 > n) + x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y & 255]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y & 255]); + } + } } -static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) -{ - int k; - if (rtype == 0) - { - int step = n / book->dimensions; - for (k = 0; k < step; ++k) - if (!codebook_decode_step(f, book, target + offset + k, n - offset - k, step)) - return FALSE; - } - else - { - for (k = 0; k < n;) - { - if (!codebook_decode(f, book, target + offset, n - k)) - return FALSE; - k += book->dimensions; - offset += book->dimensions; - } - } - return TRUE; +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) { + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k = 0; k < step; ++k) + if (!codebook_decode_step(f, book, target + offset + k, n - offset - k, step)) + return FALSE; + } else { + for (k = 0; k < n;) { + if (!codebook_decode(f, book, target + offset, n - k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; } // n is 1/2 of the blocksize -- // specification: "Correct per-vector decode length is [n]/2" -static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) -{ - int i, j, pass; - Residue *r = f->residue_config + rn; - int rtype = f->residue_types[rn]; - int c = r->classbook; - int classwords = f->codebooks[c].dimensions; - unsigned int actual_size = rtype == 2 ? n * 2 : n; - unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); - unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); - int n_read = limit_r_end - limit_r_begin; - int part_read = n_read / r->part_size; - int temp_alloc_point = temp_alloc_save(f); +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { + int i, j, pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n * 2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - uint8 ***part_classdata = (uint8 ***)temp_block_array(f, f->channels, part_read * sizeof(**part_classdata)); + uint8 ***part_classdata = (uint8 ***)temp_block_array(f, f->channels, part_read * sizeof(**part_classdata)); #else - int **classifications = (int **)temp_block_array(f, f->channels, part_read * sizeof(**classifications)); + int **classifications = (int **)temp_block_array(f, f->channels, part_read * sizeof(**classifications)); #endif - CHECK(f); + CHECK(f); - for (i = 0; i < ch; ++i) - if (!do_not_decode[i]) - memset(residue_buffers[i], 0, sizeof(float) * n); + for (i = 0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); - if (rtype == 2 && ch != 1) - { - for (j = 0; j < ch; ++j) - if (!do_not_decode[j]) - break; - if (j == ch) - goto done; + if (rtype == 2 && ch != 1) { + for (j = 0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; - for (pass = 0; pass < 8; ++pass) - { - int pcount = 0, class_set = 0; - if (ch == 2) - { - while (pcount < part_read) - { - int z = r->begin + pcount * r->part_size; - int c_inter = (z & 1), p_inter = z >> 1; - if (pass == 0) - { - Codebook *c = f->codebooks + r->classbook; - int q; - DECODE(q, f, c); - if (q == EOP) - goto done; + for (pass = 0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount * r->part_size; + int c_inter = (z & 1), p_inter = z >> 1; + if (pass == 0) { + Codebook *c = f->codebooks + r->classbook; + int q; + DECODE(q, f, c); + if (q == EOP) + goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; + part_classdata[0][class_set] = r->classdata[q]; #else - for (i = classwords - 1; i >= 0; --i) - { - classifications[0][i + pcount] = q % r->classifications; - q /= r->classifications; - } + for (i = classwords - 1; i >= 0; --i) { + classifications[0][i + pcount] = q % r->classifications; + q /= r->classifications; + } #endif - } - for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) - { - int z = r->begin + pcount * r->part_size; + } + for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount * r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; + int c = part_classdata[0][class_set][i]; #else - int c = classifications[0][pcount]; + int c = classifications[0][pcount]; #endif - int b = r->residue_books[c][pass]; - if (b >= 0) - { - Codebook *book = f->codebooks + b; + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; #else - // saves 1% - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; #endif - } - else - { - z += r->part_size; - c_inter = z & 1; - p_inter = z >> 1; - } - } + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; + ++class_set; #endif + } + } else if (ch > 2) { + while (pcount < part_read) { + int z = r->begin + pcount * r->part_size; + int c_inter = z % ch, p_inter = z / ch; + if (pass == 0) { + Codebook *c = f->codebooks + r->classbook; + int q; + DECODE(q, f, c); + if (q == EOP) + goto done; +#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; +#else + for (i = classwords - 1; i >= 0; --i) { + classifications[0][i + pcount] = q % r->classifications; + q /= r->classifications; + } +#endif + } + for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount * r->part_size; +#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; +#else + int c = classifications[0][pcount]; +#endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; +#endif + } } - } - else if (ch > 2) - { - while (pcount < part_read) - { - int z = r->begin + pcount * r->part_size; - int c_inter = z % ch, p_inter = z / ch; - if (pass == 0) - { - Codebook *c = f->codebooks + r->classbook; - int q; - DECODE(q, f, c); - if (q == EOP) - goto done; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[0][class_set] = r->classdata[q]; -#else - for (i = classwords - 1; i >= 0; --i) - { - classifications[0][i + pcount] = q % r->classifications; - q /= r->classifications; - } -#endif - } - for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) - { - int z = r->begin + pcount * r->part_size; -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[0][class_set][i]; -#else - int c = classifications[0][pcount]; -#endif - int b = r->residue_books[c][pass]; - if (b >= 0) - { - Codebook *book = f->codebooks + b; - if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) - goto done; - } - else - { - z += r->part_size; - c_inter = z % ch; - p_inter = z / ch; - } - } -#ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; -#endif - } - } - } - goto done; - } - CHECK(f); + } + goto done; + } + CHECK(f); - for (pass = 0; pass < 8; ++pass) - { - int pcount = 0, class_set = 0; - while (pcount < part_read) - { - if (pass == 0) - { - for (j = 0; j < ch; ++j) - { - if (!do_not_decode[j]) - { - Codebook *c = f->codebooks + r->classbook; - int temp; - DECODE(temp, f, c); - if (temp == EOP) - goto done; + for (pass = 0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + while (pcount < part_read) { + if (pass == 0) { + for (j = 0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks + r->classbook; + int temp; + DECODE(temp, f, c); + if (temp == EOP) + goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - part_classdata[j][class_set] = r->classdata[temp]; + part_classdata[j][class_set] = r->classdata[temp]; #else - for (i = classwords - 1; i >= 0; --i) - { - classifications[j][i + pcount] = temp % r->classifications; - temp /= r->classifications; - } + for (i = classwords - 1; i >= 0; --i) { + classifications[j][i + pcount] = temp % r->classifications; + temp /= r->classifications; + } #endif - } + } + } } - } - for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) - { - for (j = 0; j < ch; ++j) - { - if (!do_not_decode[j]) - { + for (i = 0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j = 0; j < ch; ++j) { + if (!do_not_decode[j]) { #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - int c = part_classdata[j][class_set][i]; + int c = part_classdata[j][class_set][i]; #else - int c = classifications[j][pcount]; + int c = classifications[j][pcount]; #endif - int b = r->residue_books[c][pass]; - if (b >= 0) - { - float *target = residue_buffers[j]; - int offset = r->begin + pcount * r->part_size; - int n = r->part_size; - Codebook *book = f->codebooks + b; - if (!residue_decode(f, book, target, offset, n, rtype)) - goto done; - } - } + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } } - } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - ++class_set; + ++class_set; #endif - } - } + } + } done: - CHECK(f); + CHECK(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - temp_free(f, part_classdata); + temp_free(f, part_classdata); #else - temp_free(f, classifications); + temp_free(f, classifications); #endif - temp_alloc_restore(f, temp_alloc_point); + temp_alloc_restore(f, temp_alloc_point); } #if 0 @@ -2525,60 +2249,55 @@ void inverse_mdct_slow(float *buffer, int n) } #elif 0 // same as above, but just barely able to run in real time on modern machines -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - float mcos[16384]; - int i, j; - int n2 = n >> 1, nmask = (n << 2) - 1; - float *x = (float *)malloc(sizeof(*x) * n2); - memcpy(x, buffer, sizeof(*x) * n2); - for (i = 0; i < 4 * n; ++i) - mcos[i] = (float)cos(M_PI / 2 * i / n); +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { + float mcos[16384]; + int i, j; + int n2 = n >> 1, nmask = (n << 2) - 1; + float *x = (float *)malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i = 0; i < 4 * n; ++i) + mcos[i] = (float)cos(M_PI / 2 * i / n); - for (i = 0; i < n; ++i) - { - float acc = 0; - for (j = 0; j < n2; ++j) - acc += x[j] * mcos[(2 * i + 1 + n2) * (2 * j + 1) & nmask]; - buffer[i] = acc; - } - free(x); + for (i = 0; i < n; ++i) { + float acc = 0; + for (j = 0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2) * (2 * j + 1) & nmask]; + buffer[i] = acc; + } + free(x); } #elif 0 // transform to use a slow dct-iv; this is STILL basically trivial, // but only requires half as many ops -void dct_iv_slow(float *buffer, int n) -{ - float mcos[16384]; - float x[2048]; - int i, j; - int n2 = n >> 1, nmask = (n << 3) - 1; - memcpy(x, buffer, sizeof(*x) * n); - for (i = 0; i < 8 * n; ++i) - mcos[i] = (float)cos(M_PI / 4 * i / n); - for (i = 0; i < n; ++i) - { - float acc = 0; - for (j = 0; j < n; ++j) - acc += x[j] * mcos[((2 * i + 1) * (2 * j + 1)) & nmask]; - buffer[i] = acc; - } +void dct_iv_slow(float *buffer, int n) { + float mcos[16384]; + float x[2048]; + int i, j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i = 0; i < 8 * n; ++i) + mcos[i] = (float)cos(M_PI / 4 * i / n); + for (i = 0; i < n; ++i) { + float acc = 0; + for (j = 0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1) * (2 * j + 1)) & nmask]; + buffer[i] = acc; + } } -void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) -{ - int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; - float temp[4096]; +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; - memcpy(temp, buffer, n2 * sizeof(float)); - dct_iv_slow(temp, n2); // returns -c'-d, a-b' + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' - for (i = 0; i < n4; ++i) - buffer[i] = temp[i + n4]; // a-b' - for (; i < n3_4; ++i) - buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' - for (; i < n; ++i) - buffer[i] = -temp[i - n3_4]; // c'+d + for (i = 0; i < n4; ++i) + buffer[i] = temp[i + n4]; // a-b' + for (; i < n3_4; ++i) + buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for (; i < n; ++i) + buffer[i] = -temp[i - n3_4]; // c'+d } #endif @@ -2591,13 +2310,13 @@ void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) // by Jeff Roberts... useful for performance comparison typedef struct { - int n; - int log2n; + int n; + int log2n; - float *trig; - int *bitrev; + float *trig; + int *bitrev; - float scale; + float scale; } mdct_lookup; extern void mdct_init(mdct_lookup *lookup, int n); @@ -2606,565 +2325,542 @@ extern void mdct_backward(mdct_lookup *init, float *in, float *out); mdct_lookup M1, M2; -void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - mdct_lookup *M; - if (M1.n == n) - M = &M1; - else if (M2.n == n) - M = &M2; - else if (M1.n == 0) - { - mdct_init(&M1, n); - M = &M1; - } - else - { - if (M2.n) - __asm int 3; - mdct_init(&M2, n); - M = &M2; - } +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { + mdct_lookup *M; + if (M1.n == n) + M = &M1; + else if (M2.n == n) + M = &M2; + else if (M1.n == 0) { + mdct_init(&M1, n); + M = &M1; + } else { + if (M2.n) + __asm int 3; + mdct_init(&M2, n); + M = &M2; + } - mdct_backward(M, buffer, buffer); + mdct_backward(M, buffer, buffer); } #endif // the following were split out into separate functions while optimizing; // they could be pushed back up but eh. __forceinline showed no change; // they're probably already being inlined. -static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) -{ - float *ee0 = e + i_off; - float *ee2 = ee0 + k_off; - int i; +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) { + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; - assert((n & 3) == 0); - for (i = (n >> 2); i > 0; --i) - { - float k00_20, k01_21; - k00_20 = ee0[0] - ee2[0]; - k01_21 = ee0[-1] - ee2[-1]; - ee0[0] += ee2[0]; // ee0[ 0] = ee0[ 0] + ee2[ 0]; - ee0[-1] += ee2[-1]; // ee0[-1] = ee0[-1] + ee2[-1]; - ee2[0] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; + assert((n & 3) == 0); + for (i = (n >> 2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[0] - ee2[0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[0] += ee2[0]; // ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1]; // ee0[-1] = ee0[-1] + ee2[-1]; + ee2[0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; - k00_20 = ee0[-2] - ee2[-2]; - k01_21 = ee0[-3] - ee2[-3]; - ee0[-2] += ee2[-2]; // ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] += ee2[-3]; // ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2]; // ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3]; // ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; - k00_20 = ee0[-4] - ee2[-4]; - k01_21 = ee0[-5] - ee2[-5]; - ee0[-4] += ee2[-4]; // ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] += ee2[-5]; // ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4]; // ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5]; // ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; - k00_20 = ee0[-6] - ee2[-6]; - k01_21 = ee0[-7] - ee2[-7]; - ee0[-6] += ee2[-6]; // ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] += ee2[-7]; // ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; - ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; - A += 8; - ee0 -= 8; - ee2 -= 8; - } + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6]; // ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7]; // ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } } -static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) -{ - int i; - float k00_20, k01_21; +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) { + int i; + float k00_20, k01_21; - float *e0 = e + d0; - float *e2 = e0 + k_off; + float *e0 = e + d0; + float *e2 = e0 + k_off; - for (i = lim >> 2; i > 0; --i) - { - k00_20 = e0[-0] - e2[-0]; - k01_21 = e0[-1] - e2[-1]; - e0[-0] += e2[-0]; // e0[-0] = e0[-0] + e2[-0]; - e0[-1] += e2[-1]; // e0[-1] = e0[-1] + e2[-1]; - e2[-0] = (k00_20)*A[0] - (k01_21)*A[1]; - e2[-1] = (k01_21)*A[0] + (k00_20)*A[1]; + for (i = lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0]; // e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1]; // e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21)*A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20)*A[1]; - A += k1; + A += k1; - k00_20 = e0[-2] - e2[-2]; - k01_21 = e0[-3] - e2[-3]; - e0[-2] += e2[-2]; // e0[-2] = e0[-2] + e2[-2]; - e0[-3] += e2[-3]; // e0[-3] = e0[-3] + e2[-3]; - e2[-2] = (k00_20)*A[0] - (k01_21)*A[1]; - e2[-3] = (k01_21)*A[0] + (k00_20)*A[1]; + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2]; // e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3]; // e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21)*A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20)*A[1]; - A += k1; + A += k1; - k00_20 = e0[-4] - e2[-4]; - k01_21 = e0[-5] - e2[-5]; - e0[-4] += e2[-4]; // e0[-4] = e0[-4] + e2[-4]; - e0[-5] += e2[-5]; // e0[-5] = e0[-5] + e2[-5]; - e2[-4] = (k00_20)*A[0] - (k01_21)*A[1]; - e2[-5] = (k01_21)*A[0] + (k00_20)*A[1]; + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4]; // e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5]; // e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21)*A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20)*A[1]; - A += k1; + A += k1; - k00_20 = e0[-6] - e2[-6]; - k01_21 = e0[-7] - e2[-7]; - e0[-6] += e2[-6]; // e0[-6] = e0[-6] + e2[-6]; - e0[-7] += e2[-7]; // e0[-7] = e0[-7] + e2[-7]; - e2[-6] = (k00_20)*A[0] - (k01_21)*A[1]; - e2[-7] = (k01_21)*A[0] + (k00_20)*A[1]; + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6]; // e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7]; // e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21)*A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20)*A[1]; - e0 -= 8; - e2 -= 8; + e0 -= 8; + e2 -= 8; - A += k1; - } + A += k1; + } } -static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) -{ - int i; - float A0 = A[0]; - float A1 = A[0 + 1]; - float A2 = A[0 + a_off]; - float A3 = A[0 + a_off + 1]; - float A4 = A[0 + a_off * 2 + 0]; - float A5 = A[0 + a_off * 2 + 1]; - float A6 = A[0 + a_off * 3 + 0]; - float A7 = A[0 + a_off * 3 + 1]; +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) { + int i; + float A0 = A[0]; + float A1 = A[0 + 1]; + float A2 = A[0 + a_off]; + float A3 = A[0 + a_off + 1]; + float A4 = A[0 + a_off * 2 + 0]; + float A5 = A[0 + a_off * 2 + 1]; + float A6 = A[0 + a_off * 3 + 0]; + float A7 = A[0 + a_off * 3 + 1]; - float k00, k11; + float k00, k11; - float *ee0 = e + i_off; - float *ee2 = ee0 + k_off; + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; - for (i = n; i > 0; --i) - { - k00 = ee0[0] - ee2[0]; - k11 = ee0[-1] - ee2[-1]; - ee0[0] = ee0[0] + ee2[0]; - ee0[-1] = ee0[-1] + ee2[-1]; - ee2[0] = (k00)*A0 - (k11)*A1; - ee2[-1] = (k11)*A0 + (k00)*A1; + for (i = n; i > 0; --i) { + k00 = ee0[0] - ee2[0]; + k11 = ee0[-1] - ee2[-1]; + ee0[0] = ee0[0] + ee2[0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[0] = (k00)*A0 - (k11)*A1; + ee2[-1] = (k11)*A0 + (k00)*A1; - k00 = ee0[-2] - ee2[-2]; - k11 = ee0[-3] - ee2[-3]; - ee0[-2] = ee0[-2] + ee2[-2]; - ee0[-3] = ee0[-3] + ee2[-3]; - ee2[-2] = (k00)*A2 - (k11)*A3; - ee2[-3] = (k11)*A2 + (k00)*A3; + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00)*A2 - (k11)*A3; + ee2[-3] = (k11)*A2 + (k00)*A3; - k00 = ee0[-4] - ee2[-4]; - k11 = ee0[-5] - ee2[-5]; - ee0[-4] = ee0[-4] + ee2[-4]; - ee0[-5] = ee0[-5] + ee2[-5]; - ee2[-4] = (k00)*A4 - (k11)*A5; - ee2[-5] = (k11)*A4 + (k00)*A5; + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00)*A4 - (k11)*A5; + ee2[-5] = (k11)*A4 + (k00)*A5; - k00 = ee0[-6] - ee2[-6]; - k11 = ee0[-7] - ee2[-7]; - ee0[-6] = ee0[-6] + ee2[-6]; - ee0[-7] = ee0[-7] + ee2[-7]; - ee2[-6] = (k00)*A6 - (k11)*A7; - ee2[-7] = (k11)*A6 + (k00)*A7; + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00)*A6 - (k11)*A7; + ee2[-7] = (k11)*A6 + (k00)*A7; - ee0 -= k0; - ee2 -= k0; - } + ee0 -= k0; + ee2 -= k0; + } } -static __forceinline void iter_54(float *z) -{ - float k00, k11, k22, k33; - float y0, y1, y2, y3; +static __forceinline void iter_54(float *z) { + float k00, k11, k22, k33; + float y0, y1, y2, y3; - k00 = z[0] - z[-4]; - y0 = z[0] + z[-4]; - y2 = z[-2] + z[-6]; - k22 = z[-2] - z[-6]; + k00 = z[0] - z[-4]; + y0 = z[0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; - z[-0] = y0 + y2; // z0 + z4 + z2 + z6 - z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 - // done with y0,y2 + // done with y0,y2 - k33 = z[-3] - z[-7]; + k33 = z[-3] - z[-7]; - z[-4] = k00 + k33; // z0 - z4 + z3 - z7 - z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 - // done with k33 + // done with k33 - k11 = z[-1] - z[-5]; - y1 = z[-1] + z[-5]; - y3 = z[-3] + z[-7]; + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; - z[-1] = y1 + y3; // z1 + z5 + z3 + z7 - z[-3] = y1 - y3; // z1 + z5 - z3 - z7 - z[-5] = k11 - k22; // z1 - z5 + z2 - z6 - z[-7] = k11 + k22; // z1 - z5 - z2 + z6 + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 } -static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) -{ - int a_off = base_n >> 3; - float A2 = A[0 + a_off]; - float *z = e + i_off; - float *base = z - 16 * n; +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { + int a_off = base_n >> 3; + float A2 = A[0 + a_off]; + float *z = e + i_off; + float *base = z - 16 * n; - while (z > base) - { - float k00, k11; + while (z > base) { + float k00, k11; - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11; + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11; - k00 = z[-2] - z[-10]; - k11 = z[-3] - z[-11]; - z[-2] = z[-2] + z[-10]; - z[-3] = z[-3] + z[-11]; - z[-10] = (k00 + k11) * A2; - z[-11] = (k11 - k00) * A2; + k00 = z[-2] - z[-10]; + k11 = z[-3] - z[-11]; + z[-2] = z[-2] + z[-10]; + z[-3] = z[-3] + z[-11]; + z[-10] = (k00 + k11) * A2; + z[-11] = (k11 - k00) * A2; - k00 = z[-12] - z[-4]; // reverse to avoid a unary negation - k11 = z[-5] - z[-13]; - z[-4] = z[-4] + z[-12]; - z[-5] = z[-5] + z[-13]; - z[-12] = k11; - z[-13] = k00; + k00 = z[-12] - z[-4]; // reverse to avoid a unary negation + k11 = z[-5] - z[-13]; + z[-4] = z[-4] + z[-12]; + z[-5] = z[-5] + z[-13]; + z[-12] = k11; + z[-13] = k00; - k00 = z[-14] - z[-6]; // reverse to avoid a unary negation - k11 = z[-7] - z[-15]; - z[-6] = z[-6] + z[-14]; - z[-7] = z[-7] + z[-15]; - z[-14] = (k00 + k11) * A2; - z[-15] = (k00 - k11) * A2; + k00 = z[-14] - z[-6]; // reverse to avoid a unary negation + k11 = z[-7] - z[-15]; + z[-6] = z[-6] + z[-14]; + z[-7] = z[-7] + z[-15]; + z[-14] = (k00 + k11) * A2; + z[-15] = (k00 - k11) * A2; - iter_54(z); - iter_54(z - 8); - z -= 16; - } + iter_54(z); + iter_54(z - 8); + z -= 16; + } } -static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) -{ - int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; - int ld; - // @OPTIMIZE: reduce register pressure by using fewer variables? - int save_point = temp_alloc_save(f); - float *buf2 = (float *)temp_alloc(f, n2 * sizeof(*buf2)); - float *u = NULL, *v = NULL; - // twiddle factors - float *A = f->A[blocktype]; +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *)temp_alloc(f, n2 * sizeof(*buf2)); + float *u = NULL, *v = NULL; + // twiddle factors + float *A = f->A[blocktype]; - // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" - // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. - // kernel from paper + // kernel from paper - // merged: - // copy and reflect spectral data - // step 0 + // merged: + // copy and reflect spectral data + // step 0 - // note that it turns out that the items added together during - // this step are, in fact, being added to themselves (as reflected - // by step 0). inexplicable inefficiency! this became obvious - // once I combined the passes. + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. - // so there's a missing 'times 2' here (for adding X to itself). - // this propagates through linearly to the end, where the numbers - // are 1/2 too small, and need to be compensated for. + // so there's a missing 'times 2' here (for adding X to itself). + // this propagates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. - { - float *d, *e, *AA, *e_stop; - d = &buf2[n2 - 2]; - AA = A; - e = &buffer[0]; - e_stop = &buffer[n2]; - while (e != e_stop) - { - d[1] = (e[0] * AA[0] - e[2] * AA[1]); - d[0] = (e[0] * AA[1] + e[2] * AA[0]); - d -= 2; - AA += 2; - e += 4; - } + { + float *d, *e, *AA, *e_stop; + d = &buf2[n2 - 2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2] * AA[1]); + d[0] = (e[0] * AA[1] + e[2] * AA[0]); + d -= 2; + AA += 2; + e += 4; + } - e = &buffer[n2 - 3]; - while (d >= buf2) - { - d[1] = (-e[2] * AA[0] - -e[0] * AA[1]); - d[0] = (-e[2] * AA[1] + -e[0] * AA[0]); - d -= 2; - AA += 2; - e -= 4; - } - } + e = &buffer[n2 - 3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0] * AA[1]); + d[0] = (-e[2] * AA[1] + -e[0] * AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } - // now we use symbolic names for these, so that we can - // possibly swap their meaning as we change which operations - // are in place + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place - u = buffer; - v = buf2; + u = buffer; + v = buf2; - // step 2 (paper output is w, now u) - // this could be in place, but the data ends up in the wrong - // place... _somebody_'s got to swap it, so this is nominated - { - float *AA = &A[n2 - 8]; - float *d0, *d1, *e0, *e1; + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2 - 8]; + float *d0, *d1, *e0, *e1; - e0 = &v[n4]; - e1 = &v[0]; + e0 = &v[n4]; + e1 = &v[0]; - d0 = &u[n4]; - d1 = &u[0]; + d0 = &u[n4]; + d1 = &u[0]; - while (AA >= A) - { - float v40_20, v41_21; + while (AA >= A) { + float v40_20, v41_21; - v41_21 = e0[1] - e1[1]; - v40_20 = e0[0] - e1[0]; - d0[1] = e0[1] + e1[1]; - d0[0] = e0[0] + e1[0]; - d1[1] = v41_21 * AA[4] - v40_20 * AA[5]; - d1[0] = v40_20 * AA[4] + v41_21 * AA[5]; + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21 * AA[4] - v40_20 * AA[5]; + d1[0] = v40_20 * AA[4] + v41_21 * AA[5]; - v41_21 = e0[3] - e1[3]; - v40_20 = e0[2] - e1[2]; - d0[3] = e0[3] + e1[3]; - d0[2] = e0[2] + e1[2]; - d1[3] = v41_21 * AA[0] - v40_20 * AA[1]; - d1[2] = v40_20 * AA[0] + v41_21 * AA[1]; + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21 * AA[0] - v40_20 * AA[1]; + d1[2] = v40_20 * AA[0] + v41_21 * AA[1]; - AA -= 8; + AA -= 8; - d0 += 4; - d1 += 4; - e0 += 4; - e1 += 4; - } - } + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } - // step 3 - ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions - // optimized step 3: + // optimized step 3: - // the original step3 loop can be nested r inside s or s inside r; - // it's written originally as s inside r, but this is dumb when r - // iterates many times, and s few. So I have two copies of it and - // switch between them halfway. + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. - // this is iteration 0 of step 3 - imdct_step3_iter0_loop(n >> 4, u, n2 - 1 - n4 * 0, -(n >> 3), A); - imdct_step3_iter0_loop(n >> 4, u, n2 - 1 - n4 * 1, -(n >> 3), A); + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2 - 1 - n4 * 0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2 - 1 - n4 * 1, -(n >> 3), A); - // this is iteration 1 of step 3 - imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 0, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 1, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 2, -(n >> 4), A, 16); - imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 3, -(n >> 4), A, 16); + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2 - 1 - n8 * 3, -(n >> 4), A, 16); - l = 2; - for (; l < (ld - 3) >> 1; ++l) - { - int k0 = n >> (l + 2), k0_2 = k0 >> 1; - int lim = 1 << (l + 1); - int i; - for (i = 0; i < lim; ++i) - imdct_step3_inner_r_loop(n >> (l + 4), u, n2 - 1 - k0 * i, -k0_2, A, 1 << (l + 3)); - } + l = 2; + for (; l < (ld - 3) >> 1; ++l) { + int k0 = n >> (l + 2), k0_2 = k0 >> 1; + int lim = 1 << (l + 1); + int i; + for (i = 0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l + 4), u, n2 - 1 - k0 * i, -k0_2, A, 1 << (l + 3)); + } - for (; l < ld - 6; ++l) - { - int k0 = n >> (l + 2), k1 = 1 << (l + 3), k0_2 = k0 >> 1; - int rlim = n >> (l + 6), r; - int lim = 1 << (l + 1); - int i_off; - float *A0 = A; - i_off = n2 - 1; - for (r = rlim; r > 0; --r) - { - imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); - A0 += k1 * 4; - i_off -= 8; - } - } + for (; l < ld - 6; ++l) { + int k0 = n >> (l + 2), k1 = 1 << (l + 3), k0_2 = k0 >> 1; + int rlim = n >> (l + 6), r; + int lim = 1 << (l + 1); + int i_off; + float *A0 = A; + i_off = n2 - 1; + for (r = rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1 * 4; + i_off -= 8; + } + } - // iterations with count: - // ld-6,-5,-4 all interleaved together - // the big win comes from getting rid of needless flops - // due to the constants on pass 5 & 4 being all 1 and 0; - // combining them to be simultaneous to improve cache made little difference - imdct_step3_inner_s_loop_ld654(n >> 5, u, n2 - 1, A, n); + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2 - 1, A, n); - // output is u + // output is u - // step 4, 5, and 6 - // cannot be in-place because of step 5 - { - uint16 *bitrev = f->bit_reverse[blocktype]; - // weirdly, I'd have thought reading sequentially and writing - // erratically would have been better than vice-versa, but in - // fact that's not what my testing showed. (That is, with - // j = bitreverse(i), do you read i and write j, or read j and write i.) + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) - float *d0 = &v[n4 - 4]; - float *d1 = &v[n2 - 4]; - while (d0 >= v) - { - int k4; + float *d0 = &v[n4 - 4]; + float *d1 = &v[n2 - 4]; + while (d0 >= v) { + int k4; - k4 = bitrev[0]; - d1[3] = u[k4 + 0]; - d1[2] = u[k4 + 1]; - d0[3] = u[k4 + 2]; - d0[2] = u[k4 + 3]; + k4 = bitrev[0]; + d1[3] = u[k4 + 0]; + d1[2] = u[k4 + 1]; + d0[3] = u[k4 + 2]; + d0[2] = u[k4 + 3]; - k4 = bitrev[1]; - d1[1] = u[k4 + 0]; - d1[0] = u[k4 + 1]; - d0[1] = u[k4 + 2]; - d0[0] = u[k4 + 3]; + k4 = bitrev[1]; + d1[1] = u[k4 + 0]; + d1[0] = u[k4 + 1]; + d0[1] = u[k4 + 2]; + d0[0] = u[k4 + 3]; - d0 -= 4; - d1 -= 4; - bitrev += 2; - } - } - // (paper output is u, now v) + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) - // data must be in buf2 - assert(v == buf2); + // data must be in buf2 + assert(v == buf2); - // step 7 (paper output is v, now v) - // this is now in place - { - float *C = f->C[blocktype]; - float *d, *e; + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; - d = v; - e = v + n2 - 4; + d = v; + e = v + n2 - 4; - while (d < e) - { - float a02, a11, b0, b1, b2, b3; + while (d < e) { + float a02, a11, b0, b1, b2, b3; - a02 = d[0] - e[2]; - a11 = d[1] + e[3]; + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; - b0 = C[1] * a02 + C[0] * a11; - b1 = C[1] * a11 - C[0] * a02; + b0 = C[1] * a02 + C[0] * a11; + b1 = C[1] * a11 - C[0] * a02; - b2 = d[0] + e[2]; - b3 = d[1] - e[3]; + b2 = d[0] + e[2]; + b3 = d[1] - e[3]; - d[0] = b2 + b0; - d[1] = b3 + b1; - e[2] = b2 - b0; - e[3] = b1 - b3; + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; - a02 = d[2] - e[0]; - a11 = d[3] + e[1]; + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; - b0 = C[3] * a02 + C[2] * a11; - b1 = C[3] * a11 - C[2] * a02; + b0 = C[3] * a02 + C[2] * a11; + b1 = C[3] * a11 - C[2] * a02; - b2 = d[2] + e[0]; - b3 = d[3] - e[1]; + b2 = d[2] + e[0]; + b3 = d[3] - e[1]; - d[2] = b2 + b0; - d[3] = b3 + b1; - e[0] = b2 - b0; - e[1] = b1 - b3; + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; - C += 4; - d += 4; - e -= 4; - } - } + C += 4; + d += 4; + e -= 4; + } + } - // data must be in buf2 + // data must be in buf2 - // step 8+decode (paper output is X, now buffer) - // this generates pairs of data a la 8 and pushes them directly through - // the decode kernel (pushing rather than pulling) to avoid having - // to make another pass later + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later - // this cannot POSSIBLY be in place, so we refer to the buffers directly + // this cannot POSSIBLY be in place, so we refer to the buffers directly - { - float *d0, *d1, *d2, *d3; + { + float *d0, *d1, *d2, *d3; - float *B = f->B[blocktype] + n2 - 8; - float *e = buf2 + n2 - 8; - d0 = &buffer[0]; - d1 = &buffer[n2 - 4]; - d2 = &buffer[n2]; - d3 = &buffer[n - 4]; - while (e >= v) - { - float p0, p1, p2, p3; + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2 - 4]; + d2 = &buffer[n2]; + d3 = &buffer[n - 4]; + while (e >= v) { + float p0, p1, p2, p3; - p3 = e[6] * B[7] - e[7] * B[6]; - p2 = -e[6] * B[6] - e[7] * B[7]; + p3 = e[6] * B[7] - e[7] * B[6]; + p2 = -e[6] * B[6] - e[7] * B[7]; - d0[0] = p3; - d1[3] = -p3; - d2[0] = p2; - d3[3] = p2; + d0[0] = p3; + d1[3] = -p3; + d2[0] = p2; + d3[3] = p2; - p1 = e[4] * B[5] - e[5] * B[4]; - p0 = -e[4] * B[4] - e[5] * B[5]; + p1 = e[4] * B[5] - e[5] * B[4]; + p0 = -e[4] * B[4] - e[5] * B[5]; - d0[1] = p1; - d1[2] = -p1; - d2[1] = p0; - d3[2] = p0; + d0[1] = p1; + d1[2] = -p1; + d2[1] = p0; + d3[2] = p0; - p3 = e[2] * B[3] - e[3] * B[2]; - p2 = -e[2] * B[2] - e[3] * B[3]; + p3 = e[2] * B[3] - e[3] * B[2]; + p2 = -e[2] * B[2] - e[3] * B[3]; - d0[2] = p3; - d1[1] = -p3; - d2[2] = p2; - d3[1] = p2; + d0[2] = p3; + d1[1] = -p3; + d2[2] = p2; + d3[1] = p2; - p1 = e[0] * B[1] - e[1] * B[0]; - p0 = -e[0] * B[0] - e[1] * B[1]; + p1 = e[0] * B[1] - e[1] * B[0]; + p0 = -e[0] * B[0] - e[1] * B[1]; - d0[3] = p1; - d1[0] = -p1; - d2[3] = p0; - d3[0] = p0; + d0[3] = p1; + d1[0] = -p1; + d2[3] = p0; + d3[0] = p0; - B -= 8; - e -= 8; - d0 += 4; - d2 += 4; - d1 -= 4; - d3 -= 4; - } - } + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } - temp_free(f, buf2); - temp_alloc_restore(f, save_point); + temp_free(f, buf2); + temp_alloc_restore(f, save_point); } #if 0 @@ -3295,14 +2991,13 @@ void inverse_mdct_naive(float *buffer, int n) } #endif -static float *get_window(vorb *f, int len) -{ - len <<= 1; - if (len == f->blocksize_0) - return f->window[0]; - if (len == f->blocksize_1) - return f->window[1]; - return NULL; +static float *get_window(vorb *f, int len) { + len <<= 1; + if (len == f->blocksize_0) + return f->window[0]; + if (len == f->blocksize_1) + return f->window[1]; + return NULL; } #ifndef STB_VORBIS_NO_DEFER_FLOOR @@ -3310,46 +3005,40 @@ typedef int16 YTYPE; #else typedef int YTYPE; #endif -static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) -{ - int n2 = n >> 1; - int s = map->chan[i].mux, floor; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) - { - return error(f, VORBIS_invalid_stream); - } - else - { - Floor1 *g = &f->floor_config[floor].floor1; - int j, q; - int lx = 0, ly = finalY[0] * g->floor1_multiplier; - for (q = 1; q < g->values; ++q) - { - j = g->sorted_order[q]; +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) { + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j, q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q = 1; q < g->values; ++q) { + j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR - if (finalY[j] >= 0) + if (finalY[j] >= 0) #else - if (step2_flag[j]) + if (step2_flag[j]) #endif - { - int hy = finalY[j] * g->floor1_multiplier; - int hx = g->Xlist[j]; - if (lx != hx) - draw_line(target, lx, ly, hx, hy, n2); + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx, ly, hx, hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j = lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); CHECK(f); - lx = hx, ly = hy; - } - } - if (lx < n2) - { - // optimization of: draw_line(target, lx,ly, n,ly, n2); - for (j = lx; j < n2; ++j) - LINE_OP(target[j], inverse_db_table[ly]); - CHECK(f); - } - } - return TRUE; + } + } + return TRUE; } // The meaning of "left" and "right" @@ -3366,1732 +3055,1533 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f // has to be the same as frame N+1's left_end-left_start (which they are by // construction) -static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - Mode *m; - int i, n, prev, next, window_center; - f->channel_buffer_start = f->channel_buffer_end = 0; +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; retry: - if (f->eof) - return FALSE; - if (!maybe_start_packet(f)) - return FALSE; - // check packet type - if (get_bits(f, 1) != 0) - { - if (IS_PUSH_MODE(f)) - return error(f, VORBIS_bad_packet_type); - while (EOP != get8_packet(f)) - ; - goto retry; - } + if (f->eof) + return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f, 1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f, VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - i = get_bits(f, ilog(f->mode_count - 1)); - if (i == EOP) - return FALSE; - if (i >= f->mode_count) - return FALSE; - *mode = i; - m = f->mode_config + i; - if (m->blockflag) - { - n = f->blocksize_1; - prev = get_bits(f, 1); - next = get_bits(f, 1); - } - else - { - prev = next = 0; - n = f->blocksize_0; - } + i = get_bits(f, ilog(f->mode_count - 1)); + if (i == EOP) + return FALSE; + if (i >= f->mode_count) + return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f, 1); + next = get_bits(f, 1); + } else { + prev = next = 0; + n = f->blocksize_0; + } - // WINDOWING + // WINDOWING - window_center = n >> 1; - if (m->blockflag && !prev) - { - *p_left_start = (n - f->blocksize_0) >> 2; - *p_left_end = (n + f->blocksize_0) >> 2; - } - else - { - *p_left_start = 0; - *p_left_end = window_center; - } - if (m->blockflag && !next) - { - *p_right_start = (n * 3 - f->blocksize_0) >> 2; - *p_right_end = (n * 3 + f->blocksize_0) >> 2; - } - else - { - *p_right_start = window_center; - *p_right_end = n; - } + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n * 3 - f->blocksize_0) >> 2; + *p_right_end = (n * 3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } - return TRUE; + return TRUE; } -static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) -{ - Mapping *map; - int i, j, k, n, n2; - int zero_channel[256]; - int really_zero_channel[256]; +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) { + Mapping *map; + int i, j, k, n, n2; + int zero_channel[256]; + int really_zero_channel[256]; - // WINDOWING + // WINDOWING - n = f->blocksize[m->blockflag]; - map = &f->mapping[m->mapping]; + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; - // FLOORS - n2 = n >> 1; + // FLOORS + n2 = n >> 1; - CHECK(f); + CHECK(f); - for (i = 0; i < f->channels; ++i) - { - int s = map->chan[i].mux, floor; - zero_channel[i] = FALSE; - floor = map->submap_floor[s]; - if (f->floor_types[floor] == 0) - { - return error(f, VORBIS_invalid_stream); - } - else - { - Floor1 *g = &f->floor_config[floor].floor1; - if (get_bits(f, 1)) - { - short *finalY; - uint8 step2_flag[256]; - static int range_list[4] = {256, 128, 86, 64}; - int range = range_list[g->floor1_multiplier - 1]; - int offset = 2; - finalY = f->finalY[i]; - finalY[0] = get_bits(f, ilog(range) - 1); - finalY[1] = get_bits(f, ilog(range) - 1); - for (j = 0; j < g->partitions; ++j) - { - int pclass = g->partition_class_list[j]; - int cdim = g->class_dimensions[pclass]; - int cbits = g->class_subclasses[pclass]; - int csub = (1 << cbits) - 1; - int cval = 0; - if (cbits) - { - Codebook *c = f->codebooks + g->class_masterbooks[pclass]; - DECODE(cval, f, c); - } - for (k = 0; k < cdim; ++k) - { - int book = g->subclass_books[pclass][cval & csub]; - cval = cval >> cbits; - if (book >= 0) - { - int temp; - Codebook *c = f->codebooks + book; - DECODE(temp, f, c); - finalY[offset++] = temp; - } - else - finalY[offset++] = 0; - } - } - if (f->valid_bits == INVALID_BITS) - goto error; // behavior according to spec - step2_flag[0] = step2_flag[1] = 1; - for (j = 2; j < g->values; ++j) - { - int low, high, pred, highroom, lowroom, room, val; - low = g->neighbors[j][0]; - high = g->neighbors[j][1]; - // neighbors(g->Xlist, j, &low, &high); - pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); - val = finalY[j]; - highroom = range - pred; - lowroom = pred; - if (highroom < lowroom) - room = highroom * 2; - else - room = lowroom * 2; - if (val) - { - step2_flag[low] = step2_flag[high] = 1; - step2_flag[j] = 1; - if (val >= room) - if (highroom > lowroom) - finalY[j] = val - lowroom + pred; - else - finalY[j] = pred - val + highroom - 1; - else if (val & 1) - finalY[j] = pred - ((val + 1) >> 1); - else - finalY[j] = pred + (val >> 1); - } - else - { - step2_flag[j] = 0; - finalY[j] = pred; - } - } - -#ifdef STB_VORBIS_NO_DEFER_FLOOR - do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); -#else - // defer final floor computation until _after_ residue - for (j = 0; j < g->values; ++j) - { - if (!step2_flag[j]) - finalY[j] = -1; - } -#endif - } - else - { - error: - zero_channel[i] = TRUE; - } - // So we just defer everything else to later - - // at this point we've decoded the floor into buffer - } - } - CHECK(f); - // at this point we've decoded all floors - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - - // re-enable coupled channels if necessary - memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); - for (i = 0; i < map->coupling_steps; ++i) - if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) - { - zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; - } - - CHECK(f); - // RESIDUE DECODE - for (i = 0; i < map->submaps; ++i) - { - float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; - int r; - uint8 do_not_decode[256]; - int ch = 0; - for (j = 0; j < f->channels; ++j) - { - if (map->chan[j].mux == i) - { - if (zero_channel[j]) - { - do_not_decode[ch] = TRUE; - residue_buffers[ch] = NULL; - } - else - { - do_not_decode[ch] = FALSE; - residue_buffers[ch] = f->channel_buffers[j]; - } - ++ch; - } - } - r = map->submap_residue[i]; - decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); - } - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - CHECK(f); - - // INVERSE COUPLING - for (i = map->coupling_steps - 1; i >= 0; --i) - { - int n2 = n >> 1; - float *m = f->channel_buffers[map->chan[i].magnitude]; - float *a = f->channel_buffers[map->chan[i].angle]; - for (j = 0; j < n2; ++j) - { - float a2, m2; - if (m[j] > 0) - if (a[j] > 0) - m2 = m[j], a2 = m[j] - a[j]; - else - a2 = m[j], m2 = m[j] + a[j]; - else if (a[j] > 0) - m2 = m[j], a2 = m[j] + a[j]; - else - a2 = m[j], m2 = m[j] - a[j]; - m[j] = m2; - a[j] = a2; - } - } - CHECK(f); - - // finish decoding the floors -#ifndef STB_VORBIS_NO_DEFER_FLOOR - for (i = 0; i < f->channels; ++i) - { - if (really_zero_channel[i]) - { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } - else - { - do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); - } - } -#else - for (i = 0; i < f->channels; ++i) - { - if (really_zero_channel[i]) - { - memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); - } - else - { - for (j = 0; j < n2; ++j) - f->channel_buffers[i][j] *= f->floor_buffers[i][j]; - } - } -#endif - - // INVERSE MDCT - CHECK(f); - for (i = 0; i < f->channels; ++i) - inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); - CHECK(f); - - // this shouldn't be necessary, unless we exited on an error - // and want to flush to get to the next packet - flush_packet(f); - - if (f->first_decode) - { - // assume we start so first non-discarded sample is sample 0 - // this isn't to spec, but spec would require us to read ahead - // and decode the size of all current frames--could be done, - // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard - // we might have to discard samples "from" the next frame too, - // if we're lapping a large block then a small at the start? - f->discard_samples_deferred = n - right_end; - f->current_loc_valid = TRUE; - f->first_decode = FALSE; - } - else if (f->discard_samples_deferred) - { - if (f->discard_samples_deferred >= right_start - left_start) - { - f->discard_samples_deferred -= (right_start - left_start); - left_start = right_start; - *p_left = left_start; - } - else - { - left_start += f->discard_samples_deferred; - *p_left = left_start; - f->discard_samples_deferred = 0; - } - } - else if (f->previous_length == 0 && f->current_loc_valid) - { - // we're recovering from a seek... that means we're going to discard - // the samples from this packet even though we know our position from - // the last page header, so we need to update the position based on - // the discarded samples here - // but wait, the code below is going to add this in itself even - // on a discard, so we don't need to do it here... - } - - // check if we have ogg information about the sample # for this packet - if (f->last_seg_which == f->end_seg_with_known_loc) - { - // if we have a valid current loc, and this is final: - if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) - { - uint32 current_end = f->known_loc_for_packet; - // then let's infer the size of the (probably) short final frame - if (current_end < f->current_loc + (right_end - left_start)) - { - if (current_end < f->current_loc) - { - // negative truncation, that's impossible! - *len = 0; - } - else - { - *len = current_end - f->current_loc; - } - *len += left_start; // this doesn't seem right, but has no ill effect on my test files - if (*len > right_end) - *len = right_end; // this should never happen - f->current_loc += *len; - return TRUE; - } - } - // otherwise, just set our sample loc - // guess that the ogg granule pos refers to the _middle_ of the - // last frame? - // set f->current_loc to the position of left_start - f->current_loc = f->known_loc_for_packet - (n2 - left_start); - f->current_loc_valid = TRUE; - } - if (f->current_loc_valid) - f->current_loc += (right_start - left_start); - - if (f->alloc.alloc_buffer) - assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); - *len = right_end; // ignore samples after the window goes to 0 - CHECK(f); - - return TRUE; -} - -static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) -{ - int mode, left_end, right_end; - if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) - return 0; - return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); -} - -static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) -{ - int prev, i, j; - // we use right&left (the start of the right- and left-window sin()-regions) - // to determine how much to return, rather than inferring from the rules - // (same result, clearer code); 'left' indicates where our sin() window - // starts, therefore where the previous window's right edge starts, and - // therefore where to start mixing from the previous buffer. 'right' - // indicates where our sin() ending-window starts, therefore that's where - // we start saving, and where our returned-data ends. - - // mixin from previous window - if (f->previous_length) - { - int i, j, n = f->previous_length; - float *w = get_window(f, n); - if (w == NULL) - return 0; - for (i = 0; i < f->channels; ++i) - { - for (j = 0; j < n; ++j) - f->channel_buffers[i][left + j] = - f->channel_buffers[i][left + j] * w[j] + - f->previous_window[i][j] * w[n - 1 - j]; - } - } - - prev = f->previous_length; - - // last half of this data becomes previous window - f->previous_length = len - right; - - // @OPTIMIZE: could avoid this copy by double-buffering the - // output (flipping previous_window with channel_buffers), but - // then previous_window would have to be 2x as large, and - // channel_buffers couldn't be temp mem (although they're NOT - // currently temp mem, they could be (unless we want to level - // performance by spreading out the computation)) - for (i = 0; i < f->channels; ++i) - for (j = 0; right + j < len; ++j) - f->previous_window[i][j] = f->channel_buffers[i][right + j]; - - if (!prev) - // there was no previous packet, so this data isn't valid... - // this isn't entirely true, only the would-have-overlapped data - // isn't valid, but this seems to be what the spec requires - return 0; - - // truncate a short frame - if (len < right) - right = len; - - f->samples_output += right - left; - - return right - left; -} - -static int vorbis_pump_first_frame(stb_vorbis *f) -{ - int len, right, left, res; - res = vorbis_decode_packet(f, &len, &left, &right); - if (res) - vorbis_finish_frame(f, len, left, right); - return res; -} - -#ifndef STB_VORBIS_NO_PUSHDATA_API -static int is_whole_packet_present(stb_vorbis *f) -{ - // make sure that we have the packet available before continuing... - // this requires a full ogg parse, but we know we can fetch from f->stream - - // instead of coding this out explicitly, we could save the current read state, - // read the next packet with get8() until end-of-packet, check f->eof, then - // reset the state? but that would be slower, esp. since we'd have over 256 bytes - // of state to restore (primarily the page segment table) - - int s = f->next_seg, first = TRUE; - uint8 *p = f->stream; - - if (s != -1) - { // if we're not starting the packet with a 'continue on next page' flag - for (; s < f->segment_count; ++s) - { - p += f->segments[s]; - if (f->segments[s] < 255) // stop at first short segment - break; - } - // either this continues, or it ends it... - if (s == f->segment_count) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) - return error(f, VORBIS_need_more_data); - first = FALSE; - } - for (; s == -1;) - { - uint8 *q; - int n; - - // check that we have the page header ready - if (p + 26 >= f->stream_end) - return error(f, VORBIS_need_more_data); - // validate the page - if (memcmp(p, ogg_page_header, 4)) - return error(f, VORBIS_invalid_stream); - if (p[4] != 0) - return error(f, VORBIS_invalid_stream); - if (first) - { // the first segment must NOT have 'continued_packet', later ones MUST - if (f->previous_length) - if ((p[5] & PAGEFLAG_continued_packet)) - return error(f, VORBIS_invalid_stream); - // if no previous length, we're resynching, so we can come in on a continued-packet, - // which we'll just drop - } - else - { - if (!(p[5] & PAGEFLAG_continued_packet)) + for (i = 0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); - } - n = p[26]; // segment counts - q = p + 27; // q points to segment table - p = q + n; // advance past header - // make sure we've read the segment table - if (p > f->stream_end) - return error(f, VORBIS_need_more_data); - for (s = 0; s < n; ++s) - { - p += q[s]; - if (q[s] < 255) - break; - } - if (s == n) - s = -1; // set 'crosses page' flag - if (p > f->stream_end) - return error(f, VORBIS_need_more_data); - first = FALSE; - } - return TRUE; + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = {256, 128, 86, 64}; + int range = range_list[g->floor1_multiplier - 1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range) - 1); + finalY[1] = get_bits(f, ilog(range) - 1); + for (j = 0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits) - 1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval, f, c); + } + for (k = 0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp, f, c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) + goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j = 2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + // neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else if (val & 1) + finalY[j] = pred - ((val + 1) >> 1); + else + finalY[j] = pred + (val >> 1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j = 0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i = 0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); + // RESIDUE DECODE + for (i = 0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j = 0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + + // INVERSE COUPLING + for (i = map->coupling_steps - 1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle]; + for (j = 0; j < n2; ++j) { + float a2, m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i = 0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i = 0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j = 0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + + // INVERSE MDCT + CHECK(f); + for (i = 0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end - left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) + *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2 - left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; } -#endif // !STB_VORBIS_NO_PUSHDATA_API -static int start_decoder(vorb *f) -{ - uint8 header[6], x, y; - int len, i, j, k, max_submaps = 0; - int longest_floorlist = 0; +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) { + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) + return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} - // first page, first packet - f->first_decode = TRUE; +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) { + int prev, i, j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. - if (!start_page(f)) - return FALSE; - // validate page flag - if (!(f->page_flag & PAGEFLAG_first_page)) - return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_last_page) - return error(f, VORBIS_invalid_first_page); - if (f->page_flag & PAGEFLAG_continued_packet) - return error(f, VORBIS_invalid_first_page); - // check for expected packet length - if (f->segment_count != 1) - return error(f, VORBIS_invalid_first_page); - if (f->segments[0] != 30) - { - // check for the Ogg skeleton fishead identifying header to refine our error - if (f->segments[0] == 64 && - getn(f, header, 6) && - header[0] == 'f' && - header[1] == 'i' && - header[2] == 's' && - header[3] == 'h' && - header[4] == 'e' && - header[5] == 'a' && - get8(f) == 'd' && - get8(f) == '\0') - return error(f, VORBIS_ogg_skeleton_not_supported); - else - return error(f, VORBIS_invalid_first_page); - } + // mixin from previous window + if (f->previous_length) { + int i, j, n = f->previous_length; + float *w = get_window(f, n); + if (w == NULL) + return 0; + for (i = 0; i < f->channels; ++i) { + for (j = 0; j < n; ++j) + f->channel_buffers[i][left + j] = + f->channel_buffers[i][left + j] * w[j] + + f->previous_window[i][j] * w[n - 1 - j]; + } + } - // read packet - // check packet header - if (get8(f) != VORBIS_packet_id) - return error(f, VORBIS_invalid_first_page); - if (!getn(f, header, 6)) - return error(f, VORBIS_unexpected_eof); - if (!vorbis_validate(header)) - return error(f, VORBIS_invalid_first_page); - // vorbis_version - if (get32(f) != 0) - return error(f, VORBIS_invalid_first_page); - f->channels = get8(f); - if (!f->channels) - return error(f, VORBIS_invalid_first_page); - if (f->channels > STB_VORBIS_MAX_CHANNELS) - return error(f, VORBIS_too_many_channels); - f->sample_rate = get32(f); - if (!f->sample_rate) - return error(f, VORBIS_invalid_first_page); - get32(f); // bitrate_maximum - get32(f); // bitrate_nominal - get32(f); // bitrate_minimum - x = get8(f); - { - int log0, log1; - log0 = x & 15; - log1 = x >> 4; - f->blocksize_0 = 1 << log0; - f->blocksize_1 = 1 << log1; - if (log0 < 6 || log0 > 13) - return error(f, VORBIS_invalid_setup); - if (log1 < 6 || log1 > 13) - return error(f, VORBIS_invalid_setup); - if (log0 > log1) - return error(f, VORBIS_invalid_setup); - } + prev = f->previous_length; - // framing_flag - x = get8(f); - if (!(x & 1)) - return error(f, VORBIS_invalid_first_page); + // last half of this data becomes previous window + f->previous_length = len - right; - // second packet! - if (!start_page(f)) - return FALSE; + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i = 0; i < f->channels; ++i) + for (j = 0; right + j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right + j]; - if (!start_packet(f)) - return FALSE; + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; - if (!next_segment(f)) - return FALSE; + // truncate a short frame + if (len < right) + right = len; - if (get8_packet(f) != VORBIS_packet_comment) - return error(f, VORBIS_invalid_setup); - for (i = 0; i < 6; ++i) - header[i] = get8_packet(f); - if (!vorbis_validate(header)) - return error(f, VORBIS_invalid_setup); - // file vendor - len = get32_packet(f); - f->vendor = (char *)setup_malloc(f, sizeof(char) * (len + 1)); - if (f->vendor == NULL) - return error(f, VORBIS_outofmem); - for (i = 0; i < len; ++i) - { - f->vendor[i] = get8_packet(f); - } - f->vendor[len] = (char)'\0'; - // user comments - f->comment_list_length = get32_packet(f); - if (f->comment_list_length > 0) - { - f->comment_list = (char **)setup_malloc(f, sizeof(char *) * (f->comment_list_length)); - if (f->comment_list == NULL) - return error(f, VORBIS_outofmem); - } + f->samples_output += right - left; - for (i = 0; i < f->comment_list_length; ++i) - { - len = get32_packet(f); - f->comment_list[i] = (char *)setup_malloc(f, sizeof(char) * (len + 1)); - if (f->comment_list[i] == NULL) - return error(f, VORBIS_outofmem); + return right - left; +} - for (j = 0; j < len; ++j) - { - f->comment_list[i][j] = get8_packet(f); - } - f->comment_list[i][len] = (char)'\0'; - } - - // framing_flag - x = get8_packet(f); - if (!(x & 1)) - return error(f, VORBIS_invalid_setup); - - skip(f, f->bytes_in_seg); - f->bytes_in_seg = 0; - - do - { - len = next_segment(f); - skip(f, len); - f->bytes_in_seg = 0; - } while (len); - - // third packet! - if (!start_packet(f)) - return FALSE; +static int vorbis_pump_first_frame(stb_vorbis *f) { + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} #ifndef STB_VORBIS_NO_PUSHDATA_API - if (IS_PUSH_MODE(f)) - { - if (!is_whole_packet_present(f)) - { - // convert error in ogg header to write type - if (f->error == VORBIS_invalid_stream) - f->error = VORBIS_invalid_setup; - return FALSE; - } - } -#endif +static int is_whole_packet_present(stb_vorbis *f) { + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream - crc32_init(); // always init it, to avoid multithread race conditions + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) - if (get8_packet(f) != VORBIS_packet_setup) - return error(f, VORBIS_invalid_setup); - for (i = 0; i < 6; ++i) - header[i] = get8_packet(f); - if (!vorbis_validate(header)) - return error(f, VORBIS_invalid_setup); + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; - // codebooks + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) + return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; - f->codebook_count = get_bits(f, 8) + 1; - f->codebooks = (Codebook *)setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); - if (f->codebooks == NULL) - return error(f, VORBIS_outofmem); - memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); - for (i = 0; i < f->codebook_count; ++i) - { - uint32 *values; - int ordered, sorted_count; - int total = 0; - uint8 *lengths; - Codebook *c = f->codebooks + i; - CHECK(f); - x = get_bits(f, 8); - if (x != 0x42) - return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); - if (x != 0x43) - return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); - if (x != 0x56) - return error(f, VORBIS_invalid_setup); - x = get_bits(f, 8); - c->dimensions = (get_bits(f, 8) << 8) + x; - x = get_bits(f, 8); - y = get_bits(f, 8); - c->entries = (get_bits(f, 8) << 16) + (y << 8) + x; - ordered = get_bits(f, 1); - c->sparse = ordered ? 0 : get_bits(f, 1); + // check that we have the page header ready + if (p + 26 >= f->stream_end) + return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) + return error(f, VORBIS_invalid_stream); + if (p[4] != 0) + return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) + return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) + return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p + 27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) + return error(f, VORBIS_need_more_data); + for (s = 0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) + return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API - if (c->dimensions == 0 && c->entries != 0) - return error(f, VORBIS_invalid_setup); +static int start_decoder(vorb *f) { + uint8 header[6], x, y; + int len, i, j, k, max_submaps = 0; + int longest_floorlist = 0; - if (c->sparse) - lengths = (uint8 *)setup_temp_malloc(f, c->entries); - else - lengths = c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); + // first page, first packet + f->first_decode = TRUE; - if (!lengths) - return error(f, VORBIS_outofmem); + if (!start_page(f)) + return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) + return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) + return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) + return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) { + // check for the Ogg skeleton fishead identifying header to refine our error + if (f->segments[0] == 64 && + getn(f, header, 6) && + header[0] == 'f' && + header[1] == 'i' && + header[2] == 's' && + header[3] == 'h' && + header[4] == 'e' && + header[5] == 'a' && + get8(f) == 'd' && + get8(f) == '\0') + return error(f, VORBIS_ogg_skeleton_not_supported); + else + return error(f, VORBIS_invalid_first_page); + } - if (ordered) - { - int current_entry = 0; - int current_length = get_bits(f, 5) + 1; - while (current_entry < c->entries) - { - int limit = c->entries - current_entry; - int n = get_bits(f, ilog(limit)); - if (current_length >= 32) - return error(f, VORBIS_invalid_setup); - if (current_entry + n > (int)c->entries) - { - return error(f, VORBIS_invalid_setup); - } - memset(lengths + current_entry, current_length, n); - current_entry += n; - ++current_length; - } - } - else - { - for (j = 0; j < c->entries; ++j) - { - int present = c->sparse ? get_bits(f, 1) : 1; - if (present) - { - lengths[j] = get_bits(f, 5) + 1; - ++total; - if (lengths[j] == 32) - return error(f, VORBIS_invalid_setup); - } - else - { - lengths[j] = NO_CODE; - } - } - } - - if (c->sparse && total >= c->entries >> 2) - { - // convert sparse items to non-sparse! - if (c->entries > (int)f->setup_temp_memory_required) - f->setup_temp_memory_required = c->entries; - - c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); - if (c->codeword_lengths == NULL) - return error(f, VORBIS_outofmem); - memcpy(c->codeword_lengths, lengths, c->entries); - setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! - lengths = c->codeword_lengths; - c->sparse = 0; - } - - // compute the size of the sorted tables - if (c->sparse) - { - sorted_count = total; - } - else - { - sorted_count = 0; -#ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH - for (j = 0; j < c->entries; ++j) - if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) - ++sorted_count; -#endif - } - - c->sorted_entries = sorted_count; - values = NULL; - - CHECK(f); - if (!c->sparse) - { - c->codewords = (uint32 *)setup_malloc(f, sizeof(c->codewords[0]) * c->entries); - if (!c->codewords) - return error(f, VORBIS_outofmem); - } - else - { - unsigned int size; - if (c->sorted_entries) - { - c->codeword_lengths = (uint8 *)setup_malloc(f, c->sorted_entries); - if (!c->codeword_lengths) - return error(f, VORBIS_outofmem); - c->codewords = (uint32 *)setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); - if (!c->codewords) - return error(f, VORBIS_outofmem); - values = (uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); - if (!values) - return error(f, VORBIS_outofmem); - } - size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; - if (size > f->setup_temp_memory_required) - f->setup_temp_memory_required = size; - } - - if (!compute_codewords(c, lengths, c->entries, values)) - { - if (c->sparse) - setup_temp_free(f, values, 0); - return error(f, VORBIS_invalid_setup); - } - - if (c->sorted_entries) - { - // allocate an extra slot for sentinels - c->sorted_codewords = (uint32 *)setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1)); - if (c->sorted_codewords == NULL) - return error(f, VORBIS_outofmem); - // allocate an extra slot at the front so that c->sorted_values[-1] is defined - // so that we can catch that case without an extra if - c->sorted_values = (int *)setup_malloc(f, sizeof(*c->sorted_values) * (c->sorted_entries + 1)); - if (c->sorted_values == NULL) - return error(f, VORBIS_outofmem); - ++c->sorted_values; - c->sorted_values[-1] = -1; - compute_sorted_huffman(c, lengths, values); - } - - if (c->sparse) - { - setup_temp_free(f, values, sizeof(*values) * c->sorted_entries); - setup_temp_free(f, c->codewords, sizeof(*c->codewords) * c->sorted_entries); - setup_temp_free(f, lengths, c->entries); - c->codewords = NULL; - } - - compute_accelerated_huffman(c); - - CHECK(f); - c->lookup_type = get_bits(f, 4); - if (c->lookup_type > 2) - return error(f, VORBIS_invalid_setup); - if (c->lookup_type > 0) - { - uint16 *mults; - c->minimum_value = float32_unpack(get_bits(f, 32)); - c->delta_value = float32_unpack(get_bits(f, 32)); - c->value_bits = get_bits(f, 4) + 1; - c->sequence_p = get_bits(f, 1); - if (c->lookup_type == 1) - { - int values = lookup1_values(c->entries, c->dimensions); - if (values < 0) - return error(f, VORBIS_invalid_setup); - c->lookup_values = (uint32)values; - } - else - { - c->lookup_values = c->entries * c->dimensions; - } - if (c->lookup_values == 0) + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) + return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) + return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) + return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) + return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); + if (!f->channels) + return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) + return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); + if (!f->sample_rate) + return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0, log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); - mults = (uint16 *)setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); - if (mults == NULL) - return error(f, VORBIS_outofmem); - for (j = 0; j < (int)c->lookup_values; ++j) - { - int q = get_bits(f, c->value_bits); - if (q == EOP) - { - setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); - return error(f, VORBIS_invalid_setup); - } - mults[j] = q; - } + if (log1 < 6 || log1 > 13) + return error(f, VORBIS_invalid_setup); + if (log0 > log1) + return error(f, VORBIS_invalid_setup); + } -#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - if (c->lookup_type == 1) - { - int len, sparse = c->sparse; - float last = 0; - // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop - if (sparse) - { - if (c->sorted_entries == 0) - goto skip; - c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + // framing_flag + x = get8(f); + if (!(x & 1)) + return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) + return FALSE; + + if (!start_packet(f)) + return FALSE; + + if (!next_segment(f)) + return FALSE; + + if (get8_packet(f) != VORBIS_packet_comment) + return error(f, VORBIS_invalid_setup); + for (i = 0; i < 6; ++i) + header[i] = get8_packet(f); + if (!vorbis_validate(header)) + return error(f, VORBIS_invalid_setup); + // file vendor + len = get32_packet(f); + f->vendor = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->vendor == NULL) + return error(f, VORBIS_outofmem); + for (i = 0; i < len; ++i) { + f->vendor[i] = get8_packet(f); + } + f->vendor[len] = (char)'\0'; + // user comments + f->comment_list_length = get32_packet(f); + if (f->comment_list_length > 0) { + f->comment_list = (char **)setup_malloc(f, sizeof(char *) * (f->comment_list_length)); + if (f->comment_list == NULL) + return error(f, VORBIS_outofmem); + } + + for (i = 0; i < f->comment_list_length; ++i) { + len = get32_packet(f); + f->comment_list[i] = (char *)setup_malloc(f, sizeof(char) * (len + 1)); + if (f->comment_list[i] == NULL) + return error(f, VORBIS_outofmem); + + for (j = 0; j < len; ++j) { + f->comment_list[i][j] = get8_packet(f); + } + f->comment_list[i][len] = (char)'\0'; + } + + // framing_flag + x = get8_packet(f); + if (!(x & 1)) + return error(f, VORBIS_invalid_setup); + + skip(f, f->bytes_in_seg); + f->bytes_in_seg = 0; + + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) + return FALSE; + +#ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } +#endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) + return error(f, VORBIS_invalid_setup); + for (i = 0; i < 6; ++i) + header[i] = get8_packet(f); + if (!vorbis_validate(header)) + return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f, 8) + 1; + f->codebooks = (Codebook *)setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) + return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i = 0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total = 0; + uint8 *lengths; + Codebook *c = f->codebooks + i; + CHECK(f); + x = get_bits(f, 8); + if (x != 0x42) + return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + if (x != 0x43) + return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + if (x != 0x56) + return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8) << 8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8) << 16) + (y << 8) + x; + ordered = get_bits(f, 1); + c->sparse = ordered ? 0 : get_bits(f, 1); + + if (c->dimensions == 0 && c->entries != 0) + return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *)setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); + + if (!lengths) + return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f, 5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_length >= 32) + return error(f, VORBIS_invalid_setup); + if (current_entry + n > (int)c->entries) { + return error(f, VORBIS_invalid_setup); + } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; } - else - c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); - if (c->multiplicands == NULL) - { - setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); - return error(f, VORBIS_outofmem); - } - len = sparse ? c->sorted_entries : c->entries; - for (j = 0; j < len; ++j) - { - unsigned int z = sparse ? c->sorted_values[j] : j; - unsigned int div = 1; - for (k = 0; k < c->dimensions; ++k) - { - int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off] * c->delta_value + c->minimum_value + last; - c->multiplicands[j * c->dimensions + k] = val; - if (c->sequence_p) - last = val; - if (k + 1 < c->dimensions) - { - if (div > UINT_MAX / (unsigned int)c->lookup_values) - { - setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); + } else { + for (j = 0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f, 1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); - } - div *= c->lookup_values; - } - } + } else { + lengths[j] = NO_CODE; + } } - c->lookup_type = 2; - } - else + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int)f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *)setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) + return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; +#ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j = 0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; #endif - { - float last = 0; - CHECK(f); - c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); - if (c->multiplicands == NULL) - { - setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); - return error(f, VORBIS_outofmem); + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *)setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) + return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *)setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) + return error(f, VORBIS_outofmem); + c->codewords = (uint32 *)setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) + return error(f, VORBIS_outofmem); + values = (uint32 *)setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) + return error(f, VORBIS_outofmem); } - for (j = 0; j < (int)c->lookup_values; ++j) - { - float val = mults[j] * c->delta_value + c->minimum_value + last; - c->multiplicands[j] = val; - if (c->sequence_p) - last = val; + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) + setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *)setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries + 1)); + if (c->sorted_codewords == NULL) + return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = (int *)setup_malloc(f, sizeof(*c->sorted_values) * (c->sorted_entries + 1)); + if (c->sorted_values == NULL) + return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values) * c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords) * c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) + return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4) + 1; + c->sequence_p = get_bits(f, 1); + if (c->lookup_type == 1) { + int values = lookup1_values(c->entries, c->dimensions); + if (values < 0) + return error(f, VORBIS_invalid_setup); + c->lookup_values = (uint32)values; + } else { + c->lookup_values = c->entries * c->dimensions; } - } + if (c->lookup_values == 0) + return error(f, VORBIS_invalid_setup); + mults = (uint16 *)setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) + return error(f, VORBIS_outofmem); + for (j = 0; j < (int)c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { + setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + mults[j] = q; + } + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK - skip:; + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last = 0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) + goto skip; + c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { + setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); + return error(f, VORBIS_outofmem); + } + len = sparse ? c->sorted_entries : c->entries; + for (j = 0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div = 1; + for (k = 0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off] * c->delta_value + c->minimum_value + last; + c->multiplicands[j * c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k + 1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int)c->lookup_values) { + setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } else #endif - setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); - - CHECK(f); - } - CHECK(f); - } - - // time domain transfers (notused) - - x = get_bits(f, 6) + 1; - for (i = 0; i < x; ++i) - { - uint32 z = get_bits(f, 16); - if (z != 0) - return error(f, VORBIS_invalid_setup); - } - - // Floors - f->floor_count = get_bits(f, 6) + 1; - f->floor_config = (Floor *)setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); - if (f->floor_config == NULL) - return error(f, VORBIS_outofmem); - for (i = 0; i < f->floor_count; ++i) - { - f->floor_types[i] = get_bits(f, 16); - if (f->floor_types[i] > 1) - return error(f, VORBIS_invalid_setup); - if (f->floor_types[i] == 0) - { - Floor0 *g = &f->floor_config[i].floor0; - g->order = get_bits(f, 8); - g->rate = get_bits(f, 16); - g->bark_map_size = get_bits(f, 16); - g->amplitude_bits = get_bits(f, 6); - g->amplitude_offset = get_bits(f, 8); - g->number_of_books = get_bits(f, 4) + 1; - for (j = 0; j < g->number_of_books; ++j) - g->book_list[j] = get_bits(f, 8); - return error(f, VORBIS_feature_not_supported); - } - else - { - stbv__floor_ordering p[31 * 8 + 2]; - Floor1 *g = &f->floor_config[i].floor1; - int max_class = -1; - g->partitions = get_bits(f, 5); - for (j = 0; j < g->partitions; ++j) - { - g->partition_class_list[j] = get_bits(f, 4); - if (g->partition_class_list[j] > max_class) - max_class = g->partition_class_list[j]; - } - for (j = 0; j <= max_class; ++j) - { - g->class_dimensions[j] = get_bits(f, 3) + 1; - g->class_subclasses[j] = get_bits(f, 2); - if (g->class_subclasses[j]) { - g->class_masterbooks[j] = get_bits(f, 8); - if (g->class_masterbooks[j] >= f->codebook_count) - return error(f, VORBIS_invalid_setup); + float last = 0; + CHECK(f); + c->multiplicands = (codetype *)setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { + setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); + return error(f, VORBIS_outofmem); + } + for (j = 0; j < (int)c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } } - for (k = 0; k < 1 << g->class_subclasses[j]; ++k) - { - g->subclass_books[j][k] = get_bits(f, 8) - 1; - if (g->subclass_books[j][k] >= f->codebook_count) - return error(f, VORBIS_invalid_setup); - } - } - g->floor1_multiplier = get_bits(f, 2) + 1; - g->rangebits = get_bits(f, 4); - g->Xlist[0] = 0; - g->Xlist[1] = 1 << g->rangebits; - g->values = 2; - for (j = 0; j < g->partitions; ++j) - { - int c = g->partition_class_list[j]; - for (k = 0; k < g->class_dimensions[c]; ++k) - { - g->Xlist[g->values] = get_bits(f, g->rangebits); - ++g->values; - } - } - // precompute the sorting - for (j = 0; j < g->values; ++j) - { - p[j].x = g->Xlist[j]; - p[j].id = j; - } - qsort(p, g->values, sizeof(p[0]), point_compare); - for (j = 0; j < g->values - 1; ++j) - if (p[j].x == p[j + 1].x) - return error(f, VORBIS_invalid_setup); - for (j = 0; j < g->values; ++j) - g->sorted_order[j] = (uint8)p[j].id; - // precompute the neighbors - for (j = 2; j < g->values; ++j) - { - int low = 0, hi = 0; - neighbors(g->Xlist, j, &low, &hi); - g->neighbors[j][0] = low; - g->neighbors[j][1] = hi; - } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0]) * c->lookup_values); - if (g->values > longest_floorlist) - longest_floorlist = g->values; - } - } + CHECK(f); + } + CHECK(f); + } - // Residue - f->residue_count = get_bits(f, 6) + 1; - f->residue_config = (Residue *)setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); - if (f->residue_config == NULL) - return error(f, VORBIS_outofmem); - memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); - for (i = 0; i < f->residue_count; ++i) - { - uint8 residue_cascade[64]; - Residue *r = f->residue_config + i; - f->residue_types[i] = get_bits(f, 16); - if (f->residue_types[i] > 2) - return error(f, VORBIS_invalid_setup); - r->begin = get_bits(f, 24); - r->end = get_bits(f, 24); - if (r->end < r->begin) - return error(f, VORBIS_invalid_setup); - r->part_size = get_bits(f, 24) + 1; - r->classifications = get_bits(f, 6) + 1; - r->classbook = get_bits(f, 8); - if (r->classbook >= f->codebook_count) - return error(f, VORBIS_invalid_setup); - for (j = 0; j < r->classifications; ++j) - { - uint8 high_bits = 0; - uint8 low_bits = get_bits(f, 3); - if (get_bits(f, 1)) - high_bits = get_bits(f, 5); - residue_cascade[j] = high_bits * 8 + low_bits; - } - r->residue_books = (short (*)[8])setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); - if (r->residue_books == NULL) - return error(f, VORBIS_outofmem); - for (j = 0; j < r->classifications; ++j) - { - for (k = 0; k < 8; ++k) - { - if (residue_cascade[j] & (1 << k)) - { - r->residue_books[j][k] = get_bits(f, 8); - if (r->residue_books[j][k] >= f->codebook_count) - return error(f, VORBIS_invalid_setup); + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i = 0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) + return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6) + 1; + f->floor_config = (Floor *)setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) + return error(f, VORBIS_outofmem); + for (i = 0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) + return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f, 8); + g->rate = get_bits(f, 16); + g->bark_map_size = get_bits(f, 16); + g->amplitude_bits = get_bits(f, 6); + g->amplitude_offset = get_bits(f, 8); + g->number_of_books = get_bits(f, 4) + 1; + for (j = 0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f, 8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31 * 8 + 2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j = 0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; } - else - { - r->residue_books[j][k] = -1; + for (j = 0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3) + 1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) + return error(f, VORBIS_invalid_setup); + } + for (k = 0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f, 8) - 1; + if (g->subclass_books[j][k] >= f->codebook_count) + return error(f, VORBIS_invalid_setup); + } } - } - } - // precompute the classifications[] array to avoid inner-loop mod/divide - // call it 'classdata' since we already have r->classifications - r->classdata = (uint8 **)setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - if (!r->classdata) - return error(f, VORBIS_outofmem); - memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); - for (j = 0; j < f->codebooks[r->classbook].entries; ++j) - { - int classwords = f->codebooks[r->classbook].dimensions; - int temp = j; - r->classdata[j] = (uint8 *)setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); - if (r->classdata[j] == NULL) + g->floor1_multiplier = get_bits(f, 2) + 1; + g->rangebits = get_bits(f, 4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j = 0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k = 0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j = 0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j = 0; j < g->values - 1; ++j) + if (p[j].x == p[j + 1].x) + return error(f, VORBIS_invalid_setup); + for (j = 0; j < g->values; ++j) + g->sorted_order[j] = (uint8)p[j].id; + // precompute the neighbors + for (j = 2; j < g->values; ++j) { + int low = 0, hi = 0; + neighbors(g->Xlist, j, &low, &hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6) + 1; + f->residue_config = (Residue *)setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) + return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i = 0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config + i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) + return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) + return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f, 24) + 1; + r->classifications = get_bits(f, 6) + 1; + r->classbook = get_bits(f, 8); + if (r->classbook >= f->codebook_count) + return error(f, VORBIS_invalid_setup); + for (j = 0; j < r->classifications; ++j) { + uint8 high_bits = 0; + uint8 low_bits = get_bits(f, 3); + if (get_bits(f, 1)) + high_bits = get_bits(f, 5); + residue_cascade[j] = high_bits * 8 + low_bits; + } + r->residue_books = (short (*)[8])setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); - for (k = classwords - 1; k >= 0; --k) - { - r->classdata[j][k] = temp % r->classifications; - temp /= r->classifications; - } - } - } + for (j = 0; j < r->classifications; ++j) { + for (k = 0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) + return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **)setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) + return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j = 0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *)setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) + return error(f, VORBIS_outofmem); + for (k = classwords - 1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } - f->mapping_count = get_bits(f, 6) + 1; - f->mapping = (Mapping *)setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); - if (f->mapping == NULL) - return error(f, VORBIS_outofmem); - memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); - for (i = 0; i < f->mapping_count; ++i) - { - Mapping *m = f->mapping + i; - int mapping_type = get_bits(f, 16); - if (mapping_type != 0) - return error(f, VORBIS_invalid_setup); - m->chan = (MappingChannel *)setup_malloc(f, f->channels * sizeof(*m->chan)); - if (m->chan == NULL) - return error(f, VORBIS_outofmem); - if (get_bits(f, 1)) - m->submaps = get_bits(f, 4) + 1; - else - m->submaps = 1; - if (m->submaps > max_submaps) - max_submaps = m->submaps; - if (get_bits(f, 1)) - { - m->coupling_steps = get_bits(f, 8) + 1; - if (m->coupling_steps > f->channels) + f->mapping_count = get_bits(f, 6) + 1; + f->mapping = (Mapping *)setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) + return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i = 0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f, 16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); - for (k = 0; k < m->coupling_steps; ++k) - { - m->chan[k].magnitude = get_bits(f, ilog(f->channels - 1)); - m->chan[k].angle = get_bits(f, ilog(f->channels - 1)); - if (m->chan[k].magnitude >= f->channels) - return error(f, VORBIS_invalid_setup); - if (m->chan[k].angle >= f->channels) - return error(f, VORBIS_invalid_setup); - if (m->chan[k].magnitude == m->chan[k].angle) - return error(f, VORBIS_invalid_setup); - } - } - else - m->coupling_steps = 0; + m->chan = (MappingChannel *)setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) + return error(f, VORBIS_outofmem); + if (get_bits(f, 1)) + m->submaps = get_bits(f, 4) + 1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f, 1)) { + m->coupling_steps = get_bits(f, 8) + 1; + if (m->coupling_steps > f->channels) + return error(f, VORBIS_invalid_setup); + for (k = 0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels - 1)); + m->chan[k].angle = get_bits(f, ilog(f->channels - 1)); + if (m->chan[k].magnitude >= f->channels) + return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) + return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) + return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; - // reserved field - if (get_bits(f, 2)) - return error(f, VORBIS_invalid_setup); - if (m->submaps > 1) - { - for (j = 0; j < f->channels; ++j) - { - m->chan[j].mux = get_bits(f, 4); - if (m->chan[j].mux >= m->submaps) - return error(f, VORBIS_invalid_setup); - } - } - else - // @SPECIFICATION: this case is missing from the spec - for (j = 0; j < f->channels; ++j) - m->chan[j].mux = 0; - - for (j = 0; j < m->submaps; ++j) - { - get_bits(f, 8); // discard - m->submap_floor[j] = get_bits(f, 8); - m->submap_residue[j] = get_bits(f, 8); - if (m->submap_floor[j] >= f->floor_count) + // reserved field + if (get_bits(f, 2)) return error(f, VORBIS_invalid_setup); - if (m->submap_residue[j] >= f->residue_count) + if (m->submaps > 1) { + for (j = 0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) + return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j = 0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j = 0; j < m->submaps; ++j) { + get_bits(f, 8); // discard + m->submap_floor[j] = get_bits(f, 8); + m->submap_residue[j] = get_bits(f, 8); + if (m->submap_floor[j] >= f->floor_count) + return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) + return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6) + 1; + for (i = 0; i < f->mode_count; ++i) { + Mode *m = f->mode_config + i; + m->blockflag = get_bits(f, 1); + m->windowtype = get_bits(f, 16); + m->transformtype = get_bits(f, 16); + m->mapping = get_bits(f, 8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); - } - } + if (m->transformtype != 0) + return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) + return error(f, VORBIS_invalid_setup); + } - // Modes - f->mode_count = get_bits(f, 6) + 1; - for (i = 0; i < f->mode_count; ++i) - { - Mode *m = f->mode_config + i; - m->blockflag = get_bits(f, 1); - m->windowtype = get_bits(f, 16); - m->transformtype = get_bits(f, 16); - m->mapping = get_bits(f, 8); - if (m->windowtype != 0) - return error(f, VORBIS_invalid_setup); - if (m->transformtype != 0) - return error(f, VORBIS_invalid_setup); - if (m->mapping >= f->mapping_count) - return error(f, VORBIS_invalid_setup); - } + flush_packet(f); - flush_packet(f); + f->previous_length = 0; - f->previous_length = 0; - - for (i = 0; i < f->channels; ++i) - { - f->channel_buffers[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1); - f->previous_window[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1 / 2); - f->finalY[i] = (int16 *)setup_malloc(f, sizeof(int16) * longest_floorlist); - if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) - return error(f, VORBIS_outofmem); - memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + for (i = 0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1 / 2); + f->finalY[i] = (int16 *)setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) + return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR - f->floor_buffers[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1 / 2); - if (f->floor_buffers[i] == NULL) - return error(f, VORBIS_outofmem); + f->floor_buffers[i] = (float *)setup_malloc(f, sizeof(float) * f->blocksize_1 / 2); + if (f->floor_buffers[i] == NULL) + return error(f, VORBIS_outofmem); #endif - } + } - if (!init_blocksize(f, 0, f->blocksize_0)) - return FALSE; - if (!init_blocksize(f, 1, f->blocksize_1)) - return FALSE; - f->blocksize[0] = f->blocksize_0; - f->blocksize[1] = f->blocksize_1; + if (!init_blocksize(f, 0, f->blocksize_0)) + return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) + return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; #ifdef STB_VORBIS_DIVIDE_TABLE - if (integer_divide_table[1][1] == 0) - for (i = 0; i < DIVTAB_NUMER; ++i) - for (j = 1; j < DIVTAB_DENOM; ++j) - integer_divide_table[i][j] = i / j; + if (integer_divide_table[1][1] == 0) + for (i = 0; i < DIVTAB_NUMER; ++i) + for (j = 1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; #endif - // compute how much temporary memory is needed + // compute how much temporary memory is needed - // 1. - { - uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); - uint32 classify_mem; - int i, max_part_read = 0; - for (i = 0; i < f->residue_count; ++i) - { - Residue *r = f->residue_config + i; - unsigned int actual_size = f->blocksize_1 / 2; - unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; - unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; - int n_read = limit_r_end - limit_r_begin; - int part_read = n_read / r->part_size; - if (part_read > max_part_read) - max_part_read = part_read; - } + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i, max_part_read = 0; + for (i = 0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE - classify_mem = f->channels * (sizeof(void *) + max_part_read * sizeof(uint8 *)); + classify_mem = f->channels * (sizeof(void *) + max_part_read * sizeof(uint8 *)); #else - classify_mem = f->channels * (sizeof(void *) + max_part_read * sizeof(int *)); + classify_mem = f->channels * (sizeof(void *) + max_part_read * sizeof(int *)); #endif - // maximum reasonable partition size is f->blocksize_1 + // maximum reasonable partition size is f->blocksize_1 - f->temp_memory_required = classify_mem; - if (imdct_mem > f->temp_memory_required) - f->temp_memory_required = imdct_mem; - } + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } - if (f->alloc.alloc_buffer) - { - assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); - // check if there's enough temp memory so we don't error later - if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned)f->temp_offset) - return error(f, VORBIS_outofmem); - } + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned)f->temp_offset) + return error(f, VORBIS_outofmem); + } - // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page - // without PAGEFLAG_continued_packet, so this either points to the first page, or - // the page after the end of the headers. It might be cleaner to point to a page - // in the middle of the headers, when that's the page where the first audio packet - // starts, but we'd have to also correctly skip the end of any continued packet in - // stb_vorbis_seek_start. - if (f->next_seg == -1) - { - f->first_audio_page_offset = stb_vorbis_get_file_offset(f); - } - else - { - f->first_audio_page_offset = 0; - } + // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page + // without PAGEFLAG_continued_packet, so this either points to the first page, or + // the page after the end of the headers. It might be cleaner to point to a page + // in the middle of the headers, when that's the page where the first audio packet + // starts, but we'd have to also correctly skip the end of any continued packet in + // stb_vorbis_seek_start. + if (f->next_seg == -1) { + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + } else { + f->first_audio_page_offset = 0; + } - return TRUE; + return TRUE; } -static void vorbis_deinit(stb_vorbis *p) -{ - int i, j; +static void vorbis_deinit(stb_vorbis *p) { + int i, j; - setup_free(p, p->vendor); - for (i = 0; i < p->comment_list_length; ++i) - { - setup_free(p, p->comment_list[i]); - } - setup_free(p, p->comment_list); + setup_free(p, p->vendor); + for (i = 0; i < p->comment_list_length; ++i) { + setup_free(p, p->comment_list[i]); + } + setup_free(p, p->comment_list); - if (p->residue_config) - { - for (i = 0; i < p->residue_count; ++i) - { - Residue *r = p->residue_config + i; - if (r->classdata) - { - for (j = 0; j < p->codebooks[r->classbook].entries; ++j) - setup_free(p, r->classdata[j]); - setup_free(p, r->classdata); - } - setup_free(p, r->residue_books); - } - } + if (p->residue_config) { + for (i = 0; i < p->residue_count; ++i) { + Residue *r = p->residue_config + i; + if (r->classdata) { + for (j = 0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } - if (p->codebooks) - { - CHECK(p); - for (i = 0; i < p->codebook_count; ++i) - { - Codebook *c = p->codebooks + i; - setup_free(p, c->codeword_lengths); - setup_free(p, c->multiplicands); - setup_free(p, c->codewords); - setup_free(p, c->sorted_codewords); - // c->sorted_values[-1] is the first entry in the array - setup_free(p, c->sorted_values ? c->sorted_values - 1 : NULL); - } - setup_free(p, p->codebooks); - } - setup_free(p, p->floor_config); - setup_free(p, p->residue_config); - if (p->mapping) - { - for (i = 0; i < p->mapping_count; ++i) - setup_free(p, p->mapping[i].chan); - setup_free(p, p->mapping); - } - CHECK(p); - for (i = 0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) - { - setup_free(p, p->channel_buffers[i]); - setup_free(p, p->previous_window[i]); + if (p->codebooks) { + CHECK(p); + for (i = 0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values - 1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i = 0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i = 0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); #ifdef STB_VORBIS_NO_DEFER_FLOOR - setup_free(p, p->floor_buffers[i]); + setup_free(p, p->floor_buffers[i]); #endif - setup_free(p, p->finalY[i]); - } - for (i = 0; i < 2; ++i) - { - setup_free(p, p->A[i]); - setup_free(p, p->B[i]); - setup_free(p, p->C[i]); - setup_free(p, p->window[i]); - setup_free(p, p->bit_reverse[i]); - } + setup_free(p, p->finalY[i]); + } + for (i = 0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } #ifndef STB_VORBIS_NO_STDIO - if (p->close_on_free) - fclose(p->f); + if (p->close_on_free) + fclose(p->f); #endif } -void stb_vorbis_close(stb_vorbis *p) -{ - if (p == NULL) - return; - vorbis_deinit(p); - setup_free(p, p); +void stb_vorbis_close(stb_vorbis *p) { + if (p == NULL) + return; + vorbis_deinit(p); + setup_free(p, p); } -static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) -{ - memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start - if (z) - { - p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes &= ~7; - p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; - } - p->eof = 0; - p->error = VORBIS__no_error; - p->stream = NULL; - p->codebooks = NULL; - p->page_crc_tests = -1; +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes &= ~7; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; #ifndef STB_VORBIS_NO_STDIO - p->close_on_free = FALSE; - p->f = NULL; + p->close_on_free = FALSE; + p->f = NULL; #endif } -int stb_vorbis_get_sample_offset(stb_vorbis *f) -{ - if (f->current_loc_valid) - return f->current_loc; - else - return -1; +int stb_vorbis_get_sample_offset(stb_vorbis *f) { + if (f->current_loc_valid) + return f->current_loc; + else + return -1; } -stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) -{ - stb_vorbis_info d; - d.channels = f->channels; - d.sample_rate = f->sample_rate; - d.setup_memory_required = f->setup_memory_required; - d.setup_temp_memory_required = f->setup_temp_memory_required; - d.temp_memory_required = f->temp_memory_required; - d.max_frame_size = f->blocksize_1 >> 1; - return d; +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; } -stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) -{ - stb_vorbis_comment d; - d.vendor = f->vendor; - d.comment_list_length = f->comment_list_length; - d.comment_list = f->comment_list; - return d; +stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { + stb_vorbis_comment d; + d.vendor = f->vendor; + d.comment_list_length = f->comment_list_length; + d.comment_list = f->comment_list; + return d; } -int stb_vorbis_get_error(stb_vorbis *f) -{ - int e = f->error; - f->error = VORBIS__no_error; - return e; +int stb_vorbis_get_error(stb_vorbis *f) { + int e = f->error; + f->error = VORBIS__no_error; + return e; } -static stb_vorbis *vorbis_alloc(stb_vorbis *f) -{ - stb_vorbis *p = (stb_vorbis *)setup_malloc(f, sizeof(*p)); - return p; +static stb_vorbis *vorbis_alloc(stb_vorbis *f) { + stb_vorbis *p = (stb_vorbis *)setup_malloc(f, sizeof(*p)); + return p; } #ifndef STB_VORBIS_NO_PUSHDATA_API -void stb_vorbis_flush_pushdata(stb_vorbis *f) -{ - f->previous_length = 0; - f->page_crc_tests = 0; - f->discard_samples_deferred = 0; - f->current_loc_valid = FALSE; - f->first_decode = FALSE; - f->samples_output = 0; - f->channel_buffer_start = 0; - f->channel_buffer_end = 0; +void stb_vorbis_flush_pushdata(stb_vorbis *f) { + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; } -static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) -{ - int i, n; - for (i = 0; i < f->page_crc_tests; ++i) - f->scan[i].bytes_done = 0; +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) { + int i, n; + for (i = 0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; - // if we have room for more scans, search for them first, because - // they may cause us to stop early if their header is incomplete - if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) - { - if (data_len < 4) - return 0; - data_len -= 3; // need to look for 4-byte sequence, so don't miss - // one that straddles a boundary - for (i = 0; i < data_len; ++i) - { - if (data[i] == 0x4f) - { - if (0 == memcmp(data + i, ogg_page_header, 4)) - { - int j, len; - uint32 crc; - // make sure we have the whole page header - if (i + 26 >= data_len || i + 27 + data[i + 26] >= data_len) - { - // only read up to this page start, so hopefully we'll - // have the whole page header start next time - data_len = i; - break; - } - // ok, we have it all; compute the length of the page - len = 27 + data[i + 26]; - for (j = 0; j < data[i + 26]; ++j) - len += data[i + 27 + j]; - // scan everything up to the embedded crc (which we must 0) - crc = 0; - for (j = 0; j < 22; ++j) - crc = crc32_update(crc, data[i + j]); - // now process 4 0-bytes - for (; j < 26; ++j) - crc = crc32_update(crc, 0); - // len is the total number of bytes we need to scan - n = f->page_crc_tests++; - f->scan[n].bytes_left = len - j; - f->scan[n].crc_so_far = crc; - f->scan[n].goal_crc = data[i + 22] + (data[i + 23] << 8) + (data[i + 24] << 16) + (data[i + 25] << 24); - // if the last frame on a page is continued to the next, then - // we can't recover the sample_loc immediately - if (data[i + 27 + data[i + 26] - 1] == 255) - f->scan[n].sample_loc = ~0; - else - f->scan[n].sample_loc = data[i + 6] + (data[i + 7] << 8) + (data[i + 8] << 16) + (data[i + 9] << 24); - f->scan[n].bytes_done = i + j; - if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) - break; - // keep going if we still have room for more + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) + return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i = 0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0 == memcmp(data + i, ogg_page_header, 4)) { + int j, len; + uint32 crc; + // make sure we have the whole page header + if (i + 26 >= data_len || i + 27 + data[i + 26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i + 26]; + for (j = 0; j < data[i + 26]; ++j) + len += data[i + 27 + j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j = 0; j < 22; ++j) + crc = crc32_update(crc, data[i + j]); + // now process 4 0-bytes + for (; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len - j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i + 22] + (data[i + 23] << 8) + (data[i + 24] << 16) + (data[i + 25] << 24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i + 27 + data[i + 26] - 1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i + 6] + (data[i + 7] << 8) + (data[i + 8] << 16) + (data[i + 9] << 24); + f->scan[n].bytes_done = i + j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } } - } - } - } + } + } - for (i = 0; i < f->page_crc_tests;) - { - uint32 crc; - int j; - int n = f->scan[i].bytes_done; - int m = f->scan[i].bytes_left; - if (m > data_len - n) - m = data_len - n; - // m is the bytes to scan in the current chunk - crc = f->scan[i].crc_so_far; - for (j = 0; j < m; ++j) - crc = crc32_update(crc, data[n + j]); - f->scan[i].bytes_left -= m; - f->scan[i].crc_so_far = crc; - if (f->scan[i].bytes_left == 0) - { - // does it match? - if (f->scan[i].crc_so_far == f->scan[i].goal_crc) - { - // Houston, we have page - data_len = n + m; // consumption amount is wherever that scan ended - f->page_crc_tests = -1; // drop out of page scan mode - f->previous_length = 0; // decode-but-don't-output one frame - f->next_seg = -1; // start a new page - f->current_loc = f->scan[i].sample_loc; // set the current sample location - // to the amount we'd have decoded had we decoded this page - f->current_loc_valid = f->current_loc != ~0U; - return data_len; - } - // delete entry - f->scan[i] = f->scan[--f->page_crc_tests]; - } - else - { - ++i; - } - } + for (i = 0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) + m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j = 0; j < m; ++j) + crc = crc32_update(crc, data[n + j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n + m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } - return data_len; + return data_len; } // return value: number of bytes we used int stb_vorbis_decode_frame_pushdata( - stb_vorbis *f, // the file we're decoding - const uint8 *data, int data_len, // the memory available for decoding - int *channels, // place to write number of float * buffers - float ***output, // place to write float ** array of float * buffers - int *samples // place to write number of output samples -) -{ - int i; - int len, right, left; + stb_vorbis *f, // the file we're decoding + const uint8 *data, + int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples +) { + int i; + int len, right, left; - if (!IS_PUSH_MODE(f)) - return error(f, VORBIS_invalid_api_mixing); + if (!IS_PUSH_MODE(f)) + return error(f, VORBIS_invalid_api_mixing); - if (f->page_crc_tests >= 0) - { - *samples = 0; - return vorbis_search_for_page_pushdata(f, (uint8 *)data, data_len); - } + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *)data, data_len); + } - f->stream = (uint8 *)data; - f->stream_end = (uint8 *)data + data_len; - f->error = VORBIS__no_error; + f->stream = (uint8 *)data; + f->stream_end = (uint8 *)data + data_len; + f->error = VORBIS__no_error; - // check that we have the entire packet in memory - if (!is_whole_packet_present(f)) - { - *samples = 0; - return 0; - } + // check that we have the entire packet in memory + if (!is_whole_packet_present(f)) { + *samples = 0; + return 0; + } - if (!vorbis_decode_packet(f, &len, &left, &right)) - { - // save the actual error we encountered - enum STBVorbisError error = f->error; - if (error == VORBIS_bad_packet_type) - { - // flush and resynch - f->error = VORBIS__no_error; - while (get8_packet(f) != EOP) - if (f->eof) - break; - *samples = 0; - return (int)(f->stream - data); - } - if (error == VORBIS_continued_packet_flag_invalid) - { - if (f->previous_length == 0) - { - // we may be resynching, in which case it's ok to hit one - // of these; just discard the packet + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch f->error = VORBIS__no_error; while (get8_packet(f) != EOP) - if (f->eof) - break; + if (f->eof) + break; *samples = 0; return (int)(f->stream - data); - } - } - // if we get an error while parsing, what to do? - // well, it DEFINITELY won't work to continue from where we are! - stb_vorbis_flush_pushdata(f); - // restore the error that actually made us bail - f->error = error; - *samples = 0; - return 1; - } + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) + break; + *samples = 0; + return (int)(f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } - // success! - len = vorbis_finish_frame(f, len, left, right); - for (i = 0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i = 0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; - if (channels) - *channels = f->channels; - *samples = len; - *output = f->outputs; - return (int)(f->stream - data); + if (channels) + *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int)(f->stream - data); } stb_vorbis *stb_vorbis_open_pushdata( - const unsigned char *data, int data_len, // the memory available for decoding - int *data_used, // only defined if result is not NULL - int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.stream = (uint8 *)data; - p.stream_end = (uint8 *)data + data_len; - p.push_mode = TRUE; - if (!start_decoder(&p)) - { - if (p.eof) - *error = VORBIS_need_more_data; - else - *error = p.error; - return NULL; - } - f = vorbis_alloc(&p); - if (f) - { - *f = p; - *data_used = (int)(f->stream - data); - *error = 0; - return f; - } - else - { - vorbis_deinit(&p); - return NULL; - } + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, + const stb_vorbis_alloc *alloc) { + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *)data; + p.stream_end = (uint8 *)data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int)(f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } } -#endif // STB_VORBIS_NO_PUSHDATA_API +#endif // STB_VORBIS_NO_PUSHDATA_API -unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) -{ +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API - if (f->push_mode) - return 0; + if (f->push_mode) + return 0; #endif - if (USE_MEMORY(f)) - return (unsigned int)(f->stream - f->stream_start); + if (USE_MEMORY(f)) + return (unsigned int)(f->stream - f->stream_start); #ifndef STB_VORBIS_NO_STDIO - return (unsigned int)(ftell(f->f) - f->f_start); + return (unsigned int)(ftell(f->f) - f->f_start); #endif } @@ -5100,85 +4590,78 @@ unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) // DATA-PULLING API // -static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) -{ - for (;;) - { - int n; - if (f->eof) - return 0; - n = get8(f); - if (n == 0x4f) - { // page header candidate - unsigned int retry_loc = stb_vorbis_get_file_offset(f); - int i; - // check if we're off the end of a file_section stream - if (retry_loc - 25 > f->stream_len) +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { + for (;;) { + int n; + if (f->eof) return 0; - // check the rest of the header - for (i = 1; i < 4; ++i) - if (get8(f) != ogg_page_header[i]) - break; - if (f->eof) - return 0; - if (i == 4) - { - uint8 header[27]; - uint32 i, crc, goal, len; - for (i = 0; i < 4; ++i) - header[i] = ogg_page_header[i]; - for (; i < 27; ++i) - header[i] = get8(f); + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i = 1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; if (f->eof) - return 0; - if (header[4] != 0) - goto invalid; - goal = header[22] + (header[23] << 8) + (header[24] << 16) + (header[25] << 24); - for (i = 22; i < 26; ++i) - header[i] = 0; - crc = 0; - for (i = 0; i < 27; ++i) - crc = crc32_update(crc, header[i]); - len = 0; - for (i = 0; i < header[26]; ++i) - { - int s = get8(f); - crc = crc32_update(crc, s); - len += s; + return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i = 0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) + return 0; + if (header[4] != 0) + goto invalid; + goal = header[22] + (header[23] << 8) + (header[24] << 16) + (header[25] << 24); + for (i = 22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i = 0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i = 0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) + return 0; + for (i = 0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc - 1); + return 1; + } } - if (len && f->eof) - return 0; - for (i = 0; i < len; ++i) - crc = crc32_update(crc, get8(f)); - // finished parsing probable page - if (crc == goal) - { - // we could now check that it's either got the last - // page flag set, OR it's followed by the capture - // pattern, but I guess TECHNICALLY you could have - // a file with garbage between each ogg page and recover - // from it automatically? So even though that paranoia - // might decrease the chance of an invalid decode by - // another 2^32, not worth it since it would hose those - // invalid-but-useful files? - if (end) - *end = stb_vorbis_get_file_offset(f); - if (last) - { - if (header[5] & 0x04) - *last = 1; - else - *last = 0; - } - set_file_offset(f, retry_loc - 1); - return 1; - } - } - invalid: - // not a valid page, so rewind and look for next one - set_file_offset(f, retry_loc); - } - } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } } #define SAMPLE_unknown 0xffffffff @@ -5192,542 +4675,491 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) // to try to bound either side of the binary search sensibly, while still // working in O(log n) time if they fail. -static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) -{ - uint8 header[27], lacing[255]; - int i, len; +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) { + uint8 header[27], lacing[255]; + int i, len; - // record where the page starts - z->page_start = stb_vorbis_get_file_offset(f); + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); - // parse the header - getn(f, header, 27); - if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') - return 0; - getn(f, lacing, header[26]); + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); - // determine the length of the payload - len = 0; - for (i = 0; i < header[26]; ++i) - len += lacing[i]; + // determine the length of the payload + len = 0; + for (i = 0; i < header[26]; ++i) + len += lacing[i]; - // this implies where the page ends - z->page_end = z->page_start + 27 + header[26] + len; + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; - // read the last-decoded sample out of the data - z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); - // restore file state to where we were - set_file_offset(f, z->page_start); - return 1; + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; } // rarely used function to seek back to the preceding page while finding the // start of a packet -static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) -{ - unsigned int previous_safe, end; +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { + unsigned int previous_safe, end; - // now we want to seek back 64K from the limit - if (limit_offset >= 65536 && limit_offset - 65536 >= f->first_audio_page_offset) - previous_safe = limit_offset - 65536; - else - previous_safe = f->first_audio_page_offset; + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset - 65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; - set_file_offset(f, previous_safe); + set_file_offset(f, previous_safe); - while (vorbis_find_page(f, &end, NULL)) - { - if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) - return 1; - set_file_offset(f, end); - } + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } - return 0; + return 0; } // implements the search logic for finding a page and starting decoding. if // the function succeeds, current_loc_valid will be true and current_loc will // be less than or equal to the provided sample number (the closer the // better). -static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) -{ - ProbedPage left, right, mid; - int i, start_seg_with_known_loc, end_pos, page_start; - uint32 delta, stream_length, padding, last_sample_limit; - double offset = 0.0, bytes_per_sample = 0.0; - int probe = 0; +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding, last_sample_limit; + double offset = 0.0, bytes_per_sample = 0.0; + int probe = 0; - // find the last page and validate the target sample - stream_length = stb_vorbis_stream_length_in_samples(f); - if (stream_length == 0) - return error(f, VORBIS_seek_without_length); - if (sample_number > stream_length) - return error(f, VORBIS_seek_invalid); + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) + return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) + return error(f, VORBIS_seek_invalid); - // this is the maximum difference between the window-center (which is the - // actual granule position value), and the right-start (which the spec - // indicates should be the granule position (give or take one)). - padding = ((f->blocksize_1 - f->blocksize_0) >> 2); - if (sample_number < padding) - last_sample_limit = 0; - else - last_sample_limit = sample_number - padding; + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + last_sample_limit = 0; + else + last_sample_limit = sample_number - padding; - left = f->p_first; - while (left.last_decoded_sample == ~0U) - { - // (untested) the first page does not have a 'last_decoded_sample' - set_file_offset(f, left.page_end); - if (!get_seek_page_info(f, &left)) - goto error; - } + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) + goto error; + } - right = f->p_last; - assert(right.last_decoded_sample != ~0U); + right = f->p_last; + assert(right.last_decoded_sample != ~0U); - // starting from the start is handled differently - if (last_sample_limit <= left.last_decoded_sample) - { - if (stb_vorbis_seek_start(f)) - { - if (f->current_loc > sample_number) - return error(f, VORBIS_seek_failed); - return 1; - } - return 0; - } + // starting from the start is handled differently + if (last_sample_limit <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) { + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + } + return 0; + } - while (left.page_end != right.page_start) - { - assert(left.page_end < right.page_start); - // search range in bytes - delta = right.page_start - left.page_end; - if (delta <= 65536) - { - // there's only 64K left to search - handle it linearly - set_file_offset(f, left.page_end); - } - else - { - if (probe < 2) - { - if (probe == 0) - { - // first probe (interpolate) - double data_bytes = right.page_end - left.page_start; - bytes_per_sample = data_bytes / right.last_decoded_sample; - offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double)last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) + error = 8000; + if (error < 0 && error > -8000) + error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int)offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); } + + if (!vorbis_find_page(f, NULL, NULL)) + goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) + goto error; + if (mid.last_decoded_sample != ~0U) + break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough (if it wasn't an interpolation probe). + if (mid.page_start == right.page_start) { + if (probe >= 2 || delta <= 65536) + break; + } else { + if (last_sample_limit < mid.last_decoded_sample) + right = mid; else - { - // second probe (try to bound the other side) - double error = ((double)last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; - if (error >= 0 && error < 8000) - error = 8000; - if (error < 0 && error > -8000) - error = -8000; - offset += error * 2; - } + left = mid; + } - // ensure the offset is valid - if (offset < left.page_end) - offset = left.page_end; - if (offset > right.page_start - 65536) - offset = right.page_start - 65536; + ++probe; + } - set_file_offset(f, (unsigned int)offset); - } - else - { - // binary search for large ranges (offset by 32K to ensure - // we don't hit the right page) - set_file_offset(f, left.page_end + (delta / 2) - 32768); - } + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) + return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); - if (!vorbis_find_page(f, NULL, NULL)) + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i - 1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) goto error; - } - for (;;) - { - if (!get_seek_page_info(f, &mid)) + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; - if (mid.last_decoded_sample != ~0U) - break; - // (untested) no frames end on this page - set_file_offset(f, mid.page_end); - assert(mid.page_start < right.page_start); - } + end_pos = f->segment_count - 1; + } - // if we've just found the last page again then we're in a tricky file, - // and we're close enough (if it wasn't an interpolation probe). - if (mid.page_start == right.page_start) - { - if (probe >= 2 || delta <= 65536) - break; - } - else - { - if (last_sample_limit < mid.last_decoded_sample) - right = mid; - else - left = mid; - } + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; - ++probe; - } + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); - // seek back to start of the last packet - page_start = left.page_start; - set_file_offset(f, page_start); - if (!start_page(f)) - return error(f, VORBIS_seek_failed); - end_pos = f->end_seg_with_known_loc; - assert(end_pos >= 0); - - for (;;) - { - for (i = end_pos; i > 0; --i) - if (f->segments[i - 1] != 255) - break; - - start_seg_with_known_loc = i; - - if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) - break; - - // (untested) the final packet begins on an earlier page - if (!go_to_page_before(f, page_start)) - goto error; - - page_start = stb_vorbis_get_file_offset(f); - if (!start_page(f)) - goto error; - end_pos = f->segment_count - 1; - } - - // prepare to start decoding - f->current_loc_valid = FALSE; - f->last_seg = FALSE; - f->valid_bits = 0; - f->packet_bytes = 0; - f->bytes_in_seg = 0; - f->previous_length = 0; - f->next_seg = start_seg_with_known_loc; - - for (i = 0; i < start_seg_with_known_loc; i++) - skip(f, f->segments[i]); - - // start decoding (optimizable - this frame is generally discarded) - if (!vorbis_pump_first_frame(f)) - return 0; - if (f->current_loc > sample_number) - return error(f, VORBIS_seek_failed); - return 1; + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; error: - // try to restore the file to a valid state - stb_vorbis_seek_start(f); - return error(f, VORBIS_seek_failed); + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); } // the same as vorbis_decode_initial, but without advancing -static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) -{ - int bits_read, bytes_read; +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { + int bits_read, bytes_read; - if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) - return 0; + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; - // either 1 or 2 bytes were read, figure out which so we can rewind - bits_read = 1 + ilog(f->mode_count - 1); - if (f->mode_config[*mode].blockflag) - bits_read += 2; - bytes_read = (bits_read + 7) / 8; + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count - 1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; - f->bytes_in_seg += bytes_read; - f->packet_bytes -= bytes_read; - skip(f, -bytes_read); - if (f->next_seg == -1) - f->next_seg = f->segment_count - 1; - else - f->next_seg--; - f->valid_bits = 0; + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; - return 1; + return 1; } -int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) -{ - uint32 max_frame_samples; +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { + uint32 max_frame_samples; - if (IS_PUSH_MODE(f)) - return error(f, VORBIS_invalid_api_mixing); + if (IS_PUSH_MODE(f)) + return error(f, VORBIS_invalid_api_mixing); - // fast page-level search - if (!seek_to_sample_coarse(f, sample_number)) - return 0; + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; - assert(f->current_loc_valid); - assert(f->current_loc <= sample_number); + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); - // linear search for the relevant packet - max_frame_samples = (f->blocksize_1 * 3 - f->blocksize_0) >> 2; - while (f->current_loc < sample_number) - { - int left_start, left_end, right_start, right_end, mode, frame_samples; - if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) - return error(f, VORBIS_seek_failed); - // calculate the number of samples returned by the next frame - frame_samples = right_start - left_start; - if (f->current_loc + frame_samples > sample_number) - { - return 1; // the next frame will contain the sample - } - else if (f->current_loc + frame_samples + max_frame_samples > sample_number) - { - // there's a chance the frame after this could contain the sample - vorbis_pump_first_frame(f); - } - else - { - // this frame is too early to be relevant - f->current_loc += frame_samples; - f->previous_length = 0; - maybe_start_packet(f); - flush_packet(f); - } - } - // the next frame should start with the sample - if (f->current_loc != sample_number) - return error(f, VORBIS_seek_failed); - return 1; + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1 * 3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame should start with the sample + if (f->current_loc != sample_number) + return error(f, VORBIS_seek_failed); + return 1; } -int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) -{ - if (!stb_vorbis_seek_frame(f, sample_number)) - return 0; +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) { + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; - if (sample_number != f->current_loc) - { - int n; - uint32 frame_start = f->current_loc; - stb_vorbis_get_frame_float(f, &n, NULL); - assert(sample_number > frame_start); - assert(f->channel_buffer_start + (int)(sample_number - frame_start) <= f->channel_buffer_end); - f->channel_buffer_start += (sample_number - frame_start); - } + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int)(sample_number - frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } - return 1; + return 1; } -int stb_vorbis_seek_start(stb_vorbis *f) -{ - if (IS_PUSH_MODE(f)) - { - return error(f, VORBIS_invalid_api_mixing); - } - set_file_offset(f, f->first_audio_page_offset); - f->previous_length = 0; - f->first_decode = TRUE; - f->next_seg = -1; - return vorbis_pump_first_frame(f); +int stb_vorbis_seek_start(stb_vorbis *f) { + if (IS_PUSH_MODE(f)) { + return error(f, VORBIS_invalid_api_mixing); + } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); } -unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) -{ - unsigned int restore_offset, previous_safe; - unsigned int end, last_page_loc; +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; - if (IS_PUSH_MODE(f)) - return error(f, VORBIS_invalid_api_mixing); - if (!f->total_samples) - { - unsigned int last; - uint32 lo, hi; - char header[6]; + if (IS_PUSH_MODE(f)) + return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo, hi; + char header[6]; - // first, store the current decode position so we can restore it - restore_offset = stb_vorbis_get_file_offset(f); + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); - // now we want to seek back 64K from the end (the last page must - // be at most a little less than 64K, but let's allow a little slop) - if (f->stream_len >= 65536 && f->stream_len - 65536 >= f->first_audio_page_offset) - previous_safe = f->stream_len - 65536; - else - previous_safe = f->first_audio_page_offset; + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len - 65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; - set_file_offset(f, previous_safe); - // previous_safe is now our candidate 'earliest known place that seeking - // to will lead to the final page' + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' - if (!vorbis_find_page(f, &end, &last)) - { - // if we can't find a page, we're hosed! - f->error = VORBIS_cant_find_last_page; - f->total_samples = 0xffffffff; - goto done; - } + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } - // check if there are more pages - last_page_loc = stb_vorbis_get_file_offset(f); + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); - // stop when the last_page flag is set, not when we reach eof; - // this allows us to stop short of a 'file_section' end without - // explicitly checking the length of the section - while (!last) - { - set_file_offset(f, end); - if (!vorbis_find_page(f, &end, &last)) - { - // the last page we found didn't have the 'last page' flag - // set. whoops! - break; - } - previous_safe = last_page_loc + 1; - last_page_loc = stb_vorbis_get_file_offset(f); - } + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc + 1; + last_page_loc = stb_vorbis_get_file_offset(f); + } - set_file_offset(f, last_page_loc); + set_file_offset(f, last_page_loc); - // parse the header - getn(f, (unsigned char *)header, 6); - // extract the absolute granule position - lo = get32(f); - hi = get32(f); - if (lo == 0xffffffff && hi == 0xffffffff) - { - f->error = VORBIS_cant_find_last_page; - f->total_samples = SAMPLE_unknown; - goto done; - } - if (hi) - lo = 0xfffffffe; // saturate - f->total_samples = lo; + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; - f->p_last.page_start = last_page_loc; - f->p_last.page_end = end; - f->p_last.last_decoded_sample = lo; + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; - done: - set_file_offset(f, restore_offset); - } - return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; } -float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) -{ - return stb_vorbis_stream_length_in_samples(f) / (float)f->sample_rate; +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) { + return stb_vorbis_stream_length_in_samples(f) / (float)f->sample_rate; } -int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) -{ - int len, right, left, i; - if (IS_PUSH_MODE(f)) - return error(f, VORBIS_invalid_api_mixing); +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) { + int len, right, left, i; + if (IS_PUSH_MODE(f)) + return error(f, VORBIS_invalid_api_mixing); - if (!vorbis_decode_packet(f, &len, &left, &right)) - { - f->channel_buffer_start = f->channel_buffer_end = 0; - return 0; - } + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } - len = vorbis_finish_frame(f, len, left, right); - for (i = 0; i < f->channels; ++i) - f->outputs[i] = f->channel_buffers[i] + left; + len = vorbis_finish_frame(f, len, left, right); + for (i = 0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; - f->channel_buffer_start = left; - f->channel_buffer_end = left + len; + f->channel_buffer_start = left; + f->channel_buffer_end = left + len; - if (channels) - *channels = f->channels; - if (output) - *output = f->outputs; - return len; + if (channels) + *channels = f->channels; + if (output) + *output = f->outputs; + return len; } #ifndef STB_VORBIS_NO_STDIO -stb_vorbis *stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) -{ - stb_vorbis *f, p; - vorbis_init(&p, alloc); - p.f = file; - p.f_start = (uint32)ftell(file); - p.stream_len = length; - p.close_on_free = close_on_free; - if (start_decoder(&p)) - { - f = vorbis_alloc(&p); - if (f) - { - *f = p; - vorbis_pump_first_frame(f); - return f; - } - } - if (error) - *error = p.error; - vorbis_deinit(&p); - return NULL; +stb_vorbis *stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32)ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) + *error = p.error; + vorbis_deinit(&p); + return NULL; } -stb_vorbis *stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) -{ - unsigned int len, start; - start = (unsigned int)ftell(file); - fseek(file, 0, SEEK_END); - len = (unsigned int)(ftell(file) - start); - fseek(file, start, SEEK_SET); - return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +stb_vorbis *stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { + unsigned int len, start; + start = (unsigned int)ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int)(ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } -stb_vorbis *stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) -{ - FILE *f; +stb_vorbis *stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { + FILE *f; #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) - if (0 != fopen_s(&f, filename, "rb")) - f = NULL; + if (0 != fopen_s(&f, filename, "rb")) + f = NULL; #else - f = fopen(filename, "rb"); + f = fopen(filename, "rb"); #endif - if (f) - return stb_vorbis_open_file(f, TRUE, error, alloc); - if (error) - *error = VORBIS_file_open_failure; - return NULL; + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) + *error = VORBIS_file_open_failure; + return NULL; } -#endif // STB_VORBIS_NO_STDIO +#endif // STB_VORBIS_NO_STDIO -stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) -{ - stb_vorbis *f, p; - if (data == NULL) - return NULL; - vorbis_init(&p, alloc); - p.stream = (uint8 *)data; - p.stream_end = (uint8 *)data + len; - p.stream_start = (uint8 *)p.stream; - p.stream_len = len; - p.push_mode = FALSE; - if (start_decoder(&p)) - { - f = vorbis_alloc(&p); - if (f) - { - *f = p; - vorbis_pump_first_frame(f); - if (error) - *error = VORBIS__no_error; - return f; - } - } - if (error) - *error = p.error; - vorbis_deinit(&p); - return NULL; +stb_vorbis *stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { + stb_vorbis *f, p; + if (data == NULL) + return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *)data; + p.stream_end = (uint8 *)data + len; + p.stream_start = (uint8 *)p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + if (error) + *error = VORBIS__no_error; + return f; + } + } + if (error) + *error = p.error; + vorbis_deinit(&p); + return NULL; } #ifndef STB_VORBIS_NO_INTEGER_CONVERSION @@ -5751,10 +5183,9 @@ static int8 channel_position[7][6] = }; #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT -typedef union -{ - float f; - int i; +typedef union { + float f; + int i; } float_conv; typedef char stb_vorbis_float_size_test[sizeof(float) == 4 && sizeof(int) == 4]; #define FASTDEF(x) float_conv x @@ -5769,382 +5200,330 @@ typedef char stb_vorbis_float_size_test[sizeof(float) == 4 && sizeof(int) == 4]; #define FASTDEF(x) #endif -static void copy_samples(short *dest, float *src, int len) -{ - int i; - check_endianness(); - for (i = 0; i < len; ++i) - { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15); - if ((unsigned int)(v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - dest[i] = v; - } -} - -static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) -{ -#define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE; - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) - { - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) - n = len - o; - for (j = 0; j < num_c; ++j) - { - if (channel_position[num_c][j] & mask) - { - for (i = 0; i < n; ++i) - buffer[i] += data[j][d_offset + o + i]; - } - } - for (i = 0; i < n; ++i) - { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) +static void copy_samples(short *dest, float *src, int len) { + int i; + check_endianness(); + for (i = 0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i], 15); + if ((unsigned int)(v + 32768) > 65535) v = v < 0 ? -32768 : 32767; - output[o + i] = v; - } - } + dest[i] = v; + } } -static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) -{ +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i, j, o, n = BUFFER_SIZE >> 1; - // o is the offset in the source data - check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) - { - // o2 is the offset in the output data - int o2 = o << 1; - memset(buffer, 0, sizeof(buffer)); - if (o + n > len) - n = len - o; - for (j = 0; j < num_c; ++j) - { - int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); - if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) - { - for (i = 0; i < n; ++i) - { - buffer[i * 2 + 0] += data[j][d_offset + o + i]; - buffer[i * 2 + 1] += data[j][d_offset + o + i]; + float buffer[BUFFER_SIZE]; + int i, j, o, n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) + n = len - o; + for (j = 0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i = 0; i < n; ++i) + buffer[i] += data[j][d_offset + o + i]; } - } - else if (m == PLAYBACK_LEFT) - { - for (i = 0; i < n; ++i) - { - buffer[i * 2 + 0] += data[j][d_offset + o + i]; - } - } - else if (m == PLAYBACK_RIGHT) - { - for (i = 0; i < n; ++i) - { - buffer[i * 2 + 1] += data[j][d_offset + o + i]; - } - } - } - for (i = 0; i < (n << 1); ++i) - { - FASTDEF(temp); - int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); - if ((unsigned int)(v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - output[o2 + i] = v; - } - } -} - -static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) -{ - int i; - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) - { - static int channel_selector[3][2] = {{0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT}}; - for (i = 0; i < buf_c; ++i) - compute_samples(channel_selector[buf_c][i], buffer[i] + b_offset, data_c, data, d_offset, samples); - } - else - { - int limit = buf_c < data_c ? buf_c : data_c; - for (i = 0; i < limit; ++i) - copy_samples(buffer[i] + b_offset, data[i] + d_offset, samples); - for (; i < buf_c; ++i) - memset(buffer[i] + b_offset, 0, sizeof(short) * samples); - } -} - -int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) -{ - float **output = NULL; - int len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len > num_samples) - len = num_samples; - if (len) - convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); - return len; -} - -static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) -{ - int i; - check_endianness(); - if (buf_c != data_c && buf_c <= 2 && data_c <= 6) - { - assert(buf_c == 2); - for (i = 0; i < buf_c; ++i) - compute_stereo_samples(buffer, data_c, data, d_offset, len); - } - else - { - int limit = buf_c < data_c ? buf_c : data_c; - int j; - for (j = 0; j < len; ++j) - { - for (i = 0; i < limit; ++i) - { + } + for (i = 0; i < n; ++i) { FASTDEF(temp); - float f = data[i][d_offset + j]; - int v = FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15); + int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); if ((unsigned int)(v + 32768) > 65535) - v = v < 0 ? -32768 : 32767; - *buffer++ = v; - } - for (; i < buf_c; ++i) - *buffer++ = 0; - } - } + v = v < 0 ? -32768 : 32767; + output[o + i] = v; + } + } } -int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) -{ - float **output; - int len; - if (num_c == 1) - return stb_vorbis_get_frame_short(f, num_c, &buffer, num_shorts); - len = stb_vorbis_get_frame_float(f, NULL, &output); - if (len) - { - if (len * num_c > num_shorts) - len = num_shorts / num_c; - convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); - } - return len; +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { +#define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i, j, o, n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) + n = len - o; + for (j = 0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i = 0; i < n; ++i) { + buffer[i * 2 + 0] += data[j][d_offset + o + i]; + buffer[i * 2 + 1] += data[j][d_offset + o + i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i = 0; i < n; ++i) { + buffer[i * 2 + 0] += data[j][d_offset + o + i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i = 0; i < n; ++i) { + buffer[i * 2 + 1] += data[j][d_offset + o + i]; + } + } + } + for (i = 0; i < (n << 1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, buffer[i], 15); + if ((unsigned int)(v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2 + i] = v; + } + } } -int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) -{ - float **outputs; - int len = num_shorts / channels; - int n = 0; - int z = f->channels; - if (z > channels) - z = channels; - while (n < len) - { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n + k >= len) - k = len - n; - if (k) - convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); - buffer += k * channels; - n += k; - f->channel_buffer_start += k; - if (n == len) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) { + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = {{0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT}}; + for (i = 0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i] + b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i = 0; i < limit; ++i) + copy_samples(buffer[i] + b_offset, data[i] + d_offset, samples); + for (; i < buf_c; ++i) + memset(buffer[i] + b_offset, 0, sizeof(short) * samples); + } } -int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) -{ - float **outputs; - int n = 0; - int z = f->channels; - if (z > channels) - z = channels; - while (n < len) - { - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n + k >= len) - k = len - n; - if (k) - convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); - n += k; - f->channel_buffer_start += k; - if (n == len) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { + float **output = NULL; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) + len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) { + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i = 0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j = 0; j < len; ++j) { + for (i = 0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset + j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f, 15); // data[i][d_offset+j],15); + if ((unsigned int)(v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for (; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) { + float **output; + int len; + if (num_c == 1) + return stb_vorbis_get_frame_short(f, num_c, &buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len * num_c > num_shorts) + len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) { + float **outputs; + int len = num_shorts / channels; + int n = 0; + int z = f->channels; + if (z > channels) + z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n + k >= len) + k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k * channels; + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { + float **outputs; + int n = 0; + int z = f->channels; + if (z > channels) + z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n + k >= len) + k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; } #ifndef STB_VORBIS_NO_STDIO -int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); - if (v == NULL) - return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *)malloc(total * sizeof(*data)); - if (data == NULL) - { - stb_vorbis_close(v); - return -2; - } - for (;;) - { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data + offset, total - offset); - if (n == 0) - break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) - { - short *data2; - total *= 2; - data2 = (short *)realloc(data, total * sizeof(*data)); - if (data2 == NULL) - { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) + return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *)malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data + offset, total - offset); + if (n == 0) + break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *)realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; } -#endif // NO_STDIO +#endif // NO_STDIO -int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) -{ - int data_len, offset, total, limit, error; - short *data; - stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); - if (v == NULL) - return -1; - limit = v->channels * 4096; - *channels = v->channels; - if (sample_rate) - *sample_rate = v->sample_rate; - offset = data_len = 0; - total = limit; - data = (short *)malloc(total * sizeof(*data)); - if (data == NULL) - { - stb_vorbis_close(v); - return -2; - } - for (;;) - { - int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data + offset, total - offset); - if (n == 0) - break; - data_len += n; - offset += n * v->channels; - if (offset + limit > total) - { - short *data2; - total *= 2; - data2 = (short *)realloc(data, total * sizeof(*data)); - if (data2 == NULL) - { - free(data); - stb_vorbis_close(v); - return -2; - } - data = data2; - } - } - *output = data; - stb_vorbis_close(v); - return data_len; +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) + return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *)malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data + offset, total - offset); + if (n == 0) + break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *)realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; } -#endif // STB_VORBIS_NO_INTEGER_CONVERSION +#endif // STB_VORBIS_NO_INTEGER_CONVERSION -int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) -{ - float **outputs; - int len = num_floats / channels; - int n = 0; - int z = f->channels; - if (z > channels) - z = channels; - while (n < len) - { - int i, j; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n + k >= len) - k = len - n; - for (j = 0; j < k; ++j) - { - for (i = 0; i < z; ++i) - *buffer++ = f->channel_buffers[i][f->channel_buffer_start + j]; - for (; i < channels; ++i) - *buffer++ = 0; - } - n += k; - f->channel_buffer_start += k; - if (n == len) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { + float **outputs; + int len = num_floats / channels; + int n = 0; + int z = f->channels; + if (z > channels) + z = channels; + while (n < len) { + int i, j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n + k >= len) + k = len - n; + for (j = 0; j < k; ++j) { + for (i = 0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start + j]; + for (; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; } -int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) -{ - float **outputs; - int n = 0; - int z = f->channels; - if (z > channels) - z = channels; - while (n < num_samples) - { - int i; - int k = f->channel_buffer_end - f->channel_buffer_start; - if (n + k >= num_samples) - k = num_samples - n; - if (k) - { - for (i = 0; i < z; ++i) - memcpy(buffer[i] + n, f->channel_buffers[i] + f->channel_buffer_start, sizeof(float) * k); - for (; i < channels; ++i) - memset(buffer[i] + n, 0, sizeof(float) * k); - } - n += k; - f->channel_buffer_start += k; - if (n == num_samples) - break; - if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) - break; - } - return n; +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) { + float **outputs; + int n = 0; + int z = f->channels; + if (z > channels) + z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n + k >= num_samples) + k = num_samples - n; + if (k) { + for (i = 0; i < z; ++i) + memcpy(buffer[i] + n, f->channel_buffers[i] + f->channel_buffer_start, sizeof(float) * k); + for (; i < channels; ++i) + memset(buffer[i] + n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; } -#endif // STB_VORBIS_NO_PULLDATA_API +#endif // STB_VORBIS_NO_PULLDATA_API /* Version history 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 @@ -6207,7 +5586,7 @@ int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, in 0.90 - first public release */ -#endif // STB_VORBIS_HEADER_ONLY +#endif // STB_VORBIS_HEADER_ONLY /* ------------------------------------------------------------------------------ diff --git a/source/fade.cpp b/source/fade.cpp index 3ce0759..c1c951a 100644 --- a/source/fade.cpp +++ b/source/fade.cpp @@ -1,374 +1,325 @@ #include "fade.h" -#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_GetRenderT... -#include <stdlib.h> // Para rand, size_t +#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_FRect, SDL_GetRenderT... +#include <stdlib.h> // Para rand, size_t + #include <algorithm> // Para min, max -#include "param.h" // Para Param, param, ParamGame, ParamFade -#include "screen.h" // Para Screen -#include "utils.h" // Para Color +#include "param.h" // Para Param, param, ParamGame, ParamFade +#include "screen.h" // Para Screen +#include "utils.h" // Para Color // Constructor Fade::Fade() - : renderer_(Screen::get()->getRenderer()) -{ - // Crea la textura donde dibujar el fade - backbuffer_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); - SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); + : renderer_(Screen::get()->getRenderer()) { + // Crea la textura donde dibujar el fade + backbuffer_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); + SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); - // Inicializa las variables - init(); + // Inicializa las variables + init(); } // Destructor -Fade::~Fade() -{ - SDL_DestroyTexture(backbuffer_); +Fade::~Fade() { + SDL_DestroyTexture(backbuffer_); } // Inicializa las variables -void Fade::init() -{ - type_ = FadeType::CENTER; - mode_ = FadeMode::OUT; - counter_ = 0; - r_ = 0; - g_ = 0; - b_ = 0; - a_ = 0; - post_duration_ = 0; - post_counter_ = 0; - pre_duration_ = 0; - pre_counter_ = 0; - num_squares_width_ = param.fade.num_squares_width; - num_squares_height_ = param.fade.num_squares_height; - fade_random_squares_delay_ = param.fade.random_squares_delay; - fade_random_squares_mult_ = param.fade.random_squares_mult; +void Fade::init() { + type_ = FadeType::CENTER; + mode_ = FadeMode::OUT; + counter_ = 0; + r_ = 0; + g_ = 0; + b_ = 0; + a_ = 0; + post_duration_ = 0; + post_counter_ = 0; + pre_duration_ = 0; + pre_counter_ = 0; + num_squares_width_ = param.fade.num_squares_width; + num_squares_height_ = param.fade.num_squares_height; + fade_random_squares_delay_ = param.fade.random_squares_delay; + fade_random_squares_mult_ = param.fade.random_squares_mult; } // Resetea algunas variables para volver a hacer el fade sin perder ciertos parametros -void Fade::reset() -{ - state_ = FadeState::NOT_ENABLED; - counter_ = 0; +void Fade::reset() { + state_ = FadeState::NOT_ENABLED; + counter_ = 0; } // Pinta una transición en pantalla -void Fade::render() -{ - if (state_ != FadeState::NOT_ENABLED) - { - SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr); - } +void Fade::render() { + if (state_ != FadeState::NOT_ENABLED) { + SDL_RenderTexture(renderer_, backbuffer_, nullptr, nullptr); + } } // Actualiza las variables internas -void Fade::update() -{ - if (state_ == FadeState::PRE) - { - // Actualiza el contador - if (pre_counter_ == pre_duration_) - { - state_ = FadeState::FADING; - } - else - { - pre_counter_++; - } - } +void Fade::update() { + if (state_ == FadeState::PRE) { + // Actualiza el contador + if (pre_counter_ == pre_duration_) { + state_ = FadeState::FADING; + } else { + pre_counter_++; + } + } - if (state_ == FadeState::FADING) - { - switch (type_) - { - case FadeType::FULLSCREEN: - { - // Modifica la transparencia - a_ = mode_ == FadeMode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255); + if (state_ == FadeState::FADING) { + switch (type_) { + case FadeType::FULLSCREEN: { + // Modifica la transparencia + a_ = mode_ == FadeMode::OUT ? std::min(counter_ * 4, 255) : 255 - std::min(counter_ * 4, 255); - SDL_SetTextureAlphaMod(backbuffer_, a_); + SDL_SetTextureAlphaMod(backbuffer_, a_); - // Comprueba si ha terminado - if (counter_ >= 255 / 4) - { - state_ = FadeState::POST; - } + // Comprueba si ha terminado + if (counter_ >= 255 / 4) { + state_ = FadeState::POST; + } - break; - } + break; + } - case FadeType::CENTER: - { - // Dibuja sobre el backbuffer_ - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); + case FadeType::CENTER: { + // Dibuja sobre el backbuffer_ + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); - SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); + SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); - for (int i = 0; i < counter_; i++) - { - rect1_.h = rect2_.h = i * 4; - rect2_.y = param.game.height - (i * 4); + for (int i = 0; i < counter_; i++) { + rect1_.h = rect2_.h = i * 4; + rect2_.y = param.game.height - (i * 4); - SDL_RenderFillRect(renderer_, &rect1_); - SDL_RenderFillRect(renderer_, &rect2_); + SDL_RenderFillRect(renderer_, &rect1_); + SDL_RenderFillRect(renderer_, &rect2_); - value_ = calculateValue(0, counter_, i); - } + value_ = calculateValue(0, counter_, i); + } - // Deja el renderizador como estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador como estaba + SDL_SetRenderTarget(renderer_, temp); - // Comprueba si ha terminado - if ((counter_ * 4) > param.game.height) - { - state_ = FadeState::POST; - a_ = 255; - } - break; - } + // Comprueba si ha terminado + if ((counter_ * 4) > param.game.height) { + state_ = FadeState::POST; + a_ = 255; + } + break; + } - case FadeType::RANDOM_SQUARE: - { - if (counter_ % fade_random_squares_delay_ == 0) - { - // Cambia el renderizador al backbuffer_ y modifica sus opciones - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); - SDL_BlendMode blend_mode; - SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); - SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); - SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); + case FadeType::RANDOM_SQUARE: { + if (counter_ % fade_random_squares_delay_ == 0) { + // Cambia el renderizador al backbuffer_ y modifica sus opciones + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); + SDL_BlendMode blend_mode; + SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); + SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); - // Dibuja el cuadrado correspondiente - const int INDEX = std::min(counter_ / fade_random_squares_delay_, (num_squares_width_ * num_squares_height_) - 1); - for (int i = 0; i < fade_random_squares_mult_; ++i) - { - const int INDEX2 = std::min(INDEX * fade_random_squares_mult_ + i, (int)square_.size() - 1); - SDL_RenderFillRect(renderer_, &square_[INDEX2]); - } + // Dibuja el cuadrado correspondiente + const int INDEX = std::min(counter_ / fade_random_squares_delay_, (num_squares_width_ * num_squares_height_) - 1); + for (int i = 0; i < fade_random_squares_mult_; ++i) { + const int INDEX2 = std::min(INDEX * fade_random_squares_mult_ + i, (int)square_.size() - 1); + SDL_RenderFillRect(renderer_, &square_[INDEX2]); + } - // Deja el renderizador como estaba - SDL_SetRenderDrawBlendMode(renderer_, blend_mode); - SDL_SetRenderTarget(renderer_, temp); - } + // Deja el renderizador como estaba + SDL_SetRenderDrawBlendMode(renderer_, blend_mode); + SDL_SetRenderTarget(renderer_, temp); + } - value_ = calculateValue(0, static_cast<int>(num_squares_width_ * num_squares_height_), static_cast<int>(counter_ * fade_random_squares_mult_ / fade_random_squares_delay_)); + value_ = calculateValue(0, static_cast<int>(num_squares_width_ * num_squares_height_), static_cast<int>(counter_ * fade_random_squares_mult_ / fade_random_squares_delay_)); - // Comprueba si ha terminado - if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >= num_squares_width_ * num_squares_height_) - { - state_ = FadeState::POST; - } + // Comprueba si ha terminado + if (counter_ * fade_random_squares_mult_ / fade_random_squares_delay_ >= num_squares_width_ * num_squares_height_) { + state_ = FadeState::POST; + } - break; - } + break; + } - case FadeType::VENETIAN: - { - // Counter debe ir de 0 a 150 <-- comprobar si esto es aún cierto - if (square_.back().h < param.fade.venetian_size) - { - // Dibuja sobre el backbuffer_ - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); - SDL_BlendMode blend_mode; - SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); - SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); - SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); + case FadeType::VENETIAN: { + // Counter debe ir de 0 a 150 <-- comprobar si esto es aún cierto + if (square_.back().h < param.fade.venetian_size) { + // Dibuja sobre el backbuffer_ + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); + SDL_BlendMode blend_mode; + SDL_GetRenderDrawBlendMode(renderer_, &blend_mode); + SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(renderer_, r_, g_, b_, a_); - // Dibuja el cuadrado correspondiente - for (const auto rect : square_) - { - SDL_RenderFillRect(renderer_, &rect); - } + // Dibuja el cuadrado correspondiente + for (const auto rect : square_) { + SDL_RenderFillRect(renderer_, &rect); + } - // Deja el renderizador como estaba - SDL_SetRenderDrawBlendMode(renderer_, blend_mode); - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador como estaba + SDL_SetRenderDrawBlendMode(renderer_, blend_mode); + SDL_SetRenderTarget(renderer_, temp); - // Modifica el tamaño de los rectangulos - const auto h = counter_ / 2; - for (size_t i = 0; i < square_.size(); ++i) - { - // A partir del segundo rectangulo se pinta en función del anterior - square_.at(i).h = i == 0 ? h : std::max(static_cast<int>(square_.at(i - 1).h) - 2, 0); - } + // Modifica el tamaño de los rectangulos + const auto h = counter_ / 2; + for (size_t i = 0; i < square_.size(); ++i) { + // A partir del segundo rectangulo se pinta en función del anterior + square_.at(i).h = i == 0 ? h : std::max(static_cast<int>(square_.at(i - 1).h) - 2, 0); + } - int completed = 0; - for (const auto &square : square_) - { - if (square.h >= param.fade.venetian_size) - { - ++completed; - } - } - value_ = calculateValue(0, square_.size() - 1, completed); - } - else - { - state_ = FadeState::POST; - } + int completed = 0; + for (const auto &square : square_) { + if (square.h >= param.fade.venetian_size) { + ++completed; + } + } + value_ = calculateValue(0, square_.size() - 1, completed); + } else { + state_ = FadeState::POST; + } - break; - } - default: - break; - } - counter_++; - } + break; + } + default: + break; + } + counter_++; + } - if (state_ == FadeState::POST) - { - // Actualiza el contador - if (post_counter_ == post_duration_) - { - state_ = FadeState::FINISHED; - } - else - { - post_counter_++; - } + if (state_ == FadeState::POST) { + // Actualiza el contador + if (post_counter_ == post_duration_) { + state_ = FadeState::FINISHED; + } else { + post_counter_++; + } - // Deja el backbuffer_ todo del mismo color - cleanBackbuffer(r_, g_, b_, a_); - } + // Deja el backbuffer_ todo del mismo color + cleanBackbuffer(r_, g_, b_, a_); + } } // Activa el fade -void Fade::activate() -{ - // Si ya está habilitado, no hay que volverlo a activar - if (state_ != FadeState::NOT_ENABLED) - { - return; - } +void Fade::activate() { + // Si ya está habilitado, no hay que volverlo a activar + if (state_ != FadeState::NOT_ENABLED) { + return; + } - state_ = FadeState::PRE; - counter_ = 0; - post_counter_ = 0; - pre_counter_ = 0; + state_ = FadeState::PRE; + counter_ = 0; + post_counter_ = 0; + pre_counter_ = 0; - switch (type_) - { - case FadeType::FULLSCREEN: - { - // Pinta el backbuffer_ de color sólido - cleanBackbuffer(r_, g_, b_, 255); - break; - } + switch (type_) { + case FadeType::FULLSCREEN: { + // Pinta el backbuffer_ de color sólido + cleanBackbuffer(r_, g_, b_, 255); + break; + } - case FadeType::CENTER: - { - rect1_ = {0, 0, static_cast<float>(param.game.width), 0}; - rect2_ = {0, 0, static_cast<float>(param.game.width), 0}; - a_ = 64; - break; - } + case FadeType::CENTER: { + rect1_ = {0, 0, static_cast<float>(param.game.width), 0}; + rect2_ = {0, 0, static_cast<float>(param.game.width), 0}; + a_ = 64; + break; + } - case FadeType::RANDOM_SQUARE: - { - rect1_ = {0, 0, static_cast<float>(param.game.width / num_squares_width_), static_cast<float>(param.game.height / num_squares_height_)}; - square_.clear(); + case FadeType::RANDOM_SQUARE: { + rect1_ = {0, 0, static_cast<float>(param.game.width / num_squares_width_), static_cast<float>(param.game.height / num_squares_height_)}; + square_.clear(); - // Añade los cuadrados al vector - for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) - { - rect1_.x = (i % num_squares_width_) * rect1_.w; - rect1_.y = (i / num_squares_width_) * rect1_.h; - square_.push_back(rect1_); - } + // Añade los cuadrados al vector + for (int i = 0; i < num_squares_width_ * num_squares_height_; ++i) { + rect1_.x = (i % num_squares_width_) * rect1_.w; + rect1_.y = (i / num_squares_width_) * rect1_.h; + square_.push_back(rect1_); + } - // Desordena el vector de cuadrados - auto num = num_squares_width_ * num_squares_height_; - while (num > 1) - { - auto num_arreu = rand() % num; - SDL_FRect temp = square_[num_arreu]; - square_[num_arreu] = square_[num - 1]; - square_[num - 1] = temp; - num--; - } + // Desordena el vector de cuadrados + auto num = num_squares_width_ * num_squares_height_; + while (num > 1) { + auto num_arreu = rand() % num; + SDL_FRect temp = square_[num_arreu]; + square_[num_arreu] = square_[num - 1]; + square_[num - 1] = temp; + num--; + } - // Limpia la textura - a_ = mode_ == FadeMode::OUT ? 0 : 255; - cleanBackbuffer(r_, g_, b_, a_); + // Limpia la textura + a_ = mode_ == FadeMode::OUT ? 0 : 255; + cleanBackbuffer(r_, g_, b_, a_); - // Deja el color listo para usar - a_ = mode_ == FadeMode::OUT ? 255 : 0; + // Deja el color listo para usar + a_ = mode_ == FadeMode::OUT ? 255 : 0; - break; - } + break; + } - case FadeType::VENETIAN: - { - // Limpia la textura - a_ = mode_ == FadeMode::OUT ? 0 : 255; - cleanBackbuffer(r_, g_, b_, a_); + case FadeType::VENETIAN: { + // Limpia la textura + a_ = mode_ == FadeMode::OUT ? 0 : 255; + cleanBackbuffer(r_, g_, b_, a_); - // Deja el color listo para usar - a_ = mode_ == FadeMode::OUT ? 255 : 0; + // Deja el color listo para usar + a_ = mode_ == FadeMode::OUT ? 255 : 0; - // Añade los cuadrados al vector - square_.clear(); - rect1_ = {0, 0, static_cast<float>(param.game.width), 0}; - const int MAX = param.game.height / param.fade.venetian_size; + // Añade los cuadrados al vector + square_.clear(); + rect1_ = {0, 0, static_cast<float>(param.game.width), 0}; + const int MAX = param.game.height / param.fade.venetian_size; - for (int i = 0; i < MAX; ++i) - { - rect1_.y = i * param.fade.venetian_size; - square_.push_back(rect1_); - } + for (int i = 0; i < MAX; ++i) { + rect1_.y = i * param.fade.venetian_size; + square_.push_back(rect1_); + } - break; - } - } + break; + } + } } // Establece el color del fade -void Fade::setColor(Uint8 r, Uint8 g, Uint8 b) -{ - r_ = r; - g_ = g; - b_ = b; +void Fade::setColor(Uint8 r, Uint8 g, Uint8 b) { + r_ = r; + g_ = g; + b_ = b; } // Establece el color del fade -void Fade::setColor(Color color) -{ - r_ = color.r; - g_ = color.g; - b_ = color.b; +void Fade::setColor(Color color) { + r_ = color.r; + g_ = color.g; + b_ = color.b; } // Limpia el backbuffer -void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a) -{ - // Dibujamos sobre el backbuffer_ - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); +void Fade::cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { + // Dibujamos sobre el backbuffer_ + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); - // Pintamos la textura con el color del fade - SDL_SetRenderDrawColor(renderer_, r, g, b, a); - SDL_RenderClear(renderer_); + // Pintamos la textura con el color del fade + SDL_SetRenderDrawColor(renderer_, r, g, b, a); + SDL_RenderClear(renderer_); - // Vuelve a dejar el renderizador como estaba - SDL_SetRenderTarget(renderer_, temp); + // Vuelve a dejar el renderizador como estaba + SDL_SetRenderTarget(renderer_, temp); } // Calcula el valor del estado del fade -int Fade::calculateValue(int min, int max, int current) -{ - if (current < min) - { - return 0; - } +int Fade::calculateValue(int min, int max, int current) { + if (current < min) { + return 0; + } - if (current > max) - { - return 100; - } + if (current > max) { + return 100; + } - return static_cast<int>(100.0 * (current - min) / (max - min)); + return static_cast<int>(100.0 * (current - min) / (max - min)); } \ No newline at end of file diff --git a/source/fade.h b/source/fade.h index dcaffeb..4d4fad2 100644 --- a/source/fade.h +++ b/source/fade.h @@ -1,93 +1,90 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_Renderer, SDL_Texture, Uint16 -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_Renderer, SDL_Texture, Uint16 + +#include <vector> // Para vector struct Color; // Tipos de fundido -enum class FadeType : Uint8 -{ - FULLSCREEN = 0, - CENTER = 1, - RANDOM_SQUARE = 2, - VENETIAN = 3, +enum class FadeType : Uint8 { + FULLSCREEN = 0, + CENTER = 1, + RANDOM_SQUARE = 2, + VENETIAN = 3, }; // Modos de fundido -enum class FadeMode : Uint8 -{ - IN = 0, - OUT = 1, +enum class FadeMode : Uint8 { + IN = 0, + OUT = 1, }; // Estados del objeto -enum class FadeState : Uint8 -{ - NOT_ENABLED = 0, - PRE = 1, - FADING = 2, - POST = 3, - FINISHED = 4, +enum class FadeState : Uint8 { + NOT_ENABLED = 0, + PRE = 1, + FADING = 2, + POST = 3, + FINISHED = 4, }; -class Fade -{ -public: - // --- Constructores y destructor --- - Fade(); - ~Fade(); +class Fade { + public: + // --- Constructores y destructor --- + Fade(); + ~Fade(); - // --- Métodos principales --- - void reset(); // Resetea variables para reutilizar el fade - void render(); // Dibuja la transición en pantalla - void update(); // Actualiza el estado interno - void activate(); // Activa el fade + // --- Métodos principales --- + void reset(); // Resetea variables para reutilizar el fade + void render(); // Dibuja la transición en pantalla + void update(); // Actualiza el estado interno + void activate(); // Activa el fade - // --- Configuración --- - void setColor(Uint8 r, Uint8 g, Uint8 b); - void setColor(Color color); - void setType(FadeType type) { type_ = type; } - void setMode(FadeMode mode) { mode_ = mode; } - void setPostDuration(int value) { post_duration_ = value; } - void setPreDuration(int value) { pre_duration_ = value; } + // --- Configuración --- + void setColor(Uint8 r, Uint8 g, Uint8 b); + void setColor(Color color); + void setType(FadeType type) { type_ = type; } + void setMode(FadeMode mode) { mode_ = mode; } + void setPostDuration(int value) { post_duration_ = value; } + void setPreDuration(int value) { pre_duration_ = value; } - // --- Getters --- - int getValue() const { return value_; } - bool isEnabled() const { return state_ != FadeState::NOT_ENABLED; } - bool hasEnded() const { return state_ == FadeState::FINISHED; } + // --- Getters --- + int getValue() const { return value_; } + bool isEnabled() const { return state_ != FadeState::NOT_ENABLED; } + bool hasEnded() const { return state_ == FadeState::FINISHED; } -private: - // --- Objetos y punteros --- - SDL_Renderer *renderer_; // Renderizador de la ventana - SDL_Texture *backbuffer_; // Backbuffer para efectos + private: + // --- Objetos y punteros --- + SDL_Renderer *renderer_; // Renderizador de la ventana + SDL_Texture *backbuffer_; // Backbuffer para efectos - // --- Variables de estado --- - FadeType type_; // Tipo de fade - FadeMode mode_; // Modo de fade - FadeState state_ = FadeState::NOT_ENABLED; // Estado actual - Uint16 counter_; // Contador interno + // --- Variables de estado --- + FadeType type_; // Tipo de fade + FadeMode mode_; // Modo de fade + FadeState state_ = FadeState::NOT_ENABLED; // Estado actual + Uint16 counter_; // Contador interno - // --- Parámetros de color y geometría --- - Uint8 r_, g_, b_, a_; // Color del fade - SDL_FRect rect1_, rect2_; // Rectángulos para efectos + // --- Parámetros de color y geometría --- + Uint8 r_, g_, b_, a_; // Color del fade + SDL_FRect rect1_, rect2_; // Rectángulos para efectos - // --- Parámetros para RANDOM_SQUARE --- - int num_squares_width_; // Cuadrados en horizontal - int num_squares_height_; // Cuadrados en vertical - std::vector<SDL_FRect> square_; // Vector de cuadrados - int fade_random_squares_delay_; // Delay entre cuadrados - int fade_random_squares_mult_; // Cuadrados por paso + // --- Parámetros para RANDOM_SQUARE --- + int num_squares_width_; // Cuadrados en horizontal + int num_squares_height_; // Cuadrados en vertical + std::vector<SDL_FRect> square_; // Vector de cuadrados + int fade_random_squares_delay_; // Delay entre cuadrados + int fade_random_squares_mult_; // Cuadrados por paso - // --- Temporizadores --- - int post_duration_ = 0, post_counter_ = 0; - int pre_duration_ = 0, pre_counter_ = 0; + // --- Temporizadores --- + int post_duration_ = 0, post_counter_ = 0; + int pre_duration_ = 0, pre_counter_ = 0; - // --- Valor de progreso --- - int value_ = 0; // Estado del fade (0-100) + // --- Valor de progreso --- + int value_ = 0; // Estado del fade (0-100) - // --- Métodos internos --- - void init(); // Inicializa variables - void cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a); // Limpia el backbuffer - int calculateValue(int min, int max, int current); // Calcula el valor del fade + // --- Métodos internos --- + void init(); // Inicializa variables + void cleanBackbuffer(Uint8 r, Uint8 g, Uint8 b, Uint8 a); // Limpia el backbuffer + int calculateValue(int min, int max, int current); // Calcula el valor del fade }; \ No newline at end of file diff --git a/source/game_logo.cpp b/source/game_logo.cpp index 39bff0f..d17456c 100644 --- a/source/game_logo.cpp +++ b/source/game_logo.cpp @@ -1,18 +1,19 @@ #include "game_logo.h" -#include <SDL3/SDL.h> // Para SDL_SetTextureScaleMode, SDL_FlipMode +#include <SDL3/SDL.h> // Para SDL_SetTextureScaleMode, SDL_FlipMode + #include <algorithm> // Para max #include <string> // Para basic_string -#include "animated_sprite.h" // Para AnimatedSprite -#include "audio.h" // Para Audio -#include "param.h" // Para Param, param, ParamGame, ParamTitle -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "smart_sprite.h" // Para SmartSprite -#include "sprite.h" // Para Sprite -#include "texture.h" // Para Texture -#include "utils.h" // Para Color +#include "animated_sprite.h" // Para AnimatedSprite +#include "audio.h" // Para Audio +#include "param.h" // Para Param, param, ParamGame, ParamTitle +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "smart_sprite.h" // Para SmartSprite +#include "sprite.h" // Para Sprite +#include "texture.h" // Para Texture +#include "utils.h" // Para Color constexpr int ZOOM_FACTOR = 5; constexpr int FLASH_DELAY = 3; @@ -33,8 +34,7 @@ GameLogo::GameLogo(int x, int y) y_(y) {} // Inicializa las variables -void GameLogo::init() -{ +void GameLogo::init() { const auto xp = x_ - coffee_texture_->getWidth() / 2; const auto desp = getInitialVerticalDesp(); @@ -97,14 +97,12 @@ void GameLogo::init() } // Pinta la clase en pantalla -void GameLogo::render() -{ +void GameLogo::render() { // Dibuja el logo coffee_sprite_->render(); crisis_sprite_->render(); - if (arcade_edition_status_ != Status::DISABLED) - { + if (arcade_edition_status_ != Status::DISABLED) { arcade_edition_sprite_->render(); } @@ -114,145 +112,119 @@ void GameLogo::render() } // Actualiza la lógica de la clase -void GameLogo::update() -{ - switch (coffee_crisis_status_) - { - case Status::MOVING: - { - coffee_sprite_->update(); - crisis_sprite_->update(); +void GameLogo::update() { + switch (coffee_crisis_status_) { + case Status::MOVING: { + coffee_sprite_->update(); + crisis_sprite_->update(); - // Si los objetos han llegado a su destino, cambia el estado - if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) - { - coffee_crisis_status_ = Status::SHAKING; + // Si los objetos han llegado a su destino, cambia el estado + if (coffee_sprite_->hasFinished() && crisis_sprite_->hasFinished()) { + coffee_crisis_status_ = Status::SHAKING; - // Reproduce el efecto sonoro - Audio::get()->playSound("title.wav"); - Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY); - Screen::get()->shake(); - } - - break; - } - - case Status::SHAKING: - { - // Agita "COFFEE CRISIS" - if (shake_.remaining > 0) - { - if (shake_.counter > 0) - { - shake_.counter--; + // Reproduce el efecto sonoro + Audio::get()->playSound("title.wav"); + Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY); + Screen::get()->shake(); } - else - { - shake_.counter = shake_.delay; - const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; - coffee_sprite_->setPosX(shake_.origin + desp); - crisis_sprite_->setPosX(shake_.origin + desp + 15); - shake_.remaining--; + + break; + } + + case Status::SHAKING: { + // Agita "COFFEE CRISIS" + if (shake_.remaining > 0) { + if (shake_.counter > 0) { + shake_.counter--; + } else { + shake_.counter = shake_.delay; + const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; + coffee_sprite_->setPosX(shake_.origin + desp); + crisis_sprite_->setPosX(shake_.origin + desp + 15); + shake_.remaining--; + } + } else { + coffee_sprite_->setPosX(shake_.origin); + crisis_sprite_->setPosX(shake_.origin + 15); + coffee_crisis_status_ = Status::FINISHED; + arcade_edition_status_ = Status::MOVING; } - } - else - { - coffee_sprite_->setPosX(shake_.origin); - crisis_sprite_->setPosX(shake_.origin + 15); - coffee_crisis_status_ = Status::FINISHED; - arcade_edition_status_ = Status::MOVING; + + dust_right_sprite_->update(); + dust_left_sprite_->update(); + + break; } - dust_right_sprite_->update(); - dust_left_sprite_->update(); + case Status::FINISHED: { + dust_right_sprite_->update(); + dust_left_sprite_->update(); - break; + break; + } + + default: + break; } - case Status::FINISHED: - { - dust_right_sprite_->update(); - dust_left_sprite_->update(); - - break; - } - - default: - break; - } - - switch (arcade_edition_status_) - { - case Status::MOVING: - { - zoom_ -= 0.1f * ZOOM_FACTOR; - arcade_edition_sprite_->setZoom(zoom_); - if (zoom_ <= 1.0f) - { - arcade_edition_status_ = Status::SHAKING; - zoom_ = 1.0f; + switch (arcade_edition_status_) { + case Status::MOVING: { + zoom_ -= 0.1f * ZOOM_FACTOR; arcade_edition_sprite_->setZoom(zoom_); - shake_.init(1, 2, 8, arcade_edition_sprite_->getX()); - Audio::get()->playSound("title.wav"); - Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY); - Screen::get()->shake(); - } - break; - } - - case Status::SHAKING: - { - // Agita "ARCADE EDITION" - if (shake_.remaining > 0) - { - if (shake_.counter > 0) - { - shake_.counter--; - } - else - { - shake_.counter = shake_.delay; - const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; - arcade_edition_sprite_->setX(shake_.origin + desp); - shake_.remaining--; + if (zoom_ <= 1.0f) { + arcade_edition_status_ = Status::SHAKING; + zoom_ = 1.0f; + arcade_edition_sprite_->setZoom(zoom_); + shake_.init(1, 2, 8, arcade_edition_sprite_->getX()); + Audio::get()->playSound("title.wav"); + Screen::get()->flash(Color(0xFF, 0xFF, 0xFF), FLASH_LENGHT, FLASH_DELAY); + Screen::get()->shake(); } + break; } - else - { - arcade_edition_sprite_->setX(shake_.origin); - arcade_edition_status_ = Status::FINISHED; - } - break; - } - default: - break; + case Status::SHAKING: { + // Agita "ARCADE EDITION" + if (shake_.remaining > 0) { + if (shake_.counter > 0) { + shake_.counter--; + } else { + shake_.counter = shake_.delay; + const auto desp = shake_.remaining % 2 == 0 ? shake_.desp * (-1) : shake_.desp; + arcade_edition_sprite_->setX(shake_.origin + desp); + shake_.remaining--; + } + } else { + arcade_edition_sprite_->setX(shake_.origin); + arcade_edition_status_ = Status::FINISHED; + } + break; + } + + default: + break; } if (coffee_crisis_status_ == Status::FINISHED && arcade_edition_status_ == Status::FINISHED && - post_finished_counter_ > 0) - { + post_finished_counter_ > 0) { --post_finished_counter_; } } // Activa la clase -void GameLogo::enable() -{ +void GameLogo::enable() { init(); coffee_crisis_status_ = Status::MOVING; } // Indica si ha terminado la animación -bool GameLogo::hasFinished() const -{ +bool GameLogo::hasFinished() const { return post_finished_counter_ == 0; } // Calcula el desplazamiento vertical inicial -int GameLogo::getInitialVerticalDesp() -{ +int GameLogo::getInitialVerticalDesp() { const float OFFSET_UP = y_; const float OFFSET_DOWN = param.game.height - y_; diff --git a/source/game_logo.h b/source/game_logo.h index e225d9e..8c0de32 100644 --- a/source/game_logo.h +++ b/source/game_logo.h @@ -1,54 +1,50 @@ #pragma once -#include <memory> // Para unique_ptr, shared_ptr +#include <memory> // Para unique_ptr, shared_ptr -#include "animated_sprite.h" // Para AnimatedSprite -#include "smart_sprite.h" // Para SmartSprite -#include "sprite.h" // Para Sprite +#include "animated_sprite.h" // Para AnimatedSprite +#include "smart_sprite.h" // Para SmartSprite +#include "sprite.h" // Para Sprite class Texture; // Clase GameLogo -class GameLogo -{ -public: +class GameLogo { + public: // --- Constructores y destructor --- GameLogo(int x, int y); ~GameLogo() = default; // --- Métodos principales --- - void render(); // Pinta la clase en pantalla - void update(); // Actualiza la lógica de la clase - void enable(); // Activa la clase + void render(); // Pinta la clase en pantalla + void update(); // Actualiza la lógica de la clase + void enable(); // Activa la clase // --- Getters --- - bool hasFinished() const; // Indica si ha terminado la animación + bool hasFinished() const; // Indica si ha terminado la animación -private: + private: // --- Tipos internos --- - enum class Status - { + enum class Status { DISABLED, MOVING, SHAKING, FINISHED, }; - struct Shake - { - int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x - int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse - int lenght = 8; // Cantidad de desplazamientos a realizar - int remaining = lenght; // Cantidad de desplazamientos pendientes a realizar - int counter = delay; // Contador para el retraso - int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento + struct Shake { + int desp = 1; // Pixels de desplazamiento para agitar la pantalla en el eje x + int delay = 2; // Retraso entre cada desplazamiento de la pantalla al agitarse + int lenght = 8; // Cantidad de desplazamientos a realizar + int remaining = lenght; // Cantidad de desplazamientos pendientes a realizar + int counter = delay; // Contador para el retraso + int origin = 0; // Valor inicial de la pantalla para dejarla igual tras el desplazamiento Shake() = default; Shake(int d, int de, int l, int o) : desp(d), delay(de), lenght(l), remaining(l), counter(de), origin(o) {} - void init(int d, int de, int l, int o) - { + void init(int d, int de, int l, int o) { desp = d; delay = de; lenght = l; @@ -59,28 +55,28 @@ private: }; // --- Objetos y punteros --- - std::shared_ptr<Texture> dust_texture_; // Textura con los graficos del polvo - std::shared_ptr<Texture> coffee_texture_; // Textura con los graficos de la palabra "COFFEE" - std::shared_ptr<Texture> crisis_texture_; // Textura con los graficos de la palabra "CRISIS" - std::shared_ptr<Texture> arcade_edition_texture_; // Textura con los graficos de "Arcade Edition" + std::shared_ptr<Texture> dust_texture_; // Textura con los graficos del polvo + std::shared_ptr<Texture> coffee_texture_; // Textura con los graficos de la palabra "COFFEE" + std::shared_ptr<Texture> crisis_texture_; // Textura con los graficos de la palabra "CRISIS" + std::shared_ptr<Texture> arcade_edition_texture_; // Textura con los graficos de "Arcade Edition" - std::unique_ptr<AnimatedSprite> dust_left_sprite_; // Sprite del polvo (izquierda) - std::unique_ptr<AnimatedSprite> dust_right_sprite_; // Sprite del polvo (derecha) - std::unique_ptr<SmartSprite> coffee_sprite_; // Sprite de "COFFEE" - std::unique_ptr<SmartSprite> crisis_sprite_; // Sprite de "CRISIS" - std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition" + std::unique_ptr<AnimatedSprite> dust_left_sprite_; // Sprite del polvo (izquierda) + std::unique_ptr<AnimatedSprite> dust_right_sprite_; // Sprite del polvo (derecha) + std::unique_ptr<SmartSprite> coffee_sprite_; // Sprite de "COFFEE" + std::unique_ptr<SmartSprite> crisis_sprite_; // Sprite de "CRISIS" + std::unique_ptr<Sprite> arcade_edition_sprite_; // Sprite de "Arcade Edition" // --- Variables de estado --- - float x_; // Posición X del logo - float y_; // Posición Y del logo - float zoom_ = 1.0f; // Zoom aplicado al texto "ARCADE EDITION" - int post_finished_counter_ = 1; // Contador final tras animaciones + float x_; // Posición X del logo + float y_; // Posición Y del logo + float zoom_ = 1.0f; // Zoom aplicado al texto "ARCADE EDITION" + int post_finished_counter_ = 1; // Contador final tras animaciones - Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS" - Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION" - Shake shake_; // Efecto de agitación + Status coffee_crisis_status_ = Status::DISABLED; // Estado de "COFFEE CRISIS" + Status arcade_edition_status_ = Status::DISABLED; // Estado de "ARCADE EDITION" + Shake shake_; // Efecto de agitación // --- Métodos internos --- - void init(); // Inicializa las variables - int getInitialVerticalDesp(); // Calcula el desplazamiento vertical inicial + void init(); // Inicializa las variables + int getInitialVerticalDesp(); // Calcula el desplazamiento vertical inicial }; \ No newline at end of file diff --git a/source/global_events.cpp b/source/global_events.cpp index fc1739b..9a08f1e 100644 --- a/source/global_events.cpp +++ b/source/global_events.cpp @@ -1,19 +1,16 @@ #include "global_events.h" -#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory +#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory -#include "mouse.h" // Para handleEvent +#include "mouse.h" // Para handleEvent #include "screen.h" #include "section.h" // Para Name, Options, name, options -namespace GlobalEvents -{ - // Comprueba los eventos que se pueden producir en cualquier sección del juego - void check(const SDL_Event &event) - { - switch (event.type) - { - case SDL_EVENT_QUIT: // Evento de salida de la aplicación +namespace GlobalEvents { +// Comprueba los eventos que se pueden producir en cualquier sección del juego +void check(const SDL_Event &event) { + switch (event.type) { + case SDL_EVENT_QUIT: // Evento de salida de la aplicación Section::name = Section::Name::QUIT; Section::options = Section::Options::NONE; return; @@ -29,8 +26,8 @@ namespace GlobalEvents default: break; - } - - Mouse::handleEvent(event); } -} \ No newline at end of file + + Mouse::handleEvent(event); +} +} // namespace GlobalEvents \ No newline at end of file diff --git a/source/global_events.h b/source/global_events.h index 247e529..cff34ca 100644 --- a/source/global_events.h +++ b/source/global_events.h @@ -2,8 +2,7 @@ #include <SDL3/SDL.h> -namespace GlobalEvents -{ - // Comprueba los eventos que se pueden producir en cualquier sección del juego - void check(const SDL_Event &event); -} \ No newline at end of file +namespace GlobalEvents { +// Comprueba los eventos que se pueden producir en cualquier sección del juego +void check(const SDL_Event &event); +} // namespace GlobalEvents \ No newline at end of file diff --git a/source/global_inputs.cpp b/source/global_inputs.cpp index d1ce276..20a854e 100644 --- a/source/global_inputs.cpp +++ b/source/global_inputs.cpp @@ -1,87 +1,72 @@ #include "global_inputs.h" -#include <string> // Para basic_string, operator+, allocator, cha... -#include <vector> // Para vector +#include <string> // Para basic_string, operator+, allocator, cha... +#include <vector> // Para vector -#include "asset.h" // Para Asset -#include "audio.h" // Para Audio -#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input... -#include "lang.h" // Para getText, Code, getNextLangCode, loadFro... -#include "notifier.h" // Para Notifier -#include "options.h" // Para SettingsOptions, settings, VideoOptions -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options, AttractMode -#include "ui/service_menu.h" // Para ServiceMenu -#include "utils.h" // Para boolToOnOff +#include "asset.h" // Para Asset +#include "audio.h" // Para Audio +#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input... +#include "lang.h" // Para getText, Code, getNextLangCode, loadFro... +#include "notifier.h" // Para Notifier +#include "options.h" // Para SettingsOptions, settings, VideoOptions +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options, AttractMode +#include "ui/service_menu.h" // Para ServiceMenu +#include "utils.h" // Para boolToOnOff -namespace GlobalInputs -{ - // Termina - void quit() - { - const std::string CODE = "QUIT"; - if (Notifier::get()->checkCode(CODE)) - { - // Si la notificación de salir está activa, cambia de sección - Section::name = Section::Name::QUIT; - Section::options = Section::Options::NONE; - } - else - { - // Si la notificación de salir no está activa, muestra la notificación - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 01"), std::string()}, -1, CODE); - } +namespace GlobalInputs { +// Termina +void quit() { + const std::string CODE = "QUIT"; + if (Notifier::get()->checkCode(CODE)) { + // Si la notificación de salir está activa, cambia de sección + Section::name = Section::Name::QUIT; + Section::options = Section::Options::NONE; + } else { + // Si la notificación de salir no está activa, muestra la notificación + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 01"), std::string()}, -1, CODE); } +} - // Reinicia - void reset() - { - const std::string CODE = "RESET"; - if (Notifier::get()->checkCode(CODE)) - { - Section::name = Section::Name::RESET; - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 15")}); - } - else - { - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 03"), std::string()}, -1, CODE); - } +// Reinicia +void reset() { + const std::string CODE = "RESET"; + if (Notifier::get()->checkCode(CODE)) { + Section::name = Section::Name::RESET; + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 15")}); + } else { + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 03"), std::string()}, -1, CODE); } +} - // Activa o desactiva el audio - void toggleAudio() - { - Options::audio.enabled = !Options::audio.enabled; - Audio::get()->enable(Options::audio.enabled); - Notifier::get()->show({"Audio " + boolToOnOff(Options::audio.enabled)}); - } +// Activa o desactiva el audio +void toggleAudio() { + Options::audio.enabled = !Options::audio.enabled; + Audio::get()->enable(Options::audio.enabled); + Notifier::get()->show({"Audio " + boolToOnOff(Options::audio.enabled)}); +} - // Cambia el modo de escalado entero - void toggleIntegerScale() - { - Screen::get()->toggleIntegerScale(); - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 12") + " " + boolToOnOff(Options::video.integer_scale)}); - } +// Cambia el modo de escalado entero +void toggleIntegerScale() { + Screen::get()->toggleIntegerScale(); + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 12") + " " + boolToOnOff(Options::video.integer_scale)}); +} - // Activa / desactiva el vsync - void toggleVSync() - { - Screen::get()->toggleVSync(); - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.v_sync)}); - } +// Activa / desactiva el vsync +void toggleVSync() { + Screen::get()->toggleVSync(); + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 14") + " " + boolToOnOff(Options::video.v_sync)}); +} - // Activa o desactiva los shaders - void toggleShaders() - { - Screen::get()->toggleShaders(); - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shaders)}); - } +// Activa o desactiva los shaders +void toggleShaders() { + Screen::get()->toggleShaders(); + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 13") + " " + boolToOnOff(Options::video.shaders)}); +} - // Obtiene una fichero a partir de un lang::Code - std::string getLangFile(Lang::Code code) - { - switch (code) - { +// Obtiene una fichero a partir de un lang::Code +std::string getLangFile(Lang::Code code) { + switch (code) { case Lang::Code::VALENCIAN: return Asset::get()->get("ba_BA.json"); break; @@ -91,14 +76,12 @@ namespace GlobalInputs default: return Asset::get()->get("en_UK.json"); break; - } } +} - // Obtiene una cadena a partir de un lang::Code - std::string getLangName(Lang::Code code) - { - switch (code) - { +// Obtiene una cadena a partir de un lang::Code +std::string getLangName(Lang::Code code) { + switch (code) { case Lang::Code::VALENCIAN: return " \"ba_BA\""; break; @@ -108,47 +91,39 @@ namespace GlobalInputs default: return " \"en_UK\""; break; - } } +} - // Cambia el idioma - void changeLang() - { - const std::string CODE = "LANG"; - if (Notifier::get()->checkCode(CODE)) - { - Options::settings.language = Lang::getNextLangCode(Options::settings.language); - Lang::loadFromFile(getLangFile(static_cast<Lang::Code>(Options::settings.language))); - Section::name = Section::Name::RESET; - Section::options = Section::Options::RELOAD; - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 05") + getLangName(Options::settings.language)}); - } - else - { - const auto NEXT = Lang::getNextLangCode(Options::settings.language); - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 04") + getLangName(NEXT), std::string()}, -1, CODE); - } +// Cambia el idioma +void changeLang() { + const std::string CODE = "LANG"; + if (Notifier::get()->checkCode(CODE)) { + Options::settings.language = Lang::getNextLangCode(Options::settings.language); + Lang::loadFromFile(getLangFile(static_cast<Lang::Code>(Options::settings.language))); + Section::name = Section::Name::RESET; + Section::options = Section::Options::RELOAD; + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 05") + getLangName(Options::settings.language)}); + } else { + const auto NEXT = Lang::getNextLangCode(Options::settings.language); + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 04") + getLangName(NEXT), std::string()}, -1, CODE); } +} - // Cambia el modo de disparo - void toggleFireMode() - { - Options::settings.autofire = !Options::settings.autofire; - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 08") + " " + boolToOnOff(Options::settings.autofire)}); - } +// Cambia el modo de disparo +void toggleFireMode() { + Options::settings.autofire = !Options::settings.autofire; + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 08") + " " + boolToOnOff(Options::settings.autofire)}); +} - // Salta una sección del juego - void skipSection() - { - switch (Section::name) - { +// Salta una sección del juego +void skipSection() { + switch (Section::name) { case Section::Name::INTRO: Audio::get()->stopMusic(); /* Continua en el case de abajo */ case Section::Name::LOGO: case Section::Name::HI_SCORE_TABLE: - case Section::Name::INSTRUCTIONS: - { + case Section::Name::INSTRUCTIONS: { Section::name = Section::Name::TITLE; Section::options = Section::Options::TITLE_1; Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; @@ -156,284 +131,243 @@ namespace GlobalInputs } default: break; + } +} + +// Activa el menu de servicio +void toggleServiceMenu() { + ServiceMenu::get()->toggle(); +} + +// Cambia el modo de pantalla completa +void toggleFullscreen() { + Screen::get()->toggleFullscreen(); + const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10"); + Notifier::get()->show({MODE}); +} + +// Reduce el tamaño de la ventana +void decWindowSize() { + if (Screen::get()->decWindowSize()) { + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); + } +} + +// Aumenta el tamaño de la ventana +void incWindowSize() { + if (Screen::get()->incWindowSize()) { + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); + } +} + +// Comprueba el boton de servicio +bool checkServiceButton() { + // Teclado + if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleServiceMenu(); + return true; + } + + // Mandos + { + for (int i = 0; i < Input::get()->getNumControllers(); ++i) { + if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { + toggleServiceMenu(); + return true; + } } } + return false; +} - // Activa el menu de servicio - void toggleServiceMenu() - { - ServiceMenu::get()->toggle(); - } +// Comprueba las entradas del menú de servicio +bool checkServiceInputs() { + if (!ServiceMenu::get()->isEnabled()) + return false; - // Cambia el modo de pantalla completa - void toggleFullscreen() + // Teclado { - Screen::get()->toggleFullscreen(); - const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10"); - Notifier::get()->show({MODE}); - } - - // Reduce el tamaño de la ventana - void decWindowSize() - { - if (Screen::get()->decWindowSize()) - { - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); - } - } - - // Aumenta el tamaño de la ventana - void incWindowSize() - { - if (Screen::get()->incWindowSize()) - { - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); - } - } - - // Comprueba el boton de servicio - bool checkServiceButton() - { - // Teclado - if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleServiceMenu(); + // Arriba + if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->setSelectorUp(); return true; } - // Mandos - { - for (int i = 0; i < Input::get()->getNumControllers(); ++i) - { - if (Input::get()->checkInput(InputAction::SERVICE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - toggleServiceMenu(); - return true; - } - } + // Abajo + if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->setSelectorDown(); + return true; + } + + // Derecha + if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->adjustOption(true); + return true; + } + + // Izquierda + if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->adjustOption(false); + return true; + } + + // Aceptar + if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->selectOption(); + return true; + } + + // Atras + if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + ServiceMenu::get()->moveBack(); + return true; } - return false; } - // Comprueba las entradas del menú de servicio - bool checkServiceInputs() + // Mandos { - if (!ServiceMenu::get()->isEnabled()) - return false; - - // Teclado - { + for (int i = 0; i < Input::get()->getNumControllers(); ++i) { // Arriba - if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->setSelectorUp(); return true; } // Abajo - if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->setSelectorDown(); return true; } // Derecha - if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->adjustOption(true); return true; } // Izquierda - if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->adjustOption(false); return true; } // Aceptar - if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->selectOption(); return true; } // Atras - if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { + if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { ServiceMenu::get()->moveBack(); return true; } } - - // Mandos - { - for (int i = 0; i < Input::get()->getNumControllers(); ++i) - { - // Arriba - if (Input::get()->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->setSelectorUp(); - return true; - } - - // Abajo - if (Input::get()->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->setSelectorDown(); - return true; - } - - // Derecha - if (Input::get()->checkInput(InputAction::RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->adjustOption(true); - return true; - } - - // Izquierda - if (Input::get()->checkInput(InputAction::LEFT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->adjustOption(false); - return true; - } - - // Aceptar - if (Input::get()->checkInput(InputAction::SM_SELECT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->selectOption(); - return true; - } - - // Atras - if (Input::get()->checkInput(InputAction::SM_BACK, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - ServiceMenu::get()->moveBack(); - return true; - } - } - } - return false; } + return false; +} - // Comprueba las entradas fuera del menú de servicio - bool checkInputs() +// Comprueba las entradas fuera del menú de servicio +bool checkInputs() { + // Teclado { - // Teclado - { - // Comprueba el teclado para cambiar entre pantalla completa y ventana - if (Input::get()->checkInput(InputAction::WINDOW_FULLSCREEN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - Screen::get()->toggleFullscreen(); - const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10"); - Notifier::get()->show({MODE}); - return true; - } + // Comprueba el teclado para cambiar entre pantalla completa y ventana + if (Input::get()->checkInput(InputAction::WINDOW_FULLSCREEN, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + Screen::get()->toggleFullscreen(); + const std::string MODE = Options::video.fullscreen ? Lang::getText("[NOTIFICATIONS] 11") : Lang::getText("[NOTIFICATIONS] 10"); + Notifier::get()->show({MODE}); + return true; + } - // Comprueba el teclado para decrementar el tamaño de la ventana - if (Input::get()->checkInput(InputAction::WINDOW_DEC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - if (Screen::get()->decWindowSize()) - { - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); - } - return true; + // Comprueba el teclado para decrementar el tamaño de la ventana + if (Input::get()->checkInput(InputAction::WINDOW_DEC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + if (Screen::get()->decWindowSize()) { + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); } + return true; + } - // Comprueba el teclado para incrementar el tamaño de la ventana - if (Input::get()->checkInput(InputAction::WINDOW_INC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - if (Screen::get()->incWindowSize()) - { - Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); - } - return true; + // Comprueba el teclado para incrementar el tamaño de la ventana + if (Input::get()->checkInput(InputAction::WINDOW_INC_SIZE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + if (Screen::get()->incWindowSize()) { + Notifier::get()->show({Lang::getText("[NOTIFICATIONS] 09") + " x" + std::to_string(Options::window.size)}); } + return true; + } - // Salir - if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - quit(); - return true; - } + // Salir + if (Input::get()->checkInput(InputAction::EXIT, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + quit(); + return true; + } - // Saltar sección - if (Input::get()->checkAnyButton() && !ServiceMenu::get()->isEnabled()) - { - skipSection(); - return true; - } + // Saltar sección + if (Input::get()->checkAnyButton() && !ServiceMenu::get()->isEnabled()) { + skipSection(); + return true; + } - // Reset - if (Input::get()->checkInput(InputAction::RESET, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - reset(); - return true; - } + // Reset + if (Input::get()->checkInput(InputAction::RESET, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + reset(); + return true; + } - // Audio - if (Input::get()->checkInput(InputAction::TOGGLE_AUDIO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleAudio(); - return true; - } + // Audio + if (Input::get()->checkInput(InputAction::TOGGLE_AUDIO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleAudio(); + return true; + } - // Autofire - if (Input::get()->checkInput(InputAction::TOGGLE_AUTO_FIRE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleFireMode(); - return true; - } + // Autofire + if (Input::get()->checkInput(InputAction::TOGGLE_AUTO_FIRE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleFireMode(); + return true; + } - // Idioma - if (Input::get()->checkInput(InputAction::CHANGE_LANG, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - changeLang(); - return true; - } + // Idioma + if (Input::get()->checkInput(InputAction::CHANGE_LANG, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + changeLang(); + return true; + } - // Shaders - if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleShaders(); - return true; - } + // Shaders + if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_SHADERS, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleShaders(); + return true; + } - // Integer Scale - if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleIntegerScale(); - return true; - } + // Integer Scale + if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_INTEGER_SCALE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleIntegerScale(); + return true; + } - // VSync - if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_VSYNC, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - toggleVSync(); - return true; - } + // VSync + if (Input::get()->checkInput(InputAction::TOGGLE_VIDEO_VSYNC, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + toggleVSync(); + return true; + } #ifdef DEBUG - // Debug info - if (Input::get()->checkInput(InputAction::SHOW_INFO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - Screen::get()->toggleDebugInfo(); - return true; - } -#endif + // Debug info + if (Input::get()->checkInput(InputAction::SHOW_INFO, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + Screen::get()->toggleDebugInfo(); + return true; } - return false; +#endif } + return false; +} - // Comprueba los inputs que se pueden introducir en cualquier sección del juego - bool check() - { - if (checkServiceButton()) - return true; - if (checkServiceInputs()) - return true; - if (checkInputs()) - return true; - return false; - } -} \ No newline at end of file +// Comprueba los inputs que se pueden introducir en cualquier sección del juego +bool check() { + if (checkServiceButton()) + return true; + if (checkServiceInputs()) + return true; + if (checkInputs()) + return true; + return false; +} +} // namespace GlobalInputs \ No newline at end of file diff --git a/source/global_inputs.h b/source/global_inputs.h index 1aabae1..2c08b7b 100644 --- a/source/global_inputs.h +++ b/source/global_inputs.h @@ -1,7 +1,6 @@ #pragma once -namespace GlobalInputs -{ - // Comprueba los inputs que se pueden introducir en cualquier sección del juego - bool check(); -} \ No newline at end of file +namespace GlobalInputs { +// Comprueba los inputs que se pueden introducir en cualquier sección del juego +bool check(); +} // namespace GlobalInputs \ No newline at end of file diff --git a/source/input.cpp b/source/input.cpp index 7b8cd14..18c1f5a 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -1,11 +1,12 @@ #include "input.h" -#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_GetGamepa... -#include <stddef.h> // Para size_t -#include <algorithm> // Para find -#include <iterator> // Para distance -#include <unordered_map> // Para unordered_map, _Node_const_iterator, operat... -#include <utility> // Para pair +#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_GetGamepa... +#include <stddef.h> // Para size_t + +#include <algorithm> // Para find +#include <iterator> // Para distance +#include <unordered_map> // Para unordered_map, _Node_const_iterator, operat... +#include <utility> // Para pair // Singleton Input *Input::instance_ = nullptr; @@ -21,232 +22,188 @@ Input *Input::get() { return Input::instance_; } // Constructor Input::Input(const std::string &game_controller_db_path) - : game_controller_db_path_(game_controller_db_path) -{ - // Inicializa el subsistema SDL_INIT_GAMEPAD - initSDLGamePad(); + : game_controller_db_path_(game_controller_db_path) { + // Inicializa el subsistema SDL_INIT_GAMEPAD + initSDLGamePad(); - // Inicializa los vectores - key_bindings_.resize(static_cast<int>(InputAction::SIZE), KeyBindings()); - controller_bindings_.resize(num_gamepads_, std::vector<ControllerBindings>(static_cast<int>(InputAction::SIZE), ControllerBindings())); + // Inicializa los vectores + key_bindings_.resize(static_cast<int>(InputAction::SIZE), KeyBindings()); + controller_bindings_.resize(num_gamepads_, std::vector<ControllerBindings>(static_cast<int>(InputAction::SIZE), ControllerBindings())); - // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas - button_inputs_ = {InputAction::FIRE_LEFT, InputAction::FIRE_CENTER, InputAction::FIRE_RIGHT, InputAction::START}; + // Listado de los inputs para jugar que utilizan botones, ni palancas ni crucetas + button_inputs_ = {InputAction::FIRE_LEFT, InputAction::FIRE_CENTER, InputAction::FIRE_RIGHT, InputAction::START}; } // Asigna inputs a teclas -void Input::bindKey(InputAction input, SDL_Scancode code) -{ - key_bindings_.at(static_cast<int>(input)).scancode = code; +void Input::bindKey(InputAction input, SDL_Scancode code) { + key_bindings_.at(static_cast<int>(input)).scancode = code; } // Asigna inputs a botones del mando -void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button) -{ - if (controller_index < num_gamepads_) - { - controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button; - } +void Input::bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button) { + if (controller_index < num_gamepads_) { + controller_bindings_.at(controller_index).at(static_cast<int>(input)).button = button; + } } // Asigna inputs a botones del mando -void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source) -{ - if (controller_index < num_gamepads_) - { - controller_bindings_.at(controller_index).at(static_cast<int>(input_target)).button = controller_bindings_.at(controller_index).at(static_cast<int>(input_source)).button; - } +void Input::bindGameControllerButton(int controller_index, InputAction input_target, InputAction input_source) { + if (controller_index < num_gamepads_) { + controller_bindings_.at(controller_index).at(static_cast<int>(input_target)).button = controller_bindings_.at(controller_index).at(static_cast<int>(input_source)).button; + } } // Comprueba si un input esta activo -bool Input::checkInput(InputAction input, bool repeat, InputDevice device, int controller_index) -{ - bool success_keyboard = false; - bool success_controller = false; - const int input_index = static_cast<int>(input); +bool Input::checkInput(InputAction input, bool repeat, InputDevice device, int controller_index) { + bool success_keyboard = false; + bool success_controller = false; + const int input_index = static_cast<int>(input); - if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) - { - if (repeat) - { // El usuario quiere saber si está pulsada (estado mantenido) - success_keyboard = key_bindings_[input_index].is_held; - } - else - { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma) - success_keyboard = key_bindings_[input_index].just_pressed; - } - } + if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) { + if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido) + success_keyboard = key_bindings_[input_index].is_held; + } else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma) + success_keyboard = key_bindings_[input_index].just_pressed; + } + } - if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) - { - if ((device == InputDevice::CONTROLLER) || (device == InputDevice::ANY)) - { - success_controller = checkAxisInput(input, controller_index, repeat); + if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) { + if ((device == InputDevice::CONTROLLER) || (device == InputDevice::ANY)) { + success_controller = checkAxisInput(input, controller_index, repeat); - if (!success_controller) - { - if (repeat) - { // El usuario quiere saber si está pulsada (estado mantenido) - success_controller = controller_bindings_.at(controller_index).at(input_index).is_held; - } - else - { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma) - success_controller = controller_bindings_.at(controller_index).at(input_index).just_pressed; - } - } - } - } + if (!success_controller) { + if (repeat) { // El usuario quiere saber si está pulsada (estado mantenido) + success_controller = controller_bindings_.at(controller_index).at(input_index).is_held; + } else { // El usuario quiere saber si ACABA de ser pulsada (evento de un solo fotograma) + success_controller = controller_bindings_.at(controller_index).at(input_index).just_pressed; + } + } + } + } - return (success_keyboard || success_controller); + return (success_keyboard || success_controller); } // Comprueba si hay almenos un input activo -bool Input::checkAnyInput(InputDevice device, int controller_index) -{ - // Obtenemos el número total de acciones posibles para iterar sobre ellas. - const int num_actions = static_cast<int>(InputAction::SIZE); +bool Input::checkAnyInput(InputDevice device, int controller_index) { + // Obtenemos el número total de acciones posibles para iterar sobre ellas. + const int num_actions = static_cast<int>(InputAction::SIZE); - // --- Comprobación del Teclado --- - if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) - { - for (int i = 0; i < num_actions; ++i) - { - // Simplemente leemos el estado pre-calculado por Input::update(). - // Ya no se llama a SDL_GetKeyboardState ni se modifica el estado '.active'. - if (key_bindings_.at(i).just_pressed) - { - return true; // Se encontró una acción recién pulsada. - } - } - } + // --- Comprobación del Teclado --- + if (device == InputDevice::KEYBOARD || device == InputDevice::ANY) { + for (int i = 0; i < num_actions; ++i) { + // Simplemente leemos el estado pre-calculado por Input::update(). + // Ya no se llama a SDL_GetKeyboardState ni se modifica el estado '.active'. + if (key_bindings_.at(i).just_pressed) { + return true; // Se encontró una acción recién pulsada. + } + } + } - // --- Comprobación del Mando --- - // Comprobamos si hay mandos y si el índice solicitado es válido. - if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) - { - if (device == InputDevice::CONTROLLER || device == InputDevice::ANY) - { - // Bucle CORREGIDO: Iteramos sobre todas las acciones, no sobre el número de mandos. - for (int i = 0; i < num_actions; ++i) - { - // Leemos el estado pre-calculado para el mando y la acción específicos. - if (controller_bindings_.at(controller_index).at(i).just_pressed) - { - return true; // Se encontró una acción recién pulsada en el mando. - } - } - } - } + // --- Comprobación del Mando --- + // Comprobamos si hay mandos y si el índice solicitado es válido. + if (gameControllerFound() && controller_index >= 0 && controller_index < num_gamepads_) { + if (device == InputDevice::CONTROLLER || device == InputDevice::ANY) { + // Bucle CORREGIDO: Iteramos sobre todas las acciones, no sobre el número de mandos. + for (int i = 0; i < num_actions; ++i) { + // Leemos el estado pre-calculado para el mando y la acción específicos. + if (controller_bindings_.at(controller_index).at(i).just_pressed) { + return true; // Se encontró una acción recién pulsada en el mando. + } + } + } + } - // Si llegamos hasta aquí, no se detectó ninguna nueva pulsación. - return false; + // Si llegamos hasta aquí, no se detectó ninguna nueva pulsación. + return false; } // Comprueba si hay algún botón pulsado. Devuelve 0 en caso de no encontrar nada o el indice del dispositivo + 1. Se hace así para poder gastar el valor devuelto como un valor "booleano" -int Input::checkAnyButton(bool repeat) -{ - // Solo comprueba los botones definidos previamente - for (auto bi : button_inputs_) - { - // Comprueba el teclado - if (checkInput(bi, repeat, InputDevice::KEYBOARD)) - { - return 1; - } +int Input::checkAnyButton(bool repeat) { + // Solo comprueba los botones definidos previamente + for (auto bi : button_inputs_) { + // Comprueba el teclado + if (checkInput(bi, repeat, InputDevice::KEYBOARD)) { + return 1; + } - // Comprueba los mandos - for (int i = 0; i < num_gamepads_; ++i) - { - if (checkInput(bi, repeat, InputDevice::CONTROLLER, i)) - { - return i + 1; - } - } - } + // Comprueba los mandos + for (int i = 0; i < num_gamepads_; ++i) { + if (checkInput(bi, repeat, InputDevice::CONTROLLER, i)) { + return i + 1; + } + } + } - return 0; + return 0; } // Busca si hay mandos conectados -bool Input::discoverGameControllers() -{ - bool found = false; +bool Input::discoverGameControllers() { + bool found = false; - if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: could not load %s file: %s", game_controller_db_path_.c_str(), SDL_GetError()); - } + if (SDL_AddGamepadMappingsFromFile(game_controller_db_path_.c_str()) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: could not load %s file: %s", game_controller_db_path_.c_str(), SDL_GetError()); + } - // En SDL3, SDL_GetJoysticks devuelve un array de IDs, no un contador - SDL_JoystickID *joystick_ids = SDL_GetJoysticks(&num_joysticks_); - num_gamepads_ = 0; + // En SDL3, SDL_GetJoysticks devuelve un array de IDs, no un contador + SDL_JoystickID *joystick_ids = SDL_GetJoysticks(&num_joysticks_); + num_gamepads_ = 0; - // Cuenta el número de mandos - joysticks_.clear(); - for (int i = 0; i < num_joysticks_; ++i) - { - // Usar el ID del joystick, no el índice - auto joy = SDL_OpenJoystick(joystick_ids[i]); - joysticks_.push_back(joy); + // Cuenta el número de mandos + joysticks_.clear(); + for (int i = 0; i < num_joysticks_; ++i) { + // Usar el ID del joystick, no el índice + auto joy = SDL_OpenJoystick(joystick_ids[i]); + joysticks_.push_back(joy); - // En SDL3, SDL_IsGamepad toma un SDL_JoystickID, no un índice - if (SDL_IsGamepad(joystick_ids[i])) - { - num_gamepads_++; - } - } + // En SDL3, SDL_IsGamepad toma un SDL_JoystickID, no un índice + if (SDL_IsGamepad(joystick_ids[i])) { + num_gamepads_++; + } + } - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ">> LOOKING FOR GAME CONTROLLERS"); - if (num_joysticks_ != num_gamepads_) - { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Joysticks found: %d", num_joysticks_); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found : %d", num_gamepads_); - } - else - { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found: %d", num_gamepads_); - } + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, ">> LOOKING FOR GAME CONTROLLERS"); + if (num_joysticks_ != num_gamepads_) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Joysticks found: %d", num_joysticks_); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found : %d", num_gamepads_); + } else { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Gamepads found: %d", num_gamepads_); + } - if (num_gamepads_ > 0) - { - found = true; + if (num_gamepads_ > 0) { + found = true; - // Recorrer los joysticks y abrir solo los que son gamepads - for (int i = 0; i < num_joysticks_; i++) - { - if (SDL_IsGamepad(joystick_ids[i])) - { - // Abre el mando usando el ID del joystick - auto pad = SDL_OpenGamepad(joystick_ids[i]); - if (pad != nullptr) - { - connected_controllers_.push_back(pad); + // Recorrer los joysticks y abrir solo los que son gamepads + for (int i = 0; i < num_joysticks_; i++) { + if (SDL_IsGamepad(joystick_ids[i])) { + // Abre el mando usando el ID del joystick + auto pad = SDL_OpenGamepad(joystick_ids[i]); + if (pad != nullptr) { + connected_controllers_.push_back(pad); - // Obtener el nombre usando el ID del joystick - const char *name_cstr = SDL_GetGamepadNameForID(joystick_ids[i]); - std::string name = name_cstr ? name_cstr : "Unknown Gamepad"; + // Obtener el nombre usando el ID del joystick + const char *name_cstr = SDL_GetGamepadNameForID(joystick_ids[i]); + std::string name = name_cstr ? name_cstr : "Unknown Gamepad"; - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "#%d: %s", i, name.c_str()); - controller_names_.push_back(name); - } - else - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open gamepad %d: %s", joystick_ids[i], SDL_GetError()); - } - } - } + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "#%d: %s", i, name.c_str()); + controller_names_.push_back(name); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to open gamepad %d: %s", joystick_ids[i], SDL_GetError()); + } + } + } - SDL_SetGamepadEventsEnabled(true); - } + SDL_SetGamepadEventsEnabled(true); + } - // Liberar el array de IDs - if (joystick_ids) - { - SDL_free(joystick_ids); - } + // Liberar el array de IDs + if (joystick_ids) { + SDL_free(joystick_ids); + } - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> FINISHED LOOKING FOR GAME CONTROLLERS"); - return found; + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> FINISHED LOOKING FOR GAME CONTROLLERS"); + return found; } // Comprueba si hay algun mando conectado @@ -259,202 +216,168 @@ std::string Input::getControllerName(int controller_index) const { return num_ga int Input::getNumControllers() const { return num_gamepads_; } // Obtiene el indice del controlador a partir de un event.id -int Input::getJoyIndex(SDL_JoystickID id) const -{ - for (int i = 0; i < num_joysticks_; ++i) - { - if (SDL_GetJoystickID(joysticks_[i]) == id) - { - return i; - } - } - return -1; +int Input::getJoyIndex(SDL_JoystickID id) const { + for (int i = 0; i < num_joysticks_; ++i) { + if (SDL_GetJoystickID(joysticks_[i]) == id) { + return i; + } + } + return -1; } // Muestra por consola los controles asignados -void Input::printBindings(InputDevice device, int controller_index) const -{ - if (device == InputDevice::ANY || device == InputDevice::KEYBOARD) - { - return; - } +void Input::printBindings(InputDevice device, int controller_index) const { + if (device == InputDevice::ANY || device == InputDevice::KEYBOARD) { + return; + } - if (device == InputDevice::CONTROLLER) - { - if (controller_index >= num_gamepads_) - { - return; - } + if (device == InputDevice::CONTROLLER) { + if (controller_index >= num_gamepads_) { + return; + } - // Muestra el nombre del mando - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n%s", controller_names_.at(controller_index).c_str()); + // Muestra el nombre del mando + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n%s", controller_names_.at(controller_index).c_str()); - // Muestra los botones asignados - for (auto bi : button_inputs_) - { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s : %d", to_string(bi).c_str(), controller_bindings_.at(controller_index).at(static_cast<int>(bi)).button); - } - } + // Muestra los botones asignados + for (auto bi : button_inputs_) { + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s : %d", to_string(bi).c_str(), controller_bindings_.at(controller_index).at(static_cast<int>(bi)).button); + } + } } // Obtiene el SDL_GamepadButton asignado a un input -SDL_GamepadButton Input::getControllerBinding(int controller_index, InputAction input) const -{ - return controller_bindings_[controller_index][static_cast<int>(input)].button; +SDL_GamepadButton Input::getControllerBinding(int controller_index, InputAction input) const { + return controller_bindings_[controller_index][static_cast<int>(input)].button; } // Obtiene el indice a partir del nombre del mando -int Input::getIndexByName(const std::string &name) const -{ - auto it = std::find(controller_names_.begin(), controller_names_.end(), name); - return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1; +int Input::getIndexByName(const std::string &name) const { + auto it = std::find(controller_names_.begin(), controller_names_.end(), name); + return it != controller_names_.end() ? std::distance(controller_names_.begin(), it) : -1; } // Convierte un InputAction a std::string -std::string Input::to_string(InputAction input) const -{ - switch (input) - { - case InputAction::FIRE_LEFT: - return "input_fire_left"; - case InputAction::FIRE_CENTER: - return "input_fire_center"; - case InputAction::FIRE_RIGHT: - return "input_fire_right"; - case InputAction::START: - return "input_start"; - case InputAction::SERVICE: - return "input_service"; - default: - return ""; - } +std::string Input::to_string(InputAction input) const { + switch (input) { + case InputAction::FIRE_LEFT: + return "input_fire_left"; + case InputAction::FIRE_CENTER: + return "input_fire_center"; + case InputAction::FIRE_RIGHT: + return "input_fire_right"; + case InputAction::START: + return "input_start"; + case InputAction::SERVICE: + return "input_service"; + default: + return ""; + } } // Convierte un std::string a InputAction -InputAction Input::to_inputs_e(const std::string &name) const -{ - static const std::unordered_map<std::string, InputAction> inputMap = { - {"input_fire_left", InputAction::FIRE_LEFT}, - {"input_fire_center", InputAction::FIRE_CENTER}, - {"input_fire_right", InputAction::FIRE_RIGHT}, - {"input_start", InputAction::START}, - {"input_service", InputAction::SERVICE}}; +InputAction Input::to_inputs_e(const std::string &name) const { + static const std::unordered_map<std::string, InputAction> inputMap = { + {"input_fire_left", InputAction::FIRE_LEFT}, + {"input_fire_center", InputAction::FIRE_CENTER}, + {"input_fire_right", InputAction::FIRE_RIGHT}, + {"input_start", InputAction::START}, + {"input_service", InputAction::SERVICE}}; - auto it = inputMap.find(name); - return it != inputMap.end() ? it->second : InputAction::NONE; + auto it = inputMap.find(name); + return it != inputMap.end() ? it->second : InputAction::NONE; } // Comprueba el eje del mando -bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat) -{ - // Umbral para considerar el eje como activo - bool axis_active_now = false; +bool Input::checkAxisInput(InputAction input, int controller_index, bool repeat) { + // Umbral para considerar el eje como activo + bool axis_active_now = false; - switch (input) - { - case InputAction::LEFT: - axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) < -AXIS_THRESHOLD_; - break; - case InputAction::RIGHT: - axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) > AXIS_THRESHOLD_; - break; - case InputAction::UP: - axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) < -AXIS_THRESHOLD_; - break; - case InputAction::DOWN: - axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) > AXIS_THRESHOLD_; - break; - default: - return false; - } + switch (input) { + case InputAction::LEFT: + axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) < -AXIS_THRESHOLD_; + break; + case InputAction::RIGHT: + axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTX) > AXIS_THRESHOLD_; + break; + case InputAction::UP: + axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) < -AXIS_THRESHOLD_; + break; + case InputAction::DOWN: + axis_active_now = SDL_GetGamepadAxis(connected_controllers_[controller_index], SDL_GAMEPAD_AXIS_LEFTY) > AXIS_THRESHOLD_; + break; + default: + return false; + } - // Referencia al binding correspondiente - auto &binding = controller_bindings_.at(controller_index).at(static_cast<int>(input)); + // Referencia al binding correspondiente + auto &binding = controller_bindings_.at(controller_index).at(static_cast<int>(input)); - if (repeat) - { - // Si se permite repetir, simplemente devolvemos el estado actual - return axis_active_now; - } - else - { - // Si no se permite repetir, aplicamos la lógica de transición - if (axis_active_now && !binding.axis_active) - { - // Transición de inactivo a activo - binding.axis_active = true; - return true; - } - else if (!axis_active_now && binding.axis_active) - { - // Transición de activo a inactivo - binding.axis_active = false; - } - // Mantener el estado actual - return false; - } + if (repeat) { + // Si se permite repetir, simplemente devolvemos el estado actual + return axis_active_now; + } else { + // Si no se permite repetir, aplicamos la lógica de transición + if (axis_active_now && !binding.axis_active) { + // Transición de inactivo a activo + binding.axis_active = true; + return true; + } else if (!axis_active_now && binding.axis_active) { + // Transición de activo a inactivo + binding.axis_active = false; + } + // Mantener el estado actual + return false; + } } -void Input::initSDLGamePad() -{ - if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) - { - if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GAMEPAD could not initialize! SDL Error: %s", SDL_GetError()); - } - else - { - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_GAMEPAD: INITIALIZING"); - discoverGameControllers(); - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_GAMEPAD: INITIALIZATION COMPLETE\n"); - } - } +void Input::initSDLGamePad() { + if (SDL_WasInit(SDL_INIT_GAMEPAD) != 1) { + if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GAMEPAD could not initialize! SDL Error: %s", SDL_GetError()); + } else { + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "\n** SDL_GAMEPAD: INITIALIZING"); + discoverGameControllers(); + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "** SDL_GAMEPAD: INITIALIZATION COMPLETE\n"); + } + } } -void Input::resetInputStates() -{ - // Resetear todos los KeyBindings.active a false - for (auto &key : key_bindings_) - { - key.is_held = false; - key.just_pressed = false; - } - // Resetear todos los ControllerBindings.active a false - for (auto &controller_vec : controller_bindings_) - { - for (auto &binding : controller_vec) - { - binding.is_held = false; - binding.just_pressed = false; - } - } +void Input::resetInputStates() { + // Resetear todos los KeyBindings.active a false + for (auto &key : key_bindings_) { + key.is_held = false; + key.just_pressed = false; + } + // Resetear todos los ControllerBindings.active a false + for (auto &controller_vec : controller_bindings_) { + for (auto &binding : controller_vec) { + binding.is_held = false; + binding.just_pressed = false; + } + } } -void Input::update() -{ - // --- TECLADO --- - const bool *key_states = SDL_GetKeyboardState(nullptr); +void Input::update() { + // --- TECLADO --- + const bool *key_states = SDL_GetKeyboardState(nullptr); - for (size_t i = 0; i < key_bindings_.size(); ++i) - { - bool key_is_down_now = key_states[key_bindings_[i].scancode]; + for (size_t i = 0; i < key_bindings_.size(); ++i) { + bool key_is_down_now = key_states[key_bindings_[i].scancode]; - // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo - key_bindings_[i].just_pressed = key_is_down_now && !key_bindings_[i].is_held; - key_bindings_[i].is_held = key_is_down_now; - } + // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo + key_bindings_[i].just_pressed = key_is_down_now && !key_bindings_[i].is_held; + key_bindings_[i].is_held = key_is_down_now; + } - // --- MANDOS --- - for (int c = 0; c < num_gamepads_; ++c) - { - for (size_t i = 0; i < controller_bindings_[c].size(); ++i) - { - bool button_is_down_now = SDL_GetGamepadButton(connected_controllers_.at(c), controller_bindings_.at(c).at(i).button) != 0; + // --- MANDOS --- + for (int c = 0; c < num_gamepads_; ++c) { + for (size_t i = 0; i < controller_bindings_[c].size(); ++i) { + bool button_is_down_now = SDL_GetGamepadButton(connected_controllers_.at(c), controller_bindings_.at(c).at(i).button) != 0; - // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo - controller_bindings_[c][i].just_pressed = button_is_down_now && !controller_bindings_[c][i].is_held; - controller_bindings_[c][i].is_held = button_is_down_now; - } - } + // El estado .is_held del fotograma anterior nos sirve para saber si es un pulso nuevo + controller_bindings_[c][i].just_pressed = button_is_down_now && !controller_bindings_[c][i].is_held; + controller_bindings_[c][i].is_held = button_is_down_now; + } + } } \ No newline at end of file diff --git a/source/input.h b/source/input.h index a080dc2..9c3fd69 100644 --- a/source/input.h +++ b/source/input.h @@ -1,154 +1,150 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_GamepadButton, Uint8, SDL_Gamepad, SDL_Joystick, SDL_JoystickID, SDL_Scancode, Sint16 -#include <string> // Para basic_string, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_GamepadButton, Uint8, SDL_Gamepad, SDL_Joystick, SDL_JoystickID, SDL_Scancode, Sint16 + +#include <string> // Para basic_string, string +#include <vector> // Para vector /* connectedControllers es un vector donde están todos los mandos encontrados [0 .. n] checkInput requiere de un índice para comprobar las pulsaciones de un controlador en concreto [0 .. n] device contiene el tipo de dispositivo a comprobar: - InputDeviceToUse::KEYBOARD solo mirará el teclado - InputDeviceToUse::CONTROLLER solo mirará el controlador especificado (Si no se especifica, el primero) - InputDeviceToUse::ANY mirará tanto el teclado como el PRIMER controlador + InputDeviceToUse::KEYBOARD solo mirará el teclado + InputDeviceToUse::CONTROLLER solo mirará el controlador especificado (Si no se especifica, el primero) + InputDeviceToUse::ANY mirará tanto el teclado como el PRIMER controlador */ // Acciones de entrada posibles en el juego -enum class InputAction : int -{ - // Inputs de movimiento - UP, - DOWN, - LEFT, - RIGHT, +enum class InputAction : int { + // Inputs de movimiento + UP, + DOWN, + LEFT, + RIGHT, - // Inputs personalizados - FIRE_LEFT, - FIRE_CENTER, - FIRE_RIGHT, - START, + // Inputs personalizados + FIRE_LEFT, + FIRE_CENTER, + FIRE_RIGHT, + START, - // Service Menu - SM_SELECT, - SM_BACK, + // Service Menu + SM_SELECT, + SM_BACK, - // Inputs de control - BACK, - EXIT, - PAUSE, - SERVICE, - WINDOW_FULLSCREEN, - WINDOW_INC_SIZE, - WINDOW_DEC_SIZE, - TOGGLE_VIDEO_SHADERS, - TOGGLE_VIDEO_INTEGER_SCALE, - TOGGLE_VIDEO_VSYNC, - RESET, - TOGGLE_AUDIO, - CHANGE_LANG, - SHOW_INFO, - CONFIG, - SWAP_CONTROLLERS, - TOGGLE_AUTO_FIRE, + // Inputs de control + BACK, + EXIT, + PAUSE, + SERVICE, + WINDOW_FULLSCREEN, + WINDOW_INC_SIZE, + WINDOW_DEC_SIZE, + TOGGLE_VIDEO_SHADERS, + TOGGLE_VIDEO_INTEGER_SCALE, + TOGGLE_VIDEO_VSYNC, + RESET, + TOGGLE_AUDIO, + CHANGE_LANG, + SHOW_INFO, + CONFIG, + SWAP_CONTROLLERS, + TOGGLE_AUTO_FIRE, - // Input obligatorio - NONE, - SIZE, + // Input obligatorio + NONE, + SIZE, }; constexpr bool INPUT_ALLOW_REPEAT = true; constexpr bool INPUT_DO_NOT_ALLOW_REPEAT = false; // Tipos de dispositivos de entrada -enum class InputDevice : int -{ - KEYBOARD = 0, - CONTROLLER = 1, - ANY = 2, +enum class InputDevice : int { + KEYBOARD = 0, + CONTROLLER = 1, + ANY = 2, }; // Clase Input: gestiona la entrada de teclado y mandos (singleton) -class Input -{ -public: - // --- Métodos de singleton --- - static void init(const std::string &game_controller_db_path); // Inicializa el singleton - static void destroy(); // Libera el singleton - static Input *get(); // Obtiene la instancia +class Input { + public: + // --- Métodos de singleton --- + static void init(const std::string &game_controller_db_path); // Inicializa el singleton + static void destroy(); // Libera el singleton + static Input *get(); // Obtiene la instancia - // --- Métodos de configuración de controles --- - void bindKey(InputAction input, SDL_Scancode code); // Asigna inputs a teclas - void bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button); // Asigna inputs a botones del mando - void bindGameControllerButton(int controller_index, InputAction inputTarget, InputAction inputSource); // Asigna inputs a otros inputs del mando + // --- Métodos de configuración de controles --- + void bindKey(InputAction input, SDL_Scancode code); // Asigna inputs a teclas + void bindGameControllerButton(int controller_index, InputAction input, SDL_GamepadButton button); // Asigna inputs a botones del mando + void bindGameControllerButton(int controller_index, InputAction inputTarget, InputAction inputSource); // Asigna inputs a otros inputs del mando - // --- Métodos de consulta de entrada --- - void update(); // Comprueba fisicamente los botones y teclas que se han pulsado - bool checkInput(InputAction input, bool repeat = true, InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si un input está activo - bool checkAnyInput(InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si hay al menos un input activo - int checkAnyButton(bool repeat = INPUT_DO_NOT_ALLOW_REPEAT); // Comprueba si hay algún botón pulsado + // --- Métodos de consulta de entrada --- + void update(); // Comprueba fisicamente los botones y teclas que se han pulsado + bool checkInput(InputAction input, bool repeat = true, InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si un input está activo + bool checkAnyInput(InputDevice device = InputDevice::ANY, int controller_index = 0); // Comprueba si hay al menos un input activo + int checkAnyButton(bool repeat = INPUT_DO_NOT_ALLOW_REPEAT); // Comprueba si hay algún botón pulsado - // --- Métodos de gestión de mandos --- - bool discoverGameControllers(); // Busca si hay mandos conectados - bool gameControllerFound(); // Comprueba si hay algún mando conectado - int getNumControllers() const; // Obtiene el número de mandos conectados - std::string getControllerName(int controller_index) const; // Obtiene el nombre de un mando de juego - int getJoyIndex(SDL_JoystickID id) const; // Obtiene el índice del controlador a partir de un event.id + // --- Métodos de gestión de mandos --- + bool discoverGameControllers(); // Busca si hay mandos conectados + bool gameControllerFound(); // Comprueba si hay algún mando conectado + int getNumControllers() const; // Obtiene el número de mandos conectados + std::string getControllerName(int controller_index) const; // Obtiene el nombre de un mando de juego + int getJoyIndex(SDL_JoystickID id) const; // Obtiene el índice del controlador a partir de un event.id - // --- Métodos de consulta y utilidades --- - void printBindings(InputDevice device = InputDevice::KEYBOARD, int controller_index = 0) const; // Muestra por consola los controles asignados - SDL_GamepadButton getControllerBinding(int controller_index, InputAction input) const; // Obtiene el SDL_GamepadButton asignado a un input - std::string to_string(InputAction input) const; // Convierte un InputAction a std::string - InputAction to_inputs_e(const std::string &name) const; // Convierte un std::string a InputAction - int getIndexByName(const std::string &name) const; // Obtiene el índice a partir del nombre del mando + // --- Métodos de consulta y utilidades --- + void printBindings(InputDevice device = InputDevice::KEYBOARD, int controller_index = 0) const; // Muestra por consola los controles asignados + SDL_GamepadButton getControllerBinding(int controller_index, InputAction input) const; // Obtiene el SDL_GamepadButton asignado a un input + std::string to_string(InputAction input) const; // Convierte un InputAction a std::string + InputAction to_inputs_e(const std::string &name) const; // Convierte un std::string a InputAction + int getIndexByName(const std::string &name) const; // Obtiene el índice a partir del nombre del mando - // --- Métodos de reseteo de estado de entrada --- - void resetInputStates(); // Pone todos los KeyBindings.active y ControllerBindings.active a false + // --- Métodos de reseteo de estado de entrada --- + void resetInputStates(); // Pone todos los KeyBindings.active y ControllerBindings.active a false -private: - // --- Singleton --- - static Input *instance_; + private: + // --- Singleton --- + static Input *instance_; - // --- Estructuras internas --- - struct KeyBindings - { - Uint8 scancode; // Scancode asociado - bool is_held; // Está pulsada ahora mismo - bool just_pressed; // Se acaba de pulsar en este fotograma + // --- Estructuras internas --- + struct KeyBindings { + Uint8 scancode; // Scancode asociado + bool is_held; // Está pulsada ahora mismo + bool just_pressed; // Se acaba de pulsar en este fotograma - KeyBindings(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false) - : scancode(scancode), is_held(is_held), just_pressed(just_pressed) {} - }; + KeyBindings(Uint8 scancode = 0, bool is_held = false, bool just_pressed = false) + : scancode(scancode), is_held(is_held), just_pressed(just_pressed) {} + }; - struct ControllerBindings - { - SDL_GamepadButton button; // GameControllerButton asociado - bool is_held; // Está pulsada ahora mismo - bool just_pressed; // Se acaba de pulsar en este fotograma - bool axis_active; // Estado del eje + struct ControllerBindings { + SDL_GamepadButton button; // GameControllerButton asociado + bool is_held; // Está pulsada ahora mismo + bool just_pressed; // Se acaba de pulsar en este fotograma + bool axis_active; // Estado del eje - ControllerBindings(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) - : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} - }; + ControllerBindings(SDL_GamepadButton btn = SDL_GAMEPAD_BUTTON_INVALID, bool is_held = false, bool just_pressed = false, bool axis_act = false) + : button(btn), is_held(is_held), just_pressed(just_pressed), axis_active(axis_act) {} + }; - // --- Constantes --- - static constexpr Sint16 AXIS_THRESHOLD_ = 30000; + // --- Constantes --- + static constexpr Sint16 AXIS_THRESHOLD_ = 30000; - // --- Variables internas --- - std::vector<SDL_Gamepad *> connected_controllers_; // Vector con todos los mandos conectados - std::vector<SDL_Joystick *> joysticks_; // Vector con todos los joysticks conectados - std::vector<KeyBindings> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos - std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociados a los inputs predefinidos para cada mando - std::vector<std::string> controller_names_; // Vector con los nombres de los mandos - std::vector<InputAction> button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones - int num_joysticks_ = 0; // Número de joysticks conectados - int num_gamepads_ = 0; // Número de mandos conectados - std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt + // --- Variables internas --- + std::vector<SDL_Gamepad *> connected_controllers_; // Vector con todos los mandos conectados + std::vector<SDL_Joystick *> joysticks_; // Vector con todos los joysticks conectados + std::vector<KeyBindings> key_bindings_; // Vector con las teclas asociadas a los inputs predefinidos + std::vector<std::vector<ControllerBindings>> controller_bindings_; // Vector con los botones asociados a los inputs predefinidos para cada mando + std::vector<std::string> controller_names_; // Vector con los nombres de los mandos + std::vector<InputAction> button_inputs_; // Inputs asignados al jugador y a botones, excluyendo direcciones + int num_joysticks_ = 0; // Número de joysticks conectados + int num_gamepads_ = 0; // Número de mandos conectados + std::string game_controller_db_path_; // Ruta al archivo gamecontrollerdb.txt - // --- Métodos internos --- - void initSDLGamePad(); // Inicializa SDL para la gestión de mandos - bool checkAxisInput(InputAction input, int controller_index, bool repeat); // Comprueba el eje del mando + // --- Métodos internos --- + void initSDLGamePad(); // Inicializa SDL para la gestión de mandos + bool checkAxisInput(InputAction input, int controller_index, bool repeat); // Comprueba el eje del mando - // --- Constructor y destructor --- - explicit Input(const std::string &game_controller_db_path); // Constructor privado - ~Input() = default; // Destructor privado + // --- Constructor y destructor --- + explicit Input(const std::string &game_controller_db_path); // Constructor privado + ~Input() = default; // Destructor privado }; \ No newline at end of file diff --git a/source/item.cpp b/source/item.cpp index 3ed3eae..7416b24 100644 --- a/source/item.cpp +++ b/source/item.cpp @@ -1,249 +1,208 @@ #include "item.h" -#include <stdlib.h> // Para rand -#include <algorithm> // Para clamp +#include <stdlib.h> // Para rand -#include "animated_sprite.h" // Para AnimatedSprite -#include "param.h" // Para Param, ParamGame, param +#include <algorithm> // Para clamp -class Texture; // lines 6-6 +#include "animated_sprite.h" // Para AnimatedSprite +#include "param.h" // Para Param, ParamGame, param + +class Texture; // lines 6-6 Item::Item(ItemType type, float x, float y, SDL_FRect &play_area, std::shared_ptr<Texture> texture, const std::vector<std::string> &animation) - : sprite_(std::make_unique<AnimatedSprite>(texture, animation)), - type_(type), - play_area_(play_area) -{ - switch (type) - { - case ItemType::COFFEE_MACHINE: - { - width_ = COFFEE_MACHINE_WIDTH; - height_ = COFFEE_MACHINE_HEIGHT; - pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w); - pos_y_ = y; - vel_x_ = ((rand() % 3) - 1) * 0.5f; - vel_y_ = -0.1f; - accel_y_ = 0.1f; - collider_.r = 10; - break; - } - default: - { - width_ = param.game.item_size; - height_ = param.game.item_size; - pos_x_ = x; - pos_y_ = y; - vel_x_ = -1.0f + ((rand() % 5) * 0.5f); - vel_y_ = -4.0f; - accel_y_ = 0.2f; - collider_.r = width_ / 2; - break; - } - } + : sprite_(std::make_unique<AnimatedSprite>(texture, animation)), + type_(type), + play_area_(play_area) { + switch (type) { + case ItemType::COFFEE_MACHINE: { + width_ = COFFEE_MACHINE_WIDTH; + height_ = COFFEE_MACHINE_HEIGHT; + pos_x_ = getCoffeeMachineSpawn(x, width_, play_area_.w); + pos_y_ = y; + vel_x_ = ((rand() % 3) - 1) * 0.5f; + vel_y_ = -0.1f; + accel_y_ = 0.1f; + collider_.r = 10; + break; + } + default: { + width_ = param.game.item_size; + height_ = param.game.item_size; + pos_x_ = x; + pos_y_ = y; + vel_x_ = -1.0f + ((rand() % 5) * 0.5f); + vel_y_ = -4.0f; + accel_y_ = 0.2f; + collider_.r = width_ / 2; + break; + } + } - // Actualiza el sprite - shiftSprite(); - shiftColliders(); + // Actualiza el sprite + shiftSprite(); + shiftColliders(); } -void Item::alignTo(int x) -{ - const float min_x = param.game.play_area.rect.x + 1; - const float max_x = play_area_.w - width_ - 1; +void Item::alignTo(int x) { + const float min_x = param.game.play_area.rect.x + 1; + const float max_x = play_area_.w - width_ - 1; - pos_x_ = x - (width_ / 2); + pos_x_ = x - (width_ / 2); - // Ajusta para que no quede fuera de la zona de juego - pos_x_ = std::clamp(pos_x_, min_x, max_x); + // Ajusta para que no quede fuera de la zona de juego + pos_x_ = std::clamp(pos_x_, min_x, max_x); - // Actualiza el sprite - shiftSprite(); - shiftColliders(); + // Actualiza el sprite + shiftSprite(); + shiftColliders(); } -void Item::render() -{ - if (enabled_) - { - if (time_to_live_ > 200) - { - sprite_->render(); - } - else if (time_to_live_ % 20 > 10) - { - sprite_->render(); - } - } +void Item::render() { + if (enabled_) { + if (time_to_live_ > 200) { + sprite_->render(); + } else if (time_to_live_ % 20 > 10) { + sprite_->render(); + } + } } -void Item::move() -{ - floor_collision_ = false; +void Item::move() { + floor_collision_ = false; - // Calcula la nueva posición - pos_x_ += vel_x_; - pos_y_ += vel_y_; + // Calcula la nueva posición + pos_x_ += vel_x_; + pos_y_ += vel_y_; - // Aplica las aceleraciones a la velocidad - vel_x_ += accel_x_; - vel_y_ += accel_y_; + // Aplica las aceleraciones a la velocidad + vel_x_ += accel_x_; + vel_y_ += accel_y_; - // Comprueba los laterales de la zona de juego - const float MIN_X = param.game.play_area.rect.x; - const float MAX_X = play_area_.w - width_; - pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); + // Comprueba los laterales de la zona de juego + const float MIN_X = param.game.play_area.rect.x; + const float MAX_X = play_area_.w - width_; + pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); - // Si toca el borde lateral, invierte la velocidad horizontal - if (pos_x_ == MIN_X || pos_x_ == MAX_X) - { - vel_x_ = -vel_x_; - } + // Si toca el borde lateral, invierte la velocidad horizontal + if (pos_x_ == MIN_X || pos_x_ == MAX_X) { + vel_x_ = -vel_x_; + } - // Si colisiona por arriba, rebota (excepto la máquina de café) - if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) - { - // Corrige - pos_y_ = param.game.play_area.rect.y; + // Si colisiona por arriba, rebota (excepto la máquina de café) + if ((pos_y_ < param.game.play_area.rect.y) && !(type_ == ItemType::COFFEE_MACHINE)) { + // Corrige + pos_y_ = param.game.play_area.rect.y; - // Invierte la velocidad - vel_y_ = -vel_y_; - } + // Invierte la velocidad + vel_y_ = -vel_y_; + } - // Si colisiona con la parte inferior - if (pos_y_ > play_area_.h - height_) - { - // Corrige la posición - pos_y_ = play_area_.h - height_; + // Si colisiona con la parte inferior + if (pos_y_ > play_area_.h - height_) { + // Corrige la posición + pos_y_ = play_area_.h - height_; - switch (type_) - { - case ItemType::COFFEE_MACHINE: - // La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido - floor_collision_ = true; - if (vel_y_ < 1.0f) - { - // Si la velocidad vertical es baja, detiene el objeto - vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; - } - else - { - // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad - vel_y_ *= -0.20f; - vel_x_ *= 0.75f; - } - break; - default: - // Si no es una máquina de café - if (vel_y_ < 1.0f) - { - // Si la velocidad vertical es baja, detiene el objeto - vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; - } - else - { - // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad - vel_y_ *= -0.5f; - vel_x_ *= 0.75f; - } - break; - } - } + switch (type_) { + case ItemType::COFFEE_MACHINE: + // La máquina de café es mas pesada y tiene una fisica diferente, ademas hace ruido + floor_collision_ = true; + if (vel_y_ < 1.0f) { + // Si la velocidad vertical es baja, detiene el objeto + vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; + } else { + // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad + vel_y_ *= -0.20f; + vel_x_ *= 0.75f; + } + break; + default: + // Si no es una máquina de café + if (vel_y_ < 1.0f) { + // Si la velocidad vertical es baja, detiene el objeto + vel_y_ = vel_x_ = accel_x_ = accel_y_ = 0; + } else { + // Si la velocidad vertical es alta, el objeto rebota y pierde velocidad + vel_y_ *= -0.5f; + vel_x_ *= 0.75f; + } + break; + } + } - // Actualiza la posición del sprite - shiftSprite(); - shiftColliders(); + // Actualiza la posición del sprite + shiftSprite(); + shiftColliders(); } void Item::disable() { enabled_ = false; } -void Item::update() -{ - move(); - sprite_->update(); - updateTimeToLive(); +void Item::update() { + move(); + sprite_->update(); + updateTimeToLive(); } -void Item::updateTimeToLive() -{ - if (time_to_live_ > 0) - { - time_to_live_--; - } - else - { - disable(); - } +void Item::updateTimeToLive() { + if (time_to_live_ > 0) { + time_to_live_--; + } else { + disable(); + } } -void Item::shiftColliders() -{ - collider_.x = static_cast<int>(pos_x_ + (width_ / 2)); - collider_.y = static_cast<int>(pos_y_ + (height_ / 2)); +void Item::shiftColliders() { + collider_.x = static_cast<int>(pos_x_ + (width_ / 2)); + collider_.y = static_cast<int>(pos_y_ + (height_ / 2)); } -void Item::shiftSprite() -{ - sprite_->setPosX(pos_x_); - sprite_->setPosY(pos_y_); +void Item::shiftSprite() { + sprite_->setPosX(pos_x_); + sprite_->setPosY(pos_y_); } // Calcula la zona de aparición de la máquina de café -int Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) -{ - // Distancia mínima del jugador (ajusta según necesites) - const int MIN_DISTANCE_FROM_PLAYER = area_width / 2; +int Item::getCoffeeMachineSpawn(int player_x, int item_width, int area_width, int margin) { + // Distancia mínima del jugador (ajusta según necesites) + const int MIN_DISTANCE_FROM_PLAYER = area_width / 2; - const int LEFT_BOUND = margin; - const int RIGHT_BOUND = area_width - item_width - margin; + const int LEFT_BOUND = margin; + const int RIGHT_BOUND = area_width - item_width - margin; - // Calcular zona de exclusión alrededor del jugador - int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER; - int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER; + // Calcular zona de exclusión alrededor del jugador + int exclude_left = player_x - MIN_DISTANCE_FROM_PLAYER; + int exclude_right = player_x + MIN_DISTANCE_FROM_PLAYER; - // Verificar si hay espacio suficiente a la izquierda - bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width); + // Verificar si hay espacio suficiente a la izquierda + bool can_spawn_left = (exclude_left > LEFT_BOUND) && (exclude_left - LEFT_BOUND > item_width); - // Verificar si hay espacio suficiente a la derecha - bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width); + // Verificar si hay espacio suficiente a la derecha + bool can_spawn_right = (exclude_right < RIGHT_BOUND) && (RIGHT_BOUND - exclude_right > item_width); - if (can_spawn_left && can_spawn_right) - { - // Ambos lados disponibles, elegir aleatoriamente - if (rand() % 2 == 0) - { - // Lado izquierdo - return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; - } - else - { - // Lado derecho - return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; - } - } - else if (can_spawn_left) - { - // Solo lado izquierdo disponible - return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; - } - else if (can_spawn_right) - { - // Solo lado derecho disponible - return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; - } - else - { - // No hay espacio suficiente lejos del jugador - // Por ahora, intentar spawn en el extremo más lejano posible - int distance_to_left = abs(player_x - LEFT_BOUND); - int distance_to_right = abs(RIGHT_BOUND - player_x); + if (can_spawn_left && can_spawn_right) { + // Ambos lados disponibles, elegir aleatoriamente + if (rand() % 2 == 0) { + // Lado izquierdo + return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; + } else { + // Lado derecho + return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; + } + } else if (can_spawn_left) { + // Solo lado izquierdo disponible + return rand() % (exclude_left - LEFT_BOUND) + LEFT_BOUND; + } else if (can_spawn_right) { + // Solo lado derecho disponible + return rand() % (RIGHT_BOUND - exclude_right) + exclude_right; + } else { + // No hay espacio suficiente lejos del jugador + // Por ahora, intentar spawn en el extremo más lejano posible + int distance_to_left = abs(player_x - LEFT_BOUND); + int distance_to_right = abs(RIGHT_BOUND - player_x); - if (distance_to_left > distance_to_right) - { - return LEFT_BOUND; - } - else - { - return RIGHT_BOUND - item_width; - } - } + if (distance_to_left > distance_to_right) { + return LEFT_BOUND; + } else { + return RIGHT_BOUND - item_width; + } + } } diff --git a/source/item.h b/source/item.h index 6343e64..c8389e0 100644 --- a/source/item.h +++ b/source/item.h @@ -1,34 +1,33 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, Uint16 -#include <memory> // Para shared_ptr, unique_ptr -#include <string> // Para string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FRect, Uint16 -#include "animated_sprite.h" // Para AnimatedSprite -#include "utils.h" // Para Circle +#include <memory> // Para shared_ptr, unique_ptr +#include <string> // Para string +#include <vector> // Para vector + +#include "animated_sprite.h" // Para AnimatedSprite +#include "utils.h" // Para Circle class Texture; // Tipos de objetos disponibles en el juego. // Define los diferentes tipos de objetos que pueden existir en el juego. -enum class ItemType : int -{ - DISK = 1, // Disco - GAVINA = 2, // Gavina - PACMAR = 3, // Pacman - CLOCK = 4, // Reloj - COFFEE = 5, // Café - DEBIAN = 6, // Debian - COFFEE_MACHINE = 7, // Máquina de café - NONE = 8, // Ninguno +enum class ItemType : int { + DISK = 1, // Disco + GAVINA = 2, // Gavina + PACMAR = 3, // Pacman + CLOCK = 4, // Reloj + COFFEE = 5, // Café + DEBIAN = 6, // Debian + COFFEE_MACHINE = 7, // Máquina de café + NONE = 8, // Ninguno }; // Clase Item. // Representa un objeto en el juego, con sus propiedades y métodos para gestionar su comportamiento. -class Item -{ -public: +class Item { + public: // Constantes static constexpr int COFFEE_MACHINE_WIDTH = 30; static constexpr int COFFEE_MACHINE_HEIGHT = 39; @@ -64,25 +63,25 @@ public: bool isOnFloor() const { return floor_collision_; } Circle &getCollider() { return collider_; } -private: + private: // Objetos y punteros - std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto + std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos del objeto // Variables de estado y físicas - float pos_x_; // Posición X del objeto - float pos_y_; // Posición Y del objeto - int width_; // Ancho del objeto - int height_; // Alto del objeto - float vel_x_; // Velocidad en el eje X - float vel_y_; // Velocidad en el eje Y - float accel_x_ = 0.0f; // Aceleración en el eje X - float accel_y_; // Aceleración en el eje Y - bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo - ItemType type_; // Tipo de objeto - bool enabled_ = true; // Indica si el objeto está habilitado - Circle collider_; // Círculo de colisión del objeto - SDL_FRect play_area_; // Rectángulo con la zona de juego - Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente + float pos_x_; // Posición X del objeto + float pos_y_; // Posición Y del objeto + int width_; // Ancho del objeto + int height_; // Alto del objeto + float vel_x_; // Velocidad en el eje X + float vel_y_; // Velocidad en el eje Y + float accel_x_ = 0.0f; // Aceleración en el eje X + float accel_y_; // Aceleración en el eje Y + bool floor_collision_ = false; // Indica si el objeto colisiona con el suelo + ItemType type_; // Tipo de objeto + bool enabled_ = true; // Indica si el objeto está habilitado + Circle collider_; // Círculo de colisión del objeto + SDL_FRect play_area_; // Rectángulo con la zona de juego + Uint16 time_to_live_ = 600; // Tiempo que el objeto está presente // Alinea el círculo de colisión con la posición del objeto. // Actualiza las coordenadas X e Y del colisionador. diff --git a/source/lang.cpp b/source/lang.cpp index b12f653..83a5845 100644 --- a/source/lang.cpp +++ b/source/lang.cpp @@ -1,123 +1,105 @@ #include "lang.h" -#include <stddef.h> // Para size_t -#include <exception> // Para exception -#include <fstream> // Para basic_ifstream, basic_istream, ifstream -#include <unordered_map> // Para unordered_map, _Node_iterator, operator== -#include <utility> // Para pair -#include <vector> // Para vector +#include <stddef.h> // Para size_t -#include "asset.h" // Para Asset -#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper... -#include "options.h" // Para Difficulty, DifficultyCode, SettingsOpt... +#include <exception> // Para exception +#include <fstream> // Para basic_ifstream, basic_istream, ifstream +#include <unordered_map> // Para unordered_map, _Node_iterator, operator== +#include <utility> // Para pair +#include <vector> // Para vector + +#include "asset.h" // Para Asset +#include "external/json.hpp" // Para basic_json, iteration_proxy_value, oper... +#include "options.h" // Para Difficulty, DifficultyCode, SettingsOpt... using json = nlohmann::json; -namespace Lang -{ - std::unordered_map<std::string, std::string> texts; +namespace Lang { +std::unordered_map<std::string, std::string> texts; - // Vector con los idiomas soportados - std::vector<Language> languages = { - {Code::SPANISH, "Castellano", "es_ES.json"}, - {Code::VALENCIAN, "Balooncia", "ba_BA.json"}, - {Code::ENGLISH, "Ingles", "en_UK.json"}}; +// Vector con los idiomas soportados +std::vector<Language> languages = { + {Code::SPANISH, "Castellano", "es_ES.json"}, + {Code::VALENCIAN, "Balooncia", "ba_BA.json"}, + {Code::ENGLISH, "Ingles", "en_UK.json"}}; - // Inicializa los textos del juego en el idioma seleccionado - bool loadFromFile(const std::string &file_path) - { - texts.clear(); +// Inicializa los textos del juego en el idioma seleccionado +bool loadFromFile(const std::string &file_path) { + texts.clear(); - std::ifstream rfile(file_path); - if (!rfile.is_open()) - return false; + std::ifstream rfile(file_path); + if (!rfile.is_open()) + return false; - try - { - json j; - rfile >> j; + try { + json j; + rfile >> j; - for (auto &el : j.items()) - { - texts[el.key()] = el.value(); - } + for (auto &el : j.items()) { + texts[el.key()] = el.value(); } - catch (const std::exception &e) - { - // Puedes loguear el error si quieres - return false; - } - - return true; + } catch (const std::exception &e) { + // Puedes loguear el error si quieres + return false; } - // Obtiene el texto por clave - std::string getText(const std::string &key) - { - auto it = texts.find(key); - if (it != texts.end()) - return it->second; - else - return "[missing text: " + key + "]"; - } + return true; +} - // Obtiene el código del siguiente idioma disponible - Code getNextLangCode(Code lang) - { - for (size_t i = 0; i < languages.size(); ++i) - { - if (languages[i].code == lang) - { - return languages[(i + 1) % languages.size()].code; - } +// Obtiene el texto por clave +std::string getText(const std::string &key) { + auto it = texts.find(key); + if (it != texts.end()) + return it->second; + else + return "[missing text: " + key + "]"; +} + +// Obtiene el código del siguiente idioma disponible +Code getNextLangCode(Code lang) { + for (size_t i = 0; i < languages.size(); ++i) { + if (languages[i].code == lang) { + return languages[(i + 1) % languages.size()].code; } - // Si no se encuentra, devuelve el primero por defecto - return languages[0].code; } + // Si no se encuentra, devuelve el primero por defecto + return languages[0].code; +} - // Obtiene un idioma del vector de idiomas a partir de un código - Language getLanguage(Code code) - { - for (const auto &lang : languages) - { - if (lang.code == code) - return lang; - } - // Si no se encuentra, devuelve el primero por defecto - return languages[0]; +// Obtiene un idioma del vector de idiomas a partir de un código +Language getLanguage(Code code) { + for (const auto &lang : languages) { + if (lang.code == code) + return lang; } + // Si no se encuentra, devuelve el primero por defecto + return languages[0]; +} - // Devuelve el código de un idioma a partir de un nombre - Code getCodeFromName(const std::string &name) - { - for (const auto &lang : languages) - { - if (lang.name == name) - return lang.code; - } - // Si no se encuentra, devuelve el primero por defecto - return languages[0].code; +// Devuelve el código de un idioma a partir de un nombre +Code getCodeFromName(const std::string &name) { + for (const auto &lang : languages) { + if (lang.name == name) + return lang.code; } + // Si no se encuentra, devuelve el primero por defecto + return languages[0].code; +} - // Devuelve el nombre de un idioma a partir de un código - std::string getNameFromCode(Code code) - { - for (const auto &lang : languages) - { - if (lang.code == code) - return lang.name; - } - // Si no se encuentra, devuelve el nombre del primer idioma por defecto - return languages[0].name; +// Devuelve el nombre de un idioma a partir de un código +std::string getNameFromCode(Code code) { + for (const auto &lang : languages) { + if (lang.code == code) + return lang.name; } + // Si no se encuentra, devuelve el nombre del primer idioma por defecto + return languages[0].name; +} - // Actualiza los nombres de los idiomas - void updateLanguageNames() - { - for (auto &lang : languages) - { - switch (lang.code) - { +// Actualiza los nombres de los idiomas +void updateLanguageNames() { + for (auto &lang : languages) { + switch (lang.code) { case Code::SPANISH: lang.name = Lang::getText("[SERVICE_MENU] LANG_ES"); break; @@ -130,17 +112,14 @@ namespace Lang default: lang.name = "Unknown"; break; - } } } +} - // Actualiza los nombres de las dificultades - void updateDifficultyNames() - { - for (auto &difficulty : Options::difficulties) - { - switch (difficulty.code) - { +// Actualiza los nombres de las dificultades +void updateDifficultyNames() { + for (auto &difficulty : Options::difficulties) { + switch (difficulty.code) { case Options::DifficultyCode::EASY: difficulty.name = Lang::getText("[SERVICE_MENU] EASY"); break; @@ -153,28 +132,25 @@ namespace Lang default: difficulty.name = "Unknown"; break; - } } } - - // Obtiene una fichero a partir de un lang::Code - std::string getLanguageFileName(Lang::Code code) - { - for (const auto &lang : languages) - { - if (lang.code == code) - return Asset::get()->get(lang.file_name); - } - // Si no se encuentra, devuelve el fichero del primer idioma por defecto - return Asset::get()->get(languages[0].file_name); - } - - // Establece el idioma - void setLanguage(Code lang) - { - Options::settings.language = lang; - loadFromFile(Asset::get()->get(getLanguage(lang).file_name)); - updateLanguageNames(); - updateDifficultyNames(); - } } + +// Obtiene una fichero a partir de un lang::Code +std::string getLanguageFileName(Lang::Code code) { + for (const auto &lang : languages) { + if (lang.code == code) + return Asset::get()->get(lang.file_name); + } + // Si no se encuentra, devuelve el fichero del primer idioma por defecto + return Asset::get()->get(languages[0].file_name); +} + +// Establece el idioma +void setLanguage(Code lang) { + Options::settings.language = lang; + loadFromFile(Asset::get()->get(getLanguage(lang).file_name)); + updateLanguageNames(); + updateDifficultyNames(); +} +} // namespace Lang diff --git a/source/lang.h b/source/lang.h index e594f09..0387434 100644 --- a/source/lang.h +++ b/source/lang.h @@ -1,52 +1,49 @@ #pragma once -#include <string> // Para string, basic_string +#include <string> // Para string, basic_string -namespace Lang -{ - // --- Códigos de idioma soportados --- - enum class Code : int - { - SPANISH = 0, - VALENCIAN = 1, - ENGLISH = 2 - }; +namespace Lang { +// --- Códigos de idioma soportados --- +enum class Code : int { + SPANISH = 0, + VALENCIAN = 1, + ENGLISH = 2 +}; - // Estructura que representa un idioma - struct Language - { - Code code; // Código que identifica al idioma - std::string name; // Nombre que identifica el idioma - std::string file_name; // Nombre del fichero con los textos +// Estructura que representa un idioma +struct Language { + Code code; // Código que identifica al idioma + std::string name; // Nombre que identifica el idioma + std::string file_name; // Nombre del fichero con los textos - Language(Code c, const std::string &n, const std::string &fn) - : code(c), name(n), file_name(fn) {} - }; + Language(Code c, const std::string &n, const std::string &fn) + : code(c), name(n), file_name(fn) {} +}; - // Carga los textos desde el fichero JSON especificado - bool loadFromFile(const std::string &file_path); +// Carga los textos desde el fichero JSON especificado +bool loadFromFile(const std::string &file_path); - // Obtiene el texto por clave - std::string getText(const std::string &key); +// Obtiene el texto por clave +std::string getText(const std::string &key); - // Obtiene el código del siguiente idioma (circular) - Code getNextLangCode(Code current_lang); +// Obtiene el código del siguiente idioma (circular) +Code getNextLangCode(Code current_lang); - // Obtiene el idioma correspondiente al código proporcionado - Language getLanguage(Code code); +// Obtiene el idioma correspondiente al código proporcionado +Language getLanguage(Code code); - // Devuelve el código de un idioma a partir de un nombre - Code getCodeFromName(const std::string &name); +// Devuelve el código de un idioma a partir de un nombre +Code getCodeFromName(const std::string &name); - // Devuelve el nombre de un idioma a partir de un código - std::string getNameFromCode(Code code); +// Devuelve el nombre de un idioma a partir de un código +std::string getNameFromCode(Code code); - // Actualiza los nombres de los idiomas - void updateLanguageNames(); +// Actualiza los nombres de los idiomas +void updateLanguageNames(); - // Obtiene el nombre del fichero de textos asociado a un código de idioma - std::string getLanguageFileName(Code code); +// Obtiene el nombre del fichero de textos asociado a un código de idioma +std::string getLanguageFileName(Code code); - // Establece el idioma actual - void setLanguage(Code lang); -} +// Establece el idioma actual +void setLanguage(Code lang); +} // namespace Lang diff --git a/source/main.cpp b/source/main.cpp index c4969e5..7b51514 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -7,12 +7,11 @@ Actualizando a la versión "Arcade Edition" en 08/05/2024 */ -#include <memory> // Para make_unique, unique_ptr +#include <memory> // Para make_unique, unique_ptr -#include "director.h" // Para Director +#include "director.h" // Para Director -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { // Crea el objeto Director auto director = std::make_unique<Director>(argc, const_cast<const char **>(argv)); diff --git a/source/manage_hiscore_table.cpp b/source/manage_hiscore_table.cpp index 179d2e4..0defb99 100644 --- a/source/manage_hiscore_table.cpp +++ b/source/manage_hiscore_table.cpp @@ -1,14 +1,14 @@ #include "manage_hiscore_table.h" -#include <SDL3/SDL.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO, SDL_GetE... +#include <SDL3/SDL.h> // Para SDL_ReadIO, SDL_WriteIO, SDL_CloseIO, SDL_GetE... + #include <algorithm> // Para find_if, sort #include <iterator> // Para distance -#include "utils.h" // Para getFileName +#include "utils.h" // Para getFileName // Resetea la tabla a los valores por defecto -void ManageHiScoreTable::clear() -{ +void ManageHiScoreTable::clear() { // Limpia la tabla table_.clear(); @@ -28,8 +28,7 @@ void ManageHiScoreTable::clear() } // Añade un elemento a la tabla -int ManageHiScoreTable::add(const HiScoreEntry &entry) -{ +int ManageHiScoreTable::add(const HiScoreEntry &entry) { // Añade la entrada a la tabla table_.push_back(entry); @@ -37,26 +36,22 @@ int ManageHiScoreTable::add(const HiScoreEntry &entry) sort(); // Encontrar la posición del nuevo elemento - auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e) - { return e.name == entry.name && - e.score == entry.score && - e.one_credit_complete == entry.one_credit_complete; }); + auto it = std::find_if(table_.begin(), table_.end(), [&](const HiScoreEntry &e) { return e.name == entry.name && + e.score == entry.score && + e.one_credit_complete == entry.one_credit_complete; }); int position = -1; - if (it != table_.end()) - { + if (it != table_.end()) { position = std::distance(table_.begin(), it); } // Deja solo las 10 primeras entradas - if (table_.size() > 10) - { + if (table_.size() > 10) { table_.resize(10); // Si el nuevo elemento quedó fuera del top 10 - if (position >= 10) - { - position = -1; // No entró en el top 10 + if (position >= 10) { + position = -1; // No entró en el top 10 } } @@ -65,8 +60,7 @@ int ManageHiScoreTable::add(const HiScoreEntry &entry) } // Ordena la tabla -void ManageHiScoreTable::sort() -{ +void ManageHiScoreTable::sort() { struct { bool operator()(const HiScoreEntry &a, const HiScoreEntry &b) const { return a.score > b.score; } @@ -76,23 +70,20 @@ void ManageHiScoreTable::sort() } // Carga la tabla desde un fichero -bool ManageHiScoreTable::loadFromFile(const std::string &file_path) -{ +bool ManageHiScoreTable::loadFromFile(const std::string &file_path) { clear(); auto success = true; auto file = SDL_IOFromFile(file_path.c_str(), "rb"); - if (file) - { - table_.clear(); // Limpia la tabla actual + if (file) { + table_.clear(); // Limpia la tabla actual // Lee el número de entradas en la tabla int table_size = 0; SDL_ReadIO(file, &table_size, sizeof(int)); // Lee los datos de cada entrada - for (int i = 0; i < table_size; ++i) - { + for (int i = 0; i < table_size; ++i) { HiScoreEntry entry; // Lee la puntuación @@ -104,7 +95,7 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path) std::vector<char> name_buffer(name_size + 1); SDL_ReadIO(file, name_buffer.data(), name_size); - name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena + name_buffer[name_size] = '\0'; // Asegurar el fin de la cadena entry.name = std::string(name_buffer.data()); // Lee el valor de one_credit_complete @@ -118,9 +109,7 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str()); SDL_CloseIO(file); - } - else - { + } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to load %s file! %s", getFileName(file_path).c_str(), SDL_GetError()); success = false; } @@ -128,20 +117,17 @@ bool ManageHiScoreTable::loadFromFile(const std::string &file_path) } // Guarda la tabla en un fichero -bool ManageHiScoreTable::saveToFile(const std::string &file_path) -{ +bool ManageHiScoreTable::saveToFile(const std::string &file_path) { auto success = true; auto file = SDL_IOFromFile(file_path.c_str(), "w+b"); - if (file) - { + if (file) { // Guarda el número de entradas en la tabla int table_size = static_cast<int>(table_.size()); SDL_WriteIO(file, &table_size, sizeof(int)); // Guarda los datos de cada entrada - for (int i = 0; i < table_size; ++i) - { + for (int i = 0; i < table_size; ++i) { const HiScoreEntry &entry = table_.at(i); // Guarda la puntuación @@ -159,9 +145,7 @@ bool ManageHiScoreTable::saveToFile(const std::string &file_path) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(file_path).c_str()); SDL_CloseIO(file); - } - else - { + } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to save %s file! %s", getFileName(file_path).c_str(), SDL_GetError()); success = false; } diff --git a/source/manage_hiscore_table.h b/source/manage_hiscore_table.h index 2784476..7eff9a2 100644 --- a/source/manage_hiscore_table.h +++ b/source/manage_hiscore_table.h @@ -1,7 +1,7 @@ #pragma once -#include <string> // Para std::string -#include <vector> // Para std::vector +#include <string> // Para std::string +#include <vector> // Para std::vector /* Esta clase sirve para añadir elementos HiScoreEntry a un vector (tabla), de manera @@ -12,11 +12,10 @@ */ // --- Estructura para las entradas de la tabla de records --- -struct HiScoreEntry -{ - std::string name; // Nombre - int score; // Puntuación - bool one_credit_complete; // Indica si se ha conseguido 1CC +struct HiScoreEntry { + std::string name; // Nombre + int score; // Puntuación + bool one_credit_complete; // Indica si se ha conseguido 1CC // Constructor explicit HiScoreEntry(const std::string &n = "", int s = 0, bool occ = false) @@ -24,9 +23,8 @@ struct HiScoreEntry }; // --- Clase ManageHiScoreTable --- -class ManageHiScoreTable -{ -public: +class ManageHiScoreTable { + public: // Constructor explicit ManageHiScoreTable(std::vector<HiScoreEntry> &table) : table_(table) {} @@ -46,7 +44,7 @@ public: // Guarda la tabla en un fichero bool saveToFile(const std::string &file_path); -private: + private: // Referencia a la tabla con los records std::vector<HiScoreEntry> &table_; diff --git a/source/mouse.cpp b/source/mouse.cpp index 0ada5bc..02080d9 100644 --- a/source/mouse.cpp +++ b/source/mouse.cpp @@ -1,33 +1,27 @@ #include "mouse.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show... +#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_HideCursor, SDL_Show... -namespace Mouse -{ - Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor - Uint32 last_mouse_move_time = 0; // Última vez que el ratón se movió - bool cursor_visible = true; // Estado del cursor +namespace Mouse { +Uint32 cursor_hide_time = 3000; // Tiempo en milisegundos para ocultar el cursor +Uint32 last_mouse_move_time = 0; // Última vez que el ratón se movió +bool cursor_visible = true; // Estado del cursor - void handleEvent(const SDL_Event &event) - { - if (event.type == SDL_EVENT_MOUSE_MOTION) - { - last_mouse_move_time = SDL_GetTicks(); - if (!cursor_visible) - { - SDL_ShowCursor(); - cursor_visible = true; - } - } - } - - void updateCursorVisibility() - { - Uint32 current_time = SDL_GetTicks(); - if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) - { - SDL_HideCursor(); - cursor_visible = false; +void handleEvent(const SDL_Event &event) { + if (event.type == SDL_EVENT_MOUSE_MOTION) { + last_mouse_move_time = SDL_GetTicks(); + if (!cursor_visible) { + SDL_ShowCursor(); + cursor_visible = true; } } } + +void updateCursorVisibility() { + Uint32 current_time = SDL_GetTicks(); + if (cursor_visible && (current_time - last_mouse_move_time > cursor_hide_time)) { + SDL_HideCursor(); + cursor_visible = false; + } +} +} // namespace Mouse diff --git a/source/mouse.h b/source/mouse.h index 9d31394..c3a046f 100644 --- a/source/mouse.h +++ b/source/mouse.h @@ -1,15 +1,14 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32, SDL_Event +#include <SDL3/SDL.h> // Para Uint32, SDL_Event -namespace Mouse -{ - // --- Variables de estado del cursor --- - extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad - extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió - extern bool cursor_visible; // Indica si el cursor está visible +namespace Mouse { +// --- Variables de estado del cursor --- +extern Uint32 cursor_hide_time; // Tiempo en milisegundos para ocultar el cursor tras inactividad +extern Uint32 last_mouse_move_time; // Última vez (en ms) que el ratón se movió +extern bool cursor_visible; // Indica si el cursor está visible - // --- Gestión de eventos y visibilidad --- - void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.) - void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad -} \ No newline at end of file +// --- Gestión de eventos y visibilidad --- +void handleEvent(const SDL_Event &event); // Procesa eventos de ratón (movimiento, clic, etc.) +void updateCursorVisibility(); // Actualiza la visibilidad del cursor según la inactividad +} // namespace Mouse \ No newline at end of file diff --git a/source/moving_sprite.cpp b/source/moving_sprite.cpp index 0e1917c..7032313 100644 --- a/source/moving_sprite.cpp +++ b/source/moving_sprite.cpp @@ -1,130 +1,119 @@ #include "moving_sprite.h" -#include "texture.h" // Para Texture +#include "texture.h" // Para Texture // Constructor MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos, Rotate rotate, float zoom_w, float zoom_h, SDL_FlipMode flip) - : Sprite(texture, pos), - x_(pos.x), - y_(pos.y), - rotate_(rotate), - zoom_w_(zoom_w), - zoom_h_(zoom_h), - flip_(flip) {} + : Sprite(texture, pos), + x_(pos.x), + y_(pos.y), + rotate_(rotate), + zoom_w_(zoom_w), + zoom_h_(zoom_h), + flip_(flip) {} MovingSprite::MovingSprite(std::shared_ptr<Texture> texture, SDL_FRect pos) - : Sprite(texture, pos), - x_(pos.x), - y_(pos.y), - rotate_(Rotate()), - zoom_w_(1.0f), - zoom_h_(1.0f), - flip_(SDL_FLIP_NONE) {} + : Sprite(texture, pos), + x_(pos.x), + y_(pos.y), + rotate_(Rotate()), + zoom_w_(1.0f), + zoom_h_(1.0f), + flip_(SDL_FLIP_NONE) {} MovingSprite::MovingSprite(std::shared_ptr<Texture> texture) - : Sprite(texture), - x_(0.0f), - y_(0.0f), - rotate_(Rotate()), - zoom_w_(1.0f), - zoom_h_(1.0f), - flip_(SDL_FLIP_NONE) { Sprite::clear(); } + : Sprite(texture), + x_(0.0f), + y_(0.0f), + rotate_(Rotate()), + zoom_w_(1.0f), + zoom_h_(1.0f), + flip_(SDL_FLIP_NONE) { Sprite::clear(); } // Reinicia todas las variables -void MovingSprite::clear() -{ - x_ = 0.0f; // Posición en el eje X - y_ = 0.0f; // Posición en el eje Y +void MovingSprite::clear() { + x_ = 0.0f; // Posición en el eje X + y_ = 0.0f; // Posición en el eje Y - vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse - vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse + vx_ = 0.0f; // Velocidad en el eje X. Cantidad de pixeles a desplazarse + vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de pixeles a desplazarse - ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad - ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad + ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad + ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad - rotate_ = Rotate(); // Inicializa la estructura + rotate_ = Rotate(); // Inicializa la estructura - zoom_w_ = 1.0f; // Zoom aplicado a la anchura - zoom_h_ = 1.0f; // Zoom aplicado a la altura + zoom_w_ = 1.0f; // Zoom aplicado a la anchura + zoom_h_ = 1.0f; // Zoom aplicado a la altura - flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite + flip_ = SDL_FLIP_NONE; // Establece como se ha de voltear el sprite - Sprite::clear(); + Sprite::clear(); } // Mueve el sprite -void MovingSprite::move() -{ - x_ += vx_; - y_ += vy_; +void MovingSprite::move() { + x_ += vx_; + y_ += vy_; - vx_ += ax_; - vy_ += ay_; + vx_ += ax_; + vy_ += ay_; - pos_.x = static_cast<int>(x_); - pos_.y = static_cast<int>(y_); + pos_.x = static_cast<int>(x_); + pos_.y = static_cast<int>(y_); } // Actualiza las variables internas del objeto -void MovingSprite::update() -{ - move(); - rotate(); +void MovingSprite::update() { + move(); + rotate(); } // Muestra el sprite por pantalla void MovingSprite::render() { texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_w_, zoom_h_, rotate_.angle, &rotate_.center, flip_); } // Establece la rotacion -void MovingSprite::rotate() -{ - if (rotate_.enabled) - { - ++rotate_.counter; - if (rotate_.counter % rotate_.speed == 0) - { - updateAngle(); - rotate_.counter = 0; - } - } +void MovingSprite::rotate() { + if (rotate_.enabled) { + ++rotate_.counter; + if (rotate_.counter % rotate_.speed == 0) { + updateAngle(); + rotate_.counter = 0; + } + } } // Activa o desactiva el efecto de rotación -void MovingSprite::setRotate(bool enable) -{ - rotate_.enabled = enable; - rotate_.counter = 0; +void MovingSprite::setRotate(bool enable) { + rotate_.enabled = enable; + rotate_.counter = 0; } // Establece la posición y_ el tamaño del objeto -void MovingSprite::setPos(SDL_FRect rect) -{ - x_ = static_cast<float>(rect.x); - y_ = static_cast<float>(rect.y); +void MovingSprite::setPos(SDL_FRect rect) { + x_ = static_cast<float>(rect.x); + y_ = static_cast<float>(rect.y); - pos_ = rect; + pos_ = rect; } // Establece el valor de las variables -void MovingSprite::setPos(float x, float y) -{ - x_ = x; - y_ = y; +void MovingSprite::setPos(float x, float y) { + x_ = x; + y_ = y; - pos_.x = static_cast<int>(x_); - pos_.y = static_cast<int>(y_); + pos_.x = static_cast<int>(x_); + pos_.y = static_cast<int>(y_); } // Establece el valor de la variable -void MovingSprite::setPosX(float value) -{ - x_ = value; - pos_.x = static_cast<int>(x_); +void MovingSprite::setPosX(float value) { + x_ = value; + pos_.x = static_cast<int>(x_); } // Establece el valor de la variable -void MovingSprite::setPosY(float value) -{ - y_ = value; - pos_.y = static_cast<int>(y_); +void MovingSprite::setPosY(float value) { + y_ = value; + pos_.y = static_cast<int>(y_); } \ No newline at end of file diff --git a/source/moving_sprite.h b/source/moving_sprite.h index 255b42c..2381852 100644 --- a/source/moving_sprite.h +++ b/source/moving_sprite.h @@ -1,26 +1,25 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_FPoint, SDL_FRect +#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_FPoint, SDL_FRect + #include <algorithm> // Para max #include <memory> // Para shared_ptr -#include "sprite.h" // Para Sprite +#include "sprite.h" // Para Sprite class Texture; // Clase MovingSprite. Añade movimiento y efectos de rotación, zoom y flip al sprite -class MovingSprite : public Sprite -{ -public: +class MovingSprite : public Sprite { + public: // --- Estructura para la rotación --- - struct Rotate - { - bool enabled; // Indica si ha de rotar - int counter; // Contador - int speed; // Velocidad de giro - double angle; // Ángulo para dibujarlo - float amount; // Cantidad de grados a girar en cada iteración - SDL_FPoint center; // Centro de rotación + struct Rotate { + bool enabled; // Indica si ha de rotar + int counter; // Contador + int speed; // Velocidad de giro + double angle; // Ángulo para dibujarlo + float amount; // Cantidad de grados a girar en cada iteración + SDL_FPoint center; // Centro de rotación Rotate() : enabled(false), counter(0), speed(1), angle(0.0), amount(0.0f), center({0.0f, 0.0f}) {} }; @@ -32,9 +31,9 @@ public: virtual ~MovingSprite() override = default; // --- Métodos principales --- - virtual void update(); // Actualiza las variables internas del objeto - void clear() override; // Reinicia todas las variables a cero - void render() override; // Muestra el sprite por pantalla + virtual void update(); // Actualiza las variables internas del objeto + void clear() override; // Reinicia todas las variables a cero + void render() override; // Muestra el sprite por pantalla // --- Getters de posición y movimiento --- float getPosX() const { return x_; } @@ -56,10 +55,10 @@ public: void setZoomH(float value) { zoom_h_ = value; } void setAngle(double value) { rotate_.angle = value; } void setRotatingCenter(SDL_FPoint point) { rotate_.center = point; } - void setRotate(bool enable); // Activa o desactiva el efecto de rotación + void setRotate(bool enable); // Activa o desactiva el efecto de rotación void setRotateSpeed(int value) { rotate_.speed = std::max(1, value); } void setRotateAmount(double value) { rotate_.amount = value; } - void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación + void switchRotate() { rotate_.amount *= -1; } // Cambia el sentido de la rotación // --- Flip --- void setFlip(SDL_FlipMode flip) { flip_ = flip; } @@ -67,30 +66,30 @@ public: SDL_FlipMode getFlip() { return flip_; } // --- Posición y tamaño --- - void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto - void setPos(float x, float y); // Establece la posición del objeto - void setPosX(float value); // Establece la posición X - void setPosY(float value); // Establece la posición Y + void setPos(SDL_FRect rect); // Establece la posición y el tamaño del objeto + void setPos(float x, float y); // Establece la posición del objeto + void setPosX(float value); // Establece la posición X + void setPosY(float value); // Establece la posición Y -protected: + protected: // --- Variables de posición y movimiento --- - float x_ = 0.0f; // Posición en el eje X - float y_ = 0.0f; // Posición en el eje Y + float x_ = 0.0f; // Posición en el eje X + float y_ = 0.0f; // Posición en el eje Y - float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de píxeles a desplazarse - float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de píxeles a desplazarse + float vx_ = 0.0f; // Velocidad en el eje X. Cantidad de píxeles a desplazarse + float vy_ = 0.0f; // Velocidad en el eje Y. Cantidad de píxeles a desplazarse - float ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad - float ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad + float ax_ = 0.0f; // Aceleración en el eje X. Variación de la velocidad + float ay_ = 0.0f; // Aceleración en el eje Y. Variación de la velocidad // --- Efectos visuales --- - Rotate rotate_; // Variables usadas para controlar la rotación del sprite - float zoom_w_; // Zoom aplicado a la anchura - float zoom_h_; // Zoom aplicado a la altura - SDL_FlipMode flip_; // Indica cómo se voltea el sprite + Rotate rotate_; // Variables usadas para controlar la rotación del sprite + float zoom_w_; // Zoom aplicado a la anchura + float zoom_h_; // Zoom aplicado a la altura + SDL_FlipMode flip_; // Indica cómo se voltea el sprite // --- Métodos internos --- - void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo - void move(); // Mueve el sprite según velocidad y aceleración - void rotate(); // Rota el sprite según los parámetros de rotación + void updateAngle() { rotate_.angle += rotate_.amount; } // Incrementa el valor del ángulo + void move(); // Mueve el sprite según velocidad y aceleración + void rotate(); // Rota el sprite según los parámetros de rotación }; \ No newline at end of file diff --git a/source/notifier.cpp b/source/notifier.cpp index 14d440e..d0516eb 100644 --- a/source/notifier.cpp +++ b/source/notifier.cpp @@ -1,16 +1,17 @@ #include "notifier.h" -#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear +#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_FRect, SDL_RenderClear + #include <algorithm> // Para remove_if #include <string> // Para basic_string, string #include <vector> // Para vector -#include "audio.h" // Para Audio -#include "param.h" // Para Param, param, ParamNotification, ParamGame -#include "screen.h" // Para Screen -#include "sprite.h" // Para Sprite -#include "text.h" // Para Text -#include "texture.h" // Para Texture +#include "audio.h" // Para Audio +#include "param.h" // Para Param, param, ParamNotification, ParamGame +#include "screen.h" // Para Screen +#include "sprite.h" // Para Sprite +#include "text.h" // Para Text +#include "texture.h" // Para Texture // Singleton Notifier *Notifier::instance_ = nullptr; @@ -35,24 +36,18 @@ Notifier::Notifier(std::string icon_file, std::shared_ptr<Text> text) has_icons_(!icon_file.empty()) {} // Dibuja las notificaciones por pantalla -void Notifier::render() -{ - for (int i = (int)notifications_.size() - 1; i >= 0; --i) - { +void Notifier::render() { + for (int i = (int)notifications_.size() - 1; i >= 0; --i) { notifications_[i].sprite->render(); } } // Actualiza el estado de las notificaiones -void Notifier::update() -{ - for (int i = 0; i < (int)notifications_.size(); ++i) - { +void Notifier::update() { + for (int i = 0; i < (int)notifications_.size(); ++i) { // Si la notificación anterior está "saliendo", no hagas nada - if (i > 0) - { - if (notifications_[i - 1].state == NotificationStatus::RISING) - { + if (i > 0) { + if (notifications_[i - 1].state == NotificationStatus::RISING) { break; } } @@ -60,12 +55,9 @@ void Notifier::update() notifications_[i].counter++; // Hace sonar la notificación en el primer frame - if (notifications_[i].counter == 1) - { - if (param.notification.sound) - { - if (notifications_[i].state == NotificationStatus::RISING) - { + if (notifications_[i].counter == 1) { + if (param.notification.sound) { + if (notifications_[i].state == NotificationStatus::RISING) { // Reproduce el sonido de la notificación Audio::get()->playSound("notify.wav", Audio::Group::INTERFACE); } @@ -73,55 +65,41 @@ void Notifier::update() } // Comprueba los estados - if (notifications_[i].state == NotificationStatus::RISING) - { + if (notifications_[i].state == NotificationStatus::RISING) { const float step = ((float)notifications_[i].counter / notifications_[i].travel_dist); const int alpha = 255 * step; - if (param.notification.pos_v == NotifyPosition::TOP) - { + if (param.notification.pos_v == NotifyPosition::TOP) { notifications_[i].rect.y++; - } - else - { + } else { notifications_[i].rect.y--; } notifications_[i].texture->setAlpha(alpha); - if (notifications_[i].rect.y == notifications_[i].y) - { + if (notifications_[i].rect.y == notifications_[i].y) { notifications_[i].state = NotificationStatus::STAY; notifications_[i].texture->setAlpha(255); notifications_[i].counter = 0; } } - else if (notifications_[i].state == NotificationStatus::STAY) - { - if (notifications_[i].counter == wait_time_) - { + else if (notifications_[i].state == NotificationStatus::STAY) { + if (notifications_[i].counter == wait_time_) { notifications_[i].state = NotificationStatus::VANISHING; notifications_[i].counter = 0; } - } - else if (notifications_[i].state == NotificationStatus::VANISHING) - { - + } else if (notifications_[i].state == NotificationStatus::VANISHING) { const float step = (notifications_[i].counter / (float)notifications_[i].travel_dist); const int alpha = 255 * (1 - step); - if (param.notification.pos_v == NotifyPosition::TOP) - { + if (param.notification.pos_v == NotifyPosition::TOP) { notifications_[i].rect.y--; - } - else - { + } else { notifications_[i].rect.y++; } notifications_[i].texture->setAlpha(alpha); - if (notifications_[i].rect.y == notifications_[i].y - notifications_[i].travel_dist) - { + if (notifications_[i].rect.y == notifications_[i].y - notifications_[i].travel_dist) { notifications_[i].state = NotificationStatus::FINISHED; } } @@ -133,40 +111,32 @@ void Notifier::update() } // Elimina las notificaciones finalizadas -void Notifier::clearFinishedNotifications() -{ - for (int i = (int)notifications_.size() - 1; i >= 0; --i) - { - if (notifications_[i].state == NotificationStatus::FINISHED) - { +void Notifier::clearFinishedNotifications() { + for (int i = (int)notifications_.size() - 1; i >= 0; --i) { + if (notifications_[i].state == NotificationStatus::FINISHED) { notifications_.erase(notifications_.begin() + i); } } } -void Notifier::show(std::vector<std::string> texts, int icon, const std::string &code) -{ +void Notifier::show(std::vector<std::string> texts, int icon, const std::string &code) { // Si no hay texto, acaba - if (texts.empty()) - { + if (texts.empty()) { return; } // Si las notificaciones no se apilan, elimina las anteriores - if (!stack_) - { + if (!stack_) { clearAllNotifications(); } // Elimina las cadenas vacías - texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s) - { return s.empty(); }), - texts.end()); + texts.erase(std::remove_if(texts.begin(), texts.end(), [](const std::string &s) { return s.empty(); }), + texts.end()); // Encuentra la cadena más larga std::string longest; - for (const auto &text : texts) - { + for (const auto &text : texts) { if (text.length() > longest.length()) longest = text; } @@ -183,23 +153,22 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string // Posición horizontal float desp_h = 0; - switch (param.notification.pos_h) - { - case NotifyPosition::LEFT: - desp_h = PADDING_OUT; - break; + switch (param.notification.pos_h) { + case NotifyPosition::LEFT: + desp_h = PADDING_OUT; + break; - case NotifyPosition::MIDDLE: - desp_h = ((param.game.width / 2) - (WIDTH / 2)); - break; + case NotifyPosition::MIDDLE: + desp_h = ((param.game.width / 2) - (WIDTH / 2)); + break; - case NotifyPosition::RIGHT: - desp_h = param.game.width - WIDTH - PADDING_OUT; - break; + case NotifyPosition::RIGHT: + desp_h = param.game.width - WIDTH - PADDING_OUT; + break; - default: - desp_h = 0; - break; + default: + desp_h = 0; + break; } // Posición vertical @@ -208,8 +177,8 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string // Offset const auto TRAVEL_DIST = HEIGHT + PADDING_OUT; auto OFFSET = notifications_.empty() - ? DESP_V - : notifications_.back().y + (param.notification.pos_v == NotifyPosition::TOP ? TRAVEL_DIST : -TRAVEL_DIST); + ? DESP_V + : notifications_.back().y + (param.notification.pos_v == NotifyPosition::TOP ? TRAVEL_DIST : -TRAVEL_DIST); // Crea la notificacion Notification n; @@ -234,8 +203,7 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string // Dibuja el fondo de la notificación SDL_SetRenderDrawColor(renderer_, bg_color_.r, bg_color_.g, bg_color_.b, 255); SDL_FRect rect; - if (SHAPE == NotificationShape::ROUNDED) - { + if (SHAPE == NotificationShape::ROUNDED) { rect = {4, 0, WIDTH - (4 * 2), HEIGHT}; SDL_RenderFillRect(renderer_, &rect); @@ -249,14 +217,12 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string SDL_RenderFillRect(renderer_, &rect); } - else if (SHAPE == NotificationShape::SQUARED) - { + else if (SHAPE == NotificationShape::SQUARED) { SDL_RenderClear(renderer_); } // Dibuja el icono de la notificación - if (has_icons_ && icon >= 0 && texts.size() >= 2) - { + if (has_icons_ && icon >= 0 && texts.size() >= 2) { auto sp = std::make_unique<Sprite>(icon_texture_, (SDL_FRect){0, 0, ICON_SIZE, ICON_SIZE}); sp->setPosition({PADDING_IN_H, PADDING_IN_V, ICON_SIZE, ICON_SIZE}); sp->setSpriteClip(SDL_FRect{ @@ -270,8 +236,7 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string // Escribe el texto de la notificación const Color color{255, 255, 255}; int iterator = 0; - for (const auto &text : texts) - { + for (const auto &text : texts) { text_->writeColored(PADDING_IN_H + ICON_SPACE, PADDING_IN_V + iterator * (text_->getCharacterSize() + 1), text, color); ++iterator; } @@ -290,10 +255,8 @@ void Notifier::show(std::vector<std::string> texts, int icon, const std::string } // Finaliza y elimnina todas las notificaciones activas -void Notifier::clearAllNotifications() -{ - for (auto ¬ification : notifications_) - { +void Notifier::clearAllNotifications() { + for (auto ¬ification : notifications_) { notification.state = NotificationStatus::FINISHED; } @@ -301,11 +264,9 @@ void Notifier::clearAllNotifications() } // Obtiene los códigos de las notificaciones -std::vector<std::string> Notifier::getCodes() -{ +std::vector<std::string> Notifier::getCodes() { std::vector<std::string> codes; - for (const auto ¬ification : notifications_) - { + for (const auto ¬ification : notifications_) { codes.emplace_back(notification.code); } return codes; diff --git a/source/notifier.h b/source/notifier.h index b4d97e2..dd36069 100644 --- a/source/notifier.h +++ b/source/notifier.h @@ -1,91 +1,87 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, SDL_Renderer -#include <memory> // Para shared_ptr -#include <string> // Para basic_string, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FRect, SDL_Renderer -#include "utils.h" // Para stringInVector, Color +#include <memory> // Para shared_ptr +#include <string> // Para basic_string, string +#include <vector> // Para vector + +#include "utils.h" // Para stringInVector, Color class Sprite; class Text; class Texture; // --- Clase Notifier: gestiona las notificaciones en pantalla (singleton) --- -class Notifier -{ -public: +class Notifier { + public: // --- Métodos de singleton --- - static void init(const std::string &icon_file, std::shared_ptr<Text> text); // Inicializa el singleton - static void destroy(); // Libera el singleton - static Notifier *get(); // Obtiene la instancia + static void init(const std::string &icon_file, std::shared_ptr<Text> text); // Inicializa el singleton + static void destroy(); // Libera el singleton + static Notifier *get(); // Obtiene la instancia // --- Métodos principales --- - void render(); // Dibuja las notificaciones por pantalla - void update(); // Actualiza el estado de las notificaciones + void render(); // Dibuja las notificaciones por pantalla + void update(); // Actualiza el estado de las notificaciones // --- Gestión de notificaciones --- - void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla - bool isActive() const { return !notifications_.empty(); } // Indica si hay notificaciones activas - std::vector<std::string> getCodes(); // Obtiene los códigos de las notificaciones activas - bool checkCode(const std::string &code) { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto + void show(std::vector<std::string> texts, int icon = -1, const std::string &code = std::string()); // Muestra una notificación de texto por pantalla + bool isActive() const { return !notifications_.empty(); } // Indica si hay notificaciones activas + std::vector<std::string> getCodes(); // Obtiene los códigos de las notificaciones activas + bool checkCode(const std::string &code) { return stringInVector(getCodes(), code); } // Comprueba si hay alguna notificación con un código concreto -private: + private: // --- Singleton --- static Notifier *instance_; // --- Tipos internos --- - enum class NotificationStatus - { + enum class NotificationStatus { RISING, STAY, VANISHING, FINISHED, }; - enum class NotificationShape - { + enum class NotificationShape { ROUNDED, SQUARED, }; // --- Estructura Notification --- - struct Notification - { - std::shared_ptr<Texture> texture; // Textura de la notificación - std::shared_ptr<Sprite> sprite; // Sprite asociado - std::vector<std::string> texts; // Textos a mostrar - int counter; // Contador de tiempo - NotificationStatus state; // Estado de la notificación - NotificationShape shape; // Forma de la notificación - SDL_FRect rect; // Rectángulo de la notificación - int y; // Posición vertical - int travel_dist; // Distancia a recorrer - std::string code; // Código identificador de la notificación + struct Notification { + std::shared_ptr<Texture> texture; // Textura de la notificación + std::shared_ptr<Sprite> sprite; // Sprite asociado + std::vector<std::string> texts; // Textos a mostrar + int counter; // Contador de tiempo + NotificationStatus state; // Estado de la notificación + NotificationShape shape; // Forma de la notificación + SDL_FRect rect; // Rectángulo de la notificación + int y; // Posición vertical + int travel_dist; // Distancia a recorrer + std::string code; // Código identificador de la notificación // Constructor explicit Notification() - : texture(nullptr), sprite(nullptr), texts(), counter(0), state(NotificationStatus::RISING), - shape(NotificationShape::SQUARED), rect{0, 0, 0, 0}, y(0), travel_dist(0), code("") {} + : texture(nullptr), sprite(nullptr), texts(), counter(0), state(NotificationStatus::RISING), shape(NotificationShape::SQUARED), rect{0, 0, 0, 0}, y(0), travel_dist(0), code("") {} }; // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - std::shared_ptr<Texture> icon_texture_; // Textura para los iconos de las notificaciones - std::shared_ptr<Text> text_; // Objeto para dibujar texto + SDL_Renderer *renderer_; // El renderizador de la ventana + std::shared_ptr<Texture> icon_texture_; // Textura para los iconos de las notificaciones + std::shared_ptr<Text> text_; // Objeto para dibujar texto // --- Variables de estado --- - Color bg_color_; // Color de fondo de las notificaciones - int wait_time_; // Tiempo que se ve la notificación - std::vector<Notification> notifications_; // Lista de notificaciones activas - bool stack_; // Indica si las notificaciones se apilan - bool has_icons_; // Indica si el notificador tiene textura para iconos + Color bg_color_; // Color de fondo de las notificaciones + int wait_time_; // Tiempo que se ve la notificación + std::vector<Notification> notifications_; // Lista de notificaciones activas + bool stack_; // Indica si las notificaciones se apilan + bool has_icons_; // Indica si el notificador tiene textura para iconos // --- Métodos internos --- - void clearFinishedNotifications(); // Elimina las notificaciones finalizadas - void clearAllNotifications(); // Finaliza y elimina todas las notificaciones activas + void clearFinishedNotifications(); // Elimina las notificaciones finalizadas + void clearAllNotifications(); // Finaliza y elimina todas las notificaciones activas // --- Constructor y destructor --- - Notifier(std::string icon_file, std::shared_ptr<Text> text); // Constructor privado - ~Notifier() = default; // Destructor privado + Notifier(std::string icon_file, std::shared_ptr<Text> text); // Constructor privado + ~Notifier() = default; // Destructor privado }; \ No newline at end of file diff --git a/source/options.cpp b/source/options.cpp index 7eeaf72..7ef88ce 100644 --- a/source/options.cpp +++ b/source/options.cpp @@ -1,423 +1,328 @@ #include "options.h" -#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError +#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError + #include <algorithm> // Para clamp -#include <fstream> // Para basic_ostream, operator<<, basic_ostream::... -#include <utility> // Para swap -#include <vector> // Para vector +#include <fstream> // Para basic_ostream, operator<<, basic_ostream::... +#include <utility> // Para swap +#include <vector> // Para vector #include "asset.h" -#include "input.h" // Para InputDeviceToUse -#include "lang.h" // Para Code -#include "utils.h" // Para boolToString, stringToBool, getFileName +#include "input.h" // Para InputDeviceToUse +#include "lang.h" // Para Code +#include "utils.h" // Para boolToString, stringToBool, getFileName -namespace Options -{ - // --- Variables globales --- - WindowOptions window; // Opciones de la ventana - SettingsOptions settings; // Opciones del juego - VideoOptions video; // Opciones de vídeo - AudioOptions audio; // Opciones de audio - std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador - PendingChanges pending_changes; // Opciones que se aplican al cerrar +namespace Options { +// --- Variables globales --- +WindowOptions window; // Opciones de la ventana +SettingsOptions settings; // Opciones del juego +VideoOptions video; // Opciones de vídeo +AudioOptions audio; // Opciones de audio +std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador +PendingChanges pending_changes; // Opciones que se aplican al cerrar - // Vector con las dificultades - std::vector<Difficulty> difficulties = { - {DifficultyCode::EASY, "Easy"}, - {DifficultyCode::NORMAL, "Normal"}, - {DifficultyCode::HARD, "Hard"}}; +// Vector con las dificultades +std::vector<Difficulty> difficulties = { + {DifficultyCode::EASY, "Easy"}, + {DifficultyCode::NORMAL, "Normal"}, + {DifficultyCode::HARD, "Hard"}}; - // Declaraciones - bool set(const std::string &var, const std::string &value); +// Declaraciones +bool set(const std::string &var, const std::string &value); - // Inicializa las opciones del programa - void init() - { - // Settings - settings.config_file = Asset::get()->get("config.txt"); +// Inicializa las opciones del programa +void init() { + // Settings + settings.config_file = Asset::get()->get("config.txt"); - // Opciones de control - controllers.clear(); - controllers.resize(2); - controllers.at(0).player_id = 1; - controllers.at(1).player_id = 2; - setKeyboardToPlayer(1); + // Opciones de control + controllers.clear(); + controllers.resize(2); + controllers.at(0).player_id = 1; + controllers.at(1).player_id = 2; + setKeyboardToPlayer(1); - // Opciones pendientes - pending_changes.new_language = settings.language; - pending_changes.new_difficulty = settings.difficulty; - pending_changes.has_pending_changes = false; - } + // Opciones pendientes + pending_changes.new_language = settings.language; + pending_changes.new_difficulty = settings.difficulty; + pending_changes.has_pending_changes = false; +} - // Carga el fichero de configuración - bool loadFromFile() - { - // Inicializa las opciones del programa - init(); +// Carga el fichero de configuración +bool loadFromFile() { + // Inicializa las opciones del programa + init(); - // Indicador de éxito en la carga - bool success = true; + // Indicador de éxito en la carga + bool success = true; - // Variables para manejar el fichero - std::ifstream file(settings.config_file); + // Variables para manejar el fichero + std::ifstream file(settings.config_file); - // Si el fichero se puede abrir - if (file.good()) - { - // Procesa el fichero línea a línea - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str()); - std::string line; - while (std::getline(file, line)) - { - // Comprueba que la línea no sea un comentario - if (line.substr(0, 1) != "#") - { - // Encuentra la posición del carácter '=' - int pos = line.find("="); - // Procesa las dos subcadenas - if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str()); - success = false; - } - } - } - file.close(); - } - // El fichero no existe - else - { - saveToFile(); // Crea el fichero con los valores por defecto - } + // Si el fichero se puede abrir + if (file.good()) { + // Procesa el fichero línea a línea + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str()); + std::string line; + while (std::getline(file, line)) { + // Comprueba que la línea no sea un comentario + if (line.substr(0, 1) != "#") { + // Encuentra la posición del carácter '=' + int pos = line.find("="); + // Procesa las dos subcadenas + if (!set(line.substr(0, pos), line.substr(pos + 1, line.length()))) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str()); + success = false; + } + } + } + file.close(); + } + // El fichero no existe + else { + saveToFile(); // Crea el fichero con los valores por defecto + } - // Normaliza los valores - if (settings.language != Lang::Code::ENGLISH && - settings.language != Lang::Code::VALENCIAN && - settings.language != Lang::Code::SPANISH) - { - settings.language = Lang::Code::ENGLISH; - } + // Normaliza los valores + if (settings.language != Lang::Code::ENGLISH && + settings.language != Lang::Code::VALENCIAN && + settings.language != Lang::Code::SPANISH) { + settings.language = Lang::Code::ENGLISH; + } - return success; - } + return success; +} - // Guarda el fichero de configuración - bool saveToFile() - { - std::ofstream file(settings.config_file); +// Guarda el fichero de configuración +bool saveToFile() { + std::ofstream file(settings.config_file); - if (!file.good()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str()); - return false; - } + if (!file.good()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str()); + return false; + } - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str()); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str()); - applyPendingChanges(); + applyPendingChanges(); - // Opciones de video - file << "## VIDEO\n"; - file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n"; - file << "\n"; + // Opciones de video + file << "## VIDEO\n"; + file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n"; + file << "\n"; - file << "window.zoom=" << window.size << "\n"; - file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n"; - file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n"; - file << "video.v_sync=" << boolToString(video.v_sync) << "\n"; - file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n"; - file << "video.shaders=" << boolToString(video.shaders) << "\n"; + file << "window.zoom=" << window.size << "\n"; + file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n"; + file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n"; + file << "video.v_sync=" << boolToString(video.v_sync) << "\n"; + file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n"; + file << "video.shaders=" << boolToString(video.shaders) << "\n"; - // Opciones de audio - file << "\n\n## AUDIO\n"; - file << "## volume [0 .. 100]\n"; - file << "\n"; + // Opciones de audio + file << "\n\n## AUDIO\n"; + file << "## volume [0 .. 100]\n"; + file << "\n"; - file << "audio.enabled=" << boolToString(audio.enabled) << "\n"; - file << "audio.volume=" << audio.volume << "\n"; - file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n"; - file << "audio.music.volume=" << audio.music.volume << "\n"; - file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n"; - file << "audio.sound.volume=" << audio.sound.volume << "\n"; + file << "audio.enabled=" << boolToString(audio.enabled) << "\n"; + file << "audio.volume=" << audio.volume << "\n"; + file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n"; + file << "audio.music.volume=" << audio.music.volume << "\n"; + file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n"; + file << "audio.sound.volume=" << audio.sound.volume << "\n"; - // Opciones del juego - file << "\n\n## GAME\n"; - file << "## game.language [0: spanish, 1: valencian, 2: english]\n"; - file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n"; - file << "\n"; + // Opciones del juego + file << "\n\n## GAME\n"; + file << "## game.language [0: spanish, 1: valencian, 2: english]\n"; + file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n"; + file << "\n"; - file << "game.language=" << static_cast<int>(settings.language) << "\n"; - file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n"; - file << "game.autofire=" << boolToString(settings.autofire) << "\n"; - file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n"; + file << "game.language=" << static_cast<int>(settings.language) << "\n"; + file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n"; + file << "game.autofire=" << boolToString(settings.autofire) << "\n"; + file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n"; - // Opciones de mandos - file << "\n\n## CONTROLLERS\n"; + // Opciones de mandos + file << "\n\n## CONTROLLERS\n"; - int controller_index = 0; - for (const auto &controller : controllers) - { - file << "\n"; - file << "controller." << controller_index << ".name=" << controller.name << "\n"; - file << "controller." << controller_index << ".player=" << controller.player_id << "\n"; - file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n"; - file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n"; - file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n"; - file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n"; - file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n"; - file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n"; + int controller_index = 0; + for (const auto &controller : controllers) { + file << "\n"; + file << "controller." << controller_index << ".name=" << controller.name << "\n"; + file << "controller." << controller_index << ".player=" << controller.player_id << "\n"; + file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n"; + file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n"; + file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n"; + file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n"; + file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n"; + file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n"; - // Incrementa el índice - ++controller_index; - } + // Incrementa el índice + ++controller_index; + } - // Cierra el fichero - file.close(); + // Cierra el fichero + file.close(); - return true; - } + return true; +} - // Asigna variables a partir de dos cadenas - bool set(const std::string &var, const std::string &value) - { - // Indicador de éxito en la asignación - auto success = true; +// Asigna variables a partir de dos cadenas +bool set(const std::string &var, const std::string &value) { + // Indicador de éxito en la asignación + auto success = true; - // Opciones de video - if (var == "video.fullscreen") - { - video.fullscreen = stringToBool(value); - } - else if (var == "window.zoom") - { - window.size = std::stoi(value); - } - else if (var == "video.scale_mode") - { - video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value)); - } - else if (var == "video.shaders") - { - video.shaders = stringToBool(value); - } - else if (var == "video.integer_scale") - { - video.integer_scale = stringToBool(value); - } - else if (var == "video.v_sync") - { - video.v_sync = stringToBool(value); - } + // Opciones de video + if (var == "video.fullscreen") { + video.fullscreen = stringToBool(value); + } else if (var == "window.zoom") { + window.size = std::stoi(value); + } else if (var == "video.scale_mode") { + video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value)); + } else if (var == "video.shaders") { + video.shaders = stringToBool(value); + } else if (var == "video.integer_scale") { + video.integer_scale = stringToBool(value); + } else if (var == "video.v_sync") { + video.v_sync = stringToBool(value); + } - // Opciones de audio - else if (var == "audio.enabled") - { - audio.enabled = stringToBool(value); - } - else if (var == "audio.volume") - { - audio.volume = std::clamp(std::stoi(value), 0, 100); - } - else if (var == "audio.music.enabled") - { - audio.music.enabled = stringToBool(value); - } - else if (var == "audio.music.volume") - { - audio.music.volume = std::clamp(std::stoi(value), 0, 100); - } - else if (var == "audio.sound.enabled") - { - audio.sound.enabled = stringToBool(value); - } - else if (var == "audio.sound.volume") - { - audio.sound.volume = std::clamp(std::stoi(value), 0, 100); - } + // Opciones de audio + else if (var == "audio.enabled") { + audio.enabled = stringToBool(value); + } else if (var == "audio.volume") { + audio.volume = std::clamp(std::stoi(value), 0, 100); + } else if (var == "audio.music.enabled") { + audio.music.enabled = stringToBool(value); + } else if (var == "audio.music.volume") { + audio.music.volume = std::clamp(std::stoi(value), 0, 100); + } else if (var == "audio.sound.enabled") { + audio.sound.enabled = stringToBool(value); + } else if (var == "audio.sound.volume") { + audio.sound.volume = std::clamp(std::stoi(value), 0, 100); + } - // Opciones de juego - else if (var == "game.language") - { - settings.language = static_cast<Lang::Code>(std::stoi(value)); - pending_changes.new_language = settings.language; - } - else if (var == "game.difficulty") - { - settings.difficulty = static_cast<DifficultyCode>(std::stoi(value)); - pending_changes.new_difficulty = settings.difficulty; - } - else if (var == "game.autofire") - { - settings.autofire = stringToBool(value); - } - else if (var == "game.shutdown_enabled") - { - settings.shutdown_enabled = stringToBool(value); - } + // Opciones de juego + else if (var == "game.language") { + settings.language = static_cast<Lang::Code>(std::stoi(value)); + pending_changes.new_language = settings.language; + } else if (var == "game.difficulty") { + settings.difficulty = static_cast<DifficultyCode>(std::stoi(value)); + pending_changes.new_difficulty = settings.difficulty; + } else if (var == "game.autofire") { + settings.autofire = stringToBool(value); + } else if (var == "game.shutdown_enabled") { + settings.shutdown_enabled = stringToBool(value); + } - // Opciones de mandos - else if (var == "controller.0.name") - { - controllers.at(0).name = value; - } - else if (var == "controller.0.player") - { - controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2); - } - else if (var == "controller.0.type") - { - controllers.at(0).type = static_cast<InputDevice>(std::stoi(value)); - } - else if (var == "controller.0.button.fire_left") - { - controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.0.button.fire_center") - { - controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.0.button.fire_right") - { - controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.0.button.start") - { - controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.0.button.service") - { - controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.1.name") - { - controllers.at(1).name = value; - } - else if (var == "controller.1.player") - { - controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2); - } - else if (var == "controller.1.type") - { - controllers.at(1).type = static_cast<InputDevice>(std::stoi(value)); - } - else if (var == "controller.1.button.fire_left") - { - controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.1.button.fire_center") - { - controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.1.button.fire_right") - { - controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.1.button.start") - { - controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } - else if (var == "controller.1.button.service") - { - controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value)); - } + // Opciones de mandos + else if (var == "controller.0.name") { + controllers.at(0).name = value; + } else if (var == "controller.0.player") { + controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2); + } else if (var == "controller.0.type") { + controllers.at(0).type = static_cast<InputDevice>(std::stoi(value)); + } else if (var == "controller.0.button.fire_left") { + controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.0.button.fire_center") { + controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.0.button.fire_right") { + controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.0.button.start") { + controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.0.button.service") { + controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.1.name") { + controllers.at(1).name = value; + } else if (var == "controller.1.player") { + controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2); + } else if (var == "controller.1.type") { + controllers.at(1).type = static_cast<InputDevice>(std::stoi(value)); + } else if (var == "controller.1.button.fire_left") { + controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.1.button.fire_center") { + controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.1.button.fire_right") { + controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.1.button.start") { + controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } else if (var == "controller.1.button.service") { + controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value)); + } - // Lineas vacias o que empiezan por comentario - else if (var.empty() || var.starts_with("#")) - { - } - else - { - success = false; - } + // Lineas vacias o que empiezan por comentario + else if (var.empty() || var.starts_with("#")) { + } else { + success = false; + } - return success; - } + return success; +} - // Asigna el teclado al jugador - void setKeyboardToPlayer(int player_id) - { - for (auto &controller : controllers) - { - if (controller.player_id == player_id) - { - controller.type = InputDevice::ANY; - } - else - { - controller.type = InputDevice::CONTROLLER; - } - } - } +// Asigna el teclado al jugador +void setKeyboardToPlayer(int player_id) { + for (auto &controller : controllers) { + if (controller.player_id == player_id) { + controller.type = InputDevice::ANY; + } else { + controller.type = InputDevice::CONTROLLER; + } + } +} - // Intercambia el teclado de jugador - void swapKeyboard() - { - std::swap(controllers.at(0).type, controllers.at(1).type); - } +// Intercambia el teclado de jugador +void swapKeyboard() { + std::swap(controllers.at(0).type, controllers.at(1).type); +} - // Intercambia los jugadores asignados a los dos primeros mandos - void swapControllers() - { - std::swap(controllers.at(0).player_id, controllers.at(1).player_id); - std::swap(controllers.at(0).type, controllers.at(1).type); - } +// Intercambia los jugadores asignados a los dos primeros mandos +void swapControllers() { + std::swap(controllers.at(0).player_id, controllers.at(1).player_id); + std::swap(controllers.at(0).type, controllers.at(1).type); +} - // Averigua quien está usando el teclado - int getPlayerWhoUsesKeyboard() - { - for (const auto &controller : controllers) - { - if (controller.type == InputDevice::ANY) - { - return controller.player_id; - } - } - return 0; - } +// Averigua quien está usando el teclado +int getPlayerWhoUsesKeyboard() { + for (const auto &controller : controllers) { + if (controller.type == InputDevice::ANY) { + return controller.player_id; + } + } + return 0; +} - // Aplica los cambios pendientes copiando los valores a sus variables - void applyPendingChanges() - { - if (pending_changes.has_pending_changes) - { - settings.language = pending_changes.new_language; - settings.difficulty = pending_changes.new_difficulty; - pending_changes.has_pending_changes = false; - } - } +// Aplica los cambios pendientes copiando los valores a sus variables +void applyPendingChanges() { + if (pending_changes.has_pending_changes) { + settings.language = pending_changes.new_language; + settings.difficulty = pending_changes.new_difficulty; + pending_changes.has_pending_changes = false; + } +} - void checkPendingChanges() - { - if (settings.language != pending_changes.new_language || - settings.difficulty != pending_changes.new_difficulty) - { - pending_changes.has_pending_changes = true; - } - else - { - pending_changes.has_pending_changes = false; - } - } +void checkPendingChanges() { + if (settings.language != pending_changes.new_language || + settings.difficulty != pending_changes.new_difficulty) { + pending_changes.has_pending_changes = true; + } else { + pending_changes.has_pending_changes = false; + } +} - DifficultyCode getDifficultyCodeFromName(const std::string &name) - { - for (const auto &difficulty : difficulties) - { - if (difficulty.name == name) - return difficulty.code; - } - // Si no se encuentra, devuelve el primero por defecto - return difficulties[0].code; - } +DifficultyCode getDifficultyCodeFromName(const std::string &name) { + for (const auto &difficulty : difficulties) { + if (difficulty.name == name) + return difficulty.code; + } + // Si no se encuentra, devuelve el primero por defecto + return difficulties[0].code; +} - std::string getDifficultyNameFromCode(DifficultyCode code) - { - for (const auto &difficulty : difficulties) - { - if (difficulty.code == code) - return difficulty.name; - } - // Si no se encuentra, devuelve el nombre del primero por defecto - return difficulties[0].name; - } -} // namespace Options \ No newline at end of file +std::string getDifficultyNameFromCode(DifficultyCode code) { + for (const auto &difficulty : difficulties) { + if (difficulty.code == code) + return difficulty.name; + } + // Si no se encuentra, devuelve el nombre del primero por defecto + return difficulties[0].name; +} +} // namespace Options \ No newline at end of file diff --git a/source/options.h b/source/options.h index 6343e05..c6e3516 100644 --- a/source/options.h +++ b/source/options.h @@ -1,202 +1,191 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_ScaleMode -#include <string> // Para string, basic_string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_GamepadButton, SDL_ScaleMode -#include "input.h" // Para InputAction, InputDevice -#include "lang.h" // Para Code -#include "manage_hiscore_table.h" // Para HiScoreEntry +#include <string> // Para string, basic_string +#include <vector> // Para vector + +#include "input.h" // Para InputAction, InputDevice +#include "lang.h" // Para Code +#include "manage_hiscore_table.h" // Para HiScoreEntry static constexpr int INVALID_INDEX = -1; -namespace Options -{ - // --- Dificultad del juego --- - enum class DifficultyCode - { - EASY = 0, - NORMAL = 1, - HARD = 2, - }; +namespace Options { +// --- Dificultad del juego --- +enum class DifficultyCode { + EASY = 0, + NORMAL = 1, + HARD = 2, +}; - // --- Estructura que representa un nivel de dificultad - struct Difficulty - { - DifficultyCode code; // Código que identifica la dificultad - std::string name; // Nombre que identifica la dificultad +// --- Estructura que representa un nivel de dificultad +struct Difficulty { + DifficultyCode code; // Código que identifica la dificultad + std::string name; // Nombre que identifica la dificultad - Difficulty(DifficultyCode c, const std::string &n) - : code(c), name(n) {} - }; + Difficulty(DifficultyCode c, const std::string &n) + : code(c), name(n) {} +}; - // --- Opciones de ventana --- - struct WindowOptions - { - std::string caption; // Texto que aparece en la barra de título de la ventana - int size; // Valor por el que se multiplica el tamaño de la ventana - int max_size; // Tamaño máximo para que la ventana no sea mayor que la pantalla +// --- Opciones de ventana --- +struct WindowOptions { + std::string caption; // Texto que aparece en la barra de título de la ventana + int size; // Valor por el que se multiplica el tamaño de la ventana + int max_size; // Tamaño máximo para que la ventana no sea mayor que la pantalla - // Constructor por defecto con valores iniciales - WindowOptions() - : caption("Coffee Crisis Arcade Edition"), - size(2), - max_size(2) {} - }; + // Constructor por defecto con valores iniciales + WindowOptions() + : caption("Coffee Crisis Arcade Edition"), + size(2), + max_size(2) {} +}; - // --- Opciones de vídeo --- - struct VideoOptions - { - SDL_ScaleMode scale_mode; // Filtro usado para el escalado de la imagen - bool fullscreen; // Indica si se usa pantalla completa - bool v_sync; // Indica si se usa vsync - bool integer_scale; // Indica si se usa escalado entero - bool shaders; // Indica si se usan shaders para los filtros de vídeo - std::string info; // Información sobre el modo de vídeo +// --- Opciones de vídeo --- +struct VideoOptions { + SDL_ScaleMode scale_mode; // Filtro usado para el escalado de la imagen + bool fullscreen; // Indica si se usa pantalla completa + bool v_sync; // Indica si se usa vsync + bool integer_scale; // Indica si se usa escalado entero + bool shaders; // Indica si se usan shaders para los filtros de vídeo + std::string info; // Información sobre el modo de vídeo - // Constructor por defecto con valores iniciales - VideoOptions() - : scale_mode(SDL_ScaleMode::SDL_SCALEMODE_NEAREST), - fullscreen(false), - v_sync(true), - integer_scale(true), - shaders(false), - info() {} - }; + // Constructor por defecto con valores iniciales + VideoOptions() + : scale_mode(SDL_ScaleMode::SDL_SCALEMODE_NEAREST), + fullscreen(false), + v_sync(true), + integer_scale(true), + shaders(false), + info() {} +}; - // --- Opciones de música --- - struct MusicOptions - { - bool enabled; // Indica si la música suena o no - int volume; // Volumen de la música +// --- Opciones de música --- +struct MusicOptions { + bool enabled; // Indica si la música suena o no + int volume; // Volumen de la música - // Constructor por defecto - MusicOptions() - : enabled(true), - volume(100) {} - }; + // Constructor por defecto + MusicOptions() + : enabled(true), + volume(100) {} +}; - // --- Opciones de sonido --- - struct SoundOptions - { - bool enabled; // Indica si los sonidos suenan o no - int volume; // Volumen de los sonidos +// --- Opciones de sonido --- +struct SoundOptions { + bool enabled; // Indica si los sonidos suenan o no + int volume; // Volumen de los sonidos - // Constructor por defecto - SoundOptions() - : enabled(true), - volume(100) {} - }; + // Constructor por defecto + SoundOptions() + : enabled(true), + volume(100) {} +}; - // --- Opciones de audio --- - struct AudioOptions - { - MusicOptions music; // Opciones para la música - SoundOptions sound; // Opciones para los efectos de sonido - bool enabled; // Indica si el audio está activo o no - int volume; // Volumen general del audio +// --- Opciones de audio --- +struct AudioOptions { + MusicOptions music; // Opciones para la música + SoundOptions sound; // Opciones para los efectos de sonido + bool enabled; // Indica si el audio está activo o no + int volume; // Volumen general del audio - // Constructor por defecto - AudioOptions() - : music(), - sound(), - enabled(true), - volume(100) {} - }; + // Constructor por defecto + AudioOptions() + : music(), + sound(), + enabled(true), + volume(100) {} +}; - // --- Opciones de configuración --- - struct SettingsOptions - { - DifficultyCode difficulty; // Dificultad del juego - Lang::Code language; // Idioma usado en el juego - bool autofire; // Indicador de autofire - bool shutdown_enabled; // Especifica si se puede apagar el sistema - std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones - std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla - std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego +// --- Opciones de configuración --- +struct SettingsOptions { + DifficultyCode difficulty; // Dificultad del juego + Lang::Code language; // Idioma usado en el juego + bool autofire; // Indicador de autofire + bool shutdown_enabled; // Especifica si se puede apagar el sistema + std::vector<HiScoreEntry> hi_score_table; // Tabla de mejores puntuaciones + std::vector<int> last_hi_score_entry; // Últimas posiciones de entrada en la tabla + std::string config_file; // Ruta al fichero donde guardar la configuración y las opciones del juego - // Constructor por defecto con valores iniciales - SettingsOptions() - : difficulty(DifficultyCode::NORMAL), - language(Lang::Code::VALENCIAN), - autofire(true), - shutdown_enabled(false), - last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}), - config_file() {} + // Constructor por defecto con valores iniciales + SettingsOptions() + : difficulty(DifficultyCode::NORMAL), + language(Lang::Code::VALENCIAN), + autofire(true), + shutdown_enabled(false), + last_hi_score_entry({INVALID_INDEX, INVALID_INDEX}), + config_file() {} - // Reinicia las últimas entradas de puntuación - void clearLastHiScoreEntries() - { - last_hi_score_entry[0] = INVALID_INDEX; - last_hi_score_entry[1] = INVALID_INDEX; - } - }; + // Reinicia las últimas entradas de puntuación + void clearLastHiScoreEntries() { + last_hi_score_entry[0] = INVALID_INDEX; + last_hi_score_entry[1] = INVALID_INDEX; + } +}; - // --- Opciones de mando --- - struct GamepadOptions - { - int index; // Índice en el vector de mandos - int player_id; // Jugador asociado al mando - InputDevice type; // Indica si se usará teclado, mando o ambos - std::string name; // Nombre del dispositivo - bool plugged; // Indica si el mando está conectado - std::vector<InputAction> inputs; // Listado de acciones asignadas - std::vector<SDL_GamepadButton> buttons; // Listado de botones asignados a cada acción +// --- Opciones de mando --- +struct GamepadOptions { + int index; // Índice en el vector de mandos + int player_id; // Jugador asociado al mando + InputDevice type; // Indica si se usará teclado, mando o ambos + std::string name; // Nombre del dispositivo + bool plugged; // Indica si el mando está conectado + std::vector<InputAction> inputs; // Listado de acciones asignadas + std::vector<SDL_GamepadButton> buttons; // Listado de botones asignados a cada acción - // Constructor por defecto - GamepadOptions() - : index(INVALID_INDEX), - player_id(INVALID_INDEX), - type(InputDevice::CONTROLLER), - name(), - plugged(false), - inputs{ - InputAction::FIRE_LEFT, - InputAction::FIRE_CENTER, - InputAction::FIRE_RIGHT, - InputAction::START, - InputAction::SERVICE}, - buttons{ - SDL_GAMEPAD_BUTTON_WEST, - SDL_GAMEPAD_BUTTON_NORTH, - SDL_GAMEPAD_BUTTON_EAST, - SDL_GAMEPAD_BUTTON_START, - SDL_GAMEPAD_BUTTON_BACK} {} - }; + // Constructor por defecto + GamepadOptions() + : index(INVALID_INDEX), + player_id(INVALID_INDEX), + type(InputDevice::CONTROLLER), + name(), + plugged(false), + inputs{ + InputAction::FIRE_LEFT, + InputAction::FIRE_CENTER, + InputAction::FIRE_RIGHT, + InputAction::START, + InputAction::SERVICE}, + buttons{ + SDL_GAMEPAD_BUTTON_WEST, + SDL_GAMEPAD_BUTTON_NORTH, + SDL_GAMEPAD_BUTTON_EAST, + SDL_GAMEPAD_BUTTON_START, + SDL_GAMEPAD_BUTTON_BACK} {} +}; - // --- Opciones pendientes de aplicar --- - struct PendingChanges - { - Lang::Code new_language; // Idioma en espera de aplicar - DifficultyCode new_difficulty; // Dificultad en espera de aplicar - bool has_pending_changes; // Indica si hay cambios pendientes +// --- Opciones pendientes de aplicar --- +struct PendingChanges { + Lang::Code new_language; // Idioma en espera de aplicar + DifficultyCode new_difficulty; // Dificultad en espera de aplicar + bool has_pending_changes; // Indica si hay cambios pendientes - // Constructor por defecto con valores iniciales - PendingChanges() - : new_language(Lang::Code::VALENCIAN), - new_difficulty(DifficultyCode::NORMAL), - has_pending_changes(false) {} - }; + // Constructor por defecto con valores iniciales + PendingChanges() + : new_language(Lang::Code::VALENCIAN), + new_difficulty(DifficultyCode::NORMAL), + has_pending_changes(false) {} +}; - // --- Variables globales --- - extern WindowOptions window; // Opciones de la ventana - extern SettingsOptions settings; // Opciones del juego - extern VideoOptions video; // Opciones de vídeo - extern AudioOptions audio; // Opciones de audio - extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador - extern PendingChanges pending_changes; // Opciones que se aplican al cerrar - extern std::vector<Difficulty> difficulties; // Lista de los diferentes tipos de dificultad +// --- Variables globales --- +extern WindowOptions window; // Opciones de la ventana +extern SettingsOptions settings; // Opciones del juego +extern VideoOptions video; // Opciones de vídeo +extern AudioOptions audio; // Opciones de audio +extern std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador +extern PendingChanges pending_changes; // Opciones que se aplican al cerrar +extern std::vector<Difficulty> difficulties; // Lista de los diferentes tipos de dificultad - // --- Funciones de configuración --- - void init(); // Inicializa las opciones del programa - bool loadFromFile(); // Carga el fichero de configuración - bool saveToFile(); // Guarda el fichero de configuración - void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador - void swapKeyboard(); // Intercambia el teclado de jugador - void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos - int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado - void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables - void checkPendingChanges(); // Verifica si hay cambios pendientes - DifficultyCode getDifficultyCodeFromName(const std::string &name); // Obtiene el código de dificultad a partir del nombre - std::string getDifficultyNameFromCode(DifficultyCode code); // Obtiene el nombre de la dificultad a partir del código -} // namespace Options \ No newline at end of file +// --- Funciones de configuración --- +void init(); // Inicializa las opciones del programa +bool loadFromFile(); // Carga el fichero de configuración +bool saveToFile(); // Guarda el fichero de configuración +void setKeyboardToPlayer(int player_id); // Asigna el teclado al jugador +void swapKeyboard(); // Intercambia el teclado de jugador +void swapControllers(); // Intercambia los jugadores asignados a los dos primeros mandos +int getPlayerWhoUsesKeyboard(); // Averigua quién está usando el teclado +void applyPendingChanges(); // Aplica los cambios pendientes copiando los valores a sus variables +void checkPendingChanges(); // Verifica si hay cambios pendientes +DifficultyCode getDifficultyCodeFromName(const std::string &name); // Obtiene el código de dificultad a partir del nombre +std::string getDifficultyNameFromCode(DifficultyCode code); // Obtiene el nombre de la dificultad a partir del código +} // namespace Options \ No newline at end of file diff --git a/source/param.cpp b/source/param.cpp index 04ce22b..40a6e86 100644 --- a/source/param.cpp +++ b/source/param.cpp @@ -1,12 +1,13 @@ #include "param.h" -#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo -#include <fstream> // Para basic_istream, basic_ifstream, ifstream -#include <sstream> // Para basic_istringstream -#include <stdexcept> // Para runtime_error -#include <string> // Para operator==, stoi, char_traits, string, ope... +#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogError, SDL_LogInfo -#include "utils.h" // Para Zone, Color, NotifyPosition, getFileName +#include <fstream> // Para basic_istream, basic_ifstream, ifstream +#include <sstream> // Para basic_istringstream +#include <stdexcept> // Para runtime_error +#include <string> // Para operator==, stoi, char_traits, string, ope... + +#include "utils.h" // Para Zone, Color, NotifyPosition, getFileName Param param; @@ -17,500 +18,420 @@ void precalculateZones(); bool setParams(const std::string &var, const std::string &value); // Establece valores por defecto a las variables -void initParam() -{ - // GAME - param.game.width = 320; - param.game.height = 256; - param.game.item_size = 20; - param.game.game_area.rect = {0, 0, param.game.width, param.game.height}; - param.game.play_area.rect = {0, 0, param.game.width, 216}; - param.game.name_entry_idle_time = 10; - param.game.name_entry_total_time = 60; - param.game.speed = 15; - param.game.hit_stop = true; - param.game.hit_stop_ms = 300; - precalculateZones(); +void initParam() { + // GAME + param.game.width = 320; + param.game.height = 256; + param.game.item_size = 20; + param.game.game_area.rect = {0, 0, param.game.width, param.game.height}; + param.game.play_area.rect = {0, 0, param.game.width, 216}; + param.game.name_entry_idle_time = 10; + param.game.name_entry_total_time = 60; + param.game.speed = 15; + param.game.hit_stop = true; + param.game.hit_stop_ms = 300; + precalculateZones(); - // SCOREBOARD - param.scoreboard.rect = {0, 216, param.game.width, 40}; - param.scoreboard.separator_autocolor = false; - param.scoreboard.separator_color = Color(); - param.scoreboard.easy_color = Color(); - param.scoreboard.normal_color = Color(); - param.scoreboard.hard_color = Color(); - param.scoreboard.text_autocolor = false; - param.scoreboard.text_color1 = Color(); - param.scoreboard.text_color2 = Color(); - param.scoreboard.skip_countdown_value = 8; + // SCOREBOARD + param.scoreboard.rect = {0, 216, param.game.width, 40}; + param.scoreboard.separator_autocolor = false; + param.scoreboard.separator_color = Color(); + param.scoreboard.easy_color = Color(); + param.scoreboard.normal_color = Color(); + param.scoreboard.hard_color = Color(); + param.scoreboard.text_autocolor = false; + param.scoreboard.text_color1 = Color(); + param.scoreboard.text_color2 = Color(); + param.scoreboard.skip_countdown_value = 8; - // FADE - param.fade.num_squares_width = param.game.width / 2; - param.fade.num_squares_height = param.game.height / 2; - param.fade.random_squares_delay = 1; - param.fade.random_squares_mult = 500; - param.fade.post_duration = 80; - param.fade.venetian_size = 16; + // FADE + param.fade.num_squares_width = param.game.width / 2; + param.fade.num_squares_height = param.game.height / 2; + param.fade.random_squares_delay = 1; + param.fade.random_squares_mult = 500; + param.fade.post_duration = 80; + param.fade.venetian_size = 16; - // TITLE - param.title.press_start_position = 160; - param.title.title_duration = 800; - param.title.arcade_edition_position = 123; - param.title.title_c_c_position = 11; - param.title.bg_color = Color(255, 255, 255); + // TITLE + param.title.press_start_position = 160; + param.title.title_duration = 800; + param.title.arcade_edition_position = 123; + param.title.title_c_c_position = 11; + param.title.bg_color = Color(255, 255, 255); - // BACKGROUND - param.background.attenuate_color = Color(255, 255, 255, 0); + // BACKGROUND + param.background.attenuate_color = Color(255, 255, 255, 0); - // BALLOONS - param.balloon.settings.at(0) = ParamBalloon::Settings(0.09f, 2.60f); - param.balloon.settings.at(1) = ParamBalloon::Settings(0.10f, 3.50f); - param.balloon.settings.at(2) = ParamBalloon::Settings(0.10f, 4.50f); - param.balloon.settings.at(3) = ParamBalloon::Settings(0.10f, 4.95f); + // BALLOONS + param.balloon.settings.at(0) = ParamBalloon::Settings(0.09f, 2.60f); + param.balloon.settings.at(1) = ParamBalloon::Settings(0.10f, 3.50f); + param.balloon.settings.at(2) = ParamBalloon::Settings(0.10f, 4.50f); + param.balloon.settings.at(3) = ParamBalloon::Settings(0.10f, 4.95f); - param.balloon.color.at(0) = "blue"; - param.balloon.color.at(1) = "orange"; - param.balloon.color.at(2) = "red"; - param.balloon.color.at(3) = "green"; + param.balloon.color.at(0) = "blue"; + param.balloon.color.at(1) = "orange"; + param.balloon.color.at(2) = "red"; + param.balloon.color.at(3) = "green"; - param.balloon.bouncing_sound = false; + param.balloon.bouncing_sound = false; - // NOTIFICATION - param.notification.pos_v = NotifyPosition::TOP; - param.notification.pos_h = NotifyPosition::LEFT; - param.notification.sound = false; - param.notification.color = Color(48, 48, 48); + // NOTIFICATION + param.notification.pos_v = NotifyPosition::TOP; + param.notification.pos_h = NotifyPosition::LEFT; + param.notification.sound = false; + param.notification.color = Color(48, 48, 48); - // INTRO - param.intro.bg_color = Color::fromHex("543149"); - param.intro.card_color = Color::fromHex("CBDBFC"); - param.intro.shadow_color = Color::fromHex("00000080"); - param.intro.text_distance_from_bottom = 48; + // INTRO + param.intro.bg_color = Color::fromHex("543149"); + param.intro.card_color = Color::fromHex("CBDBFC"); + param.intro.shadow_color = Color::fromHex("00000080"); + param.intro.text_distance_from_bottom = 48; } // Carga los parámetros desde un archivo -void loadParamsFromFile(const std::string &file_path) -{ - // Inicializa los parámetros con valores por defecto - initParam(); +void loadParamsFromFile(const std::string &file_path) { + // Inicializa los parámetros con valores por defecto + initParam(); - // Abre el archivo - std::ifstream file(file_path); - if (!file.is_open()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str()); - throw std::runtime_error("No se pudo abrir el archivo: " + file_path); - } + // Abre el archivo + std::ifstream file(file_path); + if (!file.is_open()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo abrir el archivo %s", file_path.c_str()); + throw std::runtime_error("No se pudo abrir el archivo: " + file_path); + } - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str()); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(file_path).c_str()); - std::string line, param1, param2; - while (std::getline(file, line)) - { - // Elimina comentarios - auto comment_pos = line.find('#'); - if (comment_pos != std::string::npos) - { - line.resize(comment_pos); - } + std::string line, param1, param2; + while (std::getline(file, line)) { + // Elimina comentarios + auto comment_pos = line.find('#'); + if (comment_pos != std::string::npos) { + line.resize(comment_pos); + } - // Usa un stream para separar palabras - std::istringstream iss(line); - if (iss >> param1 >> param2) - { - if (!setParams(param1, param2)) - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Parámetro desconocido: %s", param1.c_str()); - } - } - } + // Usa un stream para separar palabras + std::istringstream iss(line); + if (iss >> param1 >> param2) { + if (!setParams(param1, param2)) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Parámetro desconocido: %s", param1.c_str()); + } + } + } - // Cierra el archivo - file.close(); + // Cierra el archivo + file.close(); - // Realiza cálculos adicionales después de cargar los parámetros - precalculateZones(); + // Realiza cálculos adicionales después de cargar los parámetros + precalculateZones(); } // Asigna variables a partir de dos cadenas -bool setParams(const std::string &var, const std::string &value) -{ - // Indicador de éxito en la asignación - auto success = true; - - // GAME - if (var == "game.width") - { - param.game.width = std::stoi(value); - } - - else if (var == "game.height") - { - param.game.height = std::stoi(value); - } - - else if (var == "game.item_size") - { - param.game.item_size = std::stoi(value); - } - - else if (var == "game.play_area.rect.x") - { - param.game.play_area.rect.x = std::stoi(value); - } - - else if (var == "game.play_area.rect.y") - { - param.game.play_area.rect.y = std::stoi(value); - } - - else if (var == "game.play_area.rect.w") - { - param.game.play_area.rect.w = std::stoi(value); - } - - else if (var == "game.play_area.rect.h") - { - param.game.play_area.rect.h = std::stoi(value); - } - - else if (var == "game.name_entry_idle_time") - { - param.game.name_entry_idle_time = std::stoi(value); - } - - else if (var == "game.name_entry_total_time") - { - param.game.name_entry_total_time = std::stoi(value); - } - - else if (var == "game.hit_stop") - { - param.game.hit_stop = stringToBool(value); - } - - else if (var == "game.hit_stop_ms") - { - param.game.hit_stop_ms = std::stoi(value); - } - - // FADE - else if (var == "fade.color") - { - param.fade.color = Color::fromHex(value); - } - - else if (var == "fade.num_squares_width") - { - param.fade.num_squares_width = std::stoi(value); - } - - else if (var == "fade.num_squares_height") - { - param.fade.num_squares_height = std::stoi(value); - } - - else if (var == "fade.random_squares_delay") - { - param.fade.random_squares_delay = std::stoi(value); - } - - else if (var == "fade.random_squares_mult") - { - param.fade.random_squares_mult = std::stoi(value); - } - - else if (var == "fade.post_duration") - { - param.fade.post_duration = std::stoi(value); - } - - else if (var == "fade.venetian_size") - { - param.fade.venetian_size = std::stoi(value); - } - - // SCOREBOARD - else if (var == "scoreboard.rect.x") - { - param.scoreboard.rect.x = std::stoi(value); - } - - else if (var == "scoreboard.rect.y") - { - param.scoreboard.rect.y = std::stoi(value); - } - - else if (var == "scoreboard.rect.w") - { - param.scoreboard.rect.w = std::stoi(value); - } - - else if (var == "scoreboard.rect.h") - { - param.scoreboard.rect.h = std::stoi(value); - } - - else if (var == "scoreboard.separator_autocolor") - { - param.scoreboard.separator_autocolor = stringToBool(value); - } - - else if (var == "scoreboard.separator_color") - { - param.scoreboard.separator_color = Color::fromHex(value); - } - - else if (var == "scoreboard.easy_color") - { - param.scoreboard.easy_color = Color::fromHex(value); - } - - else if (var == "scoreboard.normal_color") - { - param.scoreboard.normal_color = Color::fromHex(value); - } - - else if (var == "scoreboard.hard_color") - { - param.scoreboard.hard_color = Color::fromHex(value); - } - - else if (var == "scoreboard.text_autocolor") - { - param.scoreboard.text_autocolor = stringToBool(value); - } - - else if (var == "scoreboard.text_color1") - { - param.scoreboard.text_color1 = Color::fromHex(value); - } - - else if (var == "scoreboard.text_color2") - { - param.scoreboard.text_color2 = Color::fromHex(value); - } - - else if (var == "scoreboard.skip_countdown_value") - { - param.scoreboard.skip_countdown_value = std::stoi(value); - } - - // TITLE - else if (var == "title.press_start_position") - { - param.title.press_start_position = std::stoi(value); - } - - else if (var == "title.title_duration") - { - param.title.title_duration = std::stoi(value); - } - - else if (var == "title.arcade_edition_position") - { - param.title.arcade_edition_position = std::stoi(value); - } - - else if (var == "title.title_c_c_position") - { - param.title.title_c_c_position = std::stoi(value); - } - - else if (var == "title.bg_color") - { - param.title.bg_color = Color::fromHex(value); - } - - // BACKGROUND - else if (var == "background.attenuate_color") - { - param.background.attenuate_color = Color::fromHex(value); - } - - // BALLOON - else if (var == "balloon.settings[0].vel") - { - param.balloon.settings.at(0).vel = std::stof(value); - } - - else if (var == "balloon.settings[0].grav") - { - param.balloon.settings.at(0).grav = std::stof(value); - } - - else if (var == "balloon.settings[1].vel") - { - param.balloon.settings.at(1).vel = std::stof(value); - } - - else if (var == "balloon.settings[1].grav") - { - param.balloon.settings.at(1).grav = std::stof(value); - } - - else if (var == "balloon.settings[2].vel") - { - param.balloon.settings.at(2).vel = std::stof(value); - } - - else if (var == "balloon.settings[2].grav") - { - param.balloon.settings.at(2).grav = std::stof(value); - } - - else if (var == "balloon.settings[3].vel") - { - param.balloon.settings.at(3).vel = std::stof(value); - } - - else if (var == "balloon.settings[3].grav") - { - param.balloon.settings.at(3).grav = std::stof(value); - } - - else if (var == "balloon.color[0]") - { - param.balloon.color.at(0) = value; - } - - else if (var == "balloon.color[1]") - { - param.balloon.color.at(1) = value; - } - - else if (var == "balloon.color[2]") - { - param.balloon.color.at(2) = value; - } - - else if (var == "balloon.color[3]") - { - param.balloon.color.at(3) = value; - } - - else if (var == "balloon.bouncing_sound") - { - param.balloon.bouncing_sound = stringToBool(value); - } - - // NOTIFICACIONES - else if (var == "notification.pos_h") - { - if (value == "LEFT") - { - param.notification.pos_h = NotifyPosition::LEFT; - } - else if (value == "MIDDLE") - { - param.notification.pos_h = NotifyPosition::MIDDLE; - } - else - { - param.notification.pos_h = NotifyPosition::RIGHT; - } - } - - else if (var == "notification.pos_v") - { - param.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM; - } - - else if (var == "notification.sound") - { - param.notification.sound = stringToBool(value); - } - - else if (var == "notification.color") - { - param.notification.color = Color::fromHex(value); - } - - // SERVICE MENU - else if (var == "service_menu.title_color") - { - param.service_menu.title_color = Color::fromHex(value); - } - - else if (var == "service_menu.text_color") - { - param.service_menu.text_color = Color::fromHex(value); - } - - else if (var == "service_menu.selected_color") - { - param.service_menu.selected_color = Color::fromHex(value); - } - - else if (var == "service_menu.bg_color") - { - param.service_menu.bg_color = Color::fromHex(value); - } - - else if (var == "service_menu.drop_shadow") - { - param.service_menu.drop_shadow = stringToBool(value); - } - - // INTRO - else if (var == "intro.bg_color") - { - param.intro.bg_color = Color::fromHex(value); - } - - else if (var == "intro.card_color") - { - param.intro.card_color = Color::fromHex(value); - } - - else if (var == "intro.shadow_color") - { - param.intro.shadow_color = Color::fromHex(value); - } - - else if (var == "intro.text_distance_from_bottom") - { - param.intro.text_distance_from_bottom = std::stoi(value); - } - - // DEBUG - else if (var == "debug.color") - { - param.debug.color = Color::fromHex(value); - } - - // RESOURCE - else if (var == "resource.color") - { - param.resource.color = Color::fromHex(value); - } - - // RESTO - else - { - success = false; - } - - return success; +bool setParams(const std::string &var, const std::string &value) { + // Indicador de éxito en la asignación + auto success = true; + + // GAME + if (var == "game.width") { + param.game.width = std::stoi(value); + } + + else if (var == "game.height") { + param.game.height = std::stoi(value); + } + + else if (var == "game.item_size") { + param.game.item_size = std::stoi(value); + } + + else if (var == "game.play_area.rect.x") { + param.game.play_area.rect.x = std::stoi(value); + } + + else if (var == "game.play_area.rect.y") { + param.game.play_area.rect.y = std::stoi(value); + } + + else if (var == "game.play_area.rect.w") { + param.game.play_area.rect.w = std::stoi(value); + } + + else if (var == "game.play_area.rect.h") { + param.game.play_area.rect.h = std::stoi(value); + } + + else if (var == "game.name_entry_idle_time") { + param.game.name_entry_idle_time = std::stoi(value); + } + + else if (var == "game.name_entry_total_time") { + param.game.name_entry_total_time = std::stoi(value); + } + + else if (var == "game.hit_stop") { + param.game.hit_stop = stringToBool(value); + } + + else if (var == "game.hit_stop_ms") { + param.game.hit_stop_ms = std::stoi(value); + } + + // FADE + else if (var == "fade.color") { + param.fade.color = Color::fromHex(value); + } + + else if (var == "fade.num_squares_width") { + param.fade.num_squares_width = std::stoi(value); + } + + else if (var == "fade.num_squares_height") { + param.fade.num_squares_height = std::stoi(value); + } + + else if (var == "fade.random_squares_delay") { + param.fade.random_squares_delay = std::stoi(value); + } + + else if (var == "fade.random_squares_mult") { + param.fade.random_squares_mult = std::stoi(value); + } + + else if (var == "fade.post_duration") { + param.fade.post_duration = std::stoi(value); + } + + else if (var == "fade.venetian_size") { + param.fade.venetian_size = std::stoi(value); + } + + // SCOREBOARD + else if (var == "scoreboard.rect.x") { + param.scoreboard.rect.x = std::stoi(value); + } + + else if (var == "scoreboard.rect.y") { + param.scoreboard.rect.y = std::stoi(value); + } + + else if (var == "scoreboard.rect.w") { + param.scoreboard.rect.w = std::stoi(value); + } + + else if (var == "scoreboard.rect.h") { + param.scoreboard.rect.h = std::stoi(value); + } + + else if (var == "scoreboard.separator_autocolor") { + param.scoreboard.separator_autocolor = stringToBool(value); + } + + else if (var == "scoreboard.separator_color") { + param.scoreboard.separator_color = Color::fromHex(value); + } + + else if (var == "scoreboard.easy_color") { + param.scoreboard.easy_color = Color::fromHex(value); + } + + else if (var == "scoreboard.normal_color") { + param.scoreboard.normal_color = Color::fromHex(value); + } + + else if (var == "scoreboard.hard_color") { + param.scoreboard.hard_color = Color::fromHex(value); + } + + else if (var == "scoreboard.text_autocolor") { + param.scoreboard.text_autocolor = stringToBool(value); + } + + else if (var == "scoreboard.text_color1") { + param.scoreboard.text_color1 = Color::fromHex(value); + } + + else if (var == "scoreboard.text_color2") { + param.scoreboard.text_color2 = Color::fromHex(value); + } + + else if (var == "scoreboard.skip_countdown_value") { + param.scoreboard.skip_countdown_value = std::stoi(value); + } + + // TITLE + else if (var == "title.press_start_position") { + param.title.press_start_position = std::stoi(value); + } + + else if (var == "title.title_duration") { + param.title.title_duration = std::stoi(value); + } + + else if (var == "title.arcade_edition_position") { + param.title.arcade_edition_position = std::stoi(value); + } + + else if (var == "title.title_c_c_position") { + param.title.title_c_c_position = std::stoi(value); + } + + else if (var == "title.bg_color") { + param.title.bg_color = Color::fromHex(value); + } + + // BACKGROUND + else if (var == "background.attenuate_color") { + param.background.attenuate_color = Color::fromHex(value); + } + + // BALLOON + else if (var == "balloon.settings[0].vel") { + param.balloon.settings.at(0).vel = std::stof(value); + } + + else if (var == "balloon.settings[0].grav") { + param.balloon.settings.at(0).grav = std::stof(value); + } + + else if (var == "balloon.settings[1].vel") { + param.balloon.settings.at(1).vel = std::stof(value); + } + + else if (var == "balloon.settings[1].grav") { + param.balloon.settings.at(1).grav = std::stof(value); + } + + else if (var == "balloon.settings[2].vel") { + param.balloon.settings.at(2).vel = std::stof(value); + } + + else if (var == "balloon.settings[2].grav") { + param.balloon.settings.at(2).grav = std::stof(value); + } + + else if (var == "balloon.settings[3].vel") { + param.balloon.settings.at(3).vel = std::stof(value); + } + + else if (var == "balloon.settings[3].grav") { + param.balloon.settings.at(3).grav = std::stof(value); + } + + else if (var == "balloon.color[0]") { + param.balloon.color.at(0) = value; + } + + else if (var == "balloon.color[1]") { + param.balloon.color.at(1) = value; + } + + else if (var == "balloon.color[2]") { + param.balloon.color.at(2) = value; + } + + else if (var == "balloon.color[3]") { + param.balloon.color.at(3) = value; + } + + else if (var == "balloon.bouncing_sound") { + param.balloon.bouncing_sound = stringToBool(value); + } + + // NOTIFICACIONES + else if (var == "notification.pos_h") { + if (value == "LEFT") { + param.notification.pos_h = NotifyPosition::LEFT; + } else if (value == "MIDDLE") { + param.notification.pos_h = NotifyPosition::MIDDLE; + } else { + param.notification.pos_h = NotifyPosition::RIGHT; + } + } + + else if (var == "notification.pos_v") { + param.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM; + } + + else if (var == "notification.sound") { + param.notification.sound = stringToBool(value); + } + + else if (var == "notification.color") { + param.notification.color = Color::fromHex(value); + } + + // SERVICE MENU + else if (var == "service_menu.title_color") { + param.service_menu.title_color = Color::fromHex(value); + } + + else if (var == "service_menu.text_color") { + param.service_menu.text_color = Color::fromHex(value); + } + + else if (var == "service_menu.selected_color") { + param.service_menu.selected_color = Color::fromHex(value); + } + + else if (var == "service_menu.bg_color") { + param.service_menu.bg_color = Color::fromHex(value); + } + + else if (var == "service_menu.drop_shadow") { + param.service_menu.drop_shadow = stringToBool(value); + } + + // INTRO + else if (var == "intro.bg_color") { + param.intro.bg_color = Color::fromHex(value); + } + + else if (var == "intro.card_color") { + param.intro.card_color = Color::fromHex(value); + } + + else if (var == "intro.shadow_color") { + param.intro.shadow_color = Color::fromHex(value); + } + + else if (var == "intro.text_distance_from_bottom") { + param.intro.text_distance_from_bottom = std::stoi(value); + } + + // DEBUG + else if (var == "debug.color") { + param.debug.color = Color::fromHex(value); + } + + // RESOURCE + else if (var == "resource.color") { + param.resource.color = Color::fromHex(value); + } + + // RESTO + else { + success = false; + } + + return success; } // Calcula variables a partir de otras variables -void precalculateZones() -{ - // playArea - param.game.play_area.center_x = param.game.play_area.rect.w / 2; - param.game.play_area.first_quarter_x = param.game.play_area.rect.w / 4; - param.game.play_area.third_quarter_x = param.game.play_area.rect.w / 4 * 3; - param.game.play_area.center_y = param.game.play_area.rect.h / 2; - param.game.play_area.first_quarter_y = param.game.play_area.rect.h / 4; - param.game.play_area.third_quarter_y = param.game.play_area.rect.h / 4 * 3; +void precalculateZones() { + // playArea + param.game.play_area.center_x = param.game.play_area.rect.w / 2; + param.game.play_area.first_quarter_x = param.game.play_area.rect.w / 4; + param.game.play_area.third_quarter_x = param.game.play_area.rect.w / 4 * 3; + param.game.play_area.center_y = param.game.play_area.rect.h / 2; + param.game.play_area.first_quarter_y = param.game.play_area.rect.h / 4; + param.game.play_area.third_quarter_y = param.game.play_area.rect.h / 4 * 3; - // gameArea - param.game.game_area.rect = {0, 0, param.game.width, param.game.height}; - param.game.game_area.center_x = param.game.game_area.rect.w / 2; - param.game.game_area.first_quarter_x = param.game.game_area.rect.w / 4; - param.game.game_area.third_quarter_x = param.game.game_area.rect.w / 4 * 3; - param.game.game_area.center_y = param.game.game_area.rect.h / 2; - param.game.game_area.first_quarter_y = param.game.game_area.rect.h / 4; - param.game.game_area.third_quarter_y = param.game.game_area.rect.h / 4 * 3; + // gameArea + param.game.game_area.rect = {0, 0, param.game.width, param.game.height}; + param.game.game_area.center_x = param.game.game_area.rect.w / 2; + param.game.game_area.first_quarter_x = param.game.game_area.rect.w / 4; + param.game.game_area.third_quarter_x = param.game.game_area.rect.w / 4 * 3; + param.game.game_area.center_y = param.game.game_area.rect.h / 2; + param.game.game_area.first_quarter_y = param.game.game_area.rect.h / 4; + param.game.game_area.third_quarter_y = param.game.game_area.rect.h / 4 * 3; } \ No newline at end of file diff --git a/source/param.h b/source/param.h index 00c505b..8c40a44 100644 --- a/source/param.h +++ b/source/param.h @@ -1,61 +1,56 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32, SDL_FRect -#include <array> // Para array -#include <string> // Para basic_string, string +#include <SDL3/SDL.h> // Para Uint32, SDL_FRect -#include "utils.h" // Para Color, NotifyPosition (ptr only), Zone +#include <array> // Para array +#include <string> // Para basic_string, string + +#include "utils.h" // Para Color, NotifyPosition (ptr only), Zone // --- Parámetros del juego --- -struct ParamGame -{ - float width; // Ancho de la resolución nativa del juego - float height; // Alto de la resolución nativa del juego - float item_size; // Tamaño de los ítems del juego - Zone play_area; // Rectángulo con la posición de la zona de juego - Zone game_area; // Rectángulo con las dimensiones del juego - int name_entry_idle_time; // Segundos para introducir el nombre al finalizar la partida si no se pulsa nada - int name_entry_total_time; // Segundos totales para introducir el nombre al finalizar la partida - Uint32 speed; // Velocidad a la que transcurre el juego - bool hit_stop; // Indica si debe haber un paro cuando el jugador es golpeado por un globo - Uint32 hit_stop_ms; // Cantidad de milisegundos que dura el hit_stop +struct ParamGame { + float width; // Ancho de la resolución nativa del juego + float height; // Alto de la resolución nativa del juego + float item_size; // Tamaño de los ítems del juego + Zone play_area; // Rectángulo con la posición de la zona de juego + Zone game_area; // Rectángulo con las dimensiones del juego + int name_entry_idle_time; // Segundos para introducir el nombre al finalizar la partida si no se pulsa nada + int name_entry_total_time; // Segundos totales para introducir el nombre al finalizar la partida + Uint32 speed; // Velocidad a la que transcurre el juego + bool hit_stop; // Indica si debe haber un paro cuando el jugador es golpeado por un globo + Uint32 hit_stop_ms; // Cantidad de milisegundos que dura el hit_stop }; // --- Parámetros del fade --- -struct ParamFade -{ - Color color; // Color del fade - float num_squares_width; // Cantidad total de cuadraditos en horizontal para el FadeType::RANDOM_SQUARE - float num_squares_height; // Cantidad total de cuadraditos en vertical para el FadeType::RANDOM_SQUARE - int random_squares_delay; // Duración entre cada pintado de cuadrados - int random_squares_mult; // Cantidad de cuadrados que se pintarán cada vez - int post_duration; // Duración final del fade - float venetian_size; // Altura de los rectángulos para FadeType::VENETIAN +struct ParamFade { + Color color; // Color del fade + float num_squares_width; // Cantidad total de cuadraditos en horizontal para el FadeType::RANDOM_SQUARE + float num_squares_height; // Cantidad total de cuadraditos en vertical para el FadeType::RANDOM_SQUARE + int random_squares_delay; // Duración entre cada pintado de cuadrados + int random_squares_mult; // Cantidad de cuadrados que se pintarán cada vez + int post_duration; // Duración final del fade + float venetian_size; // Altura de los rectángulos para FadeType::VENETIAN }; // --- Parámetros de la pantalla de título --- -struct ParamTitle -{ - int press_start_position; // Posición del texto para empezar a jugar - int title_duration; // Tiempo de inactividad del título - int arcade_edition_position; // Posición del bitmap "Arcade Edition" - int title_c_c_position; // Posición del bitmap "Coffee Crisis" - Color bg_color; // Color para los tiles de fondo +struct ParamTitle { + int press_start_position; // Posición del texto para empezar a jugar + int title_duration; // Tiempo de inactividad del título + int arcade_edition_position; // Posición del bitmap "Arcade Edition" + int title_c_c_position; // Posición del bitmap "Coffee Crisis" + Color bg_color; // Color para los tiles de fondo }; // --- Parámetros del fondo --- -struct ParamBackground -{ - Color attenuate_color; // Color para atenuar el fondo +struct ParamBackground { + Color attenuate_color; // Color para atenuar el fondo }; // --- Parámetros de los globos --- -struct ParamBalloon -{ - struct Settings - { - float grav; // Aceleración en el eje Y. Modifica la velocidad - float vel; // Velocidad inicial al rebotar contra el suelo +struct ParamBalloon { + struct Settings { + float grav; // Aceleración en el eje Y. Modifica la velocidad + float vel; // Velocidad inicial al rebotar contra el suelo // Constructor explicit Settings(float grav_val = 0.0f, float vel_val = 0.0f) @@ -64,36 +59,33 @@ struct ParamBalloon std::array<Settings, 4> settings; std::array<std::string, 4> color; - bool bouncing_sound; // Indica si los globos hacer sonido al rebotar + bool bouncing_sound; // Indica si los globos hacer sonido al rebotar }; // --- Parámetros de las notificaciones --- -struct ParamNotification -{ - NotifyPosition pos_h; // Ubicación horizontal de las notificaciones en pantalla - NotifyPosition pos_v; // Ubicación vertical de las notificaciones en pantalla - bool sound; // Indica si las notificaciones suenan - Color color; // Color de las notificaciones +struct ParamNotification { + NotifyPosition pos_h; // Ubicación horizontal de las notificaciones en pantalla + NotifyPosition pos_v; // Ubicación vertical de las notificaciones en pantalla + bool sound; // Indica si las notificaciones suenan + Color color; // Color de las notificaciones }; // --- Parámetros del marcador --- -struct ParamScoreboard -{ - SDL_FRect rect; // Posición y tamaño del marcador - bool separator_autocolor; // El separado establece su color de forma automatica - Color separator_color; // Color del separador si se pone de forma manual - Color easy_color; // Color del marcador segun la dificultad - Color normal_color; // Color del marcador segun la dificultad - Color hard_color; // Color del marcador segun la dificultad - bool text_autocolor; // El texto establece su color de forma automatica - Color text_color1; // Color del texto - Color text_color2; // Color del texto - int skip_countdown_value; // Valor a partir del cual se puede saltar la cuenta atras +struct ParamScoreboard { + SDL_FRect rect; // Posición y tamaño del marcador + bool separator_autocolor; // El separado establece su color de forma automatica + Color separator_color; // Color del separador si se pone de forma manual + Color easy_color; // Color del marcador segun la dificultad + Color normal_color; // Color del marcador segun la dificultad + Color hard_color; // Color del marcador segun la dificultad + bool text_autocolor; // El texto establece su color de forma automatica + Color text_color1; // Color del texto + Color text_color2; // Color del texto + int skip_countdown_value; // Valor a partir del cual se puede saltar la cuenta atras }; // --- Parámetros del menú de servicio --- -struct ParamServiceMenu -{ +struct ParamServiceMenu { Color title_color; Color text_color; Color selected_color; @@ -102,8 +94,7 @@ struct ParamServiceMenu }; // --- Parámetros de la intro --- -struct ParamIntro -{ +struct ParamIntro { Color bg_color; Color card_color; Color shadow_color; @@ -111,40 +102,36 @@ struct ParamIntro }; // --- Parámetros para Debug --- -struct ParamDebug -{ +struct ParamDebug { Color color; }; // --- Parámetros para Resource --- -struct ParamResource -{ +struct ParamResource { Color color; }; // --- Estructura principal para almacenar todos los parámetros del juego --- -struct Param -{ - ParamGame game; // Parámetros del juego - ParamFade fade; // Parámetros del fade - ParamScoreboard scoreboard; // Rectángulo del marcador - ParamTitle title; // Parámetros de la pantalla de título - ParamBackground background; // Parámetros del fondo - ParamBalloon balloon; // Parámetros de los globos - ParamNotification notification; // Parámetros de las notificaciones - ParamServiceMenu service_menu; // Parámetros del menú de servicio - ParamIntro intro; // Parámetros de la intro - ParamDebug debug; // Parámetros para Debug - ParamResource resource; // Parámetros para Resource +struct Param { + ParamGame game; // Parámetros del juego + ParamFade fade; // Parámetros del fade + ParamScoreboard scoreboard; // Rectángulo del marcador + ParamTitle title; // Parámetros de la pantalla de título + ParamBackground background; // Parámetros del fondo + ParamBalloon balloon; // Parámetros de los globos + ParamNotification notification; // Parámetros de las notificaciones + ParamServiceMenu service_menu; // Parámetros del menú de servicio + ParamIntro intro; // Parámetros de la intro + ParamDebug debug; // Parámetros para Debug + ParamResource resource; // Parámetros para Resource // Constructor Param() - : game(), fade(), scoreboard(), title(), background(), balloon(), - notification(), service_menu(), intro(), debug(), resource() {} + : game(), fade(), scoreboard(), title(), background(), balloon(), notification(), service_menu(), intro(), debug(), resource() {} }; // --- Variable global con los parámetros del juego --- extern Param param; // --- Funciones globales --- -void loadParamsFromFile(const std::string &file_path); // Establece valores para los parámetros a partir de un fichero de texto \ No newline at end of file +void loadParamsFromFile(const std::string &file_path); // Establece valores para los parámetros a partir de un fichero de texto \ No newline at end of file diff --git a/source/path_sprite.cpp b/source/path_sprite.cpp index 0e7ffaf..bd7ed33 100644 --- a/source/path_sprite.cpp +++ b/source/path_sprite.cpp @@ -1,169 +1,143 @@ // IWYU pragma: no_include <bits/std_abs.h> #include "path_sprite.h" -#include <cmath> // Para abs -#include <functional> // Para function -#include <utility> // Para move +#include <cmath> // Para abs +#include <functional> // Para function +#include <utility> // Para move // Devuelve un vector con los puntos que conforman la ruta -std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction) -{ - std::vector<SDL_FPoint> v; - v.reserve(steps); +std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction) { + std::vector<SDL_FPoint> v; + v.reserve(steps); - for (int i = 0; i < steps; ++i) - { - double t = static_cast<double>(i) / (steps - 1); - double value = start + (end - start) * easingFunction(t); + for (int i = 0; i < steps; ++i) { + double t = static_cast<double>(i) / (steps - 1); + double value = start + (end - start) * easingFunction(t); - if ((start > 0 && end < 0) || (start < 0 && end > 0)) - { - value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easingFunction(t); - } + if ((start > 0 && end < 0) || (start < 0 && end > 0)) { + value = start + (end > 0 ? 1 : -1) * std::abs(end - start) * easingFunction(t); + } - switch (type) - { - case PathType::HORIZONTAL: - v.emplace_back(SDL_FPoint{static_cast<float>(value), fixed_pos}); - break; - case PathType::VERTICAL: - v.emplace_back(SDL_FPoint{fixed_pos, static_cast<float>(value)}); - break; - default: - break; - } - } + switch (type) { + case PathType::HORIZONTAL: + v.emplace_back(SDL_FPoint{static_cast<float>(value), fixed_pos}); + break; + case PathType::VERTICAL: + v.emplace_back(SDL_FPoint{fixed_pos, static_cast<float>(value)}); + break; + default: + break; + } + } - return v; + return v; } // Actualiza la posición y comprueba si ha llegado a su destino -void PathSprite::update() -{ - if (enabled_ && !has_finished_) - { - moveThroughCurrentPath(); - goToNextPathOrDie(); - } +void PathSprite::update() { + if (enabled_ && !has_finished_) { + moveThroughCurrentPath(); + goToNextPathOrDie(); + } } // Muestra el sprite por pantalla -void PathSprite::render() -{ - if (enabled_) - { - Sprite::render(); - } +void PathSprite::render() { + if (enabled_) { + Sprite::render(); + } } // Añade un recorrido -void PathSprite::addPath(Path path, bool centered) -{ - PathCentered path_centered = PathCentered::NONE; - if (centered) - path_centered = (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y; +void PathSprite::addPath(Path path, bool centered) { + PathCentered path_centered = PathCentered::NONE; + if (centered) + path_centered = (path.spots.back().x == path.spots.front().x) ? PathCentered::ON_X : PathCentered::ON_Y; - switch (path_centered) - { - case PathCentered::ON_X: - { - const int x = path.spots.back().x - pos_.w / 2; - for (auto &spot : path.spots) - spot.x = x; - paths_.emplace_back(path); - break; - } - case PathCentered::ON_Y: - { - const int y = path.spots.back().y - pos_.h / 2; - for (auto &spot : path.spots) - spot.y = y; - paths_.emplace_back(path); - break; - } - default: - paths_.emplace_back(path); - break; - } + switch (path_centered) { + case PathCentered::ON_X: { + const int x = path.spots.back().x - pos_.w / 2; + for (auto &spot : path.spots) + spot.x = x; + paths_.emplace_back(path); + break; + } + case PathCentered::ON_Y: { + const int y = path.spots.back().y - pos_.h / 2; + for (auto &spot : path.spots) + spot.y = y; + paths_.emplace_back(path); + break; + } + default: + paths_.emplace_back(path); + break; + } } // Añade un recorrido -void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter) -{ - paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easingFunction), waiting_counter); +void PathSprite::addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter) { + paths_.emplace_back(createPath(start, end, type, fixed_pos, steps, easingFunction), waiting_counter); } // Añade un recorrido -void PathSprite::addPath(std::vector<SDL_FPoint> spots, int waiting_counter) -{ - paths_.emplace_back(std::move(spots), waiting_counter); +void PathSprite::addPath(std::vector<SDL_FPoint> spots, int waiting_counter) { + paths_.emplace_back(std::move(spots), waiting_counter); } // Habilita el objeto -void PathSprite::enable() -{ - if (paths_.size() == 0 || enabled_) - { - return; - } +void PathSprite::enable() { + if (paths_.size() == 0 || enabled_) { + return; + } - enabled_ = true; + enabled_ = true; - // Establece la posición - auto &path = paths_.at(current_path_); - const auto &P = path.spots.at(path.counter); - setPosition(P); + // Establece la posición + auto &path = paths_.at(current_path_); + const auto &P = path.spots.at(path.counter); + setPosition(P); } // Coloca el sprite en los diferentes puntos del recorrido -void PathSprite::moveThroughCurrentPath() -{ - auto &path = paths_.at(current_path_); +void PathSprite::moveThroughCurrentPath() { + auto &path = paths_.at(current_path_); - // Establece la posición - const auto &p = path.spots.at(path.counter); - setPosition(p); + // Establece la posición + const auto &p = path.spots.at(path.counter); + setPosition(p); - // Comprobar si ha terminado el recorrido - if (!path.on_destination) - { - ++path.counter; - if (path.counter >= static_cast<int>(path.spots.size())) - { - path.on_destination = true; - path.counter = static_cast<int>(path.spots.size()) - 1; - } - } + // Comprobar si ha terminado el recorrido + if (!path.on_destination) { + ++path.counter; + if (path.counter >= static_cast<int>(path.spots.size())) { + path.on_destination = true; + path.counter = static_cast<int>(path.spots.size()) - 1; + } + } - // Comprobar si ha terminado la espera - if (path.on_destination) - { - if (path.waiting_counter == 0) - { - path.finished = true; - } - else - { - --path.waiting_counter; - } - } + // Comprobar si ha terminado la espera + if (path.on_destination) { + if (path.waiting_counter == 0) { + path.finished = true; + } else { + --path.waiting_counter; + } + } } // Cambia de recorrido o finaliza -void PathSprite::goToNextPathOrDie() -{ - // Comprueba si ha terminado el recorrdo actual - if (paths_.at(current_path_).finished) - { - ++current_path_; - } +void PathSprite::goToNextPathOrDie() { + // Comprueba si ha terminado el recorrdo actual + if (paths_.at(current_path_).finished) { + ++current_path_; + } - // Comprueba si quedan mas recorridos - if (current_path_ >= static_cast<int>(paths_.size())) - { - has_finished_ = true; - current_path_ = 0; - } + // Comprueba si quedan mas recorridos + if (current_path_ >= static_cast<int>(paths_.size())) { + has_finished_ = true; + current_path_ = 0; + } } // Indica si ha terminado todos los recorridos diff --git a/source/path_sprite.h b/source/path_sprite.h index e5b090e..fb76f62 100644 --- a/source/path_sprite.h +++ b/source/path_sprite.h @@ -1,79 +1,76 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FPoint -#include <functional> // Para std::function -#include <memory> // Para shared_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FPoint -#include "sprite.h" // Para Sprite +#include <functional> // Para std::function +#include <memory> // Para shared_ptr +#include <vector> // Para vector + +#include "sprite.h" // Para Sprite class Texture; // --- Tipos de recorrido --- -enum class PathType -{ - VERTICAL, - HORIZONTAL, +enum class PathType { + VERTICAL, + HORIZONTAL, }; // --- Centrado del recorrido --- -enum class PathCentered -{ - ON_X, - ON_Y, - NONE, +enum class PathCentered { + ON_X, + ON_Y, + NONE, }; // --- Estructura Path: define un recorrido para el sprite --- -struct Path -{ - std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite - int waiting_counter; // Tiempo de espera una vez en el destino - bool on_destination = false; // Indica si ha llegado al destino - bool finished = false; // Indica si ha terminado de esperarse - int counter = 0; // Contador interno +struct Path { + std::vector<SDL_FPoint> spots; // Puntos por los que se desplazará el sprite + int waiting_counter; // Tiempo de espera una vez en el destino + bool on_destination = false; // Indica si ha llegado al destino + bool finished = false; // Indica si ha terminado de esperarse + int counter = 0; // Contador interno - // Constructor - Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) - : spots(spots_init), waiting_counter(waiting_counter_init) {} + // Constructor + Path(const std::vector<SDL_FPoint> &spots_init, int waiting_counter_init) + : spots(spots_init), waiting_counter(waiting_counter_init) {} }; // Devuelve un vector con los puntos que conforman la ruta std::vector<SDL_FPoint> createPath(float start, float end, PathType type, float fixed_pos, int steps, const std::function<double(double)> &easingFunction); // --- Clase PathSprite: Sprite que sigue uno o varios recorridos --- -class PathSprite : public Sprite -{ -public: - // --- Constructor y destructor --- - explicit PathSprite(std::shared_ptr<Texture> texture) - : Sprite(texture) {} - ~PathSprite() override = default; +class PathSprite : public Sprite { + public: + // --- Constructor y destructor --- + explicit PathSprite(std::shared_ptr<Texture> texture) + : Sprite(texture) {} + ~PathSprite() override = default; - // --- Métodos principales --- - void update(); // Actualiza la posición del sprite según el recorrido - void render() override; // Muestra el sprite por pantalla + // --- Métodos principales --- + void update(); // Actualiza la posición del sprite según el recorrido + void render() override; // Muestra el sprite por pantalla - // --- Gestión de recorridos --- - void addPath(Path path, bool centered = false); // Añade un recorrido (Path) - void addPath(std::vector<SDL_FPoint> spots, int waiting_counter = 0); // Añade un recorrido a partir de puntos - void addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter = 0); // Añade un recorrido generado + // --- Gestión de recorridos --- + void addPath(Path path, bool centered = false); // Añade un recorrido (Path) + void addPath(std::vector<SDL_FPoint> spots, int waiting_counter = 0); // Añade un recorrido a partir de puntos + void addPath(int start, int end, PathType type, int fixed_pos, int steps, const std::function<double(double)> &easingFunction, int waiting_counter = 0); // Añade un recorrido generado - // --- Estado y control --- - void enable(); // Habilita el objeto - bool hasFinished(); // Indica si ha terminado todos los recorridos + // --- Estado y control --- + void enable(); // Habilita el objeto + bool hasFinished(); // Indica si ha terminado todos los recorridos - // --- Getters --- - int getCurrentPath() const { return current_path_; } // Devuelve el índice del recorrido actual + // --- Getters --- + int getCurrentPath() const { return current_path_; } // Devuelve el índice del recorrido actual -private: - // --- Variables internas --- - bool enabled_ = false; // Indica si el objeto está habilitado - bool has_finished_ = false; // Indica si el objeto ha finalizado el recorrido - int current_path_ = 0; // Recorrido que se está recorriendo actualmente - std::vector<Path> paths_; // Caminos a recorrer por el sprite + private: + // --- Variables internas --- + bool enabled_ = false; // Indica si el objeto está habilitado + bool has_finished_ = false; // Indica si el objeto ha finalizado el recorrido + int current_path_ = 0; // Recorrido que se está recorriendo actualmente + std::vector<Path> paths_; // Caminos a recorrer por el sprite - // --- Métodos internos --- - void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido - void goToNextPathOrDie(); // Cambia de recorrido o finaliza + // --- Métodos internos --- + void moveThroughCurrentPath(); // Coloca el sprite en los diferentes puntos del recorrido + void goToNextPathOrDie(); // Cambia de recorrido o finaliza }; \ No newline at end of file diff --git a/source/player.cpp b/source/player.cpp index 4cebf15..187dc73 100644 --- a/source/player.cpp +++ b/source/player.cpp @@ -1,951 +1,804 @@ #include "player.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode, SDL_FRect -#include <stdlib.h> // Para rand +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_FlipMode, SDL_FRect +#include <stdlib.h> // Para rand + #include <algorithm> // Para clamp, max, min -#include "animated_sprite.h" // Para AnimatedSprite -#include "asset.h" // Para Asset -#include "audio.h" // Para Audio -#include "input.h" // Para InputAction -#include "manage_hiscore_table.h" // Para ManageHiScoreTable, HiScoreEntry -#include "param.h" // Para Param, ParamGame, param -#include "scoreboard.h" // Para ScoreboardMode, Scoreboard -#include "stage.h" // Para power_can_be_added -#include "texture.h" // Para Texture +#include "animated_sprite.h" // Para AnimatedSprite +#include "asset.h" // Para Asset +#include "audio.h" // Para Audio +#include "input.h" // Para InputAction +#include "manage_hiscore_table.h" // Para ManageHiScoreTable, HiScoreEntry +#include "param.h" // Para Param, ParamGame, param +#include "scoreboard.h" // Para ScoreboardMode, Scoreboard +#include "stage.h" // Para power_can_be_added +#include "texture.h" // Para Texture // Constructor Player::Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations) - : player_sprite_(std::make_unique<AnimatedSprite>(texture[0], animations[0])), - power_sprite_(std::make_unique<AnimatedSprite>(texture[1], animations[1])), - enter_name_(std::make_unique<EnterName>()), - id_(id), - play_area_(play_area), - default_pos_x_(x), - default_pos_y_(y), - demo_(demo) -{ - // Configura objetos - player_sprite_->getTexture()->setPalette(coffees_); - power_sprite_->getTexture()->setAlpha(224); - power_up_desp_x_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2; - power_sprite_->setPosY(y - (power_sprite_->getHeight() - player_sprite_->getHeight())); + : player_sprite_(std::make_unique<AnimatedSprite>(texture[0], animations[0])), + power_sprite_(std::make_unique<AnimatedSprite>(texture[1], animations[1])), + enter_name_(std::make_unique<EnterName>()), + id_(id), + play_area_(play_area), + default_pos_x_(x), + default_pos_y_(y), + demo_(demo) { + // Configura objetos + player_sprite_->getTexture()->setPalette(coffees_); + power_sprite_->getTexture()->setAlpha(224); + power_up_desp_x_ = (power_sprite_->getWidth() - player_sprite_->getWidth()) / 2; + power_sprite_->setPosY(y - (power_sprite_->getHeight() - player_sprite_->getHeight())); - // Inicializa variables - pos_x_ = default_pos_x_; - invulnerable_ = false; - invulnerable_counter_ = 0; - init(); + // Inicializa variables + pos_x_ = default_pos_x_; + invulnerable_ = false; + invulnerable_counter_ = 0; + init(); } // Iniciador -void Player::init() -{ - // Inicializa variables de estado - pos_y_ = default_pos_y_; - walking_state_ = PlayerState::WALKING_STOP; - firing_state_ = PlayerState::FIRING_NONE; - playing_state_ = PlayerState::WAITING; - power_up_ = false; - power_up_counter_ = POWERUP_COUNTER_; - extra_hit_ = false; - coffees_ = 0; - continue_ticks_ = 0; - continue_counter_ = 10; - name_entry_ticks_ = 0; - name_entry_idle_counter_ = 0; - name_entry_total_counter_ = 0; - shiftColliders(); - vel_x_ = 0; - vel_y_ = 0; - score_ = 0; - score_multiplier_ = 1.0f; - cant_fire_counter_ = 10; - enter_name_->init(last_enter_name_); +void Player::init() { + // Inicializa variables de estado + pos_y_ = default_pos_y_; + walking_state_ = PlayerState::WALKING_STOP; + firing_state_ = PlayerState::FIRING_NONE; + playing_state_ = PlayerState::WAITING; + power_up_ = false; + power_up_counter_ = POWERUP_COUNTER_; + extra_hit_ = false; + coffees_ = 0; + continue_ticks_ = 0; + continue_counter_ = 10; + name_entry_ticks_ = 0; + name_entry_idle_counter_ = 0; + name_entry_total_counter_ = 0; + shiftColliders(); + vel_x_ = 0; + vel_y_ = 0; + score_ = 0; + score_multiplier_ = 1.0f; + cant_fire_counter_ = 10; + enter_name_->init(last_enter_name_); - // Establece la posición del sprite - player_sprite_->clear(); - shiftSprite(); + // Establece la posición del sprite + player_sprite_->clear(); + shiftSprite(); - // Selecciona un frame para pintar - player_sprite_->setCurrentAnimation("stand"); + // Selecciona un frame para pintar + player_sprite_->setCurrentAnimation("stand"); } // Actua en consecuencia de la entrada recibida -void Player::setInput(InputAction input) -{ - switch (playing_state_) - { - case PlayerState::PLAYING: - { - setInputPlaying(input); - break; - } - case PlayerState::ENTERING_NAME: - case PlayerState::ENTERING_NAME_GAME_COMPLETED: - { - setInputEnteringName(input); - break; - } - default: - break; - } +void Player::setInput(InputAction input) { + switch (playing_state_) { + case PlayerState::PLAYING: { + setInputPlaying(input); + break; + } + case PlayerState::ENTERING_NAME: + case PlayerState::ENTERING_NAME_GAME_COMPLETED: { + setInputEnteringName(input); + break; + } + default: + break; + } } // Procesa inputs para cuando está jugando -void Player::setInputPlaying(InputAction input) -{ - switch (input) - { - case InputAction::LEFT: - { - vel_x_ = -BASE_SPEED_; - setWalkingState(PlayerState::WALKING_LEFT); - break; - } - case InputAction::RIGHT: - { - vel_x_ = BASE_SPEED_; - setWalkingState(PlayerState::WALKING_RIGHT); - break; - } - case InputAction::FIRE_CENTER: - { - setFiringState(PlayerState::FIRING_UP); - break; - } - case InputAction::FIRE_LEFT: - { - setFiringState(PlayerState::FIRING_LEFT); - break; - } - case InputAction::FIRE_RIGHT: - { - setFiringState(PlayerState::FIRING_RIGHT); - break; - } - default: - { - vel_x_ = 0; - setWalkingState(PlayerState::WALKING_STOP); - break; - } - } +void Player::setInputPlaying(InputAction input) { + switch (input) { + case InputAction::LEFT: { + vel_x_ = -BASE_SPEED_; + setWalkingState(PlayerState::WALKING_LEFT); + break; + } + case InputAction::RIGHT: { + vel_x_ = BASE_SPEED_; + setWalkingState(PlayerState::WALKING_RIGHT); + break; + } + case InputAction::FIRE_CENTER: { + setFiringState(PlayerState::FIRING_UP); + break; + } + case InputAction::FIRE_LEFT: { + setFiringState(PlayerState::FIRING_LEFT); + break; + } + case InputAction::FIRE_RIGHT: { + setFiringState(PlayerState::FIRING_RIGHT); + break; + } + default: { + vel_x_ = 0; + setWalkingState(PlayerState::WALKING_STOP); + break; + } + } } // Procesa inputs para cuando está introduciendo el nombre -void Player::setInputEnteringName(InputAction input) -{ - switch (input) - { - case InputAction::LEFT: - enter_name_->decPosition(); - break; - case InputAction::RIGHT: - enter_name_->incPosition(); - break; - case InputAction::UP: - enter_name_->incIndex(); - break; - case InputAction::DOWN: - enter_name_->decIndex(); - break; - case InputAction::START: - last_enter_name_ = getRecordName(); - break; - default: - break; - } - name_entry_idle_counter_ = 0; +void Player::setInputEnteringName(InputAction input) { + switch (input) { + case InputAction::LEFT: + enter_name_->decPosition(); + break; + case InputAction::RIGHT: + enter_name_->incPosition(); + break; + case InputAction::UP: + enter_name_->incIndex(); + break; + case InputAction::DOWN: + enter_name_->decIndex(); + break; + case InputAction::START: + last_enter_name_ = getRecordName(); + break; + default: + break; + } + name_entry_idle_counter_ = 0; } // Mueve el jugador a la posición y animación que le corresponde -void Player::move() -{ - switch (playing_state_) - { - case PlayerState::PLAYING: - { - // Mueve el jugador a derecha o izquierda - pos_x_ += vel_x_; +void Player::move() { + switch (playing_state_) { + case PlayerState::PLAYING: { + // Mueve el jugador a derecha o izquierda + pos_x_ += vel_x_; - // Si el jugador abandona el area de juego por los laterales, restaura su posición - const float MIN_X = play_area_.x - 5; - const float MAX_X = play_area_.w + 5 - WIDTH_; - pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); + // Si el jugador abandona el area de juego por los laterales, restaura su posición + const float MIN_X = play_area_.x - 5; + const float MAX_X = play_area_.w + 5 - WIDTH_; + pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); - shiftSprite(); - break; - } - case PlayerState::ROLLING: - { - // Si el jugador abandona el area de juego por los laterales lo hace rebotar - const int X = player_sprite_->getPosX(); - const int MIN_X = play_area_.x; - const int MAX_X = play_area_.x + play_area_.w - WIDTH_; - if ((X < MIN_X) || (X > MAX_X)) - { - player_sprite_->setPosX(std::clamp(X, MIN_X, MAX_X)); - player_sprite_->setVelX(-player_sprite_->getVelX()); - playSound("jump.wav"); - } + shiftSprite(); + break; + } + case PlayerState::ROLLING: { + // Si el jugador abandona el area de juego por los laterales lo hace rebotar + const int X = player_sprite_->getPosX(); + const int MIN_X = play_area_.x; + const int MAX_X = play_area_.x + play_area_.w - WIDTH_; + if ((X < MIN_X) || (X > MAX_X)) { + player_sprite_->setPosX(std::clamp(X, MIN_X, MAX_X)); + player_sprite_->setVelX(-player_sprite_->getVelX()); + playSound("jump.wav"); + } - // Si el jugador toca el suelo rebota y si tiene poca velocidad, se detiene y cambia de estado - if (player_sprite_->getPosY() > play_area_.h - HEIGHT_) - { - if (player_sprite_->getVelY() < 2.0f) - { - // Si la velocidad de rebote es baja, lo detiene y cambia de estado - const auto nextPlayerStatus = IsEligibleForHighScore() ? PlayerState::ENTERING_NAME : PlayerState::CONTINUE; - demo_ ? setPlayingState(PlayerState::LYING_ON_THE_FLOOR_FOREVER) : setPlayingState(nextPlayerStatus); - pos_x_ = player_sprite_->getPosX(); - pos_y_ = default_pos_y_; - player_sprite_->clear(); - shiftSprite(); - playSound("jump.wav"); - } - else - { - // Decrementa las velocidades de rebote - player_sprite_->setPosY(play_area_.h - HEIGHT_); - player_sprite_->setVelY(player_sprite_->getVelY() * -0.5f); - player_sprite_->setVelX(player_sprite_->getVelX() * 0.75f); - player_sprite_->setAnimationSpeed(player_sprite_->getAnimationSpeed() * 2); - playSound("jump.wav"); - } - } - break; - } - case PlayerState::TITLE_ANIMATION: - { - // Si el jugador abandona el area de juego por los laterales lo detiene - /*const int X = player_sprite_->getPosX(); - const int MIN_X = play_area_.x - WIDTH_; - const int MAX_X = play_area_.x + play_area_.w; - if ((X < MIN_X) || (X > MAX_X)) - { - setPlayingState(PlayerState::TITLE_HIDDEN); - } + // Si el jugador toca el suelo rebota y si tiene poca velocidad, se detiene y cambia de estado + if (player_sprite_->getPosY() > play_area_.h - HEIGHT_) { + if (player_sprite_->getVelY() < 2.0f) { + // Si la velocidad de rebote es baja, lo detiene y cambia de estado + const auto nextPlayerStatus = IsEligibleForHighScore() ? PlayerState::ENTERING_NAME : PlayerState::CONTINUE; + demo_ ? setPlayingState(PlayerState::LYING_ON_THE_FLOOR_FOREVER) : setPlayingState(nextPlayerStatus); + pos_x_ = player_sprite_->getPosX(); + pos_y_ = default_pos_y_; + player_sprite_->clear(); + shiftSprite(); + playSound("jump.wav"); + } else { + // Decrementa las velocidades de rebote + player_sprite_->setPosY(play_area_.h - HEIGHT_); + player_sprite_->setVelY(player_sprite_->getVelY() * -0.5f); + player_sprite_->setVelX(player_sprite_->getVelX() * 0.75f); + player_sprite_->setAnimationSpeed(player_sprite_->getAnimationSpeed() * 2); + playSound("jump.wav"); + } + } + break; + } + case PlayerState::TITLE_ANIMATION: { + // Si el jugador abandona el area de juego por los laterales lo detiene + /*const int X = player_sprite_->getPosX(); + const int MIN_X = play_area_.x - WIDTH_; + const int MAX_X = play_area_.x + play_area_.w; + if ((X < MIN_X) || (X > MAX_X)) + { + setPlayingState(PlayerState::TITLE_HIDDEN); + } - // Si el jugador toca el suelo rebota lo detiene - if (player_sprite_->getPosY() > play_area_.h) - { - setPlayingState(PlayerState::TITLE_HIDDEN); - }*/ + // Si el jugador toca el suelo rebota lo detiene + if (player_sprite_->getPosY() > play_area_.h) + { + setPlayingState(PlayerState::TITLE_HIDDEN); + }*/ - switch (id_) - { - case 1: - setInputPlaying(InputAction::LEFT); - break; - case 2: - setInputPlaying(InputAction::RIGHT); - break; - default: - break; - } - pos_x_ += vel_x_ * 2.0f; - const float MIN_X = -WIDTH_; - const float MAX_X = play_area_.w; - pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); - shiftSprite(); + switch (id_) { + case 1: + setInputPlaying(InputAction::LEFT); + break; + case 2: + setInputPlaying(InputAction::RIGHT); + break; + default: + break; + } + pos_x_ += vel_x_ * 2.0f; + const float MIN_X = -WIDTH_; + const float MAX_X = play_area_.w; + pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); + shiftSprite(); - if (pos_x_ == MIN_X || pos_x_ == MAX_X) - { - setPlayingState(PlayerState::TITLE_HIDDEN); - } - break; - } - case PlayerState::CONTINUE_TIME_OUT: - { - // Si el cadaver desaparece por el suelo, cambia de estado - if (player_sprite_->getPosY() > play_area_.h) - { - setPlayingState(PlayerState::WAITING); - } - break; - } - case PlayerState::LEAVING_SCREEN: - { - ++step_counter_; - if (step_counter_ % 10 == 0) - { - playSound("walk.wav"); - } + if (pos_x_ == MIN_X || pos_x_ == MAX_X) { + setPlayingState(PlayerState::TITLE_HIDDEN); + } + break; + } + case PlayerState::CONTINUE_TIME_OUT: { + // Si el cadaver desaparece por el suelo, cambia de estado + if (player_sprite_->getPosY() > play_area_.h) { + setPlayingState(PlayerState::WAITING); + } + break; + } + case PlayerState::LEAVING_SCREEN: { + ++step_counter_; + if (step_counter_ % 10 == 0) { + playSound("walk.wav"); + } - switch (id_) - { - case 1: - setInputPlaying(InputAction::LEFT); - break; - case 2: - setInputPlaying(InputAction::RIGHT); - break; - default: - break; - } - pos_x_ += vel_x_; - const float MIN_X = -WIDTH_; - const float MAX_X = play_area_.w; - pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); - shiftSprite(); + switch (id_) { + case 1: + setInputPlaying(InputAction::LEFT); + break; + case 2: + setInputPlaying(InputAction::RIGHT); + break; + default: + break; + } + pos_x_ += vel_x_; + const float MIN_X = -WIDTH_; + const float MAX_X = play_area_.w; + pos_x_ = std::clamp(pos_x_, MIN_X, MAX_X); + shiftSprite(); - if (pos_x_ == MIN_X || pos_x_ == MAX_X) - { - setPlayingState(PlayerState::GAME_OVER); - } - break; - } - case PlayerState::ENTERING_SCREEN: - { - ++step_counter_; - if (step_counter_ % 10 == 0) - { - playSound("walk.wav"); - } + if (pos_x_ == MIN_X || pos_x_ == MAX_X) { + setPlayingState(PlayerState::GAME_OVER); + } + break; + } + case PlayerState::ENTERING_SCREEN: { + ++step_counter_; + if (step_counter_ % 10 == 0) { + playSound("walk.wav"); + } - switch (id_) - { - case 1: - setInputPlaying(InputAction::RIGHT); - pos_x_ += vel_x_; - if (pos_x_ > default_pos_x_) - { - pos_x_ = default_pos_x_; - setPlayingState(PlayerState::PLAYING); - setInvulnerable(false); - } - break; - case 2: - setInputPlaying(InputAction::LEFT); - pos_x_ += vel_x_; - if (pos_x_ < default_pos_x_) - { - pos_x_ = default_pos_x_; - setPlayingState(PlayerState::PLAYING); - setInvulnerable(false); - } - break; - default: - break; - } - shiftSprite(); - break; - } - case PlayerState::CREDITS: - { - pos_x_ += vel_x_ / 2.0f; - if (vel_x_ > 0) - { - // setInputPlaying(InputAction::RIGHT); - if (pos_x_ > param.game.game_area.rect.w - WIDTH_) - { - pos_x_ = param.game.game_area.rect.w - WIDTH_; - vel_x_ *= -1; - } - } - else - { - // setInputPlaying(InputAction::LEFT); - if (pos_x_ < param.game.game_area.rect.x) - { - pos_x_ = param.game.game_area.rect.x; - vel_x_ *= -1; - } - } + switch (id_) { + case 1: + setInputPlaying(InputAction::RIGHT); + pos_x_ += vel_x_; + if (pos_x_ > default_pos_x_) { + pos_x_ = default_pos_x_; + setPlayingState(PlayerState::PLAYING); + setInvulnerable(false); + } + break; + case 2: + setInputPlaying(InputAction::LEFT); + pos_x_ += vel_x_; + if (pos_x_ < default_pos_x_) { + pos_x_ = default_pos_x_; + setPlayingState(PlayerState::PLAYING); + setInvulnerable(false); + } + break; + default: + break; + } + shiftSprite(); + break; + } + case PlayerState::CREDITS: { + pos_x_ += vel_x_ / 2.0f; + if (vel_x_ > 0) { + // setInputPlaying(InputAction::RIGHT); + if (pos_x_ > param.game.game_area.rect.w - WIDTH_) { + pos_x_ = param.game.game_area.rect.w - WIDTH_; + vel_x_ *= -1; + } + } else { + // setInputPlaying(InputAction::LEFT); + if (pos_x_ < param.game.game_area.rect.x) { + pos_x_ = param.game.game_area.rect.x; + vel_x_ *= -1; + } + } - if (pos_x_ > param.game.game_area.center_x - WIDTH_ / 2) - { - setWalkingState(PlayerState::WALKING_LEFT); - } - else - { - setWalkingState(PlayerState::WALKING_RIGHT); - } - shiftSprite(); - break; - } - default: - break; - } + if (pos_x_ > param.game.game_area.center_x - WIDTH_ / 2) { + setWalkingState(PlayerState::WALKING_LEFT); + } else { + setWalkingState(PlayerState::WALKING_RIGHT); + } + shiftSprite(); + break; + } + default: + break; + } } // Pinta el jugador en pantalla -void Player::render() -{ - if (power_up_ && isPlaying()) - { - if (power_up_counter_ > (POWERUP_COUNTER_ / 4) || power_up_counter_ % 20 > 4) - { - power_sprite_->render(); - } - } +void Player::render() { + if (power_up_ && isPlaying()) { + if (power_up_counter_ > (POWERUP_COUNTER_ / 4) || power_up_counter_ % 20 > 4) { + power_sprite_->render(); + } + } - if (isRenderable()) - { - player_sprite_->render(); - } + if (isRenderable()) { + player_sprite_->render(); + } } // Establece la animación correspondiente al estado -void Player::setAnimation() -{ - switch (playing_state_) - { - case PlayerState::PLAYING: - case PlayerState::ENTERING_NAME_GAME_COMPLETED: - case PlayerState::ENTERING_SCREEN: - case PlayerState::LEAVING_SCREEN: - case PlayerState::TITLE_ANIMATION: - case PlayerState::CREDITS: - { - // Crea cadenas de texto para componer el nombre de la animación - const std::string WALK_ANIMATION = walking_state_ == PlayerState::WALKING_STOP ? "stand" : "walk"; - const std::string FIRE_ANIMATION = firing_state_ == PlayerState::FIRING_UP ? "-fire-center" : "-fire-side"; - const std::string RECOIL_ANIMATION = firing_state_ == PlayerState::RECOILING_UP ? "-recoil-center" : "-recoil-side"; - const std::string COOL_ANIMATION = firing_state_ == PlayerState::COOLING_UP ? "-cool-center" : "-cool-side"; +void Player::setAnimation() { + switch (playing_state_) { + case PlayerState::PLAYING: + case PlayerState::ENTERING_NAME_GAME_COMPLETED: + case PlayerState::ENTERING_SCREEN: + case PlayerState::LEAVING_SCREEN: + case PlayerState::TITLE_ANIMATION: + case PlayerState::CREDITS: { + // Crea cadenas de texto para componer el nombre de la animación + const std::string WALK_ANIMATION = walking_state_ == PlayerState::WALKING_STOP ? "stand" : "walk"; + const std::string FIRE_ANIMATION = firing_state_ == PlayerState::FIRING_UP ? "-fire-center" : "-fire-side"; + const std::string RECOIL_ANIMATION = firing_state_ == PlayerState::RECOILING_UP ? "-recoil-center" : "-recoil-side"; + const std::string COOL_ANIMATION = firing_state_ == PlayerState::COOLING_UP ? "-cool-center" : "-cool-side"; - const SDL_FlipMode FLIP_WALK = walking_state_ == PlayerState::WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - const SDL_FlipMode FLIP_FIRE = firing_state_ == PlayerState::FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - const SDL_FlipMode FLIP_RECOIL = firing_state_ == PlayerState::RECOILING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - const SDL_FlipMode FLIP_COOL = firing_state_ == PlayerState::COOLING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_WALK = walking_state_ == PlayerState::WALKING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_FIRE = firing_state_ == PlayerState::FIRING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_RECOIL = firing_state_ == PlayerState::RECOILING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; + const SDL_FlipMode FLIP_COOL = firing_state_ == PlayerState::COOLING_RIGHT ? SDL_FLIP_HORIZONTAL : SDL_FLIP_NONE; - // Establece la animación a partir de las cadenas - if (firing_state_ == PlayerState::FIRING_NONE) - { - // No esta disparando - player_sprite_->setCurrentAnimation(WALK_ANIMATION, false); - player_sprite_->setFlip(FLIP_WALK); - } - else if (isRecoiling()) - { - // Retroceso - player_sprite_->setCurrentAnimation(WALK_ANIMATION + RECOIL_ANIMATION, false); - player_sprite_->setFlip(FLIP_RECOIL); - } - else if (isCooling()) - { - // Acaba de disparar - player_sprite_->setCurrentAnimation(WALK_ANIMATION + COOL_ANIMATION, false); - player_sprite_->setFlip(FLIP_COOL); - } - else - { - // Está disparando - player_sprite_->setCurrentAnimation(WALK_ANIMATION + FIRE_ANIMATION, false); - // Si dispara de lado, invierte el sprite segun hacia donde dispara - // Si dispara recto, invierte el sprite segun hacia donde camina - player_sprite_->setFlip(FIRE_ANIMATION == "-fire-center" ? FLIP_WALK : FLIP_FIRE); - } - break; - } - case PlayerState::ROLLING: - case PlayerState::CONTINUE_TIME_OUT: - { - player_sprite_->setCurrentAnimation("rolling"); - break; - } - case PlayerState::LYING_ON_THE_FLOOR_FOREVER: - case PlayerState::ENTERING_NAME: - case PlayerState::CONTINUE: - { - player_sprite_->setCurrentAnimation("dead"); - break; - } - case PlayerState::CELEBRATING: - { - player_sprite_->setCurrentAnimation("celebration"); - break; - } - default: - break; - } + // Establece la animación a partir de las cadenas + if (firing_state_ == PlayerState::FIRING_NONE) { + // No esta disparando + player_sprite_->setCurrentAnimation(WALK_ANIMATION, false); + player_sprite_->setFlip(FLIP_WALK); + } else if (isRecoiling()) { + // Retroceso + player_sprite_->setCurrentAnimation(WALK_ANIMATION + RECOIL_ANIMATION, false); + player_sprite_->setFlip(FLIP_RECOIL); + } else if (isCooling()) { + // Acaba de disparar + player_sprite_->setCurrentAnimation(WALK_ANIMATION + COOL_ANIMATION, false); + player_sprite_->setFlip(FLIP_COOL); + } else { + // Está disparando + player_sprite_->setCurrentAnimation(WALK_ANIMATION + FIRE_ANIMATION, false); + // Si dispara de lado, invierte el sprite segun hacia donde dispara + // Si dispara recto, invierte el sprite segun hacia donde camina + player_sprite_->setFlip(FIRE_ANIMATION == "-fire-center" ? FLIP_WALK : FLIP_FIRE); + } + break; + } + case PlayerState::ROLLING: + case PlayerState::CONTINUE_TIME_OUT: { + player_sprite_->setCurrentAnimation("rolling"); + break; + } + case PlayerState::LYING_ON_THE_FLOOR_FOREVER: + case PlayerState::ENTERING_NAME: + case PlayerState::CONTINUE: { + player_sprite_->setCurrentAnimation("dead"); + break; + } + case PlayerState::CELEBRATING: { + player_sprite_->setCurrentAnimation("celebration"); + break; + } + default: + break; + } - // Actualiza las animaciones de los sprites - player_sprite_->update(); // Hace avanzar las animaciones y mueve el cadaver del jugador - power_sprite_->update(); + // Actualiza las animaciones de los sprites + player_sprite_->update(); // Hace avanzar las animaciones y mueve el cadaver del jugador + power_sprite_->update(); } // Actualiza el valor de la variable -void Player::updateCooldown() -{ - if (playing_state_ == PlayerState::PLAYING) - { - if (cant_fire_counter_ > 0) - { - cooling_state_counter_ = COOLING_DURATION_; +void Player::updateCooldown() { + if (playing_state_ == PlayerState::PLAYING) { + if (cant_fire_counter_ > 0) { + cooling_state_counter_ = COOLING_DURATION_; - // La mitad del tiempo que no puede disparar tiene el brazo arriba (PlayerState::FIRING) - // y la otra mitad en retroceso (PlayerState::RECOILING) - if (cant_fire_counter_ == recoiling_state_duration_ / 2) - { - switch (firing_state_) - { - case PlayerState::FIRING_LEFT: - setFiringState(PlayerState::RECOILING_LEFT); - break; - case PlayerState::FIRING_RIGHT: - setFiringState(PlayerState::RECOILING_RIGHT); - break; - case PlayerState::FIRING_UP: - setFiringState(PlayerState::RECOILING_UP); - break; - default: - break; - } - } + // La mitad del tiempo que no puede disparar tiene el brazo arriba (PlayerState::FIRING) + // y la otra mitad en retroceso (PlayerState::RECOILING) + if (cant_fire_counter_ == recoiling_state_duration_ / 2) { + switch (firing_state_) { + case PlayerState::FIRING_LEFT: + setFiringState(PlayerState::RECOILING_LEFT); + break; + case PlayerState::FIRING_RIGHT: + setFiringState(PlayerState::RECOILING_RIGHT); + break; + case PlayerState::FIRING_UP: + setFiringState(PlayerState::RECOILING_UP); + break; + default: + break; + } + } - --cant_fire_counter_; - if (cant_fire_counter_ == 0) - { - recoiling_state_counter_ = recoiling_state_duration_; - } - } - else - { - if (recoiling_state_counter_ > 0) - { - --recoiling_state_counter_; - } - else - { - if (cooling_state_counter_ > COOLING_COMPLETE_) - { - if (cooling_state_counter_ == COOLING_DURATION_) - { - switch (firing_state_) - { - case PlayerState::RECOILING_LEFT: - setFiringState(PlayerState::COOLING_LEFT); - break; - case PlayerState::RECOILING_RIGHT: - setFiringState(PlayerState::COOLING_RIGHT); - break; - case PlayerState::RECOILING_UP: - setFiringState(PlayerState::COOLING_UP); - break; - default: - break; - } - } + --cant_fire_counter_; + if (cant_fire_counter_ == 0) { + recoiling_state_counter_ = recoiling_state_duration_; + } + } else { + if (recoiling_state_counter_ > 0) { + --recoiling_state_counter_; + } else { + if (cooling_state_counter_ > COOLING_COMPLETE_) { + if (cooling_state_counter_ == COOLING_DURATION_) { + switch (firing_state_) { + case PlayerState::RECOILING_LEFT: + setFiringState(PlayerState::COOLING_LEFT); + break; + case PlayerState::RECOILING_RIGHT: + setFiringState(PlayerState::COOLING_RIGHT); + break; + case PlayerState::RECOILING_UP: + setFiringState(PlayerState::COOLING_UP); + break; + default: + break; + } + } - --cooling_state_counter_; - } + --cooling_state_counter_; + } - if (cooling_state_counter_ == COOLING_COMPLETE_) - { - setFiringState(PlayerState::FIRING_NONE); - cooling_state_counter_ = -1; - } - } - } - } + if (cooling_state_counter_ == COOLING_COMPLETE_) { + setFiringState(PlayerState::FIRING_NONE); + cooling_state_counter_ = -1; + } + } + } + } } // Actualiza al jugador a su posicion, animación y controla los contadores -void Player::update() -{ - move(); - setAnimation(); - shiftColliders(); - updateCooldown(); - updatePowerUp(); - updateInvulnerable(); - updateScoreboard(); - updateContinueCounter(); - updateEnterNameCounter(); - updateShowingName(); +void Player::update() { + move(); + setAnimation(); + shiftColliders(); + updateCooldown(); + updatePowerUp(); + updateInvulnerable(); + updateScoreboard(); + updateContinueCounter(); + updateEnterNameCounter(); + updateShowingName(); } // Incrementa la puntuación del jugador -void Player::addScore(int score) -{ - if (isPlaying()) - { - score_ += score; - } +void Player::addScore(int score) { + if (isPlaying()) { + score_ += score; + } } // Actualiza el panel del marcador -void Player::updateScoreboard() -{ - switch (playing_state_) - { - case PlayerState::CONTINUE: - { - Scoreboard::get()->setContinue(getScoreBoardPanel(), getContinueCounter()); - break; - } - case PlayerState::ENTERING_NAME: - case PlayerState::ENTERING_NAME_GAME_COMPLETED: - { - Scoreboard::get()->setRecordName(getScoreBoardPanel(), enter_name_->getCurrentName()); - Scoreboard::get()->setSelectorPos(getScoreBoardPanel(), getRecordNamePos()); - break; - } - default: - break; - } +void Player::updateScoreboard() { + switch (playing_state_) { + case PlayerState::CONTINUE: { + Scoreboard::get()->setContinue(getScoreBoardPanel(), getContinueCounter()); + break; + } + case PlayerState::ENTERING_NAME: + case PlayerState::ENTERING_NAME_GAME_COMPLETED: { + Scoreboard::get()->setRecordName(getScoreBoardPanel(), enter_name_->getCurrentName()); + Scoreboard::get()->setSelectorPos(getScoreBoardPanel(), getRecordNamePos()); + break; + } + default: + break; + } } // Cambia el modo del marcador -void Player::setScoreboardMode(ScoreboardMode mode) -{ - if (!demo_) - { - Scoreboard::get()->setMode(getScoreBoardPanel(), mode); - } +void Player::setScoreboardMode(ScoreboardMode mode) { + if (!demo_) { + Scoreboard::get()->setMode(getScoreBoardPanel(), mode); + } } // Establece el estado del jugador en el juego -void Player::setPlayingState(PlayerState state) -{ - playing_state_ = state; +void Player::setPlayingState(PlayerState state) { + playing_state_ = state; - switch (playing_state_) - { - case PlayerState::RESPAWNING: - { - setInvulnerable(true); - addCredit(); - playSound("voice_thankyou.wav"); - setPlayingState(PlayerState::PLAYING); - } - case PlayerState::PLAYING: - { - init(); - playing_state_ = PlayerState::PLAYING; - setScoreboardMode(ScoreboardMode::SCORE); - Stage::power_can_be_added = true; - break; - } - case PlayerState::CONTINUE: - { - // Inicializa el contador de continuar - continue_ticks_ = SDL_GetTicks(); - continue_counter_ = 9; - setScoreboardMode(ScoreboardMode::CONTINUE); - playSound("continue_clock.wav"); - break; - } - case PlayerState::WAITING: - { - pos_x_ = default_pos_x_; - setScoreboardMode(ScoreboardMode::WAITING); - break; - } - case PlayerState::ENTERING_NAME: - { - setScoreboardMode(ScoreboardMode::ENTER_NAME); - break; - } - case PlayerState::SHOWING_NAME: - { - showing_name_ticks_ = SDL_GetTicks(); - setScoreboardMode(ScoreboardMode::SHOW_NAME); - Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_); - addScoreToScoreBoard(); - break; - } - case PlayerState::ROLLING: - { - // Activa la animación de rodar - player_sprite_->setCurrentAnimation("rolling"); - player_sprite_->setAnimationSpeed(4); - player_sprite_->setAccelY(0.2f); - player_sprite_->setVelY(-6.6f); - (rand() % 2 == 0) ? player_sprite_->setVelX(3.3f) : player_sprite_->setVelX(-3.3f); - break; - } - case PlayerState::TITLE_ANIMATION: - { - // Activa la animación de rodar - player_sprite_->setCurrentAnimation("walk"); - playSound("voice_thankyou.wav"); - break; - } - case PlayerState::TITLE_HIDDEN: - { - player_sprite_->setVelX(0.0f); - player_sprite_->setVelY(0.0f); - break; - } - case PlayerState::CONTINUE_TIME_OUT: - { - // Activa la animación de morir - player_sprite_->setAccelY(0.2f); - player_sprite_->setVelY(-4.0f); - player_sprite_->setVelX(0.0f); - player_sprite_->setCurrentAnimation("rolling"); - player_sprite_->setAnimationSpeed(5); - setScoreboardMode(ScoreboardMode::GAME_OVER); - playSound("voice_aw_aw_aw.wav"); - playSound("jump.wav"); - break; - } - case PlayerState::GAME_OVER: - { - setScoreboardMode(ScoreboardMode::GAME_OVER); - break; - } - case PlayerState::CELEBRATING: - { - game_completed_ = true; - setScoreboardMode(ScoreboardMode::SCORE); - break; - } - case PlayerState::ENTERING_NAME_GAME_COMPLETED: - { - setWalkingState(PlayerState::WALKING_STOP); - setFiringState(PlayerState::FIRING_NONE); - setScoreboardMode(ScoreboardMode::ENTER_NAME); - break; - } - case PlayerState::LEAVING_SCREEN: - { - step_counter_ = 0; - setScoreboardMode(ScoreboardMode::GAME_COMPLETED); - break; - } - case PlayerState::ENTERING_SCREEN: - { - step_counter_ = 0; - setScoreboardMode(ScoreboardMode::SCORE); - switch (id_) - { - case 1: - pos_x_ = param.game.game_area.rect.x - WIDTH_; - break; + switch (playing_state_) { + case PlayerState::RESPAWNING: { + setInvulnerable(true); + addCredit(); + playSound("voice_thankyou.wav"); + setPlayingState(PlayerState::PLAYING); + } + case PlayerState::PLAYING: { + init(); + playing_state_ = PlayerState::PLAYING; + setScoreboardMode(ScoreboardMode::SCORE); + Stage::power_can_be_added = true; + break; + } + case PlayerState::CONTINUE: { + // Inicializa el contador de continuar + continue_ticks_ = SDL_GetTicks(); + continue_counter_ = 9; + setScoreboardMode(ScoreboardMode::CONTINUE); + playSound("continue_clock.wav"); + break; + } + case PlayerState::WAITING: { + pos_x_ = default_pos_x_; + setScoreboardMode(ScoreboardMode::WAITING); + break; + } + case PlayerState::ENTERING_NAME: { + setScoreboardMode(ScoreboardMode::ENTER_NAME); + break; + } + case PlayerState::SHOWING_NAME: { + showing_name_ticks_ = SDL_GetTicks(); + setScoreboardMode(ScoreboardMode::SHOW_NAME); + Scoreboard::get()->setRecordName(scoreboard_panel_, last_enter_name_); + addScoreToScoreBoard(); + break; + } + case PlayerState::ROLLING: { + // Activa la animación de rodar + player_sprite_->setCurrentAnimation("rolling"); + player_sprite_->setAnimationSpeed(4); + player_sprite_->setAccelY(0.2f); + player_sprite_->setVelY(-6.6f); + (rand() % 2 == 0) ? player_sprite_->setVelX(3.3f) : player_sprite_->setVelX(-3.3f); + break; + } + case PlayerState::TITLE_ANIMATION: { + // Activa la animación de rodar + player_sprite_->setCurrentAnimation("walk"); + playSound("voice_thankyou.wav"); + break; + } + case PlayerState::TITLE_HIDDEN: { + player_sprite_->setVelX(0.0f); + player_sprite_->setVelY(0.0f); + break; + } + case PlayerState::CONTINUE_TIME_OUT: { + // Activa la animación de morir + player_sprite_->setAccelY(0.2f); + player_sprite_->setVelY(-4.0f); + player_sprite_->setVelX(0.0f); + player_sprite_->setCurrentAnimation("rolling"); + player_sprite_->setAnimationSpeed(5); + setScoreboardMode(ScoreboardMode::GAME_OVER); + playSound("voice_aw_aw_aw.wav"); + playSound("jump.wav"); + break; + } + case PlayerState::GAME_OVER: { + setScoreboardMode(ScoreboardMode::GAME_OVER); + break; + } + case PlayerState::CELEBRATING: { + game_completed_ = true; + setScoreboardMode(ScoreboardMode::SCORE); + break; + } + case PlayerState::ENTERING_NAME_GAME_COMPLETED: { + setWalkingState(PlayerState::WALKING_STOP); + setFiringState(PlayerState::FIRING_NONE); + setScoreboardMode(ScoreboardMode::ENTER_NAME); + break; + } + case PlayerState::LEAVING_SCREEN: { + step_counter_ = 0; + setScoreboardMode(ScoreboardMode::GAME_COMPLETED); + break; + } + case PlayerState::ENTERING_SCREEN: { + step_counter_ = 0; + setScoreboardMode(ScoreboardMode::SCORE); + switch (id_) { + case 1: + pos_x_ = param.game.game_area.rect.x - WIDTH_; + break; - case 2: - pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w; - break; + case 2: + pos_x_ = param.game.game_area.rect.x + param.game.game_area.rect.w; + break; - default: - break; - } - break; - } - case PlayerState::CREDITS: - { - vel_x_ = (walking_state_ == PlayerState::WALKING_RIGHT) ? BASE_SPEED_ : -BASE_SPEED_; - break; - } - default: - break; - } + default: + break; + } + break; + } + case PlayerState::CREDITS: { + vel_x_ = (walking_state_ == PlayerState::WALKING_RIGHT) ? BASE_SPEED_ : -BASE_SPEED_; + break; + } + default: + break; + } } // Aumenta el valor de la variable hasta un máximo -void Player::incScoreMultiplier() -{ - score_multiplier_ += 0.1f; - score_multiplier_ = std::min(score_multiplier_, 5.0f); +void Player::incScoreMultiplier() { + score_multiplier_ += 0.1f; + score_multiplier_ = std::min(score_multiplier_, 5.0f); } // Decrementa el valor de la variable hasta un mínimo -void Player::decScoreMultiplier() -{ - score_multiplier_ -= 0.1f; - score_multiplier_ = std::max(score_multiplier_, 1.0f); +void Player::decScoreMultiplier() { + score_multiplier_ -= 0.1f; + score_multiplier_ = std::max(score_multiplier_, 1.0f); } // Establece el valor del estado -void Player::setInvulnerable(bool value) -{ - invulnerable_ = value; - invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER_ : 0; +void Player::setInvulnerable(bool value) { + invulnerable_ = value; + invulnerable_counter_ = invulnerable_ ? INVULNERABLE_COUNTER_ : 0; } // Monitoriza el estado -void Player::updateInvulnerable() -{ - if (playing_state_ == PlayerState::PLAYING) - if (invulnerable_) - { - if (invulnerable_counter_ > 0) - { - --invulnerable_counter_; - invulnerable_counter_ % 8 > 3 ? player_sprite_->getTexture()->setPalette(coffees_) : player_sprite_->getTexture()->setPalette(3); - } - else - { - setInvulnerable(false); - player_sprite_->getTexture()->setPalette(coffees_); - } - } +void Player::updateInvulnerable() { + if (playing_state_ == PlayerState::PLAYING) + if (invulnerable_) { + if (invulnerable_counter_ > 0) { + --invulnerable_counter_; + invulnerable_counter_ % 8 > 3 ? player_sprite_->getTexture()->setPalette(coffees_) : player_sprite_->getTexture()->setPalette(3); + } else { + setInvulnerable(false); + player_sprite_->getTexture()->setPalette(coffees_); + } + } } // Establece el valor de la variable -void Player::setPowerUp() -{ - power_up_ = true; - power_up_counter_ = POWERUP_COUNTER_; +void Player::setPowerUp() { + power_up_ = true; + power_up_counter_ = POWERUP_COUNTER_; } // Actualiza el valor de la variable -void Player::updatePowerUp() -{ - if (playing_state_ == PlayerState::PLAYING) - if (power_up_) - { - --power_up_counter_; - power_up_ = power_up_counter_ > 0; - } +void Player::updatePowerUp() { + if (playing_state_ == PlayerState::PLAYING) + if (power_up_) { + --power_up_counter_; + power_up_ = power_up_counter_ > 0; + } } // Concede un toque extra al jugador -void Player::giveExtraHit() -{ - extra_hit_ = true; - if (coffees_ < 2) - { - coffees_++; - player_sprite_->getTexture()->setPalette(coffees_); - } +void Player::giveExtraHit() { + extra_hit_ = true; + if (coffees_ < 2) { + coffees_++; + player_sprite_->getTexture()->setPalette(coffees_); + } } // Quita el toque extra al jugador -void Player::removeExtraHit() -{ - if (coffees_ > 0) - { - coffees_--; - setInvulnerable(true); - player_sprite_->getTexture()->setPalette(coffees_); - } +void Player::removeExtraHit() { + if (coffees_ > 0) { + coffees_--; + setInvulnerable(true); + player_sprite_->getTexture()->setPalette(coffees_); + } - extra_hit_ = coffees_ == 0 ? false : true; + extra_hit_ = coffees_ == 0 ? false : true; } // Actualiza el circulo de colisión a la posición del jugador -void Player::shiftColliders() -{ - collider_.x = static_cast<int>(pos_x_ + (WIDTH_ / 2)); - collider_.y = static_cast<int>(pos_y_ + (HEIGHT_ / 2)); +void Player::shiftColliders() { + collider_.x = static_cast<int>(pos_x_ + (WIDTH_ / 2)); + collider_.y = static_cast<int>(pos_y_ + (HEIGHT_ / 2)); } // Pone las texturas del jugador -void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture) -{ - player_sprite_->setTexture(texture[0]); - power_sprite_->setTexture(texture[1]); +void Player::setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture) { + player_sprite_->setTexture(texture[0]); + power_sprite_->setTexture(texture[1]); } // Actualiza el contador de continue -void Player::updateContinueCounter() -{ - if (playing_state_ == PlayerState::CONTINUE) - { - constexpr int TICKS_SPEED = 1000; - if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) - { - decContinueCounter(); - } - } +void Player::updateContinueCounter() { + if (playing_state_ == PlayerState::CONTINUE) { + constexpr int TICKS_SPEED = 1000; + if (SDL_GetTicks() - continue_ticks_ > TICKS_SPEED) { + decContinueCounter(); + } + } } // Actualiza el contador de entrar nombre -void Player::updateEnterNameCounter() -{ - if (playing_state_ == PlayerState::ENTERING_NAME || playing_state_ == PlayerState::ENTERING_NAME_GAME_COMPLETED) - { - constexpr int TICKS_SPEED = 1000; - if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) - { - decNameEntryCounter(); - } - } +void Player::updateEnterNameCounter() { + if (playing_state_ == PlayerState::ENTERING_NAME || playing_state_ == PlayerState::ENTERING_NAME_GAME_COMPLETED) { + constexpr int TICKS_SPEED = 1000; + if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { + decNameEntryCounter(); + } + } } // Actualiza el estado de SHOWING_NAME -void Player::updateShowingName() -{ - if (playing_state_ == PlayerState::SHOWING_NAME) - { - constexpr int TICKS_SPEED = 5000; - if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) - { - game_completed_ ? setPlayingState(PlayerState::LEAVING_SCREEN) : setPlayingState(PlayerState::CONTINUE); - } - } +void Player::updateShowingName() { + if (playing_state_ == PlayerState::SHOWING_NAME) { + constexpr int TICKS_SPEED = 5000; + if (SDL_GetTicks() - name_entry_ticks_ > TICKS_SPEED) { + game_completed_ ? setPlayingState(PlayerState::LEAVING_SCREEN) : setPlayingState(PlayerState::CONTINUE); + } + } } // Decrementa el contador de continuar -void Player::decContinueCounter() -{ - continue_ticks_ = SDL_GetTicks(); - --continue_counter_; - if (continue_counter_ < 0) - { - setPlayingState(PlayerState::CONTINUE_TIME_OUT); - } - else - { - playSound("continue_clock.wav"); - } +void Player::decContinueCounter() { + continue_ticks_ = SDL_GetTicks(); + --continue_counter_; + if (continue_counter_ < 0) { + setPlayingState(PlayerState::CONTINUE_TIME_OUT); + } else { + playSound("continue_clock.wav"); + } } // Decrementa el contador de entrar nombre -void Player::decNameEntryCounter() -{ - name_entry_ticks_ = SDL_GetTicks(); +void Player::decNameEntryCounter() { + name_entry_ticks_ = SDL_GetTicks(); - // Actualiza contadores - ++name_entry_idle_counter_; - ++name_entry_total_counter_; + // Actualiza contadores + ++name_entry_idle_counter_; + ++name_entry_total_counter_; - // Comprueba los contadores - if ((name_entry_total_counter_ >= param.game.name_entry_total_time) || - (name_entry_idle_counter_ >= param.game.name_entry_idle_time)) - { - name_entry_total_counter_ = 0; - name_entry_idle_counter_ = 0; - if (playing_state_ == PlayerState::ENTERING_NAME) - { - last_enter_name_ = getRecordName(); - setPlayingState(PlayerState::SHOWING_NAME); - } - else - { - setPlayingState(PlayerState::LEAVING_SCREEN); - } - } + // Comprueba los contadores + if ((name_entry_total_counter_ >= param.game.name_entry_total_time) || + (name_entry_idle_counter_ >= param.game.name_entry_idle_time)) { + name_entry_total_counter_ = 0; + name_entry_idle_counter_ = 0; + if (playing_state_ == PlayerState::ENTERING_NAME) { + last_enter_name_ = getRecordName(); + setPlayingState(PlayerState::SHOWING_NAME); + } else { + setPlayingState(PlayerState::LEAVING_SCREEN); + } + } } // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones -int Player::getRecordNamePos() const -{ - if (enter_name_) - { - return enter_name_->getPosition(); - } +int Player::getRecordNamePos() const { + if (enter_name_) { + return enter_name_->getPosition(); + } - return 0; + return 0; } // Recoloca los sprites -void Player::shiftSprite() -{ - player_sprite_->setPosX(pos_x_); - player_sprite_->setPosY(pos_y_); - power_sprite_->setPosX(getPosX() - power_up_desp_x_); +void Player::shiftSprite() { + player_sprite_->setPosX(pos_x_); + player_sprite_->setPosY(pos_y_); + power_sprite_->setPosX(getPosX() - power_up_desp_x_); } -void Player::playSound(const std::string &name) -{ - if (demo_) - return; +void Player::playSound(const std::string &name) { + if (demo_) + return; - static auto audio = Audio::get(); - audio->playSound(name); + static auto audio = Audio::get(); + audio->playSound(name); } // Añade una puntuación a la tabla de records -void Player::addScoreToScoreBoard() -{ - const auto entry = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC()); - auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); - Options::settings.last_hi_score_entry.at(getId() - 1) = manager->add(entry); - manager->saveToFile(Asset::get()->get("score.bin")); +void Player::addScoreToScoreBoard() { + const auto entry = HiScoreEntry(trim(getLastEnterName()), getScore(), get1CC()); + auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); + Options::settings.last_hi_score_entry.at(getId() - 1) = manager->add(entry); + manager->saveToFile(Asset::get()->get("score.bin")); } \ No newline at end of file diff --git a/source/player.h b/source/player.h index 8feded1..0b0f14b 100644 --- a/source/player.h +++ b/source/player.h @@ -1,108 +1,107 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32, SDL_FRect -#include <memory> // Para unique_ptr, shared_ptr -#include <string> // Para basic_string, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para Uint32, SDL_FRect -#include "animated_sprite.h" // Para AnimatedSprite -#include "enter_name.h" // Para EnterName -#include "manage_hiscore_table.h" // Para HiScoreEntry -#include "options.h" // Para SettingsOptions, settings -#include "utils.h" // Para Circle +#include <memory> // Para unique_ptr, shared_ptr +#include <string> // Para basic_string, string +#include <vector> // Para vector + +#include "animated_sprite.h" // Para AnimatedSprite +#include "enter_name.h" // Para EnterName +#include "manage_hiscore_table.h" // Para HiScoreEntry +#include "options.h" // Para SettingsOptions, settings +#include "utils.h" // Para Circle class Texture; enum class InputAction : int; enum class ScoreboardMode; // --- Estados posibles del jugador --- -enum class PlayerState -{ +enum class PlayerState { // Estados de movimiento - WALKING_LEFT, // Caminando hacia la izquierda - WALKING_RIGHT, // Caminando hacia la derecha - WALKING_STOP, // Parado, sin moverse + WALKING_LEFT, // Caminando hacia la izquierda + WALKING_RIGHT, // Caminando hacia la derecha + WALKING_STOP, // Parado, sin moverse // Estados de disparo - FIRING_UP, // Disparando hacia arriba - FIRING_LEFT, // Disparando hacia la izquierda - FIRING_RIGHT, // Disparando hacia la derecha - FIRING_NONE, // No está disparando + FIRING_UP, // Disparando hacia arriba + FIRING_LEFT, // Disparando hacia la izquierda + FIRING_RIGHT, // Disparando hacia la derecha + FIRING_NONE, // No está disparando // Estados de retroceso tras disparar - RECOILING_UP, // Retroceso tras disparar hacia arriba - RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda - RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha + RECOILING_UP, // Retroceso tras disparar hacia arriba + RECOILING_LEFT, // Retroceso tras disparar hacia la izquierda + RECOILING_RIGHT, // Retroceso tras disparar hacia la derecha // Estados de enfriamiento tras disparar - COOLING_UP, // Enfriando tras disparar hacia arriba - COOLING_LEFT, // Enfriando tras disparar hacia la izquierda - COOLING_RIGHT, // Enfriando tras disparar hacia la derecha + COOLING_UP, // Enfriando tras disparar hacia arriba + COOLING_LEFT, // Enfriando tras disparar hacia la izquierda + COOLING_RIGHT, // Enfriando tras disparar hacia la derecha // Estados generales del jugador - PLAYING, // Está jugando activamente - CONTINUE, // Cuenta atrás para continuar tras perder - CONTINUE_TIME_OUT, // Se ha terminado la cuenta atras para continuar y se retira al jugador de la zona de juego - WAITING, // Esperando para entrar a jugar - ENTERING_NAME, // Introduciendo nombre para la tabla de puntuaciones - SHOWING_NAME, // Mostrando el nombre introducido - ROLLING, // El jugador está muriendo (animación de muerte) - LYING_ON_THE_FLOOR_FOREVER, // El jugador está inconsciente para siempre en el suelo (demo) - GAME_OVER, // Fin de la partida, no puede jugar - CELEBRATING, // Celebrando victoria (pose de victoria) - ENTERING_NAME_GAME_COMPLETED, // Introduciendo nombre tras completar el juego - LEAVING_SCREEN, // Saliendo de la pantalla (animación) - ENTERING_SCREEN, // Entrando a la pantalla (animación) - CREDITS, // Estado para mostrar los créditos del juego - TITLE_ANIMATION, // Animacion para el titulo - TITLE_HIDDEN, // Animacion para el titulo - RESPAWNING, // Tras continuar y volver al juego + PLAYING, // Está jugando activamente + CONTINUE, // Cuenta atrás para continuar tras perder + CONTINUE_TIME_OUT, // Se ha terminado la cuenta atras para continuar y se retira al jugador de la zona de juego + WAITING, // Esperando para entrar a jugar + ENTERING_NAME, // Introduciendo nombre para la tabla de puntuaciones + SHOWING_NAME, // Mostrando el nombre introducido + ROLLING, // El jugador está muriendo (animación de muerte) + LYING_ON_THE_FLOOR_FOREVER, // El jugador está inconsciente para siempre en el suelo (demo) + GAME_OVER, // Fin de la partida, no puede jugar + CELEBRATING, // Celebrando victoria (pose de victoria) + ENTERING_NAME_GAME_COMPLETED, // Introduciendo nombre tras completar el juego + LEAVING_SCREEN, // Saliendo de la pantalla (animación) + ENTERING_SCREEN, // Entrando a la pantalla (animación) + CREDITS, // Estado para mostrar los créditos del juego + TITLE_ANIMATION, // Animacion para el titulo + TITLE_HIDDEN, // Animacion para el titulo + RESPAWNING, // Tras continuar y volver al juego }; // --- Clase Player --- -class Player -{ -public: +class Player { + public: // --- Constructor y destructor --- Player(int id, float x, int y, bool demo, SDL_FRect &play_area, std::vector<std::shared_ptr<Texture>> texture, const std::vector<std::vector<std::string>> &animations); ~Player() = default; // --- Inicialización y ciclo de vida --- - void init(); // Inicializa el jugador - void update(); // Actualiza estado, animación y contadores - void render(); // Dibuja el jugador en pantalla + void init(); // Inicializa el jugador + void update(); // Actualiza estado, animación y contadores + void render(); // Dibuja el jugador en pantalla // --- Entrada y control --- - void setInput(InputAction input); // Procesa entrada general - void setInputPlaying(InputAction input); // Procesa entrada en modo jugando - void setInputEnteringName(InputAction input); // Procesa entrada al introducir nombre + void setInput(InputAction input); // Procesa entrada general + void setInputPlaying(InputAction input); // Procesa entrada en modo jugando + void setInputEnteringName(InputAction input); // Procesa entrada al introducir nombre // --- Movimiento y animación --- - void move(); // Mueve el jugador - void setAnimation(); // Establece la animación según el estado + void move(); // Mueve el jugador + void setAnimation(); // Establece la animación según el estado // --- Texturas y animaciones --- - void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador + void setPlayerTextures(const std::vector<std::shared_ptr<Texture>> &texture); // Cambia las texturas del jugador // --- Estados y contadores --- - void updateCooldown(); // Actualiza el cooldown de disparo + void updateCooldown(); // Actualiza el cooldown de disparo // --- Puntuación y marcador --- - void addScore(int score); // Añade puntos - void incScoreMultiplier(); // Incrementa el multiplicador - void decScoreMultiplier(); // Decrementa el multiplicador + void addScore(int score); // Añade puntos + void incScoreMultiplier(); // Incrementa el multiplicador + void decScoreMultiplier(); // Decrementa el multiplicador // --- Estados de juego --- - void setPlayingState(PlayerState state); // Cambia el estado de juego - void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad - void setPowerUp(); // Activa el modo PowerUp - void updatePowerUp(); // Actualiza el valor de PowerUp - void giveExtraHit(); // Concede un toque extra al jugador - void removeExtraHit(); // Quita el toque extra al jugador - void decContinueCounter(); // Decrementa el contador de continuar + void setPlayingState(PlayerState state); // Cambia el estado de juego + void setInvulnerable(bool value); // Establece el valor del estado de invulnerabilidad + void setPowerUp(); // Activa el modo PowerUp + void updatePowerUp(); // Actualiza el valor de PowerUp + void giveExtraHit(); // Concede un toque extra al jugador + void removeExtraHit(); // Quita el toque extra al jugador + void decContinueCounter(); // Decrementa el contador de continuar // --- Getters y comprobaciones de estado --- - int getRecordNamePos() const; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones + int getRecordNamePos() const; // Obtiene la posición que se está editando del nombre del jugador para la tabla de mejores puntuaciones // Comprobación de playing_state bool isLyingOnTheFloorForever() const { return playing_state_ == PlayerState::LYING_ON_THE_FLOOR_FOREVER; } @@ -160,73 +159,73 @@ public: void setWalkingState(PlayerState state) { walking_state_ = state; } void addCredit() { ++credits_used_; } -private: + private: // --- Constantes --- - static constexpr int POWERUP_COUNTER_ = 1500; // Duración del estado PowerUp - static constexpr int INVULNERABLE_COUNTER_ = 200; // Duración del estado invulnerable - static constexpr int WIDTH_ = 30; // Anchura - static constexpr int HEIGHT_ = 30; // Altura - static constexpr float BASE_SPEED_ = 1.5f; // Velocidad base del jugador + static constexpr int POWERUP_COUNTER_ = 1500; // Duración del estado PowerUp + static constexpr int INVULNERABLE_COUNTER_ = 200; // Duración del estado invulnerable + static constexpr int WIDTH_ = 30; // Anchura + static constexpr int HEIGHT_ = 30; // Altura + static constexpr float BASE_SPEED_ = 1.5f; // Velocidad base del jugador static constexpr int COOLING_DURATION_ = 50; static constexpr int COOLING_COMPLETE_ = 0; // --- Objetos y punteros --- - std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador - std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope - std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre + std::unique_ptr<AnimatedSprite> player_sprite_; // Sprite para dibujar el jugador + std::unique_ptr<AnimatedSprite> power_sprite_; // Sprite para dibujar el aura del jugador con el poder a tope + std::unique_ptr<EnterName> enter_name_; // Clase utilizada para introducir el nombre // --- Variables de estado --- - int id_; // Número de identificación para el jugador. Player1 = 1, Player2 = 2 - SDL_FRect play_area_; // Rectángulo con la zona de juego - float pos_x_ = 0.0f; // Posición en el eje X - int pos_y_ = 0; // Posición en el eje Y - float default_pos_x_; // Posición inicial para el jugador - int default_pos_y_; // Posición inicial para el jugador - float vel_x_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje X - int vel_y_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje Y - int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar - int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso - int recoiling_state_duration_ = 0; // Numero de frames que dura el estado de retroceso - int cooling_state_counter_ = 0; // Contador para la animación del estado cooling - int score_ = 0; // Puntos del jugador - float score_multiplier_ = 1.0f; // Multiplicador de puntos - PlayerState walking_state_ = PlayerState::WALKING_STOP; // Estado del jugador al moverse - PlayerState firing_state_ = PlayerState::FIRING_NONE; // Estado del jugador al disparar - PlayerState playing_state_ = PlayerState::WAITING; // Estado del jugador en el juego - bool invulnerable_ = true; // Indica si el jugador es invulnerable - int invulnerable_counter_ = INVULNERABLE_COUNTER_; // Contador para la invulnerabilidad - bool extra_hit_ = false; // Indica si el jugador tiene un toque extra - int coffees_ = 0; // Indica cuántos cafés lleva acumulados - bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp - int power_up_counter_ = POWERUP_COUNTER_; // Temporizador para el modo PowerUp - int power_up_desp_x_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador - Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador - int continue_counter_ = 10; // Contador para poder continuar - Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo - int scoreboard_panel_ = 0; // Panel del marcador asociado al jugador - std::string name_; // Nombre del jugador - int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse - bool demo_ = false; // Para que el jugador sepa si está en el modo demostración - int name_entry_idle_counter_ = 0; // Contador para poner nombre - int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre - Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo - Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME - int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente - bool game_completed_ = false; // Indica si ha completado el juego - int credits_used_ = 1; // Indica el número de veces que ha continuado - std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones + int id_; // Número de identificación para el jugador. Player1 = 1, Player2 = 2 + SDL_FRect play_area_; // Rectángulo con la zona de juego + float pos_x_ = 0.0f; // Posición en el eje X + int pos_y_ = 0; // Posición en el eje Y + float default_pos_x_; // Posición inicial para el jugador + int default_pos_y_; // Posición inicial para el jugador + float vel_x_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje X + int vel_y_ = 0.0f; // Cantidad de píxeles a desplazarse en el eje Y + int cant_fire_counter_ = 0; // Contador durante el cual no puede disparar + int recoiling_state_counter_ = 0; // Contador para la animación del estado de retroceso + int recoiling_state_duration_ = 0; // Numero de frames que dura el estado de retroceso + int cooling_state_counter_ = 0; // Contador para la animación del estado cooling + int score_ = 0; // Puntos del jugador + float score_multiplier_ = 1.0f; // Multiplicador de puntos + PlayerState walking_state_ = PlayerState::WALKING_STOP; // Estado del jugador al moverse + PlayerState firing_state_ = PlayerState::FIRING_NONE; // Estado del jugador al disparar + PlayerState playing_state_ = PlayerState::WAITING; // Estado del jugador en el juego + bool invulnerable_ = true; // Indica si el jugador es invulnerable + int invulnerable_counter_ = INVULNERABLE_COUNTER_; // Contador para la invulnerabilidad + bool extra_hit_ = false; // Indica si el jugador tiene un toque extra + int coffees_ = 0; // Indica cuántos cafés lleva acumulados + bool power_up_ = false; // Indica si el jugador tiene activo el modo PowerUp + int power_up_counter_ = POWERUP_COUNTER_; // Temporizador para el modo PowerUp + int power_up_desp_x_ = 0; // Desplazamiento del sprite de PowerUp respecto al sprite del jugador + Circle collider_ = Circle(0, 0, 9); // Círculo de colisión del jugador + int continue_counter_ = 10; // Contador para poder continuar + Uint32 continue_ticks_ = 0; // Variable para poder cambiar el contador de continue en función del tiempo + int scoreboard_panel_ = 0; // Panel del marcador asociado al jugador + std::string name_; // Nombre del jugador + int controller_index_ = 0; // Índice del array de mandos que utilizará para moverse + bool demo_ = false; // Para que el jugador sepa si está en el modo demostración + int name_entry_idle_counter_ = 0; // Contador para poner nombre + int name_entry_total_counter_ = 0; // Segundos totales que lleva acumulados poniendo nombre + Uint32 name_entry_ticks_ = 0; // Variable para poder cambiar el contador de poner nombre en función del tiempo + Uint32 showing_name_ticks_ = 0; // Tiempo en el que se entra al estado SHOWING_NAME + int step_counter_ = 0; // Cuenta los pasos para los estados en los que camina automáticamente + bool game_completed_ = false; // Indica si ha completado el juego + int credits_used_ = 1; // Indica el número de veces que ha continuado + std::string last_enter_name_; // Último nombre introducido en la tabla de puntuaciones // --- Métodos internos --- - void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador - void shiftSprite(); // Recoloca el sprite - void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad - void updateContinueCounter(); // Actualiza el contador de continue - void updateEnterNameCounter(); // Actualiza el contador de entrar nombre - void updateShowingName(); // Actualiza el estado SHOWING_NAME - void decNameEntryCounter(); // Decrementa el contador de entrar nombre - void updateScoreboard(); // Actualiza el panel del marcador - void setScoreboardMode(ScoreboardMode mode); // Cambia el modo del marcador - void playSound(const std::string &name); // Hace sonar un sonido + void shiftColliders(); // Actualiza el círculo de colisión a la posición del jugador + void shiftSprite(); // Recoloca el sprite + void updateInvulnerable(); // Monitoriza el estado de invulnerabilidad + void updateContinueCounter(); // Actualiza el contador de continue + void updateEnterNameCounter(); // Actualiza el contador de entrar nombre + void updateShowingName(); // Actualiza el estado SHOWING_NAME + void decNameEntryCounter(); // Decrementa el contador de entrar nombre + void updateScoreboard(); // Actualiza el panel del marcador + void setScoreboardMode(ScoreboardMode mode); // Cambia el modo del marcador + void playSound(const std::string &name); // Hace sonar un sonido bool isRenderable() const { return !isWaiting() && !isGameOver() && !isTitleHidden(); } - void addScoreToScoreBoard(); // Añade una puntuación a la tabla de records + void addScoreToScoreBoard(); // Añade una puntuación a la tabla de records }; \ No newline at end of file diff --git a/source/resource.cpp b/source/resource.cpp index 388dd4b..ca5a679 100644 --- a/source/resource.cpp +++ b/source/resource.cpp @@ -1,20 +1,21 @@ #include "resource.h" -#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_L... -#include <stdlib.h> // Para exit +#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_L... +#include <stdlib.h> // Para exit + #include <algorithm> // Para find_if #include <array> // Para array #include <stdexcept> // Para runtime_error -#include "asset.h" // Para Asset, AssetType -#include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_... -#include "lang.h" // Para getText -#include "param.h" // Para Param, param, ParamResource, ParamGame -#include "screen.h" // Para Screen -#include "text.h" // Para Text, loadTextFile, TextFile (ptr o... +#include "asset.h" // Para Asset, AssetType +#include "external/jail_audio.h" // Para JA_DeleteMusic, JA_DeleteSound, JA_... +#include "lang.h" // Para getText +#include "param.h" // Para Param, param, ParamResource, ParamGame +#include "screen.h" // Para Screen +#include "text.h" // Para Text, loadTextFile, TextFile (ptr o... -struct JA_Music_t; // lines 11-11 -struct JA_Sound_t; // lines 12-12 +struct JA_Music_t; // lines 11-11 +struct JA_Sound_t; // lines 12-12 // Singleton Resource *Resource::instance_ = nullptr; @@ -35,8 +36,7 @@ Resource::Resource() : loading_text_(Screen::get()->getText()) { load(); } Resource::~Resource() { clear(); } // Vacia todos los vectores de recursos -void Resource::clear() -{ +void Resource::clear() { clearSounds(); clearMusics(); textures_.clear(); @@ -47,8 +47,7 @@ void Resource::clear() } // Carga todos los recursos del juego y muestra el progreso de carga -void Resource::load() -{ +void Resource::load() { // Prepara la gestión del progreso de carga calculateTotalResources(); initProgressBar(); @@ -59,15 +58,15 @@ void Resource::load() screen->setVSync(false); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** LOADING RESOURCES"); - loadSounds(); // Carga sonidos - loadMusics(); // Carga músicas - loadTextures(); // Carga texturas - loadTextFiles(); // Carga ficheros de texto - loadAnimations(); // Carga animaciones - loadDemoData(); // Carga datos de demo - addPalettes(); // Añade paletas a las texturas - createText(); // Crea objetos de texto - createTextures(); // Crea texturas a partir de texto + loadSounds(); // Carga sonidos + loadMusics(); // Carga músicas + loadTextures(); // Carga texturas + loadTextFiles(); // Carga ficheros de texto + loadAnimations(); // Carga animaciones + loadDemoData(); // Carga datos de demo + addPalettes(); // Añade paletas a las texturas + createText(); // Crea objetos de texto + createTextures(); // Crea texturas a partir de texto SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n** RESOURCES LOADED"); // Restablece el sincronismo vertical a su valor original @@ -75,28 +74,23 @@ void Resource::load() } // Recarga todos los recursos (limpia y vuelve a cargar) -void Resource::reload() -{ +void Resource::reload() { clear(); load(); } // Recarga solo las texturas y paletas -void Resource::reloadTextures() -{ +void Resource::reloadTextures() { loadTextures(); addPalettes(); createTextures(); } // Obtiene el sonido a partir de un nombre. Lanza excepción si no existe. -JA_Sound_t *Resource::getSound(const std::string &name) -{ - auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s) - { return s.name == name; }); +JA_Sound_t *Resource::getSound(const std::string &name) { + auto it = std::find_if(sounds_.begin(), sounds_.end(), [&name](const auto &s) { return s.name == name; }); - if (it != sounds_.end()) - { + if (it != sounds_.end()) { return it->sound; } @@ -105,13 +99,10 @@ JA_Sound_t *Resource::getSound(const std::string &name) } // Obtiene la música a partir de un nombre. Lanza excepción si no existe. -JA_Music_t *Resource::getMusic(const std::string &name) -{ - auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m) - { return m.name == name; }); +JA_Music_t *Resource::getMusic(const std::string &name) { + auto it = std::find_if(musics_.begin(), musics_.end(), [&name](const auto &m) { return m.name == name; }); - if (it != musics_.end()) - { + if (it != musics_.end()) { return it->music; } @@ -120,13 +111,10 @@ JA_Music_t *Resource::getMusic(const std::string &name) } // Obtiene la textura a partir de un nombre. Lanza excepción si no existe. -std::shared_ptr<Texture> Resource::getTexture(const std::string &name) -{ - auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) - { return t.name == name; }); +std::shared_ptr<Texture> Resource::getTexture(const std::string &name) { + auto it = std::find_if(textures_.begin(), textures_.end(), [&name](const auto &t) { return t.name == name; }); - if (it != textures_.end()) - { + if (it != textures_.end()) { return it->texture; } @@ -135,13 +123,10 @@ std::shared_ptr<Texture> Resource::getTexture(const std::string &name) } // Obtiene el fichero de texto a partir de un nombre. Lanza excepción si no existe. -std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name) -{ - auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) - { return t.name == name; }); +std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name) { + auto it = std::find_if(text_files_.begin(), text_files_.end(), [&name](const auto &t) { return t.name == name; }); - if (it != text_files_.end()) - { + if (it != text_files_.end()) { return it->text_file; } @@ -150,13 +135,10 @@ std::shared_ptr<TextFile> Resource::getTextFile(const std::string &name) } // Obtiene el objeto de texto a partir de un nombre. Lanza excepción si no existe. -std::shared_ptr<Text> Resource::getText(const std::string &name) -{ - auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t) - { return t.name == name; }); +std::shared_ptr<Text> Resource::getText(const std::string &name) { + auto it = std::find_if(texts_.begin(), texts_.end(), [&name](const auto &t) { return t.name == name; }); - if (it != texts_.end()) - { + if (it != texts_.end()) { return it->text; } @@ -165,13 +147,10 @@ std::shared_ptr<Text> Resource::getText(const std::string &name) } // Obtiene la animación a partir de un nombre. Lanza excepción si no existe. -AnimationsFileBuffer &Resource::getAnimation(const std::string &name) -{ - auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a) - { return a.name == name; }); +AnimationsFileBuffer &Resource::getAnimation(const std::string &name) { + auto it = std::find_if(animations_.begin(), animations_.end(), [&name](const auto &a) { return a.name == name; }); - if (it != animations_.end()) - { + if (it != animations_.end()) { return it->animation; } @@ -180,20 +159,17 @@ AnimationsFileBuffer &Resource::getAnimation(const std::string &name) } // Obtiene el fichero con los datos para el modo demostración a partir de un índice -DemoData &Resource::getDemoData(int index) -{ +DemoData &Resource::getDemoData(int index) { return demos_.at(index); } // Carga los sonidos del juego -void Resource::loadSounds() -{ +void Resource::loadSounds() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> SOUND FILES"); auto list = Asset::get()->getListByType(AssetType::SOUND); sounds_.clear(); - for (const auto &l : list) - { + for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); sounds_.emplace_back(Resource::ResourceSound(name, JA_LoadSound(l.c_str()))); @@ -202,14 +178,12 @@ void Resource::loadSounds() } // Carga las músicas del juego -void Resource::loadMusics() -{ +void Resource::loadMusics() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> MUSIC FILES"); auto list = Asset::get()->getListByType(AssetType::MUSIC); musics_.clear(); - for (const auto &l : list) - { + for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); musics_.emplace_back(Resource::ResourceMusic(name, JA_LoadMusic(l.c_str()))); @@ -218,14 +192,12 @@ void Resource::loadMusics() } // Carga las texturas del juego -void Resource::loadTextures() -{ +void Resource::loadTextures() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXTURES"); auto list = Asset::get()->getListByType(AssetType::BITMAP); textures_.clear(); - for (const auto &l : list) - { + for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); textures_.emplace_back(Resource::ResourceTexture(name, std::make_shared<Texture>(Screen::get()->getRenderer(), l))); @@ -233,14 +205,12 @@ void Resource::loadTextures() } // Carga los ficheros de texto del juego -void Resource::loadTextFiles() -{ +void Resource::loadTextFiles() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> TEXT FILES"); auto list = Asset::get()->getListByType(AssetType::FONT); text_files_.clear(); - for (const auto &l : list) - { + for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); text_files_.emplace_back(Resource::ResourceTextFile(name, loadTextFile(l))); @@ -248,14 +218,12 @@ void Resource::loadTextFiles() } // Carga las animaciones del juego -void Resource::loadAnimations() -{ +void Resource::loadAnimations() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> ANIMATIONS"); auto list = Asset::get()->getListByType(AssetType::ANIMATION); animations_.clear(); - for (const auto &l : list) - { + for (const auto &l : list) { auto name = getFileName(l); updateLoadingProgress(name); animations_.emplace_back(Resource::ResourceAnimation(name, loadAnimationsFromFile(l))); @@ -263,22 +231,19 @@ void Resource::loadAnimations() } // Carga los datos para el modo demostración -void Resource::loadDemoData() -{ +void Resource::loadDemoData() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> DEMO FILES"); constexpr std::array<const char *, 2> demo_files = {"demo1.bin", "demo2.bin"}; - for (const auto &file : demo_files) - { + for (const auto &file : demo_files) { updateLoadingProgress(file); demos_.emplace_back(loadDemoDataFromFile(Asset::get()->get(file))); } } // Añade paletas de colores a las texturas principales -void Resource::addPalettes() -{ +void Resource::addPalettes() { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\n>> PALETTES"); // Paletas para el jugador 1 @@ -293,10 +258,8 @@ void Resource::addPalettes() } // Crea texturas a partir de textos para mostrar puntuaciones y mensajes -void Resource::createTextures() -{ - struct NameAndText - { +void Resource::createTextures() { + struct NameAndText { std::string name; std::string text; @@ -317,8 +280,7 @@ void Resource::createTextures() {"game_text_1000000_points", Lang::getText("[GAME_TEXT] 8")}}; auto text = getText("04b_25"); - for (const auto &s : strings) - { + for (const auto &s : strings) { textures_.emplace_back(Resource::ResourceTexture(s.name, text->writeToTexture(s.text, 1, -2))); printWithDots("Texture : ", s.name, "[ DONE ]"); } @@ -332,18 +294,15 @@ void Resource::createTextures() {"game_text_game_over", "Game Over"}}; auto text2 = getText("04b_25_2x"); - for (const auto &s : strings2X) - { + for (const auto &s : strings2X) { textures_.emplace_back(Resource::ResourceTexture(s.name, text2->writeToTexture(s.text, 1, -4))); printWithDots("Texture : ", s.name, "[ DONE ]"); } } // Crea los objetos de texto a partir de los archivos de textura y texto -void Resource::createText() -{ - struct ResourceInfo - { +void Resource::createText() { + struct ResourceInfo { std::string key; std::string textureFile; std::string textFile; @@ -368,22 +327,16 @@ void Resource::createText() {"smb2", "smb2.png", "smb2.txt"}, {"smb2_grad", "smb2_grad.png", "smb2.txt"}}; - for (const auto &resource : resources) - { - texts_.emplace_back(Resource::ResourceText(resource.key, std::make_shared<Text>( - getTexture(resource.textureFile), - getTextFile(resource.textFile)))); + for (const auto &resource : resources) { + texts_.emplace_back(Resource::ResourceText(resource.key, std::make_shared<Text>(getTexture(resource.textureFile), getTextFile(resource.textFile)))); printWithDots("Text : ", resource.key, "[ DONE ]"); } } // Vacía el vector de sonidos y libera la memoria asociada -void Resource::clearSounds() -{ - for (auto &sound : sounds_) - { - if (sound.sound) - { +void Resource::clearSounds() { + for (auto &sound : sounds_) { + if (sound.sound) { JA_DeleteSound(sound.sound); sound.sound = nullptr; } @@ -392,12 +345,9 @@ void Resource::clearSounds() } // Vacía el vector de músicas y libera la memoria asociada -void Resource::clearMusics() -{ - for (auto &music : musics_) - { - if (music.music) - { +void Resource::clearMusics() { + for (auto &music : musics_) { + if (music.music) { JA_DeleteMusic(music.music); music.music = nullptr; } @@ -406,8 +356,7 @@ void Resource::clearMusics() } // Calcula el número total de recursos a cargar y reinicia el contador de carga -void Resource::calculateTotalResources() -{ +void Resource::calculateTotalResources() { const std::array<AssetType, 6> ASSET_TYPES = { AssetType::SOUND, AssetType::MUSIC, @@ -417,8 +366,7 @@ void Resource::calculateTotalResources() AssetType::DEMODATA}; size_t total = 0; - for (const auto &asset_type : ASSET_TYPES) - { + for (const auto &asset_type : ASSET_TYPES) { auto list = Asset::get()->getListByType(asset_type); total += list.size(); } @@ -427,8 +375,7 @@ void Resource::calculateTotalResources() } // Muestra el progreso de carga en pantalla (barra y texto) -void Resource::renderProgress() -{ +void Resource::renderProgress() { // Obtiene la pantalla y el renderer auto screen = Screen::get(); auto renderer = screen->getRenderer(); @@ -462,29 +409,24 @@ void Resource::renderProgress() } // Comprueba los eventos durante la carga (permite salir con ESC o cerrar ventana) -void Resource::checkEvents() -{ +void Resource::checkEvents() { SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_EVENT_QUIT: - exit(0); - break; - case SDL_EVENT_KEY_DOWN: - if (event.key.key == SDLK_ESCAPE) - { + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_QUIT: exit(0); - } - break; + break; + case SDL_EVENT_KEY_DOWN: + if (event.key.key == SDLK_ESCAPE) { + exit(0); + } + break; } } } // Actualiza el progreso de carga, muestra la barra y procesa eventos -void Resource::updateLoadingProgress(std::string name) -{ +void Resource::updateLoadingProgress(std::string name) { loading_resource_name_ = name; loading_count_.increase(); updateProgressBar(); @@ -493,8 +435,7 @@ void Resource::updateLoadingProgress(std::string name) } // Inicializa los rectangulos que definen la barra de progreso -void Resource::initProgressBar() -{ +void Resource::initProgressBar() { constexpr float X_PADDING = 20.0f; constexpr float Y_PADDING = 20.0f; constexpr float BAR_HEIGHT = 10.0f; @@ -508,7 +449,6 @@ void Resource::initProgressBar() } // Actualiza la barra de estado -void Resource::updateProgressBar() -{ +void Resource::updateProgressBar() { loading_full_rect_.w = loading_wired_rect_.w * loading_count_.getPercentage(); } \ No newline at end of file diff --git a/source/resource.h b/source/resource.h index dce9ef1..c60df7b 100644 --- a/source/resource.h +++ b/source/resource.h @@ -1,157 +1,149 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect -#include <stddef.h> // Para size_t -#include <memory> // Para shared_ptr -#include <string> // Para basic_string, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FRect +#include <stddef.h> // Para size_t -#include "animated_sprite.h" // Para AnimationsFileBuffer -#include "text.h" // Para Text, TextFile -#include "texture.h" // Para Texture -#include "utils.h" // Para DemoData +#include <memory> // Para shared_ptr +#include <string> // Para basic_string, string +#include <vector> // Para vector + +#include "animated_sprite.h" // Para AnimationsFileBuffer +#include "text.h" // Para Text, TextFile +#include "texture.h" // Para Texture +#include "utils.h" // Para DemoData struct JA_Music_t; struct JA_Sound_t; // --- Clase Resource: gestiona todos los recursos del juego (singleton) --- -class Resource -{ -public: +class Resource { + public: // --- Métodos de singleton --- - static void init(); // Inicializa el objeto Resource - static void destroy(); // Libera el objeto Resource - static Resource *get(); // Obtiene el puntero al objeto Resource + static void init(); // Inicializa el objeto Resource + static void destroy(); // Libera el objeto Resource + static Resource *get(); // Obtiene el puntero al objeto Resource // --- Métodos de acceso a recursos --- - JA_Sound_t *getSound(const std::string &name); // Obtiene el sonido por nombre - JA_Music_t *getMusic(const std::string &name); // Obtiene la música por nombre - std::shared_ptr<Texture> getTexture(const std::string &name); // Obtiene la textura por nombre - std::shared_ptr<TextFile> getTextFile(const std::string &name); // Obtiene el fichero de texto por nombre - std::shared_ptr<Text> getText(const std::string &name); // Obtiene el objeto de texto por nombre - AnimationsFileBuffer &getAnimation(const std::string &name); // Obtiene la animación por nombre - DemoData &getDemoData(int index); // Obtiene los datos de demo por índice + JA_Sound_t *getSound(const std::string &name); // Obtiene el sonido por nombre + JA_Music_t *getMusic(const std::string &name); // Obtiene la música por nombre + std::shared_ptr<Texture> getTexture(const std::string &name); // Obtiene la textura por nombre + std::shared_ptr<TextFile> getTextFile(const std::string &name); // Obtiene el fichero de texto por nombre + std::shared_ptr<Text> getText(const std::string &name); // Obtiene el objeto de texto por nombre + AnimationsFileBuffer &getAnimation(const std::string &name); // Obtiene la animación por nombre + DemoData &getDemoData(int index); // Obtiene los datos de demo por índice // --- Métodos de recarga de recursos --- - void reload(); // Recarga todos los recursos - void reloadTextures(); // Recarga solo las texturas + void reload(); // Recarga todos los recursos + void reloadTextures(); // Recarga solo las texturas -private: + private: // --- Estructuras para recursos individuales --- - struct ResourceSound - { - std::string name; // Nombre del sonido - JA_Sound_t *sound; // Objeto con el sonido + struct ResourceSound { + std::string name; // Nombre del sonido + JA_Sound_t *sound; // Objeto con el sonido ResourceSound(const std::string &name, JA_Sound_t *sound) : name(name), sound(sound) {} }; - struct ResourceMusic - { - std::string name; // Nombre de la música - JA_Music_t *music; // Objeto con la música + struct ResourceMusic { + std::string name; // Nombre de la música + JA_Music_t *music; // Objeto con la música ResourceMusic(const std::string &name, JA_Music_t *music) : name(name), music(music) {} }; - struct ResourceTexture - { - std::string name; // Nombre de la textura - std::shared_ptr<Texture> texture; // Objeto con la textura + struct ResourceTexture { + std::string name; // Nombre de la textura + std::shared_ptr<Texture> texture; // Objeto con la textura ResourceTexture(const std::string &name, std::shared_ptr<Texture> texture) : name(name), texture(texture) {} }; - struct ResourceTextFile - { - std::string name; // Nombre del fichero - std::shared_ptr<TextFile> text_file; // Objeto con los descriptores de la fuente de texto + struct ResourceTextFile { + std::string name; // Nombre del fichero + std::shared_ptr<TextFile> text_file; // Objeto con los descriptores de la fuente de texto ResourceTextFile(const std::string &name, std::shared_ptr<TextFile> text_file) : name(name), text_file(text_file) {} }; - struct ResourceText - { - std::string name; // Nombre del objeto - std::shared_ptr<Text> text; // Objeto de texto + struct ResourceText { + std::string name; // Nombre del objeto + std::shared_ptr<Text> text; // Objeto de texto ResourceText(const std::string &name, std::shared_ptr<Text> text) : name(name), text(text) {} }; - struct ResourceAnimation - { - std::string name; // Nombre de la animación - AnimationsFileBuffer animation; // Objeto con las animaciones + struct ResourceAnimation { + std::string name; // Nombre de la animación + AnimationsFileBuffer animation; // Objeto con las animaciones ResourceAnimation(const std::string &name, const AnimationsFileBuffer &animation) : name(name), animation(animation) {} }; // --- Estructura para el progreso de carga --- - struct ResourceCount - { - size_t total; // Número total de recursos - size_t loaded; // Número de recursos cargados + struct ResourceCount { + size_t total; // Número total de recursos + size_t loaded; // Número de recursos cargados ResourceCount() : total(0), loaded(0) {} ResourceCount(size_t total) : total(total), loaded(0) {} void add(size_t amount) { loaded += amount; } void increase() { loaded++; } - float getPercentage() const - { + float getPercentage() const { return total > 0 ? static_cast<float>(loaded) / static_cast<float>(total) : 0.0f; } }; // --- Instancia singleton --- - static Resource *instance_; // Instancia única de Resource + static Resource *instance_; // Instancia única de Resource // --- Vectores de recursos --- - std::vector<ResourceSound> sounds_; // Vector con los sonidos - std::vector<ResourceMusic> musics_; // Vector con las músicas - std::vector<ResourceTexture> textures_; // Vector con las texturas - std::vector<ResourceTextFile> text_files_; // Vector con los ficheros de texto - std::vector<ResourceText> texts_; // Vector con los objetos de texto - std::vector<ResourceAnimation> animations_; // Vector con las animaciones - std::vector<DemoData> demos_; // Vector con los ficheros de datos para el modo demostración + std::vector<ResourceSound> sounds_; // Vector con los sonidos + std::vector<ResourceMusic> musics_; // Vector con las músicas + std::vector<ResourceTexture> textures_; // Vector con las texturas + std::vector<ResourceTextFile> text_files_; // Vector con los ficheros de texto + std::vector<ResourceText> texts_; // Vector con los objetos de texto + std::vector<ResourceAnimation> animations_; // Vector con las animaciones + std::vector<DemoData> demos_; // Vector con los ficheros de datos para el modo demostración // --- Progreso de carga --- - ResourceCount loading_count_; // Contador de recursos cargados - std::shared_ptr<Text> loading_text_; // Texto para escribir en pantalla - std::string loading_resource_name_; // Nombre del recurso que se está cargando + ResourceCount loading_count_; // Contador de recursos cargados + std::shared_ptr<Text> loading_text_; // Texto para escribir en pantalla + std::string loading_resource_name_; // Nombre del recurso que se está cargando SDL_FRect loading_wired_rect_; SDL_FRect loading_full_rect_; // --- Métodos internos de carga y gestión --- - void loadSounds(); // Carga los sonidos - void loadMusics(); // Carga las músicas - void loadTextures(); // Carga las texturas - void loadTextFiles(); // Carga los ficheros de texto - void loadAnimations(); // Carga las animaciones - void loadDemoData(); // Carga los datos para el modo demostración - void addPalettes(); // Añade paletas a las texturas - void createTextures(); // Crea las texturas a partir de los datos cargados - void createText(); // Crea los objetos de texto - void clear(); // Vacía todos los vectores de recursos - void load(); // Carga todos los recursos - void clearSounds(); // Vacía el vector de sonidos - void clearMusics(); // Vacía el vector de músicas + void loadSounds(); // Carga los sonidos + void loadMusics(); // Carga las músicas + void loadTextures(); // Carga las texturas + void loadTextFiles(); // Carga los ficheros de texto + void loadAnimations(); // Carga las animaciones + void loadDemoData(); // Carga los datos para el modo demostración + void addPalettes(); // Añade paletas a las texturas + void createTextures(); // Crea las texturas a partir de los datos cargados + void createText(); // Crea los objetos de texto + void clear(); // Vacía todos los vectores de recursos + void load(); // Carga todos los recursos + void clearSounds(); // Vacía el vector de sonidos + void clearMusics(); // Vacía el vector de músicas // --- Métodos internos para gestionar el progreso --- - void calculateTotalResources(); // Calcula el número de recursos para cargar - void renderProgress(); // Muestra el progreso de carga - void checkEvents(); // Comprueba los eventos durante la carga - void updateLoadingProgress(std::string name); // Actualiza el progreso de carga - void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso - void updateProgressBar(); // Actualiza la barra de estado + void calculateTotalResources(); // Calcula el número de recursos para cargar + void renderProgress(); // Muestra el progreso de carga + void checkEvents(); // Comprueba los eventos durante la carga + void updateLoadingProgress(std::string name); // Actualiza el progreso de carga + void initProgressBar(); // Inicializa los rectangulos que definen la barra de progreso + void updateProgressBar(); // Actualiza la barra de estado // --- Constructores y destructor privados (singleton) --- - Resource(); // Constructor privado - ~Resource(); // Destructor privado + Resource(); // Constructor privado + ~Resource(); // Destructor privado }; \ No newline at end of file diff --git a/source/scoreboard.cpp b/source/scoreboard.cpp index a0624ce..4a740c4 100644 --- a/source/scoreboard.cpp +++ b/source/scoreboard.cpp @@ -1,460 +1,412 @@ #include "scoreboard.h" -#include <SDL3/SDL.h> // Para SDL_DestroyTexture, SDL_SetRenderDrawColor -#include <math.h> // Para roundf -#include <iomanip> // Para operator<<, setfill, setw -#include <sstream> // Para basic_ostringstream, basic_ostream, basic_os... +#include <SDL3/SDL.h> // Para SDL_DestroyTexture, SDL_SetRenderDrawColor +#include <math.h> // Para roundf -#include "enter_name.h" // Para NAME_SIZE -#include "lang.h" // Para getText -#include "param.h" // Para Param, ParamScoreboard, param -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "sprite.h" // Para Sprite -#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR -#include "texture.h" // Para Texture +#include <iomanip> // Para operator<<, setfill, setw +#include <sstream> // Para basic_ostringstream, basic_ostream, basic_os... + +#include "enter_name.h" // Para NAME_SIZE +#include "lang.h" // Para getText +#include "param.h" // Para Param, ParamScoreboard, param +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "sprite.h" // Para Sprite +#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR +#include "texture.h" // Para Texture // [SINGLETON] Hay que definir las variables estáticas, desde el .h sólo la hemos declarado Scoreboard *Scoreboard::scoreboard_ = nullptr; // [SINGLETON] Crearemos el objeto score_board con esta función estática -void Scoreboard::init() -{ - Scoreboard::scoreboard_ = new Scoreboard(); +void Scoreboard::init() { + Scoreboard::scoreboard_ = new Scoreboard(); } // [SINGLETON] Destruiremos el objeto score_board con esta función estática -void Scoreboard::destroy() -{ - delete Scoreboard::scoreboard_; +void Scoreboard::destroy() { + delete Scoreboard::scoreboard_; } // [SINGLETON] Con este método obtenemos el objeto score_board y podemos trabajar con él -Scoreboard *Scoreboard::get() -{ - return Scoreboard::scoreboard_; +Scoreboard *Scoreboard::get() { + return Scoreboard::scoreboard_; } // Constructor Scoreboard::Scoreboard() - : renderer_(Screen::get()->getRenderer()), - game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")), - power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)), - text_scoreboard_(Resource::get()->getText("8bithud")) -{ - // Inicializa variables - for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) - { - name_[i].clear(); - record_name_[i].clear(); - selector_pos_[i] = 0; - score_[i] = 0; - mult_[i] = 0; - continue_counter_[i] = 0; - } + : renderer_(Screen::get()->getRenderer()), + game_power_meter_texture_(Resource::get()->getTexture("game_power_meter.png")), + power_meter_sprite_(std::make_unique<Sprite>(game_power_meter_texture_)), + text_scoreboard_(Resource::get()->getText("8bithud")) { + // Inicializa variables + for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { + name_[i].clear(); + record_name_[i].clear(); + selector_pos_[i] = 0; + score_[i] = 0; + mult_[i] = 0; + continue_counter_[i] = 0; + } - panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE; - panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE; - panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO; + panel_[SCOREBOARD_LEFT_PANEL].mode = ScoreboardMode::SCORE; + panel_[SCOREBOARD_RIGHT_PANEL].mode = ScoreboardMode::SCORE; + panel_[SCOREBOARD_CENTER_PANEL].mode = ScoreboardMode::STAGE_INFO; - // Recalcula las anclas de los elementos - recalculateAnchors(); - power_meter_sprite_->setPosition(SDL_FRect{ - static_cast<float>(slot4_2_.x - 20), - static_cast<float>(slot4_2_.y), - 40, - 7}); + // Recalcula las anclas de los elementos + recalculateAnchors(); + power_meter_sprite_->setPosition(SDL_FRect{ + static_cast<float>(slot4_2_.x - 20), + static_cast<float>(slot4_2_.y), + 40, + 7}); - // Crea la textura de fondo - background_ = nullptr; - createBackgroundTexture(); + // Crea la textura de fondo + background_ = nullptr; + createBackgroundTexture(); - // Crea las texturas de los paneles - createPanelTextures(); + // Crea las texturas de los paneles + createPanelTextures(); - // Rellena la textura de fondo - fillBackgroundTexture(); + // Rellena la textura de fondo + fillBackgroundTexture(); - // Inicializa el vector de colores para el nombre - iniNameColors(); + // Inicializa el vector de colores para el nombre + iniNameColors(); } -Scoreboard::~Scoreboard() -{ - if (background_) - { - SDL_DestroyTexture(background_); - } +Scoreboard::~Scoreboard() { + if (background_) { + SDL_DestroyTexture(background_); + } - for (auto texture : panel_texture_) - { - if (texture) - { - SDL_DestroyTexture(texture); - } - } + for (auto texture : panel_texture_) { + if (texture) { + SDL_DestroyTexture(texture); + } + } } // Transforma un valor numérico en una cadena de 7 cifras -std::string Scoreboard::updateScoreText(int num) -{ - std::ostringstream oss; - oss << std::setw(7) << std::setfill('0') << num; - return oss.str(); +std::string Scoreboard::updateScoreText(int num) { + std::ostringstream oss; + oss << std::setw(7) << std::setfill('0') << num; + return oss.str(); } // Actualiza el contador -void Scoreboard::updateTimeCounter() -{ - constexpr int TICKS_SPEED = 100; +void Scoreboard::updateTimeCounter() { + constexpr int TICKS_SPEED = 100; - if (SDL_GetTicks() - ticks_ > TICKS_SPEED) - { - ticks_ = SDL_GetTicks(); - ++time_counter_; - } + if (SDL_GetTicks() - ticks_ > TICKS_SPEED) { + ticks_ = SDL_GetTicks(); + ++time_counter_; + } } // Actualiza la lógica del marcador -void Scoreboard::update() -{ - fillBackgroundTexture(); - updateTimeCounter(); - ++loop_counter_; +void Scoreboard::update() { + fillBackgroundTexture(); + updateTimeCounter(); + ++loop_counter_; } // Pinta el marcador -void Scoreboard::render() -{ - SDL_RenderTexture(renderer_, background_, nullptr, &rect_); +void Scoreboard::render() { + SDL_RenderTexture(renderer_, background_, nullptr, &rect_); } // Establece el valor de la variable -void Scoreboard::setColor(Color color) -{ - // Actualiza las variables de colores - color_ = color; - text_color1_ = param.scoreboard.text_autocolor ? color_.lighten(100) : param.scoreboard.text_color1; - text_color2_ = param.scoreboard.text_autocolor ? color_.lighten(150) : param.scoreboard.text_color2; +void Scoreboard::setColor(Color color) { + // Actualiza las variables de colores + color_ = color; + text_color1_ = param.scoreboard.text_autocolor ? color_.lighten(100) : param.scoreboard.text_color1; + text_color2_ = param.scoreboard.text_autocolor ? color_.lighten(150) : param.scoreboard.text_color2; - // Aplica los colores - power_meter_sprite_->getTexture()->setColor(text_color2_); - fillBackgroundTexture(); - iniNameColors(); + // Aplica los colores + power_meter_sprite_->getTexture()->setColor(text_color2_); + fillBackgroundTexture(); + iniNameColors(); } // Establece el valor de la variable -void Scoreboard::setPos(SDL_FRect rect) -{ - rect_ = rect; +void Scoreboard::setPos(SDL_FRect rect) { + rect_ = rect; - recalculateAnchors(); // Recalcula las anclas de los elementos - createBackgroundTexture(); // Crea la textura de fondo - createPanelTextures(); // Crea las texturas de los paneles - fillBackgroundTexture(); // Rellena la textura de fondo + recalculateAnchors(); // Recalcula las anclas de los elementos + createBackgroundTexture(); // Crea la textura de fondo + createPanelTextures(); // Crea las texturas de los paneles + fillBackgroundTexture(); // Rellena la textura de fondo } // Rellena los diferentes paneles del marcador -void Scoreboard::fillPanelTextures() -{ - // Guarda a donde apunta actualmente el renderizador - auto temp = SDL_GetRenderTarget(renderer_); +void Scoreboard::fillPanelTextures() { + // Guarda a donde apunta actualmente el renderizador + auto temp = SDL_GetRenderTarget(renderer_); - // Genera el contenido de cada panel_ - for (size_t i = 0; i < SCOREBOARD_MAX_PANELS; ++i) - { - // Cambia el destino del renderizador - SDL_SetRenderTarget(renderer_, panel_texture_[i]); + // Genera el contenido de cada panel_ + for (size_t i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { + // Cambia el destino del renderizador + SDL_SetRenderTarget(renderer_, panel_texture_[i]); - // Dibuja el fondo de la textura - SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); - SDL_RenderClear(renderer_); + // Dibuja el fondo de la textura + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); + SDL_RenderClear(renderer_); - switch (panel_[i].mode) - { - case ScoreboardMode::SCORE: - { - // SCORE - text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); - text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); + switch (panel_[i].mode) { + case ScoreboardMode::SCORE: { + // SCORE + text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); + text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); - // MULT - text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_[i]).substr(0, 3), 1, text_color2_); - break; - } + // MULT + text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 3"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_COLOR | TEXT_CENTER, slot4_4_.x, slot4_4_.y, "x" + std::to_string(mult_[i]).substr(0, 3), 1, text_color2_); + break; + } - case ScoreboardMode::DEMO: - { - // DEMO MODE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_); + case ScoreboardMode::DEMO: { + // DEMO MODE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 6"), 1, text_color1_); - // PRESS START TO PLAY - if (time_counter_ % 10 < 8) - { - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); - } - break; - } + // PRESS START TO PLAY + if (time_counter_ % 10 < 8) { + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); + } + break; + } - case ScoreboardMode::WAITING: - { - // GAME OVER - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); + case ScoreboardMode::WAITING: { + // GAME OVER + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); - // PRESS START TO PLAY - if (time_counter_ % 10 < 8) - { - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); - } - break; - } + // PRESS START TO PLAY + if (time_counter_ % 10 < 8) { + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 8"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 9"), 1, text_color1_); + } + break; + } - case ScoreboardMode::GAME_OVER: - { - // GAME OVER - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); + case ScoreboardMode::GAME_OVER: { + // GAME OVER + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); - // PLEASE WAIT - if (time_counter_ % 10 < 8) - { - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_); - } - break; - } + // PLEASE WAIT + if (time_counter_ % 10 < 8) { + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 12"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, Lang::getText("[SCOREBOARD] 13"), 1, text_color1_); + } + break; + } - case ScoreboardMode::STAGE_INFO: - { - // STAGE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + std::to_string(stage_), 1, text_color1_); + case ScoreboardMode::STAGE_INFO: { + // STAGE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, Lang::getText("[SCOREBOARD] 5") + std::to_string(stage_), 1, text_color1_); - // POWERMETER - power_meter_sprite_->setSpriteClip(0, 0, 40, 7); - power_meter_sprite_->render(); - power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0f), 7); - power_meter_sprite_->render(); + // POWERMETER + power_meter_sprite_->setSpriteClip(0, 0, 40, 7); + power_meter_sprite_->render(); + power_meter_sprite_->setSpriteClip(40, 0, int(power_ * 40.0f), 7); + power_meter_sprite_->render(); - // HI-SCORE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_); - const std::string name = hi_score_name_ == "" ? "" : hi_score_name_ + " - "; - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_), 1, text_color2_); - break; - } + // HI-SCORE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 4"), 1, text_color1_); + const std::string name = hi_score_name_ == "" ? "" : hi_score_name_ + " - "; + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, name + updateScoreText(hi_score_), 1, text_color2_); + break; + } - case ScoreboardMode::CONTINUE: - { - // SCORE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); + case ScoreboardMode::CONTINUE: { + // SCORE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); - // CONTINUE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_[i]), 1, text_color2_); - break; - } + // CONTINUE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 10"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, std::to_string(continue_counter_[i]), 1, text_color2_); + break; + } - case ScoreboardMode::ENTER_NAME: - { - // SCORE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); + case ScoreboardMode::ENTER_NAME: { + // SCORE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); - // ENTER NAME - { - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); - SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f}; + // ENTER NAME + { + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); + SDL_FRect rect = {enter_name_pos_.x, enter_name_pos_.y, 5.0f, 7.0f}; - // Recorre todos los slots de letras del nombre - for (size_t j = 0; j < NAME_SIZE; ++j) - { - // Selecciona el color - const Color color = j < selector_pos_[i] ? text_color2_ : text_color1_; + // Recorre todos los slots de letras del nombre + for (size_t j = 0; j < NAME_SIZE; ++j) { + // Selecciona el color + const Color color = j < selector_pos_[i] ? text_color2_ : text_color1_; - if (j != selector_pos_[i] || time_counter_ % 3 == 0) - { - // Dibuja la linea - if (j >= selector_pos_[i]) - { - SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255); - SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h); - } + if (j != selector_pos_[i] || time_counter_ % 3 == 0) { + // Dibuja la linea + if (j >= selector_pos_[i]) { + SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255); + SDL_RenderLine(renderer_, rect.x, rect.y + rect.h, rect.x + rect.w, rect.y + rect.h); + } - // Dibuja la letra - if (j < record_name_[i].size()) - { - text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color); - } - } - rect.x += 7; - } - } - break; - } - case ScoreboardMode::SHOW_NAME: - { - // SCORE - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); + // Dibuja la letra + if (j < record_name_[i].size()) { + text_scoreboard_->writeColored(rect.x, rect.y, record_name_[i].substr(j, 1), color); + } + } + rect.x += 7; + } + } + break; + } + case ScoreboardMode::SHOW_NAME: { + // SCORE + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y, name_[i], 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_2_.x, slot4_2_.y, updateScoreText(score_[i]), 1, text_color2_); - // NAME - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); + // NAME + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y, Lang::getText("[SCOREBOARD] 11"), 1, text_color1_); - /* TEXTO CENTRADO */ - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); + /* TEXTO CENTRADO */ + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y, record_name_[i], 1, getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); - /* TEXTO A LA IZQUIERDA */ - // text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); - break; - } - case ScoreboardMode::GAME_COMPLETED: - { - // GAME OVER - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); + /* TEXTO A LA IZQUIERDA */ + // text_scoreboard_->writeColored(enter_name_pos_.x, enter_name_pos_.y, record_name_[i], getColorLikeKnightRider(name_colors_, loop_counter_ / 5)); + break; + } + case ScoreboardMode::GAME_COMPLETED: { + // GAME OVER + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_1_.x, slot4_1_.y + 4, Lang::getText("[SCOREBOARD] 7"), 1, text_color1_); - // SCORE - if (time_counter_ % 10 < 8) - { - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_); - text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]), 1, text_color2_); - } - } - default: - break; - } - } + // SCORE + if (time_counter_ % 10 < 8) { + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_3_.x, slot4_3_.y - 2, Lang::getText("[SCOREBOARD] 14"), 1, text_color1_); + text_scoreboard_->writeDX(TEXT_CENTER | TEXT_COLOR, slot4_4_.x, slot4_4_.y - 2, updateScoreText(score_[i]), 1, text_color2_); + } + } + default: + break; + } + } - // Deja el renderizador apuntando donde estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador apuntando donde estaba + SDL_SetRenderTarget(renderer_, temp); } // Rellena la textura de fondo -void Scoreboard::fillBackgroundTexture() -{ - // Rellena los diferentes paneles del marcador - fillPanelTextures(); +void Scoreboard::fillBackgroundTexture() { + // Rellena los diferentes paneles del marcador + fillPanelTextures(); - // Cambia el destino del renderizador - SDL_Texture *temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, background_); + // Cambia el destino del renderizador + SDL_Texture *temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, background_); - // Dibuja el fondo del marcador - SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255); - SDL_RenderClear(renderer_); + // Dibuja el fondo del marcador + SDL_SetRenderDrawColor(renderer_, color_.r, color_.g, color_.b, 255); + SDL_RenderClear(renderer_); - // Copia las texturas de los paneles - for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) - { - SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[i].pos); - } + // Copia las texturas de los paneles + for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { + SDL_RenderTexture(renderer_, panel_texture_[i], nullptr, &panel_[i].pos); + } - // Dibuja la linea que separa la zona de juego del marcador - renderSeparator(); + // Dibuja la linea que separa la zona de juego del marcador + renderSeparator(); - // Deja el renderizador apuntando donde estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador apuntando donde estaba + SDL_SetRenderTarget(renderer_, temp); } // Recalcula las anclas de los elementos -void Scoreboard::recalculateAnchors() -{ - // Recalcula la posición y el tamaño de los paneles - const float panel_width = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS; - for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) - { - panel_[i].pos.x = roundf(panel_width * i); - panel_[i].pos.y = 0; - panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x; - panel_[i].pos.h = rect_.h; - } +void Scoreboard::recalculateAnchors() { + // Recalcula la posición y el tamaño de los paneles + const float panel_width = (float)rect_.w / (float)SCOREBOARD_MAX_PANELS; + for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { + panel_[i].pos.x = roundf(panel_width * i); + panel_[i].pos.y = 0; + panel_[i].pos.w = roundf(panel_width * (i + 1)) - panel_[i].pos.x; + panel_[i].pos.h = rect_.h; + } - // Constantes para definir las zonas del panel_: 4 filas y 1 columna - const int ROW_SIZE = rect_.h / 4; - const int TEXT_HEIGHT = 7; + // Constantes para definir las zonas del panel_: 4 filas y 1 columna + const int ROW_SIZE = rect_.h / 4; + const int TEXT_HEIGHT = 7; - // Filas - const float ROW1 = 1 + (ROW_SIZE * 0) + (TEXT_HEIGHT / 2); - const float ROW2 = 1 + (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1; - const float ROW3 = 1 + (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2; - const float ROW4 = 1 + (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3; + // Filas + const float ROW1 = 1 + (ROW_SIZE * 0) + (TEXT_HEIGHT / 2); + const float ROW2 = 1 + (ROW_SIZE * 1) + (TEXT_HEIGHT / 2) - 1; + const float ROW3 = 1 + (ROW_SIZE * 2) + (TEXT_HEIGHT / 2) - 2; + const float ROW4 = 1 + (ROW_SIZE * 3) + (TEXT_HEIGHT / 2) - 3; - // Columna - const float COL = panel_width / 2; + // Columna + const float COL = panel_width / 2; - // Slots de 4 - slot4_1_ = {COL, ROW1}; - slot4_2_ = {COL, ROW2}; - slot4_3_ = {COL, ROW3}; - slot4_4_ = {COL, ROW4}; + // Slots de 4 + slot4_1_ = {COL, ROW1}; + slot4_2_ = {COL, ROW2}; + slot4_3_ = {COL, ROW3}; + slot4_4_ = {COL, ROW4}; - // Primer cuadrado para poner el nombre de record - const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A')); - enter_name_pos_.x = COL - (enter_name_lenght / 2); - enter_name_pos_.y = ROW4; + // Primer cuadrado para poner el nombre de record + const int enter_name_lenght = text_scoreboard_->lenght(std::string(NAME_SIZE, 'A')); + enter_name_pos_.x = COL - (enter_name_lenght / 2); + enter_name_pos_.y = ROW4; - // Recoloca los sprites - if (power_meter_sprite_) - { - power_meter_sprite_->setX(slot4_2_.x - 20); - power_meter_sprite_->setY(slot4_2_.y); - } + // Recoloca los sprites + if (power_meter_sprite_) { + power_meter_sprite_->setX(slot4_2_.x - 20); + power_meter_sprite_->setY(slot4_2_.y); + } } // Crea la textura de fondo -void Scoreboard::createBackgroundTexture() -{ - // Elimina la textura en caso de existir - if (background_) - { - SDL_DestroyTexture(background_); - } +void Scoreboard::createBackgroundTexture() { + // Elimina la textura en caso de existir + if (background_) { + SDL_DestroyTexture(background_); + } - // Recrea la textura de fondo - background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h); - SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND); + // Recrea la textura de fondo + background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, rect_.w, rect_.h); + SDL_SetTextureBlendMode(background_, SDL_BLENDMODE_BLEND); } // Crea las texturas de los paneles -void Scoreboard::createPanelTextures() -{ - // Elimina las texturas en caso de existir - for (auto texture : panel_texture_) - { - if (texture != nullptr) - { - SDL_DestroyTexture(texture); - } - } - panel_texture_.clear(); +void Scoreboard::createPanelTextures() { + // Elimina las texturas en caso de existir + for (auto texture : panel_texture_) { + if (texture != nullptr) { + SDL_DestroyTexture(texture); + } + } + panel_texture_.clear(); - // Crea las texturas para cada panel_ - for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) - { - SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[i].pos.h); - SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); - panel_texture_.push_back(tex); - } + // Crea las texturas para cada panel_ + for (int i = 0; i < SCOREBOARD_MAX_PANELS; ++i) { + SDL_Texture *tex = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, panel_[i].pos.w, panel_[i].pos.h); + SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); + panel_texture_.push_back(tex); + } } // Dibuja la linea que separa la zona de juego del marcador -void Scoreboard::renderSeparator() -{ - // Dibuja la linea que separa el marcador de la zona de juego - auto color = param.scoreboard.separator_autocolor ? color_.darken() : param.scoreboard.separator_color; - SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255); - SDL_RenderLine(renderer_, 0, 0, rect_.w, 0); +void Scoreboard::renderSeparator() { + // Dibuja la linea que separa el marcador de la zona de juego + auto color = param.scoreboard.separator_autocolor ? color_.darken() : param.scoreboard.separator_color; + SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 255); + SDL_RenderLine(renderer_, 0, 0, rect_.w, 0); } // Inicializa el vector de colores para el nombre -void Scoreboard::iniNameColors() -{ - Color color = color_.inverse(); +void Scoreboard::iniNameColors() { + Color color = color_.inverse(); - name_colors_.clear(); - name_colors_.emplace_back(color.lighten(50)); - name_colors_.emplace_back(color.lighten(25)); - name_colors_.emplace_back(color); - name_colors_.emplace_back(color.darken(25)); + name_colors_.clear(); + name_colors_.emplace_back(color.lighten(50)); + name_colors_.emplace_back(color.lighten(25)); + name_colors_.emplace_back(color); + name_colors_.emplace_back(color.darken(25)); } \ No newline at end of file diff --git a/source/scoreboard.h b/source/scoreboard.h index d4ece15..8032f0e 100644 --- a/source/scoreboard.h +++ b/source/scoreboard.h @@ -1,12 +1,13 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FPoint, SDL_GetTicks, SDL_FRect, SDL_Texture, SDL_Renderer, Uint64 -#include <stddef.h> // Para size_t -#include <memory> // Para shared_ptr, unique_ptr -#include <string> // Para basic_string, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FPoint, SDL_GetTicks, SDL_FRect, SDL_Texture, SDL_Renderer, Uint64 +#include <stddef.h> // Para size_t -#include "utils.h" // Para Color +#include <memory> // Para shared_ptr, unique_ptr +#include <string> // Para basic_string, string +#include <vector> // Para vector + +#include "utils.h" // Para Color class Sprite; class Text; @@ -19,105 +20,102 @@ constexpr int SCOREBOARD_RIGHT_PANEL = 2; constexpr int SCOREBOARD_MAX_PANELS = 3; // --- Enums --- -enum class ScoreboardMode : int -{ - SCORE, - STAGE_INFO, - CONTINUE, - WAITING, - GAME_OVER, - DEMO, - ENTER_NAME, - SHOW_NAME, - GAME_COMPLETED, - NUM_MODES, +enum class ScoreboardMode : int { + SCORE, + STAGE_INFO, + CONTINUE, + WAITING, + GAME_OVER, + DEMO, + ENTER_NAME, + SHOW_NAME, + GAME_COMPLETED, + NUM_MODES, }; // --- Structs --- -struct Panel -{ - ScoreboardMode mode; // Modo en el que se encuentra el panel - SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador +struct Panel { + ScoreboardMode mode; // Modo en el que se encuentra el panel + SDL_FRect pos; // Posición donde dibujar el panel dentro del marcador }; // --- Clase Scoreboard --- -class Scoreboard -{ -public: - // --- Métodos de singleton --- - static void init(); // Crea el objeto Scoreboard - static void destroy(); // Libera el objeto Scoreboard - static Scoreboard *get(); // Obtiene el puntero al objeto Scoreboard +class Scoreboard { + public: + // --- Métodos de singleton --- + static void init(); // Crea el objeto Scoreboard + static void destroy(); // Libera el objeto Scoreboard + static Scoreboard *get(); // Obtiene el puntero al objeto Scoreboard - // --- Métodos principales --- - void update(); // Actualiza la lógica del marcador - void render(); // Pinta el marcador + // --- Métodos principales --- + void update(); // Actualiza la lógica del marcador + void render(); // Pinta el marcador - // --- Setters --- - void setColor(Color color); // Establece el color del marcador - void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador - void setContinue(int panel, int continue_counter) { continue_counter_[panel] = continue_counter; } - void setHiScore(int hi_score) { hi_score_ = hi_score; } - void setHiScoreName(const std::string &name) { hi_score_name_ = name; } - void setMode(int index, ScoreboardMode mode) { panel_[index].mode = mode; } - void setMult(int panel, float mult) { mult_[panel] = mult; } - void setName(int panel, const std::string &name) { name_[panel] = name; } - void setPower(float power) { power_ = power; } - void setRecordName(int panel, const std::string &record_name) { record_name_[panel] = record_name; } - void setScore(int panel, int score) { score_[panel] = score; } - void setSelectorPos(int panel, int pos) { selector_pos_[panel] = pos; } - void setStage(int stage) { stage_ = stage; } + // --- Setters --- + void setColor(Color color); // Establece el color del marcador + void setPos(SDL_FRect rect); // Establece la posición y tamaño del marcador + void setContinue(int panel, int continue_counter) { continue_counter_[panel] = continue_counter; } + void setHiScore(int hi_score) { hi_score_ = hi_score; } + void setHiScoreName(const std::string &name) { hi_score_name_ = name; } + void setMode(int index, ScoreboardMode mode) { panel_[index].mode = mode; } + void setMult(int panel, float mult) { mult_[panel] = mult; } + void setName(int panel, const std::string &name) { name_[panel] = name; } + void setPower(float power) { power_ = power; } + void setRecordName(int panel, const std::string &record_name) { record_name_[panel] = record_name; } + void setScore(int panel, int score) { score_[panel] = score; } + void setSelectorPos(int panel, int pos) { selector_pos_[panel] = pos; } + void setStage(int stage) { stage_ = stage; } -private: - // --- Singleton --- - static Scoreboard *scoreboard_; + private: + // --- Singleton --- + static Scoreboard *scoreboard_; - // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase - std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase - std::shared_ptr<Text> text_scoreboard_; // Fuente para el marcador del juego - SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador - std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel + // --- Objetos y punteros --- + SDL_Renderer *renderer_; // El renderizador de la ventana + std::shared_ptr<Texture> game_power_meter_texture_; // Textura con el marcador de poder de la fase + std::unique_ptr<Sprite> power_meter_sprite_; // Sprite para el medidor de poder de la fase + std::shared_ptr<Text> text_scoreboard_; // Fuente para el marcador del juego + SDL_Texture *background_ = nullptr; // Textura para dibujar el marcador + std::vector<SDL_Texture *> panel_texture_; // Texturas para dibujar cada panel - // --- Variables de estado --- - std::string name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre de cada jugador - std::string record_name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre introducido para la tabla de records - size_t selector_pos_[SCOREBOARD_MAX_PANELS] = {}; // Posición del selector de letra para introducir el nombre - int score_[SCOREBOARD_MAX_PANELS] = {}; // Puntuación de los jugadores - float mult_[SCOREBOARD_MAX_PANELS] = {}; // Multiplicador de los jugadores - int continue_counter_[SCOREBOARD_MAX_PANELS] = {}; // Tiempo para continuar de los jugadores - Panel panel_[SCOREBOARD_MAX_PANELS] = {}; // Lista con todos los paneles del marcador - int stage_ = 1; // Número de fase actual - int hi_score_ = 0; // Máxima puntuación - float power_ = 0; // Poder actual de la fase - std::string hi_score_name_ = std::string(); // Nombre del jugador con la máxima puntuación - Color color_ = Color(); // Color del marcador - SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador - Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks() - int time_counter_ = 0; // Contador de segundos - int loop_counter_ = 0; // Contador de bucle - std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido + // --- Variables de estado --- + std::string name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre de cada jugador + std::string record_name_[SCOREBOARD_MAX_PANELS] = {}; // Nombre introducido para la tabla de records + size_t selector_pos_[SCOREBOARD_MAX_PANELS] = {}; // Posición del selector de letra para introducir el nombre + int score_[SCOREBOARD_MAX_PANELS] = {}; // Puntuación de los jugadores + float mult_[SCOREBOARD_MAX_PANELS] = {}; // Multiplicador de los jugadores + int continue_counter_[SCOREBOARD_MAX_PANELS] = {}; // Tiempo para continuar de los jugadores + Panel panel_[SCOREBOARD_MAX_PANELS] = {}; // Lista con todos los paneles del marcador + int stage_ = 1; // Número de fase actual + int hi_score_ = 0; // Máxima puntuación + float power_ = 0; // Poder actual de la fase + std::string hi_score_name_ = std::string(); // Nombre del jugador con la máxima puntuación + Color color_ = Color(); // Color del marcador + SDL_FRect rect_ = {0, 0, 320, 40}; // Posición y dimensiones del marcador + Uint64 ticks_ = SDL_GetTicks(); // Variable donde almacenar el valor de SDL_GetTicks() + int time_counter_ = 0; // Contador de segundos + int loop_counter_ = 0; // Contador de bucle + std::vector<Color> name_colors_; // Colores para destacar el nombre una vez introducido - // --- Variables de aspecto --- - Color text_color1_, text_color2_; // Colores para los marcadores del texto; + // --- Variables de aspecto --- + Color text_color1_, text_color2_; // Colores para los marcadores del texto; - // --- Puntos predefinidos para colocar elementos en los paneles --- - SDL_FPoint slot4_1_, slot4_2_, slot4_3_, slot4_4_; - SDL_FPoint enter_name_pos_; + // --- Puntos predefinidos para colocar elementos en los paneles --- + SDL_FPoint slot4_1_, slot4_2_, slot4_3_, slot4_4_; + SDL_FPoint enter_name_pos_; - // --- Métodos internos --- - void recalculateAnchors(); // Recalcula las anclas de los elementos - std::string updateScoreText(int num); // Transforma un valor numérico en una cadena de 7 cifras - void createBackgroundTexture(); // Crea la textura de fondo - void createPanelTextures(); // Crea las texturas de los paneles - void fillPanelTextures(); // Rellena los diferentes paneles del marcador - void fillBackgroundTexture(); // Rellena la textura de fondo - void updateTimeCounter(); // Actualiza el contador - void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador - void iniNameColors(); // Inicializa el vector de colores para el nombre + // --- Métodos internos --- + void recalculateAnchors(); // Recalcula las anclas de los elementos + std::string updateScoreText(int num); // Transforma un valor numérico en una cadena de 7 cifras + void createBackgroundTexture(); // Crea la textura de fondo + void createPanelTextures(); // Crea las texturas de los paneles + void fillPanelTextures(); // Rellena los diferentes paneles del marcador + void fillBackgroundTexture(); // Rellena la textura de fondo + void updateTimeCounter(); // Actualiza el contador + void renderSeparator(); // Dibuja la línea que separa la zona de juego del marcador + void iniNameColors(); // Inicializa el vector de colores para el nombre - // --- Constructor y destructor privados (singleton) --- - Scoreboard(); - ~Scoreboard(); + // --- Constructor y destructor privados (singleton) --- + Scoreboard(); + ~Scoreboard(); }; diff --git a/source/screen.cpp b/source/screen.cpp index 69c2780..43ba035 100644 --- a/source/screen.cpp +++ b/source/screen.cpp @@ -1,21 +1,22 @@ #include "screen.h" -#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_LogCategory +#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_LogCategory + #include <algorithm> // Para min, max #include <fstream> // Para basic_ifstream, ifstream #include <iterator> // Para istreambuf_iterator, operator== #include <memory> // Para allocator, shared_ptr, __shared_pt... #include <string> // Para basic_string, char_traits, operator+ -#include "asset.h" // Para Asset -#include "external/jail_shader.h" // Para init, render -#include "mouse.h" // Para updateCursorVisibility -#include "notifier.h" // Para Notifier -#include "options.h" // Para VideoOptions, video, WindowOptions -#include "param.h" // Para Param, param, ParamGame, ParamDebug -#include "text.h" // Para Text, TEXT_COLOR, TEXT_STROKE -#include "texture.h" // Para Texture -#include "ui/service_menu.h" // Para ServiceMenu +#include "asset.h" // Para Asset +#include "external/jail_shader.h" // Para init, render +#include "mouse.h" // Para updateCursorVisibility +#include "notifier.h" // Para Notifier +#include "options.h" // Para VideoOptions, video, WindowOptions +#include "param.h" // Para Param, param, ParamGame, ParamDebug +#include "text.h" // Para Text, TEXT_COLOR, TEXT_STROKE +#include "texture.h" // Para Texture +#include "ui/service_menu.h" // Para ServiceMenu // Singleton Screen *Screen::instance_ = nullptr; @@ -27,7 +28,7 @@ void Screen::init() { Screen::instance_ = new Screen(); } void Screen::destroy() { delete Screen::instance_; } // Obtiene la instancia -Screen *Screen::get() { return Screen::instance_; } +auto Screen::get() -> Screen * { return Screen::instance_; } // Constructor Screen::Screen() @@ -36,9 +37,8 @@ Screen::Screen() game_canvas_(nullptr), service_menu_(nullptr), notifier_(nullptr), - src_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}), - dst_rect_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}) -{ + src_rect_(SDL_FRect{0, 0, param.game.width, param.game.height}), + dst_rect_(SDL_FRect{0, 0, param.game.width, param.game.height}) { // Arranca SDL VIDEO, crea la ventana y el renderizador initSDLVideo(); @@ -61,16 +61,14 @@ Screen::Screen() } // Destructor -Screen::~Screen() -{ +Screen::~Screen() { SDL_DestroyTexture(game_canvas_); SDL_DestroyRenderer(renderer_); SDL_DestroyWindow(window_); } // Limpia la pantalla -void Screen::clean(Color color) -{ +void Screen::clean(Color color) { SDL_SetRenderDrawColor(renderer_, color.r, color.g, color.b, 0xFF); SDL_RenderClear(renderer_); } @@ -79,75 +77,59 @@ void Screen::clean(Color color) void Screen::start() { SDL_SetRenderTarget(renderer_, game_canvas_); } // Vuelca el contenido del renderizador en pantalla -void Screen::render() -{ +void Screen::render() { fps_.increment(); - - // Renderiza todos los overlays y efectos - renderOverlays(); - - // Renderiza el contenido del game_canvas_ - renderScreen(); + renderOverlays(); // Renderiza todos los overlays y efectos + renderScreen(); // Renderiza el contenido del game_canvas_ } // Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes -void Screen::coreRender() -{ +void Screen::coreRender() { fps_.increment(); #ifdef DEBUG renderInfo(); #endif - renderScreen(); + renderScreen(); // Renderiza el contenido del game_canvas_ } // Renderiza el contenido del game_canvas_ -void Screen::renderScreen() -{ +void Screen::renderScreen() { SDL_SetRenderTarget(renderer_, nullptr); clean(); - if (Options::video.shaders) - { + if (Options::video.shaders) { shader::render(); - } - else - { + } else { SDL_RenderTexture(renderer_, game_canvas_, nullptr, nullptr); SDL_RenderPresent(renderer_); } } // Establece el modo de video -void Screen::setFullscreenMode() -{ +void Screen::setFullscreenMode() { SDL_SetWindowFullscreen(window_, Options::video.fullscreen); } // Camibia entre pantalla completa y ventana -void Screen::toggleFullscreen() -{ +void Screen::toggleFullscreen() { Options::video.fullscreen = !Options::video.fullscreen; setFullscreenMode(); } // Cambia el tamaño de la ventana -void Screen::setWindowZoom(int zoom) -{ +void Screen::setWindowZoom(int zoom) { Options::window.size = zoom; adjustWindowSize(); } // Reduce el tamaño de la ventana -bool Screen::decWindowSize() -{ - if (!Options::video.fullscreen) - { +auto Screen::decWindowSize() -> bool { + if (!Options::video.fullscreen) { const int PREVIOUS_ZOOM = Options::window.size; --Options::window.size; Options::window.size = std::max(Options::window.size, 1); - if (Options::window.size != PREVIOUS_ZOOM) - { + if (Options::window.size != PREVIOUS_ZOOM) { adjustWindowSize(); return true; } @@ -157,16 +139,13 @@ bool Screen::decWindowSize() } // Aumenta el tamaño de la ventana -bool Screen::incWindowSize() -{ - if (!Options::video.fullscreen) - { +auto Screen::incWindowSize() -> bool { + if (!Options::video.fullscreen) { const int PREVIOUS_ZOOM = Options::window.size; ++Options::window.size; Options::window.size = std::min(Options::window.size, Options::window.max_size); - if (Options::window.size != PREVIOUS_ZOOM) - { + if (Options::window.size != PREVIOUS_ZOOM) { adjustWindowSize(); return true; } @@ -176,45 +155,41 @@ bool Screen::incWindowSize() } // Actualiza la lógica de la clase -void Screen::update() -{ +void Screen::update() { fps_.calculate(SDL_GetTicks()); shake_effect_.update(src_rect_, dst_rect_); flash_effect_.update(); - if (service_menu_) + if (service_menu_ != nullptr) { service_menu_->update(); - if (notifier_) + } + if (notifier_ != nullptr) { notifier_->update(); + } Mouse::updateCursorVisibility(); } // Actualiza los elementos mínimos -void Screen::coreUpdate() -{ +void Screen::coreUpdate() { fps_.calculate(SDL_GetTicks()); Mouse::updateCursorVisibility(); } // Actualiza y dibuja el efecto de flash en la pantalla -void Screen::renderFlash() -{ - if (flash_effect_.isRendarable()) - { +void Screen::renderFlash() { + if (flash_effect_.isRendarable()) { SDL_SetRenderDrawColor(renderer_, flash_effect_.color.r, flash_effect_.color.g, flash_effect_.color.b, 0xFF); SDL_RenderClear(renderer_); } } // Aplica el efecto de agitar la pantalla -void Screen::renderShake() -{ - if (shake_effect_.enabled) - { +void Screen::renderShake() { + if (shake_effect_.enabled) { // Guarda el renderizador actual para dejarlo despues como estaba - auto current_target = SDL_GetRenderTarget(renderer_); + auto *current_target = SDL_GetRenderTarget(renderer_); // Crea una textura temporal - auto temp_texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); + auto *temp_texture = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height); // Vuelca game_canvas_ a la textura temporal SDL_SetRenderTarget(renderer_, temp_texture); @@ -233,10 +208,8 @@ void Screen::renderShake() } #ifdef DEBUG // Muestra información por pantalla -void Screen::renderInfo() -{ - if (debug_info_.show) - { +void Screen::renderInfo() { + if (debug_info_.show) { // Resolution debug_info_.text->writeDX(TEXT_COLOR | TEXT_STROKE, param.game.width - debug_info_.text->lenght(Options::video.info) - 2, 1, Options::video.info, 1, param.debug.color, 1, param.debug.color.darken(150)); @@ -247,10 +220,8 @@ void Screen::renderInfo() } #endif // Carga el contenido del archivo GLSL -void Screen::loadShaders() -{ - if (shader_source_.empty()) - { +void Screen::loadShaders() { + if (shader_source_.empty()) { const std::string GLSL_FILE = param.game.game_area.rect.h == 256 ? "crtpi_256.glsl" : "crtpi_240.glsl"; std::ifstream f(Asset::get()->get(GLSL_FILE).c_str()); shader_source_ = std::string((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()); @@ -258,32 +229,30 @@ void Screen::loadShaders() } // Inicializa los shaders -void Screen::initShaders() -{ - if (Options::video.shaders) - { +void Screen::initShaders() { + if (Options::video.shaders) { loadShaders(); shader::init(window_, game_canvas_, shader_source_); } } // Calcula el tamaño de la ventana -void Screen::adjustWindowSize() -{ - if (!Options::video.fullscreen) - { +void Screen::adjustWindowSize() { + if (!Options::video.fullscreen) { // Establece el nuevo tamaño const int WIDTH = param.game.width * Options::window.size; const int HEIGHT = param.game.height * Options::window.size; - int old_width, old_height; + int old_width; + int old_height; SDL_GetWindowSize(window_, &old_width, &old_height); - int old_pos_x, old_pos_y; + int old_pos_x; + int old_pos_y; SDL_GetWindowPosition(window_, &old_pos_x, &old_pos_y); - const int NEW_POS_X = old_pos_x + (old_width - WIDTH) / 2; - const int NEW_POS_Y = old_pos_y + (old_height - HEIGHT) / 2; + const int NEW_POS_X = old_pos_x + ((old_width - WIDTH) / 2); + const int NEW_POS_Y = old_pos_y + ((old_height - HEIGHT) / 2); SDL_SetWindowPosition(window_, std::max(NEW_POS_X, WINDOWS_DECORATIONS_), std::max(NEW_POS_Y, 0)); SDL_SetWindowSize(window_, WIDTH, HEIGHT); @@ -291,8 +260,7 @@ void Screen::adjustWindowSize() } // Renderiza todos los overlays y efectos -void Screen::renderOverlays() -{ +void Screen::renderOverlays() { // Dibuja efectos y elementos sobre el game_canvas_ renderShake(); renderFlash(); @@ -305,24 +273,20 @@ void Screen::renderOverlays() } // Atenua la pantalla -void Screen::renderAttenuate() -{ - if (attenuate_effect_) - { +void Screen::renderAttenuate() { + if (attenuate_effect_) { SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 64); SDL_RenderFillRect(renderer_, nullptr); } } // Arranca SDL VIDEO y crea la ventana -bool Screen::initSDLVideo() -{ +auto Screen::initSDLVideo() -> bool { // Inicializar SDL - if (!SDL_Init(SDL_INIT_VIDEO)) - { + if (!SDL_Init(SDL_INIT_VIDEO)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "FATAL: Failed to initialize SDL_VIDEO! SDL Error: %s", - SDL_GetError()); + "FATAL: Failed to initialize SDL_VIDEO! SDL Error: %s", + SDL_GetError()); return false; } @@ -330,16 +294,14 @@ bool Screen::initSDLVideo() getDisplayInfo(); // Configurar hint para OpenGL - if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) - { + if (!SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl")) { SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Warning: Failed to set OpenGL hint!"); + "Warning: Failed to set OpenGL hint!"); } // Crear ventana SDL_WindowFlags window_flags = SDL_WINDOW_OPENGL; - if (Options::video.fullscreen) - { + if (Options::video.fullscreen) { window_flags |= SDL_WINDOW_FULLSCREEN; } window_ = SDL_CreateWindow( @@ -348,22 +310,20 @@ bool Screen::initSDLVideo() param.game.height * Options::window.size, window_flags); - if (!window_) - { + if (window_ == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "FATAL: Failed to create window! SDL Error: %s", - SDL_GetError()); + "FATAL: Failed to create window! SDL Error: %s", + SDL_GetError()); SDL_Quit(); return false; } // Crear renderer renderer_ = SDL_CreateRenderer(window_, nullptr); - if (!renderer_) - { + if (renderer_ == nullptr) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "FATAL: Failed to create renderer! SDL Error: %s", - SDL_GetError()); + "FATAL: Failed to create renderer! SDL Error: %s", + SDL_GetError()); SDL_DestroyWindow(window_); window_ = nullptr; SDL_Quit(); @@ -382,39 +342,35 @@ bool Screen::initSDLVideo() } // Obtiene información sobre la pantalla -void Screen::getDisplayInfo() -{ - int i, num_displays = 0; +void Screen::getDisplayInfo() { + int i; + int num_displays = 0; SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); - if (displays) - { - for (i = 0; i < num_displays; ++i) - { + if (displays != nullptr) { + for (i = 0; i < num_displays; ++i) { SDL_DisplayID instance_id = displays[i]; const char *name = SDL_GetDisplayName(instance_id); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, name ? name : "Unknown"); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Display %" SDL_PRIu32 ": %s", instance_id, (name != nullptr) ? name : "Unknown"); } - auto DM = SDL_GetCurrentDisplayMode(displays[0]); + const auto *dm = SDL_GetCurrentDisplayMode(displays[0]); // Calcula el máximo factor de zoom que se puede aplicar a la pantalla - Options::window.max_size = std::min(DM->w / param.game.width, DM->h / param.game.height); + Options::window.max_size = std::min(dm->w / param.game.width, dm->h / param.game.height); Options::window.size = std::min(Options::window.size, Options::window.max_size); // Muestra información sobre el tamaño de la pantalla y de la ventana de juego - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz", - static_cast<int>(DM->w), static_cast<int>(DM->h), static_cast<int>(DM->refresh_rate)); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Current display mode: %dx%d @ %dHz", static_cast<int>(dm->w), static_cast<int>(dm->h), static_cast<int>(dm->refresh_rate)); - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", - static_cast<int>(param.game.width), static_cast<int>(param.game.height), Options::window.size); + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Window resolution: %dx%d x%d", static_cast<int>(param.game.width), static_cast<int>(param.game.height), Options::window.size); - Options::video.info = std::to_string(static_cast<int>(DM->w)) + "x" + - std::to_string(static_cast<int>(DM->h)) + " @ " + - std::to_string(static_cast<int>(DM->refresh_rate)) + " Hz"; + Options::video.info = std::to_string(static_cast<int>(dm->w)) + "x" + + std::to_string(static_cast<int>(dm->h)) + " @ " + + std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz"; // Calcula el máximo factor de zoom que se puede aplicar a la pantalla - const int MAX_ZOOM = std::min(DM->w / param.game.width, (DM->h - WINDOWS_DECORATIONS_) / param.game.height); + const int MAX_ZOOM = std::min(dm->w / param.game.width, (dm->h - WINDOWS_DECORATIONS_) / param.game.height); // Normaliza los valores de zoom Options::window.size = std::min(Options::window.size, MAX_ZOOM); @@ -424,43 +380,37 @@ void Screen::getDisplayInfo() } // Alterna entre activar y desactivar los shaders -void Screen::toggleShaders() -{ +void Screen::toggleShaders() { Options::video.shaders = !Options::video.shaders; initShaders(); } // Alterna entre activar y desactivar el escalado entero -void Screen::toggleIntegerScale() -{ +void Screen::toggleIntegerScale() { Options::video.integer_scale = !Options::video.integer_scale; SDL_SetRenderLogicalPresentation(renderer_, param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); } // Alterna entre activar y desactivar el V-Sync -void Screen::toggleVSync() -{ +void Screen::toggleVSync() { Options::video.v_sync = !Options::video.v_sync; SDL_SetRenderVSync(renderer_, Options::video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED); } // Establece el estado del V-Sync -void Screen::setVSync(bool enabled) -{ +void Screen::setVSync(bool enabled) { Options::video.v_sync = enabled; SDL_SetRenderVSync(renderer_, enabled ? 1 : SDL_RENDERER_VSYNC_DISABLED); } // Obtiene los punteros a los singletones -void Screen::getSingletons() -{ +void Screen::getSingletons() { service_menu_ = ServiceMenu::get(); notifier_ = Notifier::get(); } // Aplica los valores de las opciones -void Screen::applySettings() -{ +void Screen::applySettings() { SDL_SetRenderVSync(renderer_, Options::video.v_sync ? 1 : SDL_RENDERER_VSYNC_DISABLED); SDL_SetRenderLogicalPresentation(Screen::get()->getRenderer(), param.game.width, param.game.height, Options::video.integer_scale ? SDL_LOGICAL_PRESENTATION_INTEGER_SCALE : SDL_LOGICAL_PRESENTATION_LETTERBOX); setFullscreenMode(); @@ -468,8 +418,7 @@ void Screen::applySettings() } // Crea el objeto de texto -void Screen::createText() -{ +void Screen::createText() { auto texture = std::make_shared<Texture>(getRenderer(), Asset::get()->get("aseprite.png")); text_ = std::make_shared<Text>(texture, Asset::get()->get("aseprite.txt")); } \ No newline at end of file diff --git a/source/screen.h b/source/screen.h index afd4831..51847f8 100644 --- a/source/screen.h +++ b/source/screen.h @@ -1,225 +1,211 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, SDL_HideWindow, SDL_Renderer, SDL_ShowWindow, Uint32, SDL_Texture, SDL_Window -#include <memory> // Para shared_ptr -#include <string> // Para basic_string, string +#include <SDL3/SDL.h> // Para SDL_FRect, SDL_HideWindow, SDL_Renderer, SDL_ShowWindow, Uint32, SDL_Texture, SDL_Window -#include "options.h" // Para VideoOptions, video -#include "utils.h" // Para Color +#include <memory> // Para shared_ptr +#include <string> // Para basic_string, string + +#include "options.h" // Para VideoOptions, video +#include "utils.h" // Para Color class Notifier; class ServiceMenu; class Text; // Clase Screen: gestiona la ventana, el renderizador y los efectos visuales globales -class Screen -{ -public: - // --- Métodos de singleton --- - static void init(); // Inicializa el objeto Screen - static void destroy(); // Libera el objeto Screen - static Screen *get(); // Obtiene el puntero al objeto Screen +class Screen { + public: + // --- Métodos de singleton --- + static void init(); // Inicializa el objeto Screen + static void destroy(); // Libera el objeto Screen + static Screen *get(); // Obtiene el puntero al objeto Screen - // --- Métodos principales --- - void update(); // Actualiza la lógica de la clase - void coreUpdate(); // Actualiza los elementos mínimos - void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla - void start(); // Prepara para empezar a dibujar en la textura de juego - void render(); // Vuelca el contenido del renderizador en pantalla - void coreRender(); // Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes + // --- Métodos principales --- + void update(); // Actualiza la lógica de la clase + void coreUpdate(); // Actualiza los elementos mínimos + void clean(Color color = Color(0x00, 0x00, 0x00)); // Limpia la pantalla + void start(); // Prepara para empezar a dibujar en la textura de juego + void render(); // Vuelca el contenido del renderizador en pantalla + void coreRender(); // Vuelca el contenido del renderizador en pantalla exceptuando ciertas partes - // --- Configuración de ventana y render --- - void setFullscreenMode(); // Establece el modo de pantalla completa - void toggleFullscreen(); // Cambia entre pantalla completa y ventana - void setWindowZoom(int size); // Cambia el tamaño de la ventana - bool decWindowSize(); // Reduce el tamaño de la ventana - bool incWindowSize(); // Aumenta el tamaño de la ventana - void applySettings(); // Aplica los valores de las opciones - void initShaders(); // Inicializa los shaders + // --- Configuración de ventana y render --- + void setFullscreenMode(); // Establece el modo de pantalla completa + void toggleFullscreen(); // Cambia entre pantalla completa y ventana + void setWindowZoom(int size); // Cambia el tamaño de la ventana + bool decWindowSize(); // Reduce el tamaño de la ventana + bool incWindowSize(); // Aumenta el tamaño de la ventana + void applySettings(); // Aplica los valores de las opciones + void initShaders(); // Inicializa los shaders - // --- Efectos visuales --- - void shake(int desp = 2, int delay = 3, int lenght = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, lenght); } // Agita la pantalla - void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color - void toggleShaders(); // Alterna entre activar y desactivar los shaders - void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero - void toggleVSync(); // Alterna entre activar y desactivar el V-Sync - void setVSync(bool enabled); // Establece el estado del V-Sync - void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla + // --- Efectos visuales --- + void shake(int desp = 2, int delay = 3, int lenght = 8) { shake_effect_.enable(src_rect_, dst_rect_, desp, delay, lenght); } // Agita la pantalla + void flash(Color color, int lenght = 10, int delay = 0) { flash_effect_ = FlashEffect(true, lenght, delay, color); } // Pone la pantalla de color + void toggleShaders(); // Alterna entre activar y desactivar los shaders + void toggleIntegerScale(); // Alterna entre activar y desactivar el escalado entero + void toggleVSync(); // Alterna entre activar y desactivar el V-Sync + void setVSync(bool enabled); // Establece el estado del V-Sync + void attenuate(bool value) { attenuate_effect_ = value; } // Atenúa la pantalla - // --- Getters --- - SDL_Renderer *getRenderer() { return renderer_; } // Obtiene el renderizador - void show() { SDL_ShowWindow(window_); } // Muestra la ventana - void hide() { SDL_HideWindow(window_); } // Oculta la ventana - void getSingletons(); // Obtiene los punteros a los singletones - bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync - std::shared_ptr<Text> getText() const { return text_; } // Obtiene el puntero al texto de Screen + // --- Getters --- + SDL_Renderer *getRenderer() { return renderer_; } // Obtiene el renderizador + void show() { SDL_ShowWindow(window_); } // Muestra la ventana + void hide() { SDL_HideWindow(window_); } // Oculta la ventana + void getSingletons(); // Obtiene los punteros a los singletones + bool getVSync() const { return Options::video.v_sync; } // Obtiene el valor de V-Sync + std::shared_ptr<Text> getText() const { return text_; } // Obtiene el puntero al texto de Screen #ifdef DEBUG - // --- Debug --- - void toggleDebugInfo() { debug_info_.show = !debug_info_.show; } - void setDebugInfoEnabled(bool value) { debug_info_.show = value; } + // --- Debug --- + void toggleDebugInfo() { debug_info_.show = !debug_info_.show; } + void setDebugInfoEnabled(bool value) { debug_info_.show = value; } #endif -private: - // --- Constantes --- - static constexpr int WINDOWS_DECORATIONS_ = 35; + private: + // --- Constantes --- + static constexpr int WINDOWS_DECORATIONS_ = 35; - // --- Estructuras internas --- - struct FPS - { - Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar. - int frameCount; // Número acumulado de frames en el intervalo. - int lastValue; // Número de frames calculado en el último segundo. + // --- Estructuras internas --- + struct FPS { + Uint32 ticks; // Tiempo en milisegundos desde que se comenzó a contar. + int frameCount; // Número acumulado de frames en el intervalo. + int lastValue; // Número de frames calculado en el último segundo. - FPS() : ticks(0), frameCount(0), lastValue(0) {} - void increment() { frameCount++; } - int calculate(Uint32 currentTicks) - { - if (currentTicks - ticks >= 1000) - { - lastValue = frameCount; - frameCount = 0; - ticks = currentTicks; - } - return lastValue; - } - }; + FPS() : ticks(0), frameCount(0), lastValue(0) {} + void increment() { frameCount++; } + int calculate(Uint32 currentTicks) { + if (currentTicks - ticks >= 1000) { + lastValue = frameCount; + frameCount = 0; + ticks = currentTicks; + } + return lastValue; + } + }; - // Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames - struct FlashEffect - { - bool enabled; // Indica si el efecto está activo - int lenght; // Duración total del efecto en frames - int delay; // Retraso antes de mostrar el flash - int counter; // Contador de frames restantes - Color color; // Color del flash + // Efecto de flash en pantalla: pinta la pantalla de un color durante unos frames + struct FlashEffect { + bool enabled; // Indica si el efecto está activo + int lenght; // Duración total del efecto en frames + int delay; // Retraso antes de mostrar el flash + int counter; // Contador de frames restantes + Color color; // Color del flash - explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) - : enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {} + explicit FlashEffect(bool enabled = false, int lenght = 0, int delay = 0, Color color = Color(0xFF, 0xFF, 0xFF)) + : enabled(enabled), lenght(lenght), delay(delay), counter(lenght), color(color) {} - void update() { (enabled && counter > 0) ? counter-- : enabled = false; } - bool isRendarable() { return enabled && counter < lenght - delay; } - }; + void update() { (enabled && counter > 0) ? counter-- : enabled = false; } + bool isRendarable() { return enabled && counter < lenght - delay; } + }; - // Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor - struct ShakeEffect - { - int desp; // Desplazamiento máximo de la sacudida (en píxeles) - int delay; // Frames entre cada movimiento de sacudida - int counter; // Contador de frames para el siguiente movimiento - int lenght; // Duración total del efecto en frames - int remaining; // Frames restantes de sacudida - int original_pos; // Posición original de la imagen (x) - int original_width; // Ancho original de la imagen - bool enabled; // Indica si el efecto está activo + // Efecto de sacudida/agitación de pantalla: mueve la imagen para simular un temblor + struct ShakeEffect { + int desp; // Desplazamiento máximo de la sacudida (en píxeles) + int delay; // Frames entre cada movimiento de sacudida + int counter; // Contador de frames para el siguiente movimiento + int lenght; // Duración total del efecto en frames + int remaining; // Frames restantes de sacudida + int original_pos; // Posición original de la imagen (x) + int original_width; // Ancho original de la imagen + bool enabled; // Indica si el efecto está activo - explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int origPos = 0, int origWidth = 800) - : desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(origPos), original_width(origWidth), enabled(en) {} + explicit ShakeEffect(bool en = false, int dp = 2, int dl = 3, int cnt = 0, int len = 8, int rem = 0, int origPos = 0, int origWidth = 800) + : desp(dp), delay(dl), counter(cnt), lenght(len), remaining(rem), original_pos(origPos), original_width(origWidth), enabled(en) {} - // Activa el efecto de sacudida y guarda la posición y tamaño originales - void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_lenght = -1) - { - if (!enabled) - { - enabled = true; - original_pos = src_rect.x; - original_width = src_rect.w; + // Activa el efecto de sacudida y guarda la posición y tamaño originales + void enable(SDL_FRect &src_rect, SDL_FRect &dst_rect, int new_desp = -1, int new_delay = -1, int new_lenght = -1) { + if (!enabled) { + enabled = true; + original_pos = src_rect.x; + original_width = src_rect.w; - // Usar nuevos valores si se proporcionan, sino mantener los actuales - if (new_desp != -1) - desp = new_desp; - if (new_delay != -1) - delay = new_delay; - if (new_lenght != -1) - lenght = new_lenght; + // Usar nuevos valores si se proporcionan, sino mantener los actuales + if (new_desp != -1) + desp = new_desp; + if (new_delay != -1) + delay = new_delay; + if (new_lenght != -1) + lenght = new_lenght; - src_rect.w -= desp; - dst_rect.w = src_rect.w; - } - remaining = lenght; - counter = delay; - } + src_rect.w -= desp; + dst_rect.w = src_rect.w; + } + remaining = lenght; + counter = delay; + } - // Actualiza el estado del efecto de sacudida - void update(SDL_FRect &src_rect, SDL_FRect &dst_rect) - { - if (enabled) - { - if (counter > 0) - { - counter--; - } - else - { - counter = delay; - const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp; - const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp; - src_rect.x = original_pos + SRC_DESP; - dst_rect.x = original_pos + DST_DESP; - remaining--; - if (remaining == -1) - { - enabled = false; - src_rect.x = original_pos; - src_rect.w = original_width; - dst_rect.x = original_pos; - dst_rect.w = original_width; - } - } - } - } + // Actualiza el estado del efecto de sacudida + void update(SDL_FRect &src_rect, SDL_FRect &dst_rect) { + if (enabled) { + if (counter > 0) { + counter--; + } else { + counter = delay; + const auto SRC_DESP = (remaining % 2 == 0) ? 0 : desp; + const auto DST_DESP = (remaining % 2 == 1) ? 0 : desp; + src_rect.x = original_pos + SRC_DESP; + dst_rect.x = original_pos + DST_DESP; + remaining--; + if (remaining == -1) { + enabled = false; + src_rect.x = original_pos; + src_rect.w = original_width; + dst_rect.x = original_pos; + dst_rect.w = original_width; + } + } + } + } - bool isEnabled() const { return enabled; } - }; + bool isEnabled() const { return enabled; } + }; #ifdef DEBUG - struct Debug - { - std::shared_ptr<Text> text; - bool show = false; - }; + struct Debug { + std::shared_ptr<Text> text; + bool show = false; + }; #endif - // --- Singleton --- - static Screen *instance_; + // --- Singleton --- + static Screen *instance_; - // --- Objetos y punteros --- - SDL_Window *window_; // Ventana de la aplicación - SDL_Renderer *renderer_; // El renderizador de la ventana - SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador - ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio - Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla + // --- Objetos y punteros --- + SDL_Window *window_; // Ventana de la aplicación + SDL_Renderer *renderer_; // El renderizador de la ventana + SDL_Texture *game_canvas_; // Textura donde se dibuja todo antes de volcarse al renderizador + ServiceMenu *service_menu_; // Objeto para mostrar el menú de servicio + Notifier *notifier_; // Objeto para mostrar las notificaciones por pantalla - // --- Variables de estado --- - SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego - SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego - FPS fps_; // Gestión de frames por segundo - std::string shader_source_; // Almacena el contenido del archivo GLSL - FlashEffect flash_effect_; // Efecto de flash en pantalla - ShakeEffect shake_effect_; // Efecto de agitar la pantalla - bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada + // --- Variables de estado --- + SDL_FRect src_rect_; // Coordenadas de origen para dibujar la textura del juego + SDL_FRect dst_rect_; // Coordenadas destino para dibujar la textura del juego + FPS fps_; // Gestión de frames por segundo + std::string shader_source_; // Almacena el contenido del archivo GLSL + FlashEffect flash_effect_; // Efecto de flash en pantalla + ShakeEffect shake_effect_; // Efecto de agitar la pantalla + bool attenuate_effect_ = false; // Indica si la pantalla ha de estar atenuada #ifdef DEBUG - Debug debug_info_; // Información de debug + Debug debug_info_; // Información de debug #endif - // --- Texto --- - std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla + // --- Texto --- + std::shared_ptr<Text> text_; // Objeto para escribir texto en pantalla - // --- Métodos internos --- - bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana - void renderFlash(); // Dibuja el efecto de flash en la pantalla - void renderShake(); // Aplica el efecto de agitar la pantalla - void renderInfo(); // Muestra información por pantalla - void renderScreen(); // Selecciona y ejecuta el método de renderizado adecuado - void loadShaders(); // Carga el contenido del archivo GLSL - void adjustWindowSize(); // Calcula el tamaño de la ventana - void getDisplayInfo(); // Obtiene información sobre la pantalla - void renderOverlays(); // Renderiza todos los overlays y efectos - void renderAttenuate(); // Atenúa la pantalla - void createText(); // Crea el objeto de texto + // --- Métodos internos --- + bool initSDLVideo(); // Arranca SDL VIDEO y crea la ventana + void renderFlash(); // Dibuja el efecto de flash en la pantalla + void renderShake(); // Aplica el efecto de agitar la pantalla + void renderInfo(); // Muestra información por pantalla + void renderScreen(); // Selecciona y ejecuta el método de renderizado adecuado + void loadShaders(); // Carga el contenido del archivo GLSL + void adjustWindowSize(); // Calcula el tamaño de la ventana + static void getDisplayInfo(); // Obtiene información sobre la pantalla + void renderOverlays(); // Renderiza todos los overlays y efectos + void renderAttenuate(); // Atenúa la pantalla + void createText(); // Crea el objeto de texto - // --- Constructores y destructor --- - Screen(); - ~Screen(); + // --- Constructores y destructor --- + Screen(); + ~Screen(); }; \ No newline at end of file diff --git a/source/section.cpp b/source/section.cpp index d57bbc5..11827ed 100644 --- a/source/section.cpp +++ b/source/section.cpp @@ -1,8 +1,7 @@ #include "section.h" -namespace Section -{ - Name name; - Options options; - AttractMode attract_mode; -} \ No newline at end of file +namespace Section { +Name name; +Options options; +AttractMode attract_mode; +} // namespace Section \ No newline at end of file diff --git a/source/section.h b/source/section.h index f88e597..df488e0 100644 --- a/source/section.h +++ b/source/section.h @@ -6,47 +6,43 @@ Proporciona variables globales para gestionar el flujo entre secciones. */ -namespace Section -{ - // --- Enumeraciones de secciones del programa --- - enum class Name - { - RESET, // Inicialización - LOGO, // Pantalla de logo - INTRO, // Introducción - TITLE, // Pantalla de título/menú principal - GAME, // Juego principal - HI_SCORE_TABLE, // Tabla de récords - GAME_DEMO, // Modo demo - INSTRUCTIONS, // Instrucciones - CREDITS, // Créditos - QUIT, // Salir del juego - }; +namespace Section { +// --- Enumeraciones de secciones del programa --- +enum class Name { + RESET, // Inicialización + LOGO, // Pantalla de logo + INTRO, // Introducción + TITLE, // Pantalla de título/menú principal + GAME, // Juego principal + HI_SCORE_TABLE, // Tabla de récords + GAME_DEMO, // Modo demo + INSTRUCTIONS, // Instrucciones + CREDITS, // Créditos + QUIT, // Salir del juego +}; - // --- Opciones para la sección actual --- - enum class Options - { - GAME_PLAY_1P, // Iniciar el juego con el jugador 1 - GAME_PLAY_2P, // Iniciar el juego con el jugador 2 - GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores - TITLE_TIME_OUT, // Timeout en el título - TITLE_1, // Opción 1 en el título - TITLE_2, // Opción 2 en el título - RELOAD, // Recargar sección - HI_SCORE_AFTER_PLAYING, // Mostrar récord tras jugar - SHUTDOWN, // Apagar el sistema - NONE, // Sin opción - }; +// --- Opciones para la sección actual --- +enum class Options { + GAME_PLAY_1P, // Iniciar el juego con el jugador 1 + GAME_PLAY_2P, // Iniciar el juego con el jugador 2 + GAME_PLAY_BOTH, // Iniciar el juego con los dos jugadores + TITLE_TIME_OUT, // Timeout en el título + TITLE_1, // Opción 1 en el título + TITLE_2, // Opción 2 en el título + RELOAD, // Recargar sección + HI_SCORE_AFTER_PLAYING, // Mostrar récord tras jugar + SHUTDOWN, // Apagar el sistema + NONE, // Sin opción +}; - // --- Modos para el Attract Mode --- - enum class AttractMode - { - TITLE_TO_DEMO, // Pasar de título a demo - TITLE_TO_LOGO, // Pasar de título a logo - }; +// --- Modos para el Attract Mode --- +enum class AttractMode { + TITLE_TO_DEMO, // Pasar de título a demo + TITLE_TO_LOGO, // Pasar de título a logo +}; - // --- Variables globales de estado --- - extern Name name; // Sección actual - extern Options options; // Opción seleccionada en la sección - extern AttractMode attract_mode; // Estado del Attract Mode -} \ No newline at end of file +// --- Variables globales de estado --- +extern Name name; // Sección actual +extern Options options; // Opción seleccionada en la sección +extern AttractMode attract_mode; // Estado del Attract Mode +} // namespace Section \ No newline at end of file diff --git a/source/sections/credits.cpp b/source/sections/credits.cpp index e08f6ef..ce75ec9 100644 --- a/source/sections/credits.cpp +++ b/source/sections/credits.cpp @@ -1,7 +1,8 @@ // IWYU pragma: no_include <bits/std_abs.h> #include "credits.h" -#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_RenderTexture +#include <SDL3/SDL.h> // Para SDL_RenderFillRect, SDL_RenderTexture + #include <algorithm> // Para max, min, clamp #include <array> // Para array #include <cmath> // Para abs @@ -9,24 +10,24 @@ #include <string> // Para basic_string, string #include <vector> // Para vector -#include "audio.h" // Para Audio -#include "balloon_manager.h" // Para BalloonManager -#include "fade.h" // Para Fade, FadeType, FadeMode -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input, INPUT_ALLOW_REPEAT -#include "lang.h" // Para getText -#include "param.h" // Para Param, param, ParamGame, ParamFade -#include "player.h" // Para Player, PlayerState -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name -#include "sprite.h" // Para Sprite -#include "text.h" // Para Text, TEXT_CENTER, TEXT_SHADOW -#include "texture.h" // Para Texture -#include "tiled_bg.h" // Para TiledBG, TiledBGMode -#include "ui/service_menu.h" // Para ServiceMenu -#include "utils.h" // Para Color, Zone, SHADOW_TEXT_COLOR, NO_TEXT... +#include "audio.h" // Para Audio +#include "balloon_manager.h" // Para BalloonManager +#include "fade.h" // Para Fade, FadeType, FadeMode +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input, INPUT_ALLOW_REPEAT +#include "lang.h" // Para getText +#include "param.h" // Para Param, param, ParamGame, ParamFade +#include "player.h" // Para Player, PlayerState +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name +#include "sprite.h" // Para Sprite +#include "text.h" // Para Text, TEXT_CENTER, TEXT_SHADOW +#include "texture.h" // Para Texture +#include "tiled_bg.h" // Para TiledBG, TiledBGMode +#include "ui/service_menu.h" // Para ServiceMenu +#include "utils.h" // Para Color, Zone, SHADOW_TEXT_COLOR, NO_TEXT... // Textos constexpr const char TEXT_COPYRIGHT[] = "@2020,2025 JailDesigner"; @@ -38,10 +39,8 @@ Credits::Credits() fade_in_(std::make_unique<Fade>()), fade_out_(std::make_unique<Fade>()), text_texture_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), - canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)) -{ - if (!text_texture_) - { + canvas_(SDL_CreateTexture(Screen::get()->getRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)) { + if (!text_texture_) { throw std::runtime_error("Failed to create SDL texture for text."); } Section::name = Section::Name::CREDITS; @@ -67,8 +66,7 @@ Credits::Credits() } // Destructor -Credits::~Credits() -{ +Credits::~Credits() { SDL_DestroyTexture(text_texture_); SDL_DestroyTexture(canvas_); resetVolume(); @@ -76,26 +74,21 @@ Credits::~Credits() } // Bucle principal -void Credits::run() -{ - while (Section::name == Section::Name::CREDITS) - { +void Credits::run() { + while (Section::name == Section::Name::CREDITS) { checkInput(); update(); - checkEvents(); // Tiene que ir antes del render + checkEvents(); // Tiene que ir antes del render render(); } } // Actualiza las variables -void Credits::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { +void Credits::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { ticks_ = SDL_GetTicks(); const int REPEAT = want_to_pass_ ? 4 : 1; - for (int i = 0; i < REPEAT; ++i) - { + for (int i = 0; i < REPEAT; ++i) { tiled_bg_->update(); cycleColors(); balloon_manager_->update(); @@ -113,8 +106,7 @@ void Credits::update() } // Dibuja Credits::en patalla -void Credits::render() -{ +void Credits::render() { // Prepara para empezar a dibujar en la textura de juego Screen::get()->start(); @@ -126,30 +118,23 @@ void Credits::render() } // Comprueba el manejador de eventos -void Credits::checkEvents() -{ +void Credits::checkEvents() { SDL_Event event; - while (SDL_PollEvent(&event)) - { + while (SDL_PollEvent(&event)) { GlobalEvents::check(event); } } // Comprueba las entradas -void Credits::checkInput() -{ +void Credits::checkInput() { Input::get()->update(); - if (!ServiceMenu::get()->isEnabled()) - { + if (!ServiceMenu::get()->isEnabled()) { // Comprueba si se ha pulsado cualquier botón (de los usados para jugar) - if (Input::get()->checkAnyButton(INPUT_ALLOW_REPEAT)) - { + if (Input::get()->checkAnyButton(INPUT_ALLOW_REPEAT)) { want_to_pass_ = true; fading_ = mini_logo_on_position_; - } - else - { + } else { want_to_pass_ = false; } } @@ -159,8 +144,7 @@ void Credits::checkInput() } // Crea la textura con el texto -void Credits::fillTextTexture() -{ +void Credits::fillTextTexture() { auto text = Resource::get()->getText("smb2"); auto text_grad = Resource::get()->getText("smb2_grad"); SDL_SetRenderTarget(Screen::get()->getRenderer(), text_texture_); @@ -248,8 +232,7 @@ void Credits::fillTextTexture() } // Dibuja todos los sprites en la textura -void Credits::fillCanvas() -{ +void Credits::fillCanvas() { // Cambia el destino del renderizador auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); SDL_SetRenderTarget(Screen::get()->getRenderer(), canvas_); @@ -279,8 +262,7 @@ void Credits::fillCanvas() SDL_RenderRect(Screen::get()->getRenderer(), &red_rect); // Si el mini_logo está en su destino, lo dibuja encima de lo anterior - if (mini_logo_on_position_) - { + if (mini_logo_on_position_) { SDL_RenderTexture(Screen::get()->getRenderer(), text_texture_, &mini_logo_rect_src_, &mini_logo_rect_dst_); } @@ -293,72 +275,57 @@ void Credits::fillCanvas() } // Actualiza el destino de los rectangulos de las texturas -void Credits::updateTextureDstRects() -{ - if (counter_ % 10 == 0) - { +void Credits::updateTextureDstRects() { + if (counter_ % 10 == 0) { // Comprueba la posición de la textura con los titulos de credito - if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) - { + if (credits_rect_dst_.y + credits_rect_dst_.h > play_area_.y) { --credits_rect_dst_.y; } // Comprueba la posición de la textura con el mini_logo - if (mini_logo_rect_dst_.y == mini_logo_final_pos_) - { + if (mini_logo_rect_dst_.y == mini_logo_final_pos_) { mini_logo_on_position_ = true; // Si el jugador quiere pasar los titulos de credito, el fade se inicia solo - if (want_to_pass_) - { + if (want_to_pass_) { fading_ = true; } // Se activa el contador para evitar que la sección sea infinita - if (counter_prevent_endless_ == 1000) - { + if (counter_prevent_endless_ == 1000) { fading_ = true; - } - else - { + } else { ++counter_prevent_endless_; } - } - else - { + } else { --mini_logo_rect_dst_.y; } } } // Tira globos al escenario -void Credits::throwBalloons() -{ +void Credits::throwBalloons() { constexpr int speed = 200; const std::vector<int> sets = {0, 63, 25, 67, 17, 75, 13, 50}; - if (counter_ > ((sets.size() - 1) * speed) * 3) - { + if (counter_ > ((sets.size() - 1) * speed) * 3) { return; } - if (counter_ % speed == 0) - { + if (counter_ % speed == 0) { const int index = (counter_ / speed) % sets.size(); balloon_manager_->deploySet(sets.at(index), -60); } - if (counter_ % (speed * 4) == 0 && counter_ > 0) - { + if (counter_ % (speed * 4) == 0 && counter_ > 0) { balloon_manager_->createPowerBall(); } } // Inicializa los jugadores -void Credits::initPlayers() -{ - std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores; - std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador +void Credits::initPlayers() { + std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores; + std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador // Texturas - Player1 { @@ -397,14 +364,11 @@ void Credits::initPlayers() } // Actualiza los rectangulos negros -void Credits::updateBlackRects() -{ +void Credits::updateBlackRects() { static int current_step = steps_; - if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) - { + if (top_black_rect_.h != param.game.game_area.center_y - 1 && bottom_black_rect_.y != param.game.game_area.center_y + 1) { // Si los rectangulos superior e inferior no han llegado al centro - if (counter_ % 4 == 0) - { + if (counter_ % 4 == 0) { // Incrementa la altura del rectangulo superior top_black_rect_.h = std::min(top_black_rect_.h + 1, param.game.game_area.center_y - 1); @@ -415,12 +379,9 @@ void Credits::updateBlackRects() --current_step; setVolume(static_cast<int>(initial_volume_ * current_step / steps_)); } - } - else - { + } else { // Si los rectangulos superior e inferior han llegado al centro - if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) - { + if (left_black_rect_.w != param.game.game_area.center_x && right_black_rect_.x != param.game.game_area.center_x) { constexpr int SPEED = 2; // Si los rectangulos izquierdo y derecho no han llegado al centro // Incrementa la anchura del rectangulo situado a la izquierda @@ -432,18 +393,13 @@ void Credits::updateBlackRects() --current_step; setVolume(static_cast<int>(initial_volume_ * current_step / steps_)); - } - else - { + } else { // Si los rectangulos izquierdo y derecho han llegado al centro setVolume(0); Audio::get()->stopMusic(); - if (counter_pre_fade_ == 400) - { + if (counter_pre_fade_ == 400) { fade_out_->activate(); - } - else - { + } else { ++counter_pre_fade_; } } @@ -451,8 +407,7 @@ void Credits::updateBlackRects() } // Actualiza el rectangulo rojo -void Credits::updateRedRect() -{ +void Credits::updateRedRect() { red_rect.x = left_black_rect_.x + left_black_rect_.w; red_rect.y = top_black_rect_.y + top_black_rect_.h - 1; red_rect.w = right_black_rect_.x - red_rect.x; @@ -460,76 +415,66 @@ void Credits::updateRedRect() } // Actualiza el estado de fade -void Credits::updateAllFades() -{ - if (fading_) - { +void Credits::updateAllFades() { + if (fading_) { updateBlackRects(); updateRedRect(); } fade_in_->update(); - if (fade_in_->hasEnded()) - { + if (fade_in_->hasEnded()) { Audio::get()->playMusic("credits.ogg"); } fade_out_->update(); - if (fade_out_->hasEnded()) - { + if (fade_out_->hasEnded()) { Section::name = Section::Name::HI_SCORE_TABLE; } } // Establece el nivel de volumen -void Credits::setVolume(int amount) -{ +void Credits::setVolume(int amount) { Options::audio.music.volume = std::clamp(amount, 0, 100); Audio::get()->setMusicVolume(Options::audio.music.volume); } // Reestablece el nivel de volumen -void Credits::resetVolume() -{ +void Credits::resetVolume() { Options::audio.music.volume = initial_volume_; Audio::get()->setMusicVolume(Options::audio.music.volume); } // Cambia el color del fondo -void Credits::cycleColors() -{ +void Credits::cycleColors() { // constexpr int UPPER_LIMIT = 255; // Límite superior // constexpr int LOWER_LIMIT = 80; // Límite inferior - constexpr int UPPER_LIMIT = 140; // Límite superior - constexpr int LOWER_LIMIT = 30; // Límite inferior + constexpr int UPPER_LIMIT = 140; // Límite superior + constexpr int LOWER_LIMIT = 30; // Límite inferior static float r = static_cast<float>(UPPER_LIMIT); static float g = static_cast<float>(LOWER_LIMIT); static float b = static_cast<float>(LOWER_LIMIT); - static float stepR = -0.5f; // Paso flotante para transiciones suaves + static float stepR = -0.5f; // Paso flotante para transiciones suaves static float stepG = 0.3f; static float stepB = 0.1f; // Ajustar valores de R r += stepR; - if (r >= UPPER_LIMIT || r <= LOWER_LIMIT) - { - stepR = -stepR; // Cambia de dirección al alcanzar los límites + if (r >= UPPER_LIMIT || r <= LOWER_LIMIT) { + stepR = -stepR; // Cambia de dirección al alcanzar los límites } // Ajustar valores de G g += stepG; - if (g >= UPPER_LIMIT || g <= LOWER_LIMIT) - { - stepG = -stepG; // Cambia de dirección al alcanzar los límites + if (g >= UPPER_LIMIT || g <= LOWER_LIMIT) { + stepG = -stepG; // Cambia de dirección al alcanzar los límites } // Ajustar valores de B b += stepB; - if (b >= UPPER_LIMIT || b <= LOWER_LIMIT) - { - stepB = -stepB; // Cambia de dirección al alcanzar los límites + if (b >= UPPER_LIMIT || b <= LOWER_LIMIT) { + stepB = -stepB; // Cambia de dirección al alcanzar los límites } // Aplicar el color, redondeando a enteros antes de usar @@ -538,19 +483,15 @@ void Credits::cycleColors() } // Actualza los jugadores -void Credits::updatePlayers() -{ - for (auto &player : players_) - { +void Credits::updatePlayers() { + for (auto &player : players_) { player->update(); } } // Renderiza los jugadores -void Credits::renderPlayers() -{ - for (auto const &player : players_) - { +void Credits::renderPlayers() { + for (auto const &player : players_) { player->render(); } } diff --git a/source/sections/credits.h b/source/sections/credits.h index 36682a7..d09e0da 100644 --- a/source/sections/credits.h +++ b/source/sections/credits.h @@ -1,12 +1,13 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, Uint32, SDL_Texture, Uint64 -#include <memory> // Para unique_ptr, shared_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FRect, Uint32, SDL_Texture, Uint64 -#include "options.h" // Para AudioOptions, MusicOptions, audio -#include "param.h" // Para Param, ParamGame, param -#include "utils.h" // Para Zone, Color +#include <memory> // Para unique_ptr, shared_ptr +#include <vector> // Para vector + +#include "options.h" // Para AudioOptions, MusicOptions, audio +#include "param.h" // Para Param, ParamGame, param +#include "utils.h" // Para Zone, Color // Declaraciones adelantadas class BalloonManager; @@ -14,9 +15,8 @@ class Fade; class Player; class TiledBG; -class Credits -{ -public: +class Credits { + public: // --- Constructores y destructor --- Credits(); ~Credits(); @@ -24,40 +24,40 @@ public: // --- Bucle principal --- void run(); -private: + private: // --- Constantes de clase --- static constexpr int PLAY_AREA_HEIGHT = 200; // --- Objetos principales --- - std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos - std::unique_ptr<TiledBG> tiled_bg_; // Mosaico animado de fondo - std::unique_ptr<Fade> fade_in_; // Fundido de entrada - std::unique_ptr<Fade> fade_out_; // Fundido de salida - std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores + std::unique_ptr<BalloonManager> balloon_manager_; // Gestión de globos + std::unique_ptr<TiledBG> tiled_bg_; // Mosaico animado de fondo + std::unique_ptr<Fade> fade_in_; // Fundido de entrada + std::unique_ptr<Fade> fade_out_; // Fundido de salida + std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores // --- Gestión de texturas --- - SDL_Texture *text_texture_; // Textura con el texto de créditos - SDL_Texture *canvas_; // Textura donde se dibuja todo + SDL_Texture *text_texture_; // Textura con el texto de créditos + SDL_Texture *canvas_; // Textura donde se dibuja todo // --- Temporización y contadores --- - Uint64 ticks_ = 0; // Control de velocidad del programa - Uint32 counter_ = 0; // Contador principal de lógica - Uint32 counter_pre_fade_ = 0; // Activación del fundido final - Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito + Uint64 ticks_ = 0; // Control de velocidad del programa + Uint32 counter_ = 0; // Contador principal de lógica + Uint32 counter_pre_fade_ = 0; // Activación del fundido final + Uint32 counter_prevent_endless_ = 0; // Prevención de bucle infinito // --- Variables de estado --- - bool fading_ = false; // Estado del fade final - bool want_to_pass_ = false; // Jugador quiere saltarse créditos - bool mini_logo_on_position_ = false; // Minilogo en posición final + bool fading_ = false; // Estado del fade final + bool want_to_pass_ = false; // Jugador quiere saltarse créditos + bool mini_logo_on_position_ = false; // Minilogo en posición final // --- Diseño y posicionamiento --- - float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2; // Tamaño de las barras negras - int mini_logo_final_pos_ = 0; // Posición final del minilogo - Color color_; // Color usado para los efectos + float black_bars_size_ = (param.game.game_area.rect.h - PLAY_AREA_HEIGHT) / 2; // Tamaño de las barras negras + int mini_logo_final_pos_ = 0; // Posición final del minilogo + Color color_; // Color usado para los efectos // --- Control de audio --- - int initial_volume_ = Options::audio.music.volume; // Volumen inicial - int steps_ = 0; // Pasos para reducir audio + int initial_volume_ = Options::audio.music.volume; // Volumen inicial + int steps_ = 0; // Pasos para reducir audio // --- Rectángulos de renderizado --- // Texto de créditos @@ -98,32 +98,32 @@ private: 2}; // Borde para la ventana - SDL_FRect red_rect = play_area_; // Delimitador de ventana + SDL_FRect red_rect = play_area_; // Delimitador de ventana // --- Métodos del bucle principal --- - void update(); // Actualización principal de la lógica - void render(); // Renderizado de la escena - void checkEvents(); // Manejo de eventos - void checkInput(); // Procesamiento de entrada + void update(); // Actualización principal de la lógica + void render(); // Renderizado de la escena + void checkEvents(); // Manejo de eventos + void checkInput(); // Procesamiento de entrada // --- Métodos de renderizado --- - void fillTextTexture(); // Crear textura de texto de créditos - void fillCanvas(); // Renderizar todos los sprites y fondos - void updateTextureDstRects(); // Actualizar destinos de texturas - void renderPlayers(); // Renderiza los jugadores + void fillTextTexture(); // Crear textura de texto de créditos + void fillCanvas(); // Renderizar todos los sprites y fondos + void updateTextureDstRects(); // Actualizar destinos de texturas + void renderPlayers(); // Renderiza los jugadores // --- Métodos de lógica del juego --- - void throwBalloons(); // Lanzar globos al escenario - void initPlayers(); // Inicializar jugadores - void updateAllFades(); // Actualizar estados de fade - void cycleColors(); // Cambiar colores de fondo - void updatePlayers(); // Actualza los jugadores + void throwBalloons(); // Lanzar globos al escenario + void initPlayers(); // Inicializar jugadores + void updateAllFades(); // Actualizar estados de fade + void cycleColors(); // Cambiar colores de fondo + void updatePlayers(); // Actualza los jugadores // --- Métodos de interfaz --- - void updateBlackRects(); // Actualizar rectángulos negros (letterbox) - void updateRedRect(); // Actualizar rectángulo rojo (borde) + void updateBlackRects(); // Actualizar rectángulos negros (letterbox) + void updateRedRect(); // Actualizar rectángulo rojo (borde) // --- Métodos de audio --- - void setVolume(int amount); // Establecer volumen - void resetVolume(); // Restablecer volumen + void setVolume(int amount); // Establecer volumen + void resetVolume(); // Restablecer volumen }; \ No newline at end of file diff --git a/source/sections/game.cpp b/source/sections/game.cpp index 580bb32..149b62b 100644 --- a/source/sections/game.cpp +++ b/source/sections/game.cpp @@ -1,2151 +1,1826 @@ #include "game.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget -#include <stdlib.h> // Para rand, size_t -#include <algorithm> // Para find_if, clamp, find, min -#include <functional> // Para function -#include <iterator> // Para distance, size +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget +#include <stdlib.h> // Para rand, size_t -#include "asset.h" // Para Asset -#include "audio.h" // Para Audio -#include "background.h" // Para Background -#include "balloon.h" // Para Balloon, BALLOON_SPEED -#include "balloon_manager.h" // Para BalloonManager -#include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus -#include "fade.h" // Para Fade, FadeType, FadeMode -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para InputAction, Input, INPUT_DO_NOT_A... -#include "item.h" // Para Item, ItemType -#include "lang.h" // Para getText -#include "manage_hiscore_table.h" // Para HiScoreEntry, ManageHiScoreTable -#include "notifier.h" // Para Notifier -#include "param.h" // Para Param, param, ParamGame, ParamScor... -#include "path_sprite.h" // Para Path, PathSprite, createPath, Path... -#include "player.h" // Para Player, PlayerState -#include "resource.h" // Para Resource -#include "scoreboard.h" // Para Scoreboard, ScoreboardMode, SCOREB... -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, AttractMode, Options -#include "smart_sprite.h" // Para SmartSprite -#include "stage.h" // Para number, get, Stage, total_power -#include "tabe.h" // Para Tabe, TabeState -#include "text.h" // Para Text -#include "texture.h" // Para Texture -#include "ui/service_menu.h" // Para ServiceMenu +#include <algorithm> // Para find_if, clamp, find, min +#include <functional> // Para function +#include <iterator> // Para distance, size + +#include "asset.h" // Para Asset +#include "audio.h" // Para Audio +#include "background.h" // Para Background +#include "balloon.h" // Para Balloon, BALLOON_SPEED +#include "balloon_manager.h" // Para BalloonManager +#include "bullet.h" // Para Bullet, BulletType, BulletMoveStatus +#include "fade.h" // Para Fade, FadeType, FadeMode +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para InputAction, Input, INPUT_DO_NOT_A... +#include "item.h" // Para Item, ItemType +#include "lang.h" // Para getText +#include "manage_hiscore_table.h" // Para HiScoreEntry, ManageHiScoreTable +#include "notifier.h" // Para Notifier +#include "param.h" // Para Param, param, ParamGame, ParamScor... +#include "path_sprite.h" // Para Path, PathSprite, createPath, Path... +#include "player.h" // Para Player, PlayerState +#include "resource.h" // Para Resource +#include "scoreboard.h" // Para Scoreboard, ScoreboardMode, SCOREB... +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, AttractMode, Options +#include "smart_sprite.h" // Para SmartSprite +#include "stage.h" // Para number, get, Stage, total_power +#include "tabe.h" // Para Tabe, TabeState +#include "text.h" // Para Text +#include "texture.h" // Para Texture +#include "ui/service_menu.h" // Para ServiceMenu // Constructor Game::Game(int player_id, int current_stage, bool demo) - : renderer_(Screen::get()->getRenderer()), - screen_(Screen::get()), - input_(Input::get()), - background_(std::make_unique<Background>()), - canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), - fade_in_(std::make_unique<Fade>()), - fade_out_(std::make_unique<Fade>()), - balloon_manager_(std::make_unique<BalloonManager>()), - tabe_(std::make_unique<Tabe>()) -{ - // Pasa variables - demo_.enabled = demo; + : renderer_(Screen::get()->getRenderer()), + screen_(Screen::get()), + input_(Input::get()), + background_(std::make_unique<Background>()), + canvas_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.play_area.rect.w, param.game.play_area.rect.h)), + fade_in_(std::make_unique<Fade>()), + fade_out_(std::make_unique<Fade>()), + balloon_manager_(std::make_unique<BalloonManager>()), + tabe_(std::make_unique<Tabe>()) { + // Pasa variables + demo_.enabled = demo; - // Otras variables - Section::name = Section::Name::GAME; - Section::options = Section::Options::NONE; - Stage::init(); - Stage::number = current_stage; + // Otras variables + Section::name = Section::Name::GAME; + Section::options = Section::Options::NONE; + Stage::init(); + Stage::number = current_stage; - // Asigna texturas y animaciones - setResources(); + // Asigna texturas y animaciones + setResources(); - // Crea y configura los objetos - Scoreboard::init(); - scoreboard_ = Scoreboard::get(); + // Crea y configura los objetos + Scoreboard::init(); + scoreboard_ = Scoreboard::get(); - fade_in_->setColor(param.fade.color); - fade_in_->setPreDuration(demo_.enabled ? 80 : 0); - fade_in_->setPostDuration(0); - fade_in_->setType(FadeType::RANDOM_SQUARE); - fade_in_->setMode(FadeMode::IN); - fade_in_->activate(); + fade_in_->setColor(param.fade.color); + fade_in_->setPreDuration(demo_.enabled ? 80 : 0); + fade_in_->setPostDuration(0); + fade_in_->setType(FadeType::RANDOM_SQUARE); + fade_in_->setMode(FadeMode::IN); + fade_in_->activate(); - fade_out_->setColor(param.fade.color); - fade_out_->setPostDuration(param.fade.post_duration); - fade_out_->setType(FadeType::VENETIAN); + fade_out_->setColor(param.fade.color); + fade_out_->setPostDuration(param.fade.post_duration); + fade_out_->setType(FadeType::VENETIAN); - background_->setPos(param.game.play_area.rect); + background_->setPos(param.game.play_area.rect); - balloon_manager_->setBouncingSounds(param.balloon.bouncing_sound); + balloon_manager_->setBouncingSounds(param.balloon.bouncing_sound); - SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND); + SDL_SetTextureBlendMode(canvas_, SDL_BLENDMODE_BLEND); - // Inicializa el resto de variables - initPlayers(player_id); - initScoreboard(); - initDifficultyVars(); - initDemo(player_id); - initPaths(); - setTotalPower(); + // Inicializa el resto de variables + initPlayers(player_id); + initScoreboard(); + initDifficultyVars(); + initDemo(player_id); + initPaths(); + setTotalPower(); #ifdef DEBUG - // Si se empieza en una fase que no es la primera - if (!demo_.enabled) - for (int i = 0; i < Stage::number; ++i) - { - Stage::total_power += Stage::get(i).power_to_complete; - } + // Si se empieza en una fase que no es la primera + if (!demo_.enabled) + for (int i = 0; i < Stage::number; ++i) { + Stage::total_power += Stage::get(i).power_to_complete; + } #endif } -Game::~Game() -{ - // [Modo JUEGO] Guarda puntuaciones y transita a modo título - if (!demo_.enabled) - { - auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); - manager->saveToFile(Asset::get()->get("score.bin")); - Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; - Audio::get()->stopMusic(); - } +Game::~Game() { + // [Modo JUEGO] Guarda puntuaciones y transita a modo título + if (!demo_.enabled) { + auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); + manager->saveToFile(Asset::get()->get("score.bin")); + Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; + Audio::get()->stopMusic(); + } #ifdef RECORDING - saveDemoFile(Asset::get()->get("demo1.bin"), demo_.data.at(0)); + saveDemoFile(Asset::get()->get("demo1.bin"), demo_.data.at(0)); #endif - Scoreboard::destroy(); - SDL_DestroyTexture(canvas_); + Scoreboard::destroy(); + SDL_DestroyTexture(canvas_); } // Asigna texturas y animaciones -void Game::setResources() -{ - // Texturas - Game_text - { - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_1000_points")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_2500_points")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_5000_points")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_powerup")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_one_hit")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_stop")); - game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_100000_points")); - } +void Game::setResources() { + // Texturas - Game_text + { + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_1000_points")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_2500_points")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_5000_points")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_powerup")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_one_hit")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_stop")); + game_text_textures_.emplace_back(Resource::get()->getTexture("game_text_100000_points")); + } - // Texturas - Items - { - item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_debian.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_coffee_machine.png")); - } + // Texturas - Items + { + item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_debian.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_coffee_machine.png")); + } - // Texturas - Player1 - { - std::vector<std::shared_ptr<Texture>> player_texture; - player_texture.emplace_back(Resource::get()->getTexture("player1.gif")); - player_texture.emplace_back(Resource::get()->getTexture("player1_power.png")); - player_textures_.push_back(player_texture); - } + // Texturas - Player1 + { + std::vector<std::shared_ptr<Texture>> player_texture; + player_texture.emplace_back(Resource::get()->getTexture("player1.gif")); + player_texture.emplace_back(Resource::get()->getTexture("player1_power.png")); + player_textures_.push_back(player_texture); + } - // Texturas - Player2 - { - std::vector<std::shared_ptr<Texture>> player_texture; - player_texture.emplace_back(Resource::get()->getTexture("player2.gif")); - player_texture.emplace_back(Resource::get()->getTexture("player2_power.png")); - player_textures_.push_back(player_texture); - } + // Texturas - Player2 + { + std::vector<std::shared_ptr<Texture>> player_texture; + player_texture.emplace_back(Resource::get()->getTexture("player2.gif")); + player_texture.emplace_back(Resource::get()->getTexture("player2_power.png")); + player_textures_.push_back(player_texture); + } - // Animaciones -- Jugador - { - player_animations_.emplace_back(Resource::get()->getAnimation("player.ani")); - player_animations_.emplace_back(Resource::get()->getAnimation("player_power.ani")); - } + // Animaciones -- Jugador + { + player_animations_.emplace_back(Resource::get()->getAnimation("player.ani")); + player_animations_.emplace_back(Resource::get()->getAnimation("player_power.ani")); + } - // Animaciones -- Items - { - item_animations_.emplace_back(Resource::get()->getAnimation("item_points1_disk.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_points2_gavina.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_points3_pacmar.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_clock.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_coffee.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_debian.ani")); - item_animations_.emplace_back(Resource::get()->getAnimation("item_coffee_machine.ani")); - } + // Animaciones -- Items + { + item_animations_.emplace_back(Resource::get()->getAnimation("item_points1_disk.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_points2_gavina.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_points3_pacmar.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_clock.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_coffee.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_debian.ani")); + item_animations_.emplace_back(Resource::get()->getAnimation("item_coffee_machine.ani")); + } } // Actualiza el valor de hiScore en caso necesario -void Game::updateHiScore() -{ - hi_score_.name = Options::settings.hi_score_table.front().name; +void Game::updateHiScore() { + hi_score_.name = Options::settings.hi_score_table.front().name; - // Si la puntuación actual es mayor que la máxima puntuación - for (const auto &player : players_) - { - if (player->getScore() > hi_score_.score) - { - // Actualiza la máxima puntuación - hi_score_.score = player->getScore(); - hi_score_.name.clear(); + // Si la puntuación actual es mayor que la máxima puntuación + for (const auto &player : players_) { + if (player->getScore() > hi_score_.score) { + // Actualiza la máxima puntuación + hi_score_.score = player->getScore(); + hi_score_.name.clear(); - // Si se supera la máxima puntuación emite sonido - if (hi_score_achieved_ == false) - { - hi_score_achieved_ = true; - playSound("hi_score_achieved.wav"); - } - } - } + // Si se supera la máxima puntuación emite sonido + if (hi_score_achieved_ == false) { + hi_score_achieved_ = true; + playSound("hi_score_achieved.wav"); + } + } + } } // Actualiza las variables del jugador -void Game::updatePlayers() -{ - for (auto &player : players_) - { - player->update(); +void Game::updatePlayers() { + for (auto &player : players_) { + player->update(); - if (player->isPlaying()) - { - // Comprueba la colisión entre el jugador y los globos - auto balloon = checkPlayerBalloonCollision(player); + if (player->isPlaying()) { + // Comprueba la colisión entre el jugador y los globos + auto balloon = checkPlayerBalloonCollision(player); - // Si hay colisión - if (balloon) - { - // Si el globo está parado y el temporizador activo, lo explota - if (balloon->isStopped() && time_stopped_counter_ > 0) - { - balloon_manager_->popBalloon(balloon); - } - // En caso contrario, el jugador ha sido golpeado por un globo activo - else - { - handlePlayerCollision(player); + // Si hay colisión + if (balloon) { + // Si el globo está parado y el temporizador activo, lo explota + if (balloon->isStopped() && time_stopped_counter_ > 0) { + balloon_manager_->popBalloon(balloon); + } + // En caso contrario, el jugador ha sido golpeado por un globo activo + else { + handlePlayerCollision(player); - if (demo_.enabled && allPlayersAreNotPlaying()) - { - fade_out_->setType(FadeType::RANDOM_SQUARE); - fade_out_->activate(); - } - } - } + if (demo_.enabled && allPlayersAreNotPlaying()) { + fade_out_->setType(FadeType::RANDOM_SQUARE); + fade_out_->activate(); + } + } + } - // Comprueba las colisiones entre el jugador y los items - checkPlayerItemCollision(player); - } - } + // Comprueba las colisiones entre el jugador y los items + checkPlayerItemCollision(player); + } + } - // Organiza la lista de jugadores - movePlayersToFront(); + // Organiza la lista de jugadores + movePlayersToFront(); } // Dibuja a los jugadores -void Game::renderPlayers() -{ - for (auto &player : players_) - { - if (!player->isWaiting()) - { - player->render(); - } - } +void Game::renderPlayers() { + for (auto &player : players_) { + if (!player->isWaiting()) { + player->render(); + } + } } // Comprueba si hay cambio de fase y actualiza las variables -void Game::updateStage() -{ - if (Stage::power >= Stage::get(Stage::number).power_to_complete) - { - // Cambio de fase - Stage::power = Stage::get(Stage::number).power_to_complete - Stage::power; - ++Stage::number; - playSound("stage_change.wav"); - balloon_manager_->resetBalloonSpeed(); - screen_->flash(FLASH_COLOR, 3); - screen_->shake(); +void Game::updateStage() { + if (Stage::power >= Stage::get(Stage::number).power_to_complete) { + // Cambio de fase + Stage::power = Stage::get(Stage::number).power_to_complete - Stage::power; + ++Stage::number; + playSound("stage_change.wav"); + balloon_manager_->resetBalloonSpeed(); + screen_->flash(FLASH_COLOR, 3); + screen_->shake(); - // Escribe el texto por pantalla - if (Stage::number < 10) - { - std::vector<Path> paths = {paths_.at(2), paths_.at(3)}; - if (Stage::number == 9) - { - createMessage(paths, Resource::get()->getTexture("game_text_last_stage")); - } - else - { - auto text = Resource::get()->getText("04b_25_2x"); - const std::string caption = std::to_string(10 - Stage::number) + Lang::getText("[GAME_TEXT] 2"); - createMessage(paths, text->writeToTexture(caption, 1, -4)); - } - } + // Escribe el texto por pantalla + if (Stage::number < 10) { + std::vector<Path> paths = {paths_.at(2), paths_.at(3)}; + if (Stage::number == 9) { + createMessage(paths, Resource::get()->getTexture("game_text_last_stage")); + } else { + auto text = Resource::get()->getText("04b_25_2x"); + const std::string caption = std::to_string(10 - Stage::number) + Lang::getText("[GAME_TEXT] 2"); + createMessage(paths, text->writeToTexture(caption, 1, -4)); + } + } - // Modifica el color de fondo al llegar a la Fase 10 - if (Stage::number == 9) - { - background_->setColor(Color(0xdd, 0x19, 0x1d).darken()); - background_->setAlpha(96); - } - } + // Modifica el color de fondo al llegar a la Fase 10 + if (Stage::number == 9) { + background_->setColor(Color(0xdd, 0x19, 0x1d).darken()); + background_->setAlpha(96); + } + } } // Actualiza el estado de fin de la partida -void Game::updateGameStateGameOver() -{ - fade_out_->update(); - updatePlayers(); - updateScoreboard(); - updateBackground(); - balloon_manager_->update(); - tabe_->update(); - updateBullets(); - updateItems(); - updateSmartSprites(); - updatePathSprites(); - updateTimeStopped(); - checkBulletCollision(); - cleanVectors(); +void Game::updateGameStateGameOver() { + fade_out_->update(); + updatePlayers(); + updateScoreboard(); + updateBackground(); + balloon_manager_->update(); + tabe_->update(); + updateBullets(); + updateItems(); + updateSmartSprites(); + updatePathSprites(); + updateTimeStopped(); + checkBulletCollision(); + cleanVectors(); - if (game_over_counter_ > 0) - { - if (game_over_counter_ == GAME_OVER_COUNTER_) - { - createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over")); - Audio::get()->fadeOutMusic(1000); - balloon_manager_->setBouncingSounds(true); - } + if (game_over_counter_ > 0) { + if (game_over_counter_ == GAME_OVER_COUNTER_) { + createMessage({paths_.at(2), paths_.at(3)}, Resource::get()->getTexture("game_text_game_over")); + Audio::get()->fadeOutMusic(1000); + balloon_manager_->setBouncingSounds(true); + } - game_over_counter_--; + game_over_counter_--; - if (game_over_counter_ == 150) - { - fade_out_->activate(); - } - } + if (game_over_counter_ == 150) { + fade_out_->activate(); + } + } - if (fade_out_->isEnabled()) - { - if (Options::audio.enabled) - { - const float VOL = static_cast<float>(64 * (100 - fade_out_->getValue())) / 100.0f; - Audio::get()->setSoundVolume(static_cast<int>(VOL), Audio::Group::GAME); - } - } + if (fade_out_->isEnabled()) { + if (Options::audio.enabled) { + const float VOL = static_cast<float>(64 * (100 - fade_out_->getValue())) / 100.0f; + Audio::get()->setSoundVolume(static_cast<int>(VOL), Audio::Group::GAME); + } + } - if (fade_out_->hasEnded()) - { - if (game_completed_counter_ > 0) - { - // Los jugadores han completado el juego - Section::name = Section::Name::CREDITS; - } - else - { - // La partida ha terminado con la derrota de los jugadores - Section::name = Section::Name::HI_SCORE_TABLE; - } - Section::options = Section::Options::HI_SCORE_AFTER_PLAYING; - if (Options::audio.enabled) - { - Audio::get()->stopAllSounds(); - Audio::get()->setSoundVolume(Options::audio.sound.volume, Audio::Group::GAME); - } - } + if (fade_out_->hasEnded()) { + if (game_completed_counter_ > 0) { + // Los jugadores han completado el juego + Section::name = Section::Name::CREDITS; + } else { + // La partida ha terminado con la derrota de los jugadores + Section::name = Section::Name::HI_SCORE_TABLE; + } + Section::options = Section::Options::HI_SCORE_AFTER_PLAYING; + if (Options::audio.enabled) { + Audio::get()->stopAllSounds(); + Audio::get()->setSoundVolume(Options::audio.sound.volume, Audio::Group::GAME); + } + } } // Gestiona eventos para el estado del final del juego -void Game::updateGameStateCompleted() -{ - constexpr int START_CELEBRATIONS = 400; - constexpr int END_CELEBRATIONS = START_CELEBRATIONS + 300; +void Game::updateGameStateCompleted() { + constexpr int START_CELEBRATIONS = 400; + constexpr int END_CELEBRATIONS = START_CELEBRATIONS + 300; - updatePlayers(); - updateScoreboard(); - updateBackground(); - balloon_manager_->update(); - tabe_->update(); - updateBullets(); - updateItems(); - updateSmartSprites(); - updatePathSprites(); - cleanVectors(); + updatePlayers(); + updateScoreboard(); + updateBackground(); + balloon_manager_->update(); + tabe_->update(); + updateBullets(); + updateItems(); + updateSmartSprites(); + updatePathSprites(); + cleanVectors(); - // Para la música y elimina todos los globos e items - if (game_completed_counter_ == 0) - { - stopMusic(); - Stage::number = 9; // Deja el valor dentro de los limites - balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos - playSound("power_ball_explosion.wav"); - destroyAllItems(); // Destruye todos los items - Stage::power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos los globos - background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas - } + // Para la música y elimina todos los globos e items + if (game_completed_counter_ == 0) { + stopMusic(); + Stage::number = 9; // Deja el valor dentro de los limites + balloon_manager_->destroyAllBalloons(); // Destruye a todos los globos + playSound("power_ball_explosion.wav"); + destroyAllItems(); // Destruye todos los items + Stage::power = 0; // Vuelve a dejar el poder a cero, por lo que hubiera podido subir al destruir todos los globos + background_->setAlpha(0); // Elimina el tono rojo de las últimas pantallas + } - // Comienza las celebraciones - // Muestra el mensaje de felicitación y da los puntos a los jugadores - if (game_completed_counter_ == START_CELEBRATIONS) - { - createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations")); - createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points")); + // Comienza las celebraciones + // Muestra el mensaje de felicitación y da los puntos a los jugadores + if (game_completed_counter_ == START_CELEBRATIONS) { + createMessage({paths_.at(4), paths_.at(5)}, Resource::get()->getTexture("game_text_congratulations")); + createMessage({paths_.at(6), paths_.at(7)}, Resource::get()->getTexture("game_text_1000000_points")); - for (auto &player : players_) - if (player->isPlaying()) - { - player->addScore(1000000); - player->setPlayingState(PlayerState::CELEBRATING); - } - else - { - player->setPlayingState(PlayerState::GAME_OVER); - } + for (auto &player : players_) + if (player->isPlaying()) { + player->addScore(1000000); + player->setPlayingState(PlayerState::CELEBRATING); + } else { + player->setPlayingState(PlayerState::GAME_OVER); + } - updateHiScore(); - } + updateHiScore(); + } - // Termina las celebraciones - if (game_completed_counter_ == END_CELEBRATIONS) - { - for (auto &player : players_) - { - if (player->isCelebrating()) - { - player->setPlayingState(player->IsEligibleForHighScore() ? PlayerState::ENTERING_NAME_GAME_COMPLETED : PlayerState::LEAVING_SCREEN); - } - } - } + // Termina las celebraciones + if (game_completed_counter_ == END_CELEBRATIONS) { + for (auto &player : players_) { + if (player->isCelebrating()) { + player->setPlayingState(player->IsEligibleForHighScore() ? PlayerState::ENTERING_NAME_GAME_COMPLETED : PlayerState::LEAVING_SCREEN); + } + } + } - // Si los jugadores ya no estan y no quedan mensajes en pantalla - if (allPlayersAreGameOver() && path_sprites_.size() == 0) - { - setState(GameState::GAME_OVER); - } + // Si los jugadores ya no estan y no quedan mensajes en pantalla + if (allPlayersAreGameOver() && path_sprites_.size() == 0) { + setState(GameState::GAME_OVER); + } - // Incrementa el contador al final - ++game_completed_counter_; + // Incrementa el contador al final + ++game_completed_counter_; } // Comprueba el estado del juego -void Game::checkState() -{ - if (state_ != GameState::COMPLETED && Stage::number == 10) - { - setState(GameState::COMPLETED); - } +void Game::checkState() { + if (state_ != GameState::COMPLETED && Stage::number == 10) { + setState(GameState::COMPLETED); + } - if (state_ != GameState::GAME_OVER && allPlayersAreGameOver()) - { - setState(GameState::GAME_OVER); - } + if (state_ != GameState::GAME_OVER && allPlayersAreGameOver()) { + setState(GameState::GAME_OVER); + } } // Destruye todos los items -void Game::destroyAllItems() -{ - for (auto &item : items_) - item->disable(); +void Game::destroyAllItems() { + for (auto &item : items_) + item->disable(); } // Comprueba la colisión entre el jugador y los globos activos -std::shared_ptr<Balloon> Game::checkPlayerBalloonCollision(std::shared_ptr<Player> &player) -{ - for (auto &balloon : balloon_manager_->getBalloons()) - { - if (!balloon->isInvulnerable() && !balloon->isPowerBall()) - { - if (checkCollision(player->getCollider(), balloon->getCollider())) - { - return balloon; // Devuelve el globo con el que se ha producido la colisión - } - } - } +std::shared_ptr<Balloon> Game::checkPlayerBalloonCollision(std::shared_ptr<Player> &player) { + for (auto &balloon : balloon_manager_->getBalloons()) { + if (!balloon->isInvulnerable() && !balloon->isPowerBall()) { + if (checkCollision(player->getCollider(), balloon->getCollider())) { + return balloon; // Devuelve el globo con el que se ha producido la colisión + } + } + } - return nullptr; // No se ha producido ninguna colisión + return nullptr; // No se ha producido ninguna colisión } // Comprueba la colisión entre el jugador y los items -void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) -{ - if (!player->isPlaying()) - return; +void Game::checkPlayerItemCollision(std::shared_ptr<Player> &player) { + if (!player->isPlaying()) + return; - for (auto &item : items_) - { - if (item->isEnabled()) - { - if (checkCollision(player->getCollider(), item->getCollider())) - { - switch (item->getType()) - { - case ItemType::DISK: - { - player->addScore(1000); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(0)); - playSound("item_pickup.wav"); - break; - } - case ItemType::GAVINA: - { - player->addScore(2500); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(1)); - playSound("item_pickup.wav"); - break; - } - case ItemType::PACMAR: - { - player->addScore(5000); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(2)); - playSound("item_pickup.wav"); - break; - } - case ItemType::DEBIAN: - { - player->addScore(100000); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(6)); - playSound("debian_pickup.wav"); - break; - } - case ItemType::CLOCK: - { - enableTimeStopItem(); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(5)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(5)); - playSound("item_pickup.wav"); - break; - } - case ItemType::COFFEE: - { - if (player->getCoffees() == 2) - { - player->addScore(5000); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(2)); - } - else - { - player->giveExtraHit(); - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(4)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(4)); - } - playSound("voice_coffee.wav"); - break; - } - case ItemType::COFFEE_MACHINE: - { - player->setPowerUp(); - coffee_machine_enabled_ = false; - const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(3)->getWidth()) / 2; - createItemText(x, game_text_textures_.at(3)); - playSound("voice_power_up.wav"); - break; - } - default: - break; - } + for (auto &item : items_) { + if (item->isEnabled()) { + if (checkCollision(player->getCollider(), item->getCollider())) { + switch (item->getType()) { + case ItemType::DISK: { + player->addScore(1000); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(0)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(0)); + playSound("item_pickup.wav"); + break; + } + case ItemType::GAVINA: { + player->addScore(2500); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(1)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(1)); + playSound("item_pickup.wav"); + break; + } + case ItemType::PACMAR: { + player->addScore(5000); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(2)); + playSound("item_pickup.wav"); + break; + } + case ItemType::DEBIAN: { + player->addScore(100000); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(6)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(6)); + playSound("debian_pickup.wav"); + break; + } + case ItemType::CLOCK: { + enableTimeStopItem(); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(5)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(5)); + playSound("item_pickup.wav"); + break; + } + case ItemType::COFFEE: { + if (player->getCoffees() == 2) { + player->addScore(5000); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(2)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(2)); + } else { + player->giveExtraHit(); + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(4)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(4)); + } + playSound("voice_coffee.wav"); + break; + } + case ItemType::COFFEE_MACHINE: { + player->setPowerUp(); + coffee_machine_enabled_ = false; + const auto x = item->getPosX() + (item->getWidth() - game_text_textures_.at(3)->getWidth()) / 2; + createItemText(x, game_text_textures_.at(3)); + playSound("voice_power_up.wav"); + break; + } + default: + break; + } - updateHiScore(); - item->disable(); - } - } - } + updateHiScore(); + item->disable(); + } + } + } } // Comprueba y procesa la colisión de las balas -void Game::checkBulletCollision() -{ - for (auto &bullet : bullets_) - { - // Comprueba la colisión con el Tabe - if (bullet->isEnabled() && tabe_->isEnabled()) - if (checkCollision(bullet->getCollider(), tabe_->getCollider())) - { - tabe_->setState(TabeState::HIT); - bullet->disable(); - auto pos = tabe_->getCollider(); - if (tabe_->tryToGetBonus()) - { - createItem(ItemType::DEBIAN, pos.x, pos.y); - playSound("debian_drop.wav"); - } - else - { - if (rand() % 3 == 0) - { - createItem(ItemType::COFFEE, pos.x, pos.y); - } - playSound("tabe_hit.wav"); - } - break; - } +void Game::checkBulletCollision() { + for (auto &bullet : bullets_) { + // Comprueba la colisión con el Tabe + if (bullet->isEnabled() && tabe_->isEnabled()) + if (checkCollision(bullet->getCollider(), tabe_->getCollider())) { + tabe_->setState(TabeState::HIT); + bullet->disable(); + auto pos = tabe_->getCollider(); + if (tabe_->tryToGetBonus()) { + createItem(ItemType::DEBIAN, pos.x, pos.y); + playSound("debian_drop.wav"); + } else { + if (rand() % 3 == 0) { + createItem(ItemType::COFFEE, pos.x, pos.y); + } + playSound("tabe_hit.wav"); + } + break; + } - // Comprueba la colisión con los globos - for (auto &balloon : balloon_manager_->getBalloons()) - { - if (balloon->isEnabled() && (!balloon->isInvulnerable()) && bullet->isEnabled()) - { - if (checkCollision(balloon->getCollider(), bullet->getCollider())) - { - // Obtiene al jugador que disparó la bala - auto player = getPlayer(bullet->getOwner()); + // Comprueba la colisión con los globos + for (auto &balloon : balloon_manager_->getBalloons()) { + if (balloon->isEnabled() && (!balloon->isInvulnerable()) && bullet->isEnabled()) { + if (checkCollision(balloon->getCollider(), bullet->getCollider())) { + // Obtiene al jugador que disparó la bala + auto player = getPlayer(bullet->getOwner()); - // Suelta el item si se da el caso - const auto dropped_item = dropItem(); - if (dropped_item != ItemType::NONE && !demo_.recording) - { - if (dropped_item != ItemType::COFFEE_MACHINE) - { - createItem(dropped_item, balloon->getPosX(), balloon->getPosY()); - } - else - { - createItem(dropped_item, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); - coffee_machine_enabled_ = true; - } - } + // Suelta el item si se da el caso + const auto dropped_item = dropItem(); + if (dropped_item != ItemType::NONE && !demo_.recording) { + if (dropped_item != ItemType::COFFEE_MACHINE) { + createItem(dropped_item, balloon->getPosX(), balloon->getPosY()); + } else { + createItem(dropped_item, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); + coffee_machine_enabled_ = true; + } + } - // Explota el globo - const auto SCORE = balloon_manager_->popBalloon(balloon); - evaluateAndSetMenace(); + // Explota el globo + const auto SCORE = balloon_manager_->popBalloon(balloon); + evaluateAndSetMenace(); - // Otorga los puntos al jugador que disparó la bala - if (player->isPlaying()) - { - player->addScore(SCORE * player->getScoreMultiplier() * difficulty_score_multiplier_); - player->incScoreMultiplier(); - } - updateHiScore(); + // Otorga los puntos al jugador que disparó la bala + if (player->isPlaying()) { + player->addScore(SCORE * player->getScoreMultiplier() * difficulty_score_multiplier_); + player->incScoreMultiplier(); + } + updateHiScore(); - // Deshabilita la bala - bullet->disable(); + // Deshabilita la bala + bullet->disable(); - break; - } - } - } - } + break; + } + } + } + } } // Mueve las balas activas -void Game::updateBullets() -{ - for (auto &bullet : bullets_) - { - if (bullet->update() == BulletMoveStatus::OUT) - { - getPlayer(bullet->getOwner())->decScoreMultiplier(); - } - } +void Game::updateBullets() { + for (auto &bullet : bullets_) { + if (bullet->update() == BulletMoveStatus::OUT) { + getPlayer(bullet->getOwner())->decScoreMultiplier(); + } + } } // Pinta las balas activas -void Game::renderBullets() -{ - for (auto &bullet : bullets_) - bullet->render(); +void Game::renderBullets() { + for (auto &bullet : bullets_) + bullet->render(); } // Crea un objeto bala -void Game::createBullet(int x, int y, BulletType kind, bool powered_up, int owner) -{ - bullets_.emplace_back(std::make_unique<Bullet>(x, y, kind, powered_up, owner)); +void Game::createBullet(int x, int y, BulletType kind, bool powered_up, int owner) { + bullets_.emplace_back(std::make_unique<Bullet>(x, y, kind, powered_up, owner)); } // Vacia el vector de balas -void Game::freeBullets() -{ - if (!bullets_.empty()) - for (int i = bullets_.size() - 1; i >= 0; --i) - if (!bullets_[i]->isEnabled()) - bullets_.erase(bullets_.begin() + i); +void Game::freeBullets() { + if (!bullets_.empty()) + for (int i = bullets_.size() - 1; i >= 0; --i) + if (!bullets_[i]->isEnabled()) + bullets_.erase(bullets_.begin() + i); } // Actualiza los items -void Game::updateItems() -{ - for (auto &item : items_) - if (item->isEnabled()) - { - item->update(); - if (item->isOnFloor()) - { - playSound("title.wav"); - screen_->shake(1, 2, 4); - } - } +void Game::updateItems() { + for (auto &item : items_) + if (item->isEnabled()) { + item->update(); + if (item->isOnFloor()) { + playSound("title.wav"); + screen_->shake(1, 2, 4); + } + } } // Pinta los items activos -void Game::renderItems() -{ - for (auto &item : items_) - item->render(); +void Game::renderItems() { + for (auto &item : items_) + item->render(); } // Devuelve un item al azar y luego segun sus probabilidades -ItemType Game::dropItem() -{ - const auto lucky_number = rand() % 100; - const auto item = rand() % 6; +ItemType Game::dropItem() { + const auto lucky_number = rand() % 100; + const auto item = rand() % 6; - switch (item) - { - case 0: - if (lucky_number < helper_.item_disk_odds) - { - return ItemType::DISK; - } - break; - case 1: - if (lucky_number < helper_.item_gavina_odds) - { - return ItemType::GAVINA; - } - break; - case 2: - if (lucky_number < helper_.item_pacmar_odds) - { - return ItemType::GAVINA; - } - break; - case 3: - if (lucky_number < helper_.item_clock_odds) - { - return ItemType::CLOCK; - } - break; - case 4: - if (lucky_number < helper_.item_coffee_odds) - { - helper_.item_coffee_odds = ITEM_COFFEE_ODDS_; - return ItemType::COFFEE; - } - else - { - if (helper_.need_coffee) - { - helper_.item_coffee_odds++; - } - } - break; - case 5: - if (lucky_number < helper_.item_coffee_machine_odds) - { - helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS_; - if (!coffee_machine_enabled_ && helper_.need_coffee_machine) - { - return ItemType::COFFEE_MACHINE; - } - } - else - { - if (helper_.need_coffee_machine) - { - helper_.item_coffee_machine_odds++; - } - } - break; - default: - break; - } + switch (item) { + case 0: + if (lucky_number < helper_.item_disk_odds) { + return ItemType::DISK; + } + break; + case 1: + if (lucky_number < helper_.item_gavina_odds) { + return ItemType::GAVINA; + } + break; + case 2: + if (lucky_number < helper_.item_pacmar_odds) { + return ItemType::GAVINA; + } + break; + case 3: + if (lucky_number < helper_.item_clock_odds) { + return ItemType::CLOCK; + } + break; + case 4: + if (lucky_number < helper_.item_coffee_odds) { + helper_.item_coffee_odds = ITEM_COFFEE_ODDS_; + return ItemType::COFFEE; + } else { + if (helper_.need_coffee) { + helper_.item_coffee_odds++; + } + } + break; + case 5: + if (lucky_number < helper_.item_coffee_machine_odds) { + helper_.item_coffee_machine_odds = ITEM_COFFEE_MACHINE_ODDS_; + if (!coffee_machine_enabled_ && helper_.need_coffee_machine) { + return ItemType::COFFEE_MACHINE; + } + } else { + if (helper_.need_coffee_machine) { + helper_.item_coffee_machine_odds++; + } + } + break; + default: + break; + } - return ItemType::NONE; + return ItemType::NONE; } // Crea un objeto item -void Game::createItem(ItemType type, float x, float y) -{ - items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1])); - playSound("item_drop.wav"); +void Game::createItem(ItemType type, float x, float y) { + items_.emplace_back(std::make_unique<Item>(type, x, y, param.game.play_area.rect, item_textures_[static_cast<int>(type) - 1], item_animations_[static_cast<int>(type) - 1])); + playSound("item_drop.wav"); } // Vacia el vector de items -void Game::freeItems() -{ - if (!items_.empty()) - for (int i = items_.size() - 1; i >= 0; --i) - if (!items_[i]->isEnabled()) - items_.erase(items_.begin() + i); +void Game::freeItems() { + if (!items_.empty()) + for (int i = items_.size() - 1; i >= 0; --i) + if (!items_[i]->isEnabled()) + items_.erase(items_.begin() + i); } // Crea un objeto PathSprite -void Game::createItemText(int x, std::shared_ptr<Texture> texture) -{ - path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); +void Game::createItemText(int x, std::shared_ptr<Texture> texture) { + path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); - const auto w = texture->getWidth(); - const auto h = texture->getHeight(); + const auto w = texture->getWidth(); + const auto h = texture->getHeight(); - const int y0 = param.game.play_area.rect.h - h; - const int y1 = 160 - (h / 2); - const int y2 = -h; + const int y0 = param.game.play_area.rect.h - h; + const int y1 = 160 - (h / 2); + const int y2 = -h; - // Ajusta para que no se dibuje fuera de pantalla - x = std::clamp(x, 2, static_cast<int>(param.game.play_area.rect.w) - w - 2); + // Ajusta para que no se dibuje fuera de pantalla + x = std::clamp(x, 2, static_cast<int>(param.game.play_area.rect.w) - w - 2); - // Inicializa - path_sprites_.back()->setWidth(w); - path_sprites_.back()->setHeight(h); - path_sprites_.back()->setSpriteClip({0, 0, static_cast<float>(w), static_cast<float>(h)}); - path_sprites_.back()->addPath(y0, y1, PathType::VERTICAL, x, 100, easeOutQuint, 0); - path_sprites_.back()->addPath(y1, y2, PathType::VERTICAL, x, 80, easeInQuint, 0); - path_sprites_.back()->enable(); + // Inicializa + path_sprites_.back()->setWidth(w); + path_sprites_.back()->setHeight(h); + path_sprites_.back()->setSpriteClip({0, 0, static_cast<float>(w), static_cast<float>(h)}); + path_sprites_.back()->addPath(y0, y1, PathType::VERTICAL, x, 100, easeOutQuint, 0); + path_sprites_.back()->addPath(y1, y2, PathType::VERTICAL, x, 80, easeInQuint, 0); + path_sprites_.back()->enable(); } // Crea un objeto PathSprite -void Game::createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture) -{ - path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); +void Game::createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture) { + path_sprites_.emplace_back(std::make_unique<PathSprite>(texture)); - // Inicializa - for (const auto &path : paths) - { - path_sprites_.back()->addPath(path, true); - } - path_sprites_.back()->enable(); + // Inicializa + for (const auto &path : paths) { + path_sprites_.back()->addPath(path, true); + } + path_sprites_.back()->enable(); } // Vacia el vector de smartsprites -void Game::freeSmartSprites() -{ - if (!smart_sprites_.empty()) - for (int i = smart_sprites_.size() - 1; i >= 0; --i) - if (smart_sprites_[i]->hasFinished()) - smart_sprites_.erase(smart_sprites_.begin() + i); +void Game::freeSmartSprites() { + if (!smart_sprites_.empty()) + for (int i = smart_sprites_.size() - 1; i >= 0; --i) + if (smart_sprites_[i]->hasFinished()) + smart_sprites_.erase(smart_sprites_.begin() + i); } // Vacia el vector de pathsprites -void Game::freePathSprites() -{ - if (!path_sprites_.empty()) - for (int i = path_sprites_.size() - 1; i >= 0; --i) - if (path_sprites_[i]->hasFinished()) - path_sprites_.erase(path_sprites_.begin() + i); +void Game::freePathSprites() { + if (!path_sprites_.empty()) + for (int i = path_sprites_.size() - 1; i >= 0; --i) + if (path_sprites_[i]->hasFinished()) + path_sprites_.erase(path_sprites_.begin() + i); } // Crea un SpriteSmart para arrojar el item café al recibir un impacto -void Game::throwCoffee(int x, int y) -{ - smart_sprites_.emplace_back(std::make_unique<SmartSprite>(item_textures_[4])); +void Game::throwCoffee(int x, int y) { + smart_sprites_.emplace_back(std::make_unique<SmartSprite>(item_textures_[4])); - smart_sprites_.back()->setPosX(x - 8); - smart_sprites_.back()->setPosY(y - 8); - smart_sprites_.back()->setWidth(param.game.item_size); - smart_sprites_.back()->setHeight(param.game.item_size); - smart_sprites_.back()->setVelX(-1.0f + ((rand() % 5) * 0.5f)); - smart_sprites_.back()->setVelY(-4.0f); - smart_sprites_.back()->setAccelX(0.0f); - smart_sprites_.back()->setAccelY(0.2f); - smart_sprites_.back()->setDestX(x + (smart_sprites_.back()->getVelX() * 50)); - smart_sprites_.back()->setDestY(param.game.height + 1); - smart_sprites_.back()->setEnabled(true); - smart_sprites_.back()->setFinishedCounter(1); - smart_sprites_.back()->setSpriteClip(0, param.game.item_size, param.game.item_size, param.game.item_size); - smart_sprites_.back()->setRotate(true); - smart_sprites_.back()->setRotateSpeed(10); - smart_sprites_.back()->setRotateAmount(90.0); + smart_sprites_.back()->setPosX(x - 8); + smart_sprites_.back()->setPosY(y - 8); + smart_sprites_.back()->setWidth(param.game.item_size); + smart_sprites_.back()->setHeight(param.game.item_size); + smart_sprites_.back()->setVelX(-1.0f + ((rand() % 5) * 0.5f)); + smart_sprites_.back()->setVelY(-4.0f); + smart_sprites_.back()->setAccelX(0.0f); + smart_sprites_.back()->setAccelY(0.2f); + smart_sprites_.back()->setDestX(x + (smart_sprites_.back()->getVelX() * 50)); + smart_sprites_.back()->setDestY(param.game.height + 1); + smart_sprites_.back()->setEnabled(true); + smart_sprites_.back()->setFinishedCounter(1); + smart_sprites_.back()->setSpriteClip(0, param.game.item_size, param.game.item_size, param.game.item_size); + smart_sprites_.back()->setRotate(true); + smart_sprites_.back()->setRotateSpeed(10); + smart_sprites_.back()->setRotateAmount(90.0); } // Actualiza los SmartSprites -void Game::updateSmartSprites() -{ - for (auto &sprite : smart_sprites_) - { - sprite->update(); - } +void Game::updateSmartSprites() { + for (auto &sprite : smart_sprites_) { + sprite->update(); + } } // Pinta los SmartSprites activos -void Game::renderSmartSprites() -{ - for (auto &sprite : smart_sprites_) - { - sprite->render(); - } +void Game::renderSmartSprites() { + for (auto &sprite : smart_sprites_) { + sprite->render(); + } } // Actualiza los PathSprites -void Game::updatePathSprites() -{ - for (auto &sprite : path_sprites_) - { - sprite->update(); - } +void Game::updatePathSprites() { + for (auto &sprite : path_sprites_) { + sprite->update(); + } } // Pinta los PathSprites activos -void Game::renderPathSprites() -{ - for (auto &sprite : path_sprites_) - { - sprite->render(); - } +void Game::renderPathSprites() { + for (auto &sprite : path_sprites_) { + sprite->render(); + } } // Acciones a realizar cuando el jugador colisiona con un globo -void Game::handlePlayerCollision(std::shared_ptr<Player> &player) -{ - if (!player->isPlaying() || player->isInvulnerable()) - { - // Si no está jugando o tiene inmunidad, no hace nada - return; - } +void Game::handlePlayerCollision(std::shared_ptr<Player> &player) { + if (!player->isPlaying() || player->isInvulnerable()) { + // Si no está jugando o tiene inmunidad, no hace nada + return; + } - // Si tiene cafes - if (player->hasExtraHit()) - { - // Lo pierde - player->removeExtraHit(); - throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2)); - playSound("coffee_out.wav"); - screen_->shake(); - } - else - { - // Si no tiene cafes, muere - playSound("player_collision.wav"); - if (param.game.hit_stop) - { - SDL_Delay(param.game.hit_stop_ms); - } - screen_->shake(); - playSound("voice_no.wav"); - player->setPlayingState(PlayerState::ROLLING); - players_to_reorder.push_back(player); - if (allPlayersAreNotPlaying()) - { - // No se puede subir poder de fase si no hay nadie jugando - Stage::power_can_be_added = false; - } - } + // Si tiene cafes + if (player->hasExtraHit()) { + // Lo pierde + player->removeExtraHit(); + throwCoffee(player->getPosX() + (player->getWidth() / 2), player->getPosY() + (player->getHeight() / 2)); + playSound("coffee_out.wav"); + screen_->shake(); + } else { + // Si no tiene cafes, muere + playSound("player_collision.wav"); + if (param.game.hit_stop) { + SDL_Delay(param.game.hit_stop_ms); + } + screen_->shake(); + playSound("voice_no.wav"); + player->setPlayingState(PlayerState::ROLLING); + players_to_reorder.push_back(player); + if (allPlayersAreNotPlaying()) { + // No se puede subir poder de fase si no hay nadie jugando + Stage::power_can_be_added = false; + } + } } // Actualiza y comprueba el valor de la variable -void Game::updateTimeStopped() -{ - if (time_stopped_counter_ > 0) - { - time_stopped_counter_--; - if (time_stopped_counter_ > 120) - { - if (time_stopped_counter_ % 30 == 0) - { - playSound("clock.wav"); - } - } - else - { - if (time_stopped_counter_ % 30 == 0) - { - balloon_manager_->normalColorsToAllBalloons(); - playSound("clock.wav"); - } - else if (time_stopped_counter_ % 30 == 15) - { - balloon_manager_->reverseColorsToAllBalloons(); - playSound("clock.wav"); - } - } - } - else - { - disableTimeStopItem(); - } +void Game::updateTimeStopped() { + if (time_stopped_counter_ > 0) { + time_stopped_counter_--; + if (time_stopped_counter_ > 120) { + if (time_stopped_counter_ % 30 == 0) { + playSound("clock.wav"); + } + } else { + if (time_stopped_counter_ % 30 == 0) { + balloon_manager_->normalColorsToAllBalloons(); + playSound("clock.wav"); + } else if (time_stopped_counter_ % 30 == 15) { + balloon_manager_->reverseColorsToAllBalloons(); + playSound("clock.wav"); + } + } + } else { + disableTimeStopItem(); + } } // Actualiza el juego -void Game::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - ticks_ = SDL_GetTicks(); +void Game::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + ticks_ = SDL_GetTicks(); - checkServiceMenu(); - updateDemo(); + checkServiceMenu(); + updateDemo(); #ifdef RECORDING - updateRecording(); + updateRecording(); #endif - if (!paused_) - { - switch (state_) - { - case GameState::FADE_IN: - updateGameStateFadeIn(); - break; - case GameState::ENTERING_PLAYER: - updateGameStateEnteringPlayer(); - break; - case GameState::SHOWING_GET_READY_MESSAGE: - updateGameStateShowingGetReadyMessage(); - break; - case GameState::PLAYING: - updateGameStatePlaying(); - break; - case GameState::COMPLETED: - updateGameStateCompleted(); - break; - case GameState::GAME_OVER: - updateGameStateGameOver(); - break; - default: - break; - } - } + if (!paused_) { + switch (state_) { + case GameState::FADE_IN: + updateGameStateFadeIn(); + break; + case GameState::ENTERING_PLAYER: + updateGameStateEnteringPlayer(); + break; + case GameState::SHOWING_GET_READY_MESSAGE: + updateGameStateShowingGetReadyMessage(); + break; + case GameState::PLAYING: + updateGameStatePlaying(); + break; + case GameState::COMPLETED: + updateGameStateCompleted(); + break; + case GameState::GAME_OVER: + updateGameStateGameOver(); + break; + default: + break; + } + } - screen_->update(); - fillCanvas(); - } + screen_->update(); + fillCanvas(); + } } // Actualiza el fondo -void Game::updateBackground() -{ - // Si el juego está completado, se reduce la velocidad de las nubes - if (state_ == GameState::COMPLETED) - { - Stage::total_power = (Stage::total_power > 200) ? (Stage::total_power - 25) : 200; - } +void Game::updateBackground() { + // Si el juego está completado, se reduce la velocidad de las nubes + if (state_ == GameState::COMPLETED) { + Stage::total_power = (Stage::total_power > 200) ? (Stage::total_power - 25) : 200; + } - // Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego - constexpr float CLOUDS_INITIAL_SPEED = 0.05f; - constexpr float CLOUDS_FINAL_SPEED = 2.00f - CLOUDS_INITIAL_SPEED; - const float cloudsSpeed = (-CLOUDS_INITIAL_SPEED) + (-CLOUDS_FINAL_SPEED * (static_cast<float>(Stage::total_power) / total_power_to_complete_game_)); - background_->setCloudsSpeed(cloudsSpeed); + // Calcula la velocidad en función de los globos explotados y el total de globos a explotar para acabar el juego + constexpr float CLOUDS_INITIAL_SPEED = 0.05f; + constexpr float CLOUDS_FINAL_SPEED = 2.00f - CLOUDS_INITIAL_SPEED; + const float cloudsSpeed = (-CLOUDS_INITIAL_SPEED) + (-CLOUDS_FINAL_SPEED * (static_cast<float>(Stage::total_power) / total_power_to_complete_game_)); + background_->setCloudsSpeed(cloudsSpeed); - // Calcula la transición de los diferentes fondos - constexpr float num = 1525.0f; // total_power_to_complete div 4 - const float gradient_number = std::min(Stage::total_power / num, 3.0f); - const float percent = gradient_number - static_cast<int>(gradient_number); - background_->setGradientNumber(static_cast<int>(gradient_number)); - background_->setTransition(percent); + // Calcula la transición de los diferentes fondos + constexpr float num = 1525.0f; // total_power_to_complete div 4 + const float gradient_number = std::min(Stage::total_power / num, 3.0f); + const float percent = gradient_number - static_cast<int>(gradient_number); + background_->setGradientNumber(static_cast<int>(gradient_number)); + background_->setTransition(percent); - // Calcula la posición del sol - constexpr float sun_final_power = num * 2; - background_->setSunProgression(Stage::total_power / sun_final_power); - background_->setMoonProgression(Stage::total_power / static_cast<float>(total_power_to_complete_game_)); + // Calcula la posición del sol + constexpr float sun_final_power = num * 2; + background_->setSunProgression(Stage::total_power / sun_final_power); + background_->setMoonProgression(Stage::total_power / static_cast<float>(total_power_to_complete_game_)); - // Actualiza el objeto - background_->update(); + // Actualiza el objeto + background_->update(); } // Dibuja los elementos de la zona de juego en su textura -void Game::fillCanvas() -{ - // Dibujamos el contenido de la zona de juego en su textura - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, canvas_); +void Game::fillCanvas() { + // Dibujamos el contenido de la zona de juego en su textura + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, canvas_); - // Dibuja los objetos - background_->render(); - renderPlayers(); - renderSmartSprites(); - renderItems(); - balloon_manager_->render(); - tabe_->render(); - renderBullets(); - renderPathSprites(); + // Dibuja los objetos + background_->render(); + renderPlayers(); + renderSmartSprites(); + renderItems(); + balloon_manager_->render(); + tabe_->render(); + renderBullets(); + renderPathSprites(); - // Deja el renderizador apuntando donde estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador apuntando donde estaba + SDL_SetRenderTarget(renderer_, temp); } // Dibuja el juego -void Game::render() -{ - // Prepara para empezar a dibujar en la textura de juego - screen_->start(); +void Game::render() { + // Prepara para empezar a dibujar en la textura de juego + screen_->start(); - // Copia la textura con la zona de juego a la pantalla - SDL_RenderTexture(renderer_, canvas_, nullptr, ¶m.game.play_area.rect); + // Copia la textura con la zona de juego a la pantalla + SDL_RenderTexture(renderer_, canvas_, nullptr, ¶m.game.play_area.rect); - // Dibuja el marcador - scoreboard_->render(); + // Dibuja el marcador + scoreboard_->render(); - // Dibuja el fade - fade_in_->render(); - fade_out_->render(); + // Dibuja el fade + fade_in_->render(); + fade_out_->render(); - // Vuelca el contenido del renderizador en pantalla - screen_->render(); + // Vuelca el contenido del renderizador en pantalla + screen_->render(); } // Habilita el efecto del item de detener el tiempo -void Game::enableTimeStopItem() -{ - balloon_manager_->stopAllBalloons(); - balloon_manager_->reverseColorsToAllBalloons(); - time_stopped_counter_ = TIME_STOPPED_COUNTER_; +void Game::enableTimeStopItem() { + balloon_manager_->stopAllBalloons(); + balloon_manager_->reverseColorsToAllBalloons(); + time_stopped_counter_ = TIME_STOPPED_COUNTER_; } // Deshabilita el efecto del item de detener el tiempo -void Game::disableTimeStopItem() -{ - time_stopped_counter_ = 0; - balloon_manager_->startAllBalloons(); - balloon_manager_->normalColorsToAllBalloons(); +void Game::disableTimeStopItem() { + time_stopped_counter_ = 0; + balloon_manager_->startAllBalloons(); + balloon_manager_->normalColorsToAllBalloons(); } // Bucle para el juego -void Game::run() -{ - while (Section::name == Section::Name::GAME) - { +void Game::run() { + while (Section::name == Section::Name::GAME) { #ifndef RECORDING - checkInput(); + checkInput(); #endif - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Inicializa las variables que contienen puntos de ruta para mover objetos -void Game::initPaths() -{ - // Recorrido para el texto de "Get Ready!" (0,1) - { - const auto &texture = Resource::get()->getTexture("game_text_get_ready"); - const auto w = texture->getWidth(); - const int x0 = -w; - const int x1 = param.game.play_area.center_x - w / 2; - const int x2 = param.game.play_area.rect.w; - const int y = param.game.play_area.center_y; - paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 20)); - paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); - } +void Game::initPaths() { + // Recorrido para el texto de "Get Ready!" (0,1) + { + const auto &texture = Resource::get()->getTexture("game_text_get_ready"); + const auto w = texture->getWidth(); + const int x0 = -w; + const int x1 = param.game.play_area.center_x - w / 2; + const int x2 = param.game.play_area.rect.w; + const int y = param.game.play_area.center_y; + paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 20)); + paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); + } - // Recorrido para el texto de "Last Stage!" o de "X stages left" o "Game Over" (2,3) - { - const auto &texture = Resource::get()->getTexture("game_text_last_stage"); - const auto h = texture->getHeight(); - const int y0 = param.game.play_area.rect.h - h; - const int y1 = param.game.play_area.center_y - h / 2; - const int y2 = -h; - const int x = param.game.play_area.center_x; - paths_.emplace_back(Path(createPath(y0, y1, PathType::VERTICAL, x, 80, easeOutQuint), 20)); - paths_.emplace_back(Path(createPath(y1, y2, PathType::VERTICAL, x, 80, easeInQuint), 0)); - } + // Recorrido para el texto de "Last Stage!" o de "X stages left" o "Game Over" (2,3) + { + const auto &texture = Resource::get()->getTexture("game_text_last_stage"); + const auto h = texture->getHeight(); + const int y0 = param.game.play_area.rect.h - h; + const int y1 = param.game.play_area.center_y - h / 2; + const int y2 = -h; + const int x = param.game.play_area.center_x; + paths_.emplace_back(Path(createPath(y0, y1, PathType::VERTICAL, x, 80, easeOutQuint), 20)); + paths_.emplace_back(Path(createPath(y1, y2, PathType::VERTICAL, x, 80, easeInQuint), 0)); + } - // Recorrido para el texto de "Congratulations!!" (3,4) - { - const auto &texture = Resource::get()->getTexture("game_text_congratulations"); - const auto w = texture->getWidth(); - const auto h = texture->getHeight(); - const int x0 = -w; - const int x1 = param.game.play_area.center_x - w / 2; - const int x2 = param.game.play_area.rect.w; - const int y = param.game.play_area.center_y - h / 2 - 20; - paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 400)); - paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); - } + // Recorrido para el texto de "Congratulations!!" (3,4) + { + const auto &texture = Resource::get()->getTexture("game_text_congratulations"); + const auto w = texture->getWidth(); + const auto h = texture->getHeight(); + const int x0 = -w; + const int x1 = param.game.play_area.center_x - w / 2; + const int x2 = param.game.play_area.rect.w; + const int y = param.game.play_area.center_y - h / 2 - 20; + paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 400)); + paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); + } - // Recorrido para el texto de "1.000.000 points!" (5,6) - { - const auto &texture = Resource::get()->getTexture("game_text_1000000_points"); - const auto w = texture->getWidth(); - const auto h = texture->getHeight(); - const int x0 = param.game.play_area.rect.w; - const int x1 = param.game.play_area.center_x - w / 2; - const int x2 = -w; - const int y = param.game.play_area.center_y + h / 2 - 20; - paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 400)); - paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); - } + // Recorrido para el texto de "1.000.000 points!" (5,6) + { + const auto &texture = Resource::get()->getTexture("game_text_1000000_points"); + const auto w = texture->getWidth(); + const auto h = texture->getHeight(); + const int x0 = param.game.play_area.rect.w; + const int x1 = param.game.play_area.center_x - w / 2; + const int x2 = -w; + const int y = param.game.play_area.center_y + h / 2 - 20; + paths_.emplace_back(Path(createPath(x0, x1, PathType::HORIZONTAL, y, 80, easeOutQuint), 400)); + paths_.emplace_back(Path(createPath(x1, x2, PathType::HORIZONTAL, y, 80, easeInQuint), 0)); + } } // Actualiza las variables de ayuda -void Game::updateHelper() -{ - // Solo ofrece ayuda cuando la amenaza es elevada - if (menace_current_ > 15) - { - helper_.need_coffee = true; - helper_.need_coffee_machine = true; - for (const auto &player : players_) - { - if (player->isPlaying()) - { - helper_.need_coffee &= (player->getCoffees() == 0); - helper_.need_coffee_machine &= (!player->isPowerUp()); - } - } - } - else - { - helper_.need_coffee = helper_.need_coffee_machine = false; - } +void Game::updateHelper() { + // Solo ofrece ayuda cuando la amenaza es elevada + if (menace_current_ > 15) { + helper_.need_coffee = true; + helper_.need_coffee_machine = true; + for (const auto &player : players_) { + if (player->isPlaying()) { + helper_.need_coffee &= (player->getCoffees() == 0); + helper_.need_coffee_machine &= (!player->isPowerUp()); + } + } + } else { + helper_.need_coffee = helper_.need_coffee_machine = false; + } } // Comprueba si todos los jugadores han terminado de jugar -bool Game::allPlayersAreWaitingOrGameOver() -{ - auto success = true; - for (const auto &player : players_) - success &= player->isWaiting() || player->isGameOver(); +bool Game::allPlayersAreWaitingOrGameOver() { + auto success = true; + for (const auto &player : players_) + success &= player->isWaiting() || player->isGameOver(); - return success; + return success; } // Comprueba si todos los jugadores han terminado de jugar -bool Game::allPlayersAreGameOver() -{ - auto success = true; - for (const auto &player : players_) - success &= player->isGameOver(); +bool Game::allPlayersAreGameOver() { + auto success = true; + for (const auto &player : players_) + success &= player->isGameOver(); - return success; + return success; } // Comprueba si todos los jugadores han terminado de jugar -bool Game::allPlayersAreNotPlaying() -{ - auto success = true; - for (const auto &player : players_) - success &= !player->isPlaying(); +bool Game::allPlayersAreNotPlaying() { + auto success = true; + for (const auto &player : players_) + success &= !player->isPlaying(); - return success; + return success; } // Comprueba los eventos que hay en cola -void Game::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { - switch (event.type) - { - case SDL_EVENT_WINDOW_FOCUS_LOST: - { - pause(!demo_.enabled); - break; - } - case SDL_EVENT_WINDOW_FOCUS_GAINED: - { - pause(false); - break; - } - default: - break; - } +void Game::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { + case SDL_EVENT_WINDOW_FOCUS_LOST: { + pause(!demo_.enabled); + break; + } + case SDL_EVENT_WINDOW_FOCUS_GAINED: { + pause(false); + break; + } + default: + break; + } #ifdef DEBUG - checkDebugEvents(event); + checkDebugEvents(event); #endif - GlobalEvents::check(event); - } + GlobalEvents::check(event); + } } // Actualiza el marcador -void Game::updateScoreboard() -{ - for (const auto &player : players_) - { - scoreboard_->setScore(player->getScoreBoardPanel(), player->getScore()); - scoreboard_->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier()); - } +void Game::updateScoreboard() { + for (const auto &player : players_) { + scoreboard_->setScore(player->getScoreBoardPanel(), player->getScore()); + scoreboard_->setMult(player->getScoreBoardPanel(), player->getScoreMultiplier()); + } - // Resto de marcador - scoreboard_->setStage(Stage::number + 1); - scoreboard_->setPower((float)Stage::power / (float)Stage::get(Stage::number).power_to_complete); - scoreboard_->setHiScore(hi_score_.score); - scoreboard_->setHiScoreName(hi_score_.name); + // Resto de marcador + scoreboard_->setStage(Stage::number + 1); + scoreboard_->setPower((float)Stage::power / (float)Stage::get(Stage::number).power_to_complete); + scoreboard_->setHiScore(hi_score_.score); + scoreboard_->setHiScoreName(hi_score_.name); - // Lógica del marcador - scoreboard_->update(); + // Lógica del marcador + scoreboard_->update(); } // Pausa el juego -void Game::pause(bool value) -{ - paused_ = value; - screen_->attenuate(paused_); +void Game::pause(bool value) { + paused_ = value; + screen_->attenuate(paused_); } // Añade una puntuación a la tabla de records -void Game::addScoreToScoreBoard(const std::shared_ptr<Player> &player) -{ - const auto entry = HiScoreEntry(trim(player->getLastEnterName()), player->getScore(), player->get1CC()); - auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); - Options::settings.last_hi_score_entry.at(player->getId() - 1) = manager->add(entry); - manager->saveToFile(Asset::get()->get("score.bin")); - hi_score_.name = Options::settings.hi_score_table.front().name; +void Game::addScoreToScoreBoard(const std::shared_ptr<Player> &player) { + const auto entry = HiScoreEntry(trim(player->getLastEnterName()), player->getScore(), player->get1CC()); + auto manager = std::make_unique<ManageHiScoreTable>(Options::settings.hi_score_table); + Options::settings.last_hi_score_entry.at(player->getId() - 1) = manager->add(entry); + manager->saveToFile(Asset::get()->get("score.bin")); + hi_score_.name = Options::settings.hi_score_table.front().name; } // Saca del estado de GAME OVER al jugador si el otro está activo -void Game::checkAndUpdatePlayerStatus(int activePlayerIndex, int inactivePlayerIndex) -{ - if (players_[activePlayerIndex]->isGameOver() && !players_[inactivePlayerIndex]->isGameOver() && !players_[inactivePlayerIndex]->isWaiting()) - { - players_[activePlayerIndex]->setPlayingState(PlayerState::WAITING); - } +void Game::checkAndUpdatePlayerStatus(int activePlayerIndex, int inactivePlayerIndex) { + if (players_[activePlayerIndex]->isGameOver() && !players_[inactivePlayerIndex]->isGameOver() && !players_[inactivePlayerIndex]->isWaiting()) { + players_[activePlayerIndex]->setPlayingState(PlayerState::WAITING); + } } // Comprueba el estado de los jugadores -void Game::checkPlayersStatusPlaying() -{ - if (demo_.enabled) - { - return; - } +void Game::checkPlayersStatusPlaying() { + if (demo_.enabled) { + return; + } - // Comprueba si todos los jugadores estan esperando - if (allPlayersAreWaitingOrGameOver()) - { - // Entonces los pone en estado de Game Over - for (auto &player : players_) - { - player->setPlayingState(PlayerState::GAME_OVER); - } - } + // Comprueba si todos los jugadores estan esperando + if (allPlayersAreWaitingOrGameOver()) { + // Entonces los pone en estado de Game Over + for (auto &player : players_) { + player->setPlayingState(PlayerState::GAME_OVER); + } + } - // Comprobar estado de ambos jugadores - checkAndUpdatePlayerStatus(0, 1); - checkAndUpdatePlayerStatus(1, 0); + // Comprobar estado de ambos jugadores + checkAndUpdatePlayerStatus(0, 1); + checkAndUpdatePlayerStatus(1, 0); } // Obtiene un jugador a partir de su "id" -std::shared_ptr<Player> Game::getPlayer(int id) -{ - auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) - { return player->getId() == id; }); +std::shared_ptr<Player> Game::getPlayer(int id) { + auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; }); - if (it != players_.end()) - { - return *it; - } - return nullptr; + if (it != players_.end()) { + return *it; + } + return nullptr; } // Obtiene un controlador a partir del "id" del jugador -int Game::getController(int player_id) -{ - auto it = std::find_if(Options::controllers.begin(), Options::controllers.end(), [player_id](const auto &controller) - { return controller.player_id == player_id; }); +int Game::getController(int player_id) { + auto it = std::find_if(Options::controllers.begin(), Options::controllers.end(), [player_id](const auto &controller) { return controller.player_id == player_id; }); - if (it != Options::controllers.end()) - { - return std::distance(Options::controllers.begin(), it); - } + if (it != Options::controllers.end()) { + return std::distance(Options::controllers.begin(), it); + } - return -1; + return -1; } // Gestiona la entrada durante el juego -void Game::checkInput() -{ - Input::get()->update(); +void Game::checkInput() { + Input::get()->update(); - // Comprueba las entradas si no está el menú de servicio activo - if (!ServiceMenu::get()->isEnabled()) - { - checkPauseInput(); - demo_.enabled ? DEMO_handlePassInput() : handlePlayersInput(); - } + // Comprueba las entradas si no está el menú de servicio activo + if (!ServiceMenu::get()->isEnabled()) { + checkPauseInput(); + demo_.enabled ? DEMO_handlePassInput() : handlePlayersInput(); + } - // Mueve los jugadores en el modo demo - if (demo_.enabled) - { - DEMO_handleInput(); - } + // Mueve los jugadores en el modo demo + if (demo_.enabled) { + DEMO_handleInput(); + } - // Verifica los inputs globales. - GlobalInputs::check(); + // Verifica los inputs globales. + GlobalInputs::check(); } // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego. -void Game::checkPauseInput() -{ - // Comprueba los mandos - for (int i = 0; i < input_->getNumControllers(); ++i) - { - if (input_->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) - { - pause(!paused_); - return; - } - } +void Game::checkPauseInput() { + // Comprueba los mandos + for (int i = 0; i < input_->getNumControllers(); ++i) { + if (input_->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::CONTROLLER, i)) { + pause(!paused_); + return; + } + } - // Comprueba el teclado - if (input_->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) - { - pause(!paused_); - return; - } + // Comprueba el teclado + if (input_->checkInput(InputAction::PAUSE, INPUT_DO_NOT_ALLOW_REPEAT, InputDevice::KEYBOARD)) { + pause(!paused_); + return; + } } // Gestiona las entradas de los jugadores en el modo demo para saltarse la demo. -void Game::DEMO_handlePassInput() -{ - if (input_->checkAnyButton()) - { - Section::name = Section::Name::TITLE; // Salir del modo demo y regresar al menú principal. - Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; // El juego volverá a mostrar la demo - return; - } +void Game::DEMO_handlePassInput() { + if (input_->checkAnyButton()) { + Section::name = Section::Name::TITLE; // Salir del modo demo y regresar al menú principal. + Section::attract_mode = Section::AttractMode::TITLE_TO_DEMO; // El juego volverá a mostrar la demo + return; + } } // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos. -void Game::DEMO_handleInput() -{ - int index = 0; - for (const auto &player : players_) - { - if (player->isPlaying()) - { - // Maneja el input específico del jugador en modo demo. - DEMO_handlePlayerInput(player, index); - } - ++index; - } +void Game::DEMO_handleInput() { + int index = 0; + for (const auto &player : players_) { + if (player->isPlaying()) { + // Maneja el input específico del jugador en modo demo. + DEMO_handlePlayerInput(player, index); + } + ++index; + } } // Procesa las entradas para un jugador específico durante el modo demo. -void Game::DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index) -{ - const auto &demoData = demo_.data[index][demo_.counter]; +void Game::DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index) { + const auto &demoData = demo_.data[index][demo_.counter]; - if (demoData.left == 1) - { - player->setInput(InputAction::LEFT); - } - else if (demoData.right == 1) - { - player->setInput(InputAction::RIGHT); - } - else if (demoData.no_input == 1) - { - player->setInput(InputAction::NONE); - } + if (demoData.left == 1) { + player->setInput(InputAction::LEFT); + } else if (demoData.right == 1) { + player->setInput(InputAction::RIGHT); + } else if (demoData.no_input == 1) { + player->setInput(InputAction::NONE); + } - if (demoData.fire == 1) - { - handleFireInput(player, BulletType::UP); - } - else if (demoData.fire_left == 1) - { - handleFireInput(player, BulletType::LEFT); - } - else if (demoData.fire_right == 1) - { - handleFireInput(player, BulletType::RIGHT); - } + if (demoData.fire == 1) { + handleFireInput(player, BulletType::UP); + } else if (demoData.fire_left == 1) { + handleFireInput(player, BulletType::LEFT); + } else if (demoData.fire_right == 1) { + handleFireInput(player, BulletType::RIGHT); + } } // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos. -void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bullet_type) -{ - if (player->canFire()) - { - SDL_Point bullet = {0, 0}; - switch (bullet_type) - { - case BulletType::UP: - player->setInput(InputAction::FIRE_CENTER); - bullet.x = 2 + player->getPosX() + (player->getWidth() - Bullet::WIDTH) / 2; - bullet.y = player->getPosY() - (Bullet::HEIGHT / 2); - break; - case BulletType::LEFT: - player->setInput(InputAction::FIRE_LEFT); - bullet.x = player->getPosX() - (Bullet::WIDTH / 2); - bullet.y = player->getPosY(); - break; - case BulletType::RIGHT: - player->setInput(InputAction::FIRE_RIGHT); - bullet.x = player->getPosX() + player->getWidth() - (Bullet::WIDTH / 2); - bullet.y = player->getPosY(); - break; - default: - break; - } - createBullet(bullet.x, bullet.y, bullet_type, player->isPowerUp(), player->getId()); - playSound("bullet.wav"); +void Game::handleFireInput(const std::shared_ptr<Player> &player, BulletType bullet_type) { + if (player->canFire()) { + SDL_Point bullet = {0, 0}; + switch (bullet_type) { + case BulletType::UP: + player->setInput(InputAction::FIRE_CENTER); + bullet.x = 2 + player->getPosX() + (player->getWidth() - Bullet::WIDTH) / 2; + bullet.y = player->getPosY() - (Bullet::HEIGHT / 2); + break; + case BulletType::LEFT: + player->setInput(InputAction::FIRE_LEFT); + bullet.x = player->getPosX() - (Bullet::WIDTH / 2); + bullet.y = player->getPosY(); + break; + case BulletType::RIGHT: + player->setInput(InputAction::FIRE_RIGHT); + bullet.x = player->getPosX() + player->getWidth() - (Bullet::WIDTH / 2); + bullet.y = player->getPosY(); + break; + default: + break; + } + createBullet(bullet.x, bullet.y, bullet_type, player->isPowerUp(), player->getId()); + playSound("bullet.wav"); - // Establece un tiempo de espera para el próximo disparo. - constexpr int POWERUP_COOLDOWN = 5; - constexpr int AUTOFIRE_COOLDOWN = 10; - constexpr int NORMAL_COOLDOWN = 7; + // Establece un tiempo de espera para el próximo disparo. + constexpr int POWERUP_COOLDOWN = 5; + constexpr int AUTOFIRE_COOLDOWN = 10; + constexpr int NORMAL_COOLDOWN = 7; - int cant_fire_counter; - if (player->isPowerUp()) - { - cant_fire_counter = POWERUP_COOLDOWN; - } - else if (Options::settings.autofire) - { - cant_fire_counter = AUTOFIRE_COOLDOWN; - } - else - { - cant_fire_counter = NORMAL_COOLDOWN; - } + int cant_fire_counter; + if (player->isPowerUp()) { + cant_fire_counter = POWERUP_COOLDOWN; + } else if (Options::settings.autofire) { + cant_fire_counter = AUTOFIRE_COOLDOWN; + } else { + cant_fire_counter = NORMAL_COOLDOWN; + } - player->setCantFireCounter(cant_fire_counter); - } + player->setCantFireCounter(cant_fire_counter); + } } // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo). -void Game::handlePlayersInput() -{ - for (const auto &PLAYER : players_) - { - if (PLAYER->isPlaying()) - { - // Maneja el input de los jugadores en modo normal. - handleNormalPlayerInput(PLAYER); - } - else if (PLAYER->isContinue() || PLAYER->isWaiting()) - { - // Gestiona la continuación del jugador. - handlePlayerContinue(PLAYER); - } - else if (PLAYER->isEnteringName() || PLAYER->isEnteringNameGameCompleted() || PLAYER->isShowingName()) - { - // Gestiona la introducción del nombre del jugador. - handleNameInput(PLAYER); - } - } +void Game::handlePlayersInput() { + for (const auto &PLAYER : players_) { + if (PLAYER->isPlaying()) { + // Maneja el input de los jugadores en modo normal. + handleNormalPlayerInput(PLAYER); + } else if (PLAYER->isContinue() || PLAYER->isWaiting()) { + // Gestiona la continuación del jugador. + handlePlayerContinue(PLAYER); + } else if (PLAYER->isEnteringName() || PLAYER->isEnteringNameGameCompleted() || PLAYER->isShowingName()) { + // Gestiona la introducción del nombre del jugador. + handleNameInput(PLAYER); + } + } } // Maneja las entradas de movimiento y disparo para un jugador en modo normal. -void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) -{ - const auto &CONTROLLER = Options::controllers.at(player->getController()); - const bool AUTOFIRE = player->isPowerUp() || Options::settings.autofire; +void Game::handleNormalPlayerInput(const std::shared_ptr<Player> &player) { + const auto &CONTROLLER = Options::controllers.at(player->getController()); + const bool AUTOFIRE = player->isPowerUp() || Options::settings.autofire; - if (input_->checkInput(InputAction::LEFT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) - { - player->setInput(InputAction::LEFT); + if (input_->checkInput(InputAction::LEFT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) { + player->setInput(InputAction::LEFT); #ifdef RECORDING - demo_.keys.left = 1; + demo_.keys.left = 1; #endif - } - else if (input_->checkInput(InputAction::RIGHT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) - { - player->setInput(InputAction::RIGHT); + } else if (input_->checkInput(InputAction::RIGHT, INPUT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) { + player->setInput(InputAction::RIGHT); #ifdef RECORDING - demo_.keys.right = 1; + demo_.keys.right = 1; #endif - } - else - { - player->setInput(InputAction::NONE); + } else { + player->setInput(InputAction::NONE); #ifdef RECORDING - demo_.keys.no_input = 1; + demo_.keys.no_input = 1; #endif - } + } - handleFireInputs(player, AUTOFIRE, player->getController()); // Verifica y maneja todas las posibles entradas de disparo. + handleFireInputs(player, AUTOFIRE, player->getController()); // Verifica y maneja todas las posibles entradas de disparo. } // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. -void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex) -{ - if (input_->checkInput(InputAction::FIRE_CENTER, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - handleFireInput(player, BulletType::UP); +void Game::handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex) { + if (input_->checkInput(InputAction::FIRE_CENTER, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + handleFireInput(player, BulletType::UP); #ifdef RECORDING - demo_.keys.fire = 1; + demo_.keys.fire = 1; #endif - } - else if (input_->checkInput(InputAction::FIRE_LEFT, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - handleFireInput(player, BulletType::LEFT); + } else if (input_->checkInput(InputAction::FIRE_LEFT, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + handleFireInput(player, BulletType::LEFT); #ifdef RECORDING - demo_.keys.fire_left = 1; + demo_.keys.fire_left = 1; #endif - } - else if (input_->checkInput(InputAction::FIRE_RIGHT, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - handleFireInput(player, BulletType::RIGHT); + } else if (input_->checkInput(InputAction::FIRE_RIGHT, autofire, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + handleFireInput(player, BulletType::RIGHT); #ifdef RECORDING - demo_.keys.fire_right = 1; + demo_.keys.fire_right = 1; #endif - } + } } // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio. -void Game::handlePlayerContinue(const std::shared_ptr<Player> &player) -{ - const auto controllerIndex = player->getController(); - if (input_->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - player->setPlayingState(PlayerState::RESPAWNING); - } +void Game::handlePlayerContinue(const std::shared_ptr<Player> &player) { + const auto controllerIndex = player->getController(); + if (input_->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + player->setPlayingState(PlayerState::RESPAWNING); + } - // Disminuye el contador de continuación si se presiona cualquier botón de disparo. - if (input_->checkInput(InputAction::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || - input_->checkInput(InputAction::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || - input_->checkInput(InputAction::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - if (player->getContinueCounter() < param.scoreboard.skip_countdown_value) - { - player->decContinueCounter(); - } - } + // Disminuye el contador de continuación si se presiona cualquier botón de disparo. + if (input_->checkInput(InputAction::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || + input_->checkInput(InputAction::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || + input_->checkInput(InputAction::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + if (player->getContinueCounter() < param.scoreboard.skip_countdown_value) { + player->decContinueCounter(); + } + } } // Procesa las entradas para la introducción del nombre del jugador. -void Game::handleNameInput(const std::shared_ptr<Player> &player) -{ - const auto controllerIndex = player->getController(); - if (input_->checkInput(InputAction::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - if (player->isShowingName()) - { - player->setPlayingState(PlayerState::CONTINUE); - } - else if (player->getEnterNamePositionOverflow()) - { - player->setInput(InputAction::START); - addScoreToScoreBoard(player); - player->setPlayingState(PlayerState::SHOWING_NAME); - } - else - { - player->setInput(InputAction::RIGHT); - } - } - else if (input_->checkInput(InputAction::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || - input_->checkInput(InputAction::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - if (player->isShowingName()) - { - player->setPlayingState(PlayerState::CONTINUE); - } - else - { - player->setInput(InputAction::LEFT); - } - } - else if (input_->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - player->setInput(InputAction::UP); - } - else if (input_->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - player->setInput(InputAction::DOWN); - } - else if (input_->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) - { - if (player->isShowingName()) - { - player->setPlayingState(PlayerState::CONTINUE); - } - else - { - player->setInput(InputAction::START); - addScoreToScoreBoard(player); - player->setPlayingState(PlayerState::SHOWING_NAME); - } - } +void Game::handleNameInput(const std::shared_ptr<Player> &player) { + const auto controllerIndex = player->getController(); + if (input_->checkInput(InputAction::FIRE_LEFT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + if (player->isShowingName()) { + player->setPlayingState(PlayerState::CONTINUE); + } else if (player->getEnterNamePositionOverflow()) { + player->setInput(InputAction::START); + addScoreToScoreBoard(player); + player->setPlayingState(PlayerState::SHOWING_NAME); + } else { + player->setInput(InputAction::RIGHT); + } + } else if (input_->checkInput(InputAction::FIRE_CENTER, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index) || + input_->checkInput(InputAction::FIRE_RIGHT, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + if (player->isShowingName()) { + player->setPlayingState(PlayerState::CONTINUE); + } else { + player->setInput(InputAction::LEFT); + } + } else if (input_->checkInput(InputAction::UP, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + player->setInput(InputAction::UP); + } else if (input_->checkInput(InputAction::DOWN, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + player->setInput(InputAction::DOWN); + } else if (input_->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, Options::controllers[controllerIndex].type, Options::controllers[controllerIndex].index)) { + if (player->isShowingName()) { + player->setPlayingState(PlayerState::CONTINUE); + } else { + player->setInput(InputAction::START); + addScoreToScoreBoard(player); + player->setPlayingState(PlayerState::SHOWING_NAME); + } + } } // Inicializa las variables para el modo DEMO -void Game::initDemo(int player_id) -{ - if (demo_.enabled) - { - // Cambia el estado del juego - setState(GameState::PLAYING); +void Game::initDemo(int player_id) { + if (demo_.enabled) { + // Cambia el estado del juego + setState(GameState::PLAYING); - // Aleatoriza la asignación del fichero con los datos del modo demostracion - { - const auto DEMO1 = rand() % 2; - const auto DEMO2 = (DEMO1 == 0) ? 1 : 0; - demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1)); - demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2)); - } + // Aleatoriza la asignación del fichero con los datos del modo demostracion + { + const auto DEMO1 = rand() % 2; + const auto DEMO2 = (DEMO1 == 0) ? 1 : 0; + demo_.data.emplace_back(Resource::get()->getDemoData(DEMO1)); + demo_.data.emplace_back(Resource::get()->getDemoData(DEMO2)); + } - // Selecciona una pantalla al azar - { - constexpr auto NUM_DEMOS = 3; - const auto DEMO = rand() % NUM_DEMOS; - const int STAGES[NUM_DEMOS] = {0, 3, 5}; - Stage::number = STAGES[DEMO]; - } + // Selecciona una pantalla al azar + { + constexpr auto NUM_DEMOS = 3; + const auto DEMO = rand() % NUM_DEMOS; + const int STAGES[NUM_DEMOS] = {0, 3, 5}; + Stage::number = STAGES[DEMO]; + } - // Actualiza el numero de globos explotados según la fase del modo demostración - for (int i = 0; i < Stage::number; ++i) - { - Stage::total_power += Stage::get(i).power_to_complete; - } + // Actualiza el numero de globos explotados según la fase del modo demostración + for (int i = 0; i < Stage::number; ++i) { + Stage::total_power += Stage::get(i).power_to_complete; + } - // Activa o no al otro jugador - if (rand() % 3 != 0) - { - const auto OTHER_PLAYER_ID = player_id == 1 ? 2 : 1; - auto other_player = getPlayer(OTHER_PLAYER_ID); - other_player->setPlayingState(PlayerState::PLAYING); - } + // Activa o no al otro jugador + if (rand() % 3 != 0) { + const auto OTHER_PLAYER_ID = player_id == 1 ? 2 : 1; + auto other_player = getPlayer(OTHER_PLAYER_ID); + other_player->setPlayingState(PlayerState::PLAYING); + } - // Asigna cafes a los jugadores - for (auto &player : players_) - { - for (int i = 0; i < rand() % 3; ++i) - player->giveExtraHit(); + // Asigna cafes a los jugadores + for (auto &player : players_) { + for (int i = 0; i < rand() % 3; ++i) + player->giveExtraHit(); - player->setInvulnerable(true); - } + player->setInvulnerable(true); + } - // Configura los marcadores - scoreboard_->setMode(SCOREBOARD_LEFT_PANEL, ScoreboardMode::DEMO); - scoreboard_->setMode(SCOREBOARD_RIGHT_PANEL, ScoreboardMode::DEMO); + // Configura los marcadores + scoreboard_->setMode(SCOREBOARD_LEFT_PANEL, ScoreboardMode::DEMO); + scoreboard_->setMode(SCOREBOARD_RIGHT_PANEL, ScoreboardMode::DEMO); - // Silencia los globos - balloon_manager_->setSounds(false); - } + // Silencia los globos + balloon_manager_->setSounds(false); + } - // Modo grabar demo + // Modo grabar demo #ifdef RECORDING - demo_.recording = true; + demo_.recording = true; #else - demo_.recording = false; + demo_.recording = false; #endif - demo_.counter = 0; + demo_.counter = 0; } // Calcula el poder total necesario para completar el juego -void Game::setTotalPower() -{ - total_power_to_complete_game_ = 0; - for (const auto &stage : Stage::stages) - { - total_power_to_complete_game_ += stage.power_to_complete; - } +void Game::setTotalPower() { + total_power_to_complete_game_ = 0; + for (const auto &stage : Stage::stages) { + total_power_to_complete_game_ += stage.power_to_complete; + } } // Inicializa el marcador -void Game::initScoreboard() -{ - scoreboard_->setPos(param.scoreboard.rect); - scoreboard_->setMode(SCOREBOARD_CENTER_PANEL, ScoreboardMode::STAGE_INFO); - for (const auto &player : players_) - { - scoreboard_->setName(player->getScoreBoardPanel(), player->getName()); - if (player->isWaiting()) - { - scoreboard_->setMode(player->getScoreBoardPanel(), ScoreboardMode::WAITING); - } - } +void Game::initScoreboard() { + scoreboard_->setPos(param.scoreboard.rect); + scoreboard_->setMode(SCOREBOARD_CENTER_PANEL, ScoreboardMode::STAGE_INFO); + for (const auto &player : players_) { + scoreboard_->setName(player->getScoreBoardPanel(), player->getName()); + if (player->isWaiting()) { + scoreboard_->setMode(player->getScoreBoardPanel(), ScoreboardMode::WAITING); + } + } } // Inicializa las opciones relacionadas con la dificultad -void Game::initDifficultyVars() -{ - // Variables relacionadas con la dificultad - switch (difficulty_) - { - case Options::DifficultyCode::EASY: - { - balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[0]); - difficulty_score_multiplier_ = 0.5f; - scoreboard_->setColor(param.scoreboard.easy_color); - break; - } +void Game::initDifficultyVars() { + // Variables relacionadas con la dificultad + switch (difficulty_) { + case Options::DifficultyCode::EASY: { + balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[0]); + difficulty_score_multiplier_ = 0.5f; + scoreboard_->setColor(param.scoreboard.easy_color); + break; + } - case Options::DifficultyCode::NORMAL: - { - balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[0]); - difficulty_score_multiplier_ = 1.0f; - scoreboard_->setColor(param.scoreboard.normal_color); - break; - } + case Options::DifficultyCode::NORMAL: { + balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[0]); + difficulty_score_multiplier_ = 1.0f; + scoreboard_->setColor(param.scoreboard.normal_color); + break; + } - case Options::DifficultyCode::HARD: - { - balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[4]); - difficulty_score_multiplier_ = 1.5f; - scoreboard_->setColor(param.scoreboard.hard_color); - break; - } + case Options::DifficultyCode::HARD: { + balloon_manager_->setDefaultBalloonSpeed(BALLOON_SPEED[4]); + difficulty_score_multiplier_ = 1.5f; + scoreboard_->setColor(param.scoreboard.hard_color); + break; + } - default: - break; - } + default: + break; + } - balloon_manager_->resetBalloonSpeed(); + balloon_manager_->resetBalloonSpeed(); } // Inicializa los jugadores -void Game::initPlayers(int player_id) -{ - // Crea los dos jugadores - constexpr int PLAYER_HEIGHT = 32; - constexpr int PLAYER_WIDTH = 32; - const int Y = param.game.play_area.rect.h - PLAYER_HEIGHT + 1; - players_.emplace_back(std::make_unique<Player>(1, param.game.play_area.first_quarter_x - (PLAYER_WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_[0], player_animations_)); - players_.back()->setScoreBoardPanel(SCOREBOARD_LEFT_PANEL); - players_.back()->setName(Lang::getText("[SCOREBOARD] 1")); - players_.back()->setController(getController(players_.back()->getId())); +void Game::initPlayers(int player_id) { + // Crea los dos jugadores + constexpr int PLAYER_HEIGHT = 32; + constexpr int PLAYER_WIDTH = 32; + const int Y = param.game.play_area.rect.h - PLAYER_HEIGHT + 1; + players_.emplace_back(std::make_unique<Player>(1, param.game.play_area.first_quarter_x - (PLAYER_WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_[0], player_animations_)); + players_.back()->setScoreBoardPanel(SCOREBOARD_LEFT_PANEL); + players_.back()->setName(Lang::getText("[SCOREBOARD] 1")); + players_.back()->setController(getController(players_.back()->getId())); - players_.emplace_back(std::make_unique<Player>(2, param.game.play_area.third_quarter_x - (PLAYER_WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_[1], player_animations_)); - players_.back()->setScoreBoardPanel(SCOREBOARD_RIGHT_PANEL); - players_.back()->setName(Lang::getText("[SCOREBOARD] 2")); - players_.back()->setController(getController(players_.back()->getId())); + players_.emplace_back(std::make_unique<Player>(2, param.game.play_area.third_quarter_x - (PLAYER_WIDTH / 2), Y, demo_.enabled, param.game.play_area.rect, player_textures_[1], player_animations_)); + players_.back()->setScoreBoardPanel(SCOREBOARD_RIGHT_PANEL); + players_.back()->setName(Lang::getText("[SCOREBOARD] 2")); + players_.back()->setController(getController(players_.back()->getId())); - // Activa el jugador que coincide con el "player_id" o ambos si es "0" - if (player_id == 0) - { - // Activa ambos jugadores - getPlayer(1)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); - getPlayer(2)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); - } - else - { - getPlayer(player_id)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); - } + // Activa el jugador que coincide con el "player_id" o ambos si es "0" + if (player_id == 0) { + // Activa ambos jugadores + getPlayer(1)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); + getPlayer(2)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); + } else { + getPlayer(player_id)->setPlayingState(demo_.enabled ? PlayerState::PLAYING : PlayerState::ENTERING_SCREEN); + } } // Hace sonar la música -void Game::playMusic() -{ - Audio::get()->playMusic("playing.ogg"); +void Game::playMusic() { + Audio::get()->playMusic("playing.ogg"); } // Detiene la música -void Game::stopMusic() -{ - if (!demo_.enabled) - { - Audio::get()->stopMusic(); - } +void Game::stopMusic() { + if (!demo_.enabled) { + Audio::get()->stopMusic(); + } } // Actualiza las variables durante el modo demo -void Game::updateDemo() -{ - if (demo_.enabled) - { - balloon_manager_->setCreationTimeEnabled((balloon_manager_->getNumBalloons() == 0) ? false : true); +void Game::updateDemo() { + if (demo_.enabled) { + balloon_manager_->setCreationTimeEnabled((balloon_manager_->getNumBalloons() == 0) ? false : true); - // Actualiza ambos fades - fade_in_->update(); - fade_out_->update(); + // Actualiza ambos fades + fade_in_->update(); + fade_out_->update(); - // Incrementa el contador de la demo - if (demo_.counter < TOTAL_DEMO_DATA) - { - demo_.counter++; - } + // Incrementa el contador de la demo + if (demo_.counter < TOTAL_DEMO_DATA) { + demo_.counter++; + } - // Activa el fundido antes de acabar con los datos de la demo - if (demo_.counter == TOTAL_DEMO_DATA - 200) - { - fade_out_->setType(FadeType::RANDOM_SQUARE); - fade_out_->activate(); - } + // Activa el fundido antes de acabar con los datos de la demo + if (demo_.counter == TOTAL_DEMO_DATA - 200) { + fade_out_->setType(FadeType::RANDOM_SQUARE); + fade_out_->activate(); + } - // Si ha terminado el fundido, cambia de sección - if (fade_out_->hasEnded()) - { - Section::name = Section::Name::HI_SCORE_TABLE; - return; - } - } + // Si ha terminado el fundido, cambia de sección + if (fade_out_->hasEnded()) { + Section::name = Section::Name::HI_SCORE_TABLE; + return; + } + } } #ifdef RECORDING // Actualiza las variables durante el modo de grabación -void Game::updateRecording() -{ - // Solo mira y guarda el input en cada update - checkInput(); +void Game::updateRecording() { + // Solo mira y guarda el input en cada update + checkInput(); - // Incrementa el contador de la demo - if (demo_.counter < TOTAL_DEMO_DATA) - demo_.counter++; + // Incrementa el contador de la demo + if (demo_.counter < TOTAL_DEMO_DATA) + demo_.counter++; - // Si se ha llenado el vector con datos, sale del programa - else - { - section::name = section::Name::QUIT; - return; - } + // Si se ha llenado el vector con datos, sale del programa + else { + section::name = section::Name::QUIT; + return; + } } #endif // Actualiza las variables durante dicho estado -void Game::updateGameStateFadeIn() -{ - fade_in_->update(); - updateScoreboard(); - updateBackground(); - if (fade_in_->hasEnded()) - { - setState(GameState::ENTERING_PLAYER); - balloon_manager_->createTwoBigBalloons(); - evaluateAndSetMenace(); - } +void Game::updateGameStateFadeIn() { + fade_in_->update(); + updateScoreboard(); + updateBackground(); + if (fade_in_->hasEnded()) { + setState(GameState::ENTERING_PLAYER); + balloon_manager_->createTwoBigBalloons(); + evaluateAndSetMenace(); + } } // Actualiza las variables durante dicho estado -void Game::updateGameStateEnteringPlayer() -{ - balloon_manager_->update(); - updatePlayers(); - updateScoreboard(); - updateBackground(); - for (const auto &player : players_) - { - if (player->isPlaying()) - { - setState(GameState::SHOWING_GET_READY_MESSAGE); - createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); - playSound("voice_get_ready.wav"); - } - } +void Game::updateGameStateEnteringPlayer() { + balloon_manager_->update(); + updatePlayers(); + updateScoreboard(); + updateBackground(); + for (const auto &player : players_) { + if (player->isPlaying()) { + setState(GameState::SHOWING_GET_READY_MESSAGE); + createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); + playSound("voice_get_ready.wav"); + } + } } // Actualiza las variables durante dicho estado -void Game::updateGameStateShowingGetReadyMessage() -{ - balloon_manager_->update(); - updatePathSprites(); - updatePlayers(); - updateBullets(); - updateScoreboard(); - updateBackground(); - freePathSprites(); - if (path_sprites_.size() == 0) - { - setState(GameState::PLAYING); - } - if (counter_ == 100) - { - playMusic(); - } - ++counter_; +void Game::updateGameStateShowingGetReadyMessage() { + balloon_manager_->update(); + updatePathSprites(); + updatePlayers(); + updateBullets(); + updateScoreboard(); + updateBackground(); + freePathSprites(); + if (path_sprites_.size() == 0) { + setState(GameState::PLAYING); + } + if (counter_ == 100) { + playMusic(); + } + ++counter_; } // Actualiza las variables durante el transcurso normal del juego -void Game::updateGameStatePlaying() -{ +void Game::updateGameStatePlaying() { #ifdef DEBUG - if (auto_pop_balloons_) - { - Stage::addPower(5); - } + if (auto_pop_balloons_) { + Stage::addPower(5); + } #endif - updatePlayers(); - checkPlayersStatusPlaying(); - updateScoreboard(); - updateBackground(); - balloon_manager_->update(); - tabe_->update(); - updateBullets(); - updateItems(); - updateStage(); - updateSmartSprites(); - updatePathSprites(); - updateTimeStopped(); - updateHelper(); - checkBulletCollision(); - updateMenace(); - checkAndUpdateBalloonSpeed(); - checkState(); - cleanVectors(); - // playMusic(); + updatePlayers(); + checkPlayersStatusPlaying(); + updateScoreboard(); + updateBackground(); + balloon_manager_->update(); + tabe_->update(); + updateBullets(); + updateItems(); + updateStage(); + updateSmartSprites(); + updatePathSprites(); + updateTimeStopped(); + updateHelper(); + checkBulletCollision(); + updateMenace(); + checkAndUpdateBalloonSpeed(); + checkState(); + cleanVectors(); + // playMusic(); } // Vacía los vectores de elementos deshabilitados -void Game::cleanVectors() -{ - freeBullets(); - balloon_manager_->freeBalloons(); - freeItems(); - freeSmartSprites(); - freePathSprites(); +void Game::cleanVectors() { + freeBullets(); + balloon_manager_->freeBalloons(); + freeItems(); + freeSmartSprites(); + freePathSprites(); } // Gestiona el nivel de amenaza -void Game::updateMenace() -{ - if (state_ == GameState::PLAYING) - { - const auto stage = Stage::get(Stage::number); - const float percent = Stage::power / stage.power_to_complete; - const int difference = stage.max_menace - stage.min_menace; +void Game::updateMenace() { + if (state_ == GameState::PLAYING) { + const auto stage = Stage::get(Stage::number); + const float percent = Stage::power / stage.power_to_complete; + const int difference = stage.max_menace - stage.min_menace; - // Aumenta el nivel de amenaza en función de la puntuación - menace_threshold_ = stage.min_menace + (difference * percent); + // Aumenta el nivel de amenaza en función de la puntuación + menace_threshold_ = stage.min_menace + (difference * percent); - // Si el nivel de amenza es inferior al umbral - if (menace_current_ < menace_threshold_) - { - // Crea una formación de enemigos - balloon_manager_->deployBalloonFormation(Stage::number); + // Si el nivel de amenza es inferior al umbral + if (menace_current_ < menace_threshold_) { + // Crea una formación de enemigos + balloon_manager_->deployBalloonFormation(Stage::number); - // Recalcula el nivel de amenaza con el nuevo globo - evaluateAndSetMenace(); - } - } + // Recalcula el nivel de amenaza con el nuevo globo + evaluateAndSetMenace(); + } + } } // Calcula y establece el valor de amenaza en funcion de los globos activos -void Game::evaluateAndSetMenace() -{ - menace_current_ = balloon_manager_->getMenace(); +void Game::evaluateAndSetMenace() { + menace_current_ = balloon_manager_->getMenace(); } // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase -void Game::checkAndUpdateBalloonSpeed() -{ - if (difficulty_ != Options::DifficultyCode::NORMAL) - return; +void Game::checkAndUpdateBalloonSpeed() { + if (difficulty_ != Options::DifficultyCode::NORMAL) + return; - const float percent = static_cast<float>(Stage::power) / Stage::get(Stage::number).power_to_complete; - const float thresholds[] = {0.2f, 0.4f, 0.6f, 0.8f}; + const float percent = static_cast<float>(Stage::power) / Stage::get(Stage::number).power_to_complete; + const float thresholds[] = {0.2f, 0.4f, 0.6f, 0.8f}; - for (size_t i = 0; i < std::size(thresholds); ++i) - { - if (balloon_manager_->getBalloonSpeed() == BALLOON_SPEED[i] && percent > thresholds[i]) - { - balloon_manager_->setBalloonSpeed(BALLOON_SPEED[i + 1]); - break; // Salir del bucle una vez actualizada la velocidad y aplicada - } - } + for (size_t i = 0; i < std::size(thresholds); ++i) { + if (balloon_manager_->getBalloonSpeed() == BALLOON_SPEED[i] && percent > thresholds[i]) { + balloon_manager_->setBalloonSpeed(BALLOON_SPEED[i + 1]); + break; // Salir del bucle una vez actualizada la velocidad y aplicada + } + } } // Cambia el estado del juego -void Game::setState(GameState state) -{ - state_ = state; - counter_ = 0; +void Game::setState(GameState state) { + state_ = state; + counter_ = 0; } -void Game::playSound(const std::string &name) -{ - if (demo_.enabled) - return; +void Game::playSound(const std::string &name) { + if (demo_.enabled) + return; - static auto audio = Audio::get(); - audio->playSound(name); + static auto audio = Audio::get(); + audio->playSound(name); } // Organiza los jugadores para que los vivos se pinten sobre los muertos -void Game::movePlayersToFront() -{ - if (players_to_reorder.empty()) - return; +void Game::movePlayersToFront() { + if (players_to_reorder.empty()) + return; - for (auto &player : players_to_reorder) - { - auto it = std::find(players_.begin(), players_.end(), player); - if (it != players_.end() && it != players_.begin()) - { - std::shared_ptr<Player> dyingPlayer = *it; - players_.erase(it); - players_.insert(players_.begin(), dyingPlayer); - } - } - players_to_reorder.clear(); + for (auto &player : players_to_reorder) { + auto it = std::find(players_.begin(), players_.end(), player); + if (it != players_.end() && it != players_.begin()) { + std::shared_ptr<Player> dyingPlayer = *it; + players_.erase(it); + players_.insert(players_.begin(), dyingPlayer); + } + } + players_to_reorder.clear(); } // Comprueba si está activo el menu de servicio para poner el juego en pausa -void Game::checkServiceMenu() -{ - if (demo_.enabled) - return; +void Game::checkServiceMenu() { + if (demo_.enabled) + return; - static bool was_paused_before_service_menu = false; - static bool service_menu_was_active = false; + static bool was_paused_before_service_menu = false; + static bool service_menu_was_active = false; - bool service_menu_is_active = ServiceMenu::get()->isEnabled(); + bool service_menu_is_active = ServiceMenu::get()->isEnabled(); - if (service_menu_is_active && !service_menu_was_active) - { - // El menú acaba de abrirse - was_paused_before_service_menu = paused_; - pause(true); - } - else if (!service_menu_is_active && service_menu_was_active) - { - // El menú acaba de cerrarse - pause(was_paused_before_service_menu); - } + if (service_menu_is_active && !service_menu_was_active) { + // El menú acaba de abrirse + was_paused_before_service_menu = paused_; + pause(true); + } else if (!service_menu_is_active && service_menu_was_active) { + // El menú acaba de cerrarse + pause(was_paused_before_service_menu); + } - service_menu_was_active = service_menu_is_active; + service_menu_was_active = service_menu_is_active; } #ifdef DEBUG // Comprueba los eventos en el modo DEBUG -void Game::checkDebugEvents(const SDL_Event &event) -{ - if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) - { - switch (event.key.key) - { - case SDLK_1: // Crea una powerball - { - balloon_manager_->createPowerBall(); - break; - } - case SDLK_2: // Activa o desactiva la aparición de globos - { - static bool deploy_balloons = true; - deploy_balloons = !deploy_balloons; - balloon_manager_->enableBalloonDeployment(deploy_balloons); - break; - } - case SDLK_3: // Activa el modo para pasar el juego automaticamente - { - auto_pop_balloons_ = !auto_pop_balloons_; - Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)}); - if (auto_pop_balloons_) - { - balloon_manager_->destroyAllBalloons(); - playSound("power_ball_explosion.wav"); - } - balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_); - break; - } - case SDLK_4: // Suelta un item - { - createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40); - break; - } - case SDLK_5: // 5.000 - { - const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; - createItemText(X, game_text_textures_.at(2)); - break; - } - case SDLK_6: // Crea un mensaje - { - createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); - break; - } - case SDLK_7: // 100.000 - { - const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; - createItemText(X, game_text_textures_.at(6)); - break; - } - case SDLK_8: - { - for (const auto &player : players_) - { - if (player->isPlaying()) - { - createItem(ItemType::COFFEE_MACHINE, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); - break; - } - } - break; - } - case SDLK_9: - { - tabe_->enable(); - break; - } - default: - break; - } - } +void Game::checkDebugEvents(const SDL_Event &event) { + if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) { + switch (event.key.key) { + case SDLK_1: // Crea una powerball + { + balloon_manager_->createPowerBall(); + break; + } + case SDLK_2: // Activa o desactiva la aparición de globos + { + static bool deploy_balloons = true; + deploy_balloons = !deploy_balloons; + balloon_manager_->enableBalloonDeployment(deploy_balloons); + break; + } + case SDLK_3: // Activa el modo para pasar el juego automaticamente + { + auto_pop_balloons_ = !auto_pop_balloons_; + Notifier::get()->show({"auto advance: " + boolToString(auto_pop_balloons_)}); + if (auto_pop_balloons_) { + balloon_manager_->destroyAllBalloons(); + playSound("power_ball_explosion.wav"); + } + balloon_manager_->enableBalloonDeployment(!auto_pop_balloons_); + break; + } + case SDLK_4: // Suelta un item + { + createItem(ItemType::CLOCK, players_.at(0)->getPosX(), players_.at(0)->getPosY() - 40); + break; + } + case SDLK_5: // 5.000 + { + const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; + createItemText(X, game_text_textures_.at(2)); + break; + } + case SDLK_6: // Crea un mensaje + { + createMessage({paths_.at(0), paths_.at(1)}, Resource::get()->getTexture("game_text_get_ready")); + break; + } + case SDLK_7: // 100.000 + { + const int X = players_.at(0)->getPosX() + (players_.at(0)->getWidth() - game_text_textures_[3]->getWidth()) / 2; + createItemText(X, game_text_textures_.at(6)); + break; + } + case SDLK_8: { + for (const auto &player : players_) { + if (player->isPlaying()) { + createItem(ItemType::COFFEE_MACHINE, player->getPosX(), param.game.game_area.rect.y - Item::COFFEE_MACHINE_HEIGHT); + break; + } + } + break; + } + case SDLK_9: { + tabe_->enable(); + break; + } + default: + break; + } + } } #endif \ No newline at end of file diff --git a/source/sections/game.h b/source/sections/game.h index 81d659b..3e9d2c3 100644 --- a/source/sections/game.h +++ b/source/sections/game.h @@ -1,14 +1,15 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64, Uint8 -#include <memory> // Para shared_ptr, unique_ptr -#include <string> // Para string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64, Uint8 -#include "manage_hiscore_table.h" // Para HiScoreEntry -#include "options.h" // Para SettingsOptions, settings, DifficultyCode (ptr only) -#include "player.h" // Para Player -#include "utils.h" // Para Demo +#include <memory> // Para shared_ptr, unique_ptr +#include <string> // Para string +#include <vector> // Para vector + +#include "manage_hiscore_table.h" // Para HiScoreEntry +#include "options.h" // Para SettingsOptions, settings, DifficultyCode (ptr only) +#include "player.h" // Para Player +#include "utils.h" // Para Demo class Background; class Balloon; @@ -35,216 +36,213 @@ constexpr bool GAME_MODE_DEMO_ON = true; constexpr int TOTAL_SCORE_DATA = 3; // Clase Game -class Game -{ -public: - // Constructor - Game(int playerID, int current_stage, bool demo); +class Game { + public: + // Constructor + Game(int playerID, int current_stage, bool demo); - // Destructor - ~Game(); + // Destructor + ~Game(); - // Bucle principal del juego - void run(); + // Bucle principal del juego + void run(); -private: - // --- Tipos internos --- - enum class GameState - { - FADE_IN, - ENTERING_PLAYER, - SHOWING_GET_READY_MESSAGE, - PLAYING, - COMPLETED, - GAME_OVER, - }; + private: + // --- Tipos internos --- + enum class GameState { + FADE_IN, + ENTERING_PLAYER, + SHOWING_GET_READY_MESSAGE, + PLAYING, + COMPLETED, + GAME_OVER, + }; - // --- Constantes internas --- - static constexpr int HELP_COUNTER_ = 1000; - static constexpr int GAME_COMPLETED_START_FADE_ = 500; - static constexpr int GAME_COMPLETED_END_ = 700; - static constexpr int GAME_OVER_COUNTER_ = 350; - static constexpr int TIME_STOPPED_COUNTER_ = 360; - static constexpr int ITEM_POINTS_1_DISK_ODDS_ = 10; - static constexpr int ITEM_POINTS_2_GAVINA_ODDS_ = 6; - static constexpr int ITEM_POINTS_3_PACMAR_ODDS_ = 3; - static constexpr int ITEM_CLOCK_ODDS_ = 5; - static constexpr int ITEM_COFFEE_ODDS_ = 5; - static constexpr int ITEM_POWER_BALL_ODDS_ = 0; - static constexpr int ITEM_COFFEE_MACHINE_ODDS_ = 4; + // --- Constantes internas --- + static constexpr int HELP_COUNTER_ = 1000; + static constexpr int GAME_COMPLETED_START_FADE_ = 500; + static constexpr int GAME_COMPLETED_END_ = 700; + static constexpr int GAME_OVER_COUNTER_ = 350; + static constexpr int TIME_STOPPED_COUNTER_ = 360; + static constexpr int ITEM_POINTS_1_DISK_ODDS_ = 10; + static constexpr int ITEM_POINTS_2_GAVINA_ODDS_ = 6; + static constexpr int ITEM_POINTS_3_PACMAR_ODDS_ = 3; + static constexpr int ITEM_CLOCK_ODDS_ = 5; + static constexpr int ITEM_COFFEE_ODDS_ = 5; + static constexpr int ITEM_POWER_BALL_ODDS_ = 0; + static constexpr int ITEM_COFFEE_MACHINE_ODDS_ = 4; - // --- Estructuras --- - struct Helper - { - bool need_coffee; // Indica si se necesitan cafes - bool need_coffee_machine; // Indica si se necesita PowerUp - bool need_power_ball; // Indica si se necesita una PowerBall - int counter; // Contador para no dar ayudas consecutivas - int item_disk_odds; // Probabilidad de aparición del objeto - int item_gavina_odds; // Probabilidad de aparición del objeto - int item_pacmar_odds; // Probabilidad de aparición del objeto - int item_clock_odds; // Probabilidad de aparición del objeto - int item_coffee_odds; // Probabilidad de aparición del objeto - int item_coffee_machine_odds; // Probabilidad de aparición del objeto + // --- Estructuras --- + struct Helper { + bool need_coffee; // Indica si se necesitan cafes + bool need_coffee_machine; // Indica si se necesita PowerUp + bool need_power_ball; // Indica si se necesita una PowerBall + int counter; // Contador para no dar ayudas consecutivas + int item_disk_odds; // Probabilidad de aparición del objeto + int item_gavina_odds; // Probabilidad de aparición del objeto + int item_pacmar_odds; // Probabilidad de aparición del objeto + int item_clock_odds; // Probabilidad de aparición del objeto + int item_coffee_odds; // Probabilidad de aparición del objeto + int item_coffee_machine_odds; // Probabilidad de aparición del objeto - Helper() - : need_coffee(false), - need_coffee_machine(false), - need_power_ball(false), - counter(HELP_COUNTER_), - item_disk_odds(ITEM_POINTS_1_DISK_ODDS_), - item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS_), - item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS_), - item_clock_odds(ITEM_CLOCK_ODDS_), - item_coffee_odds(ITEM_COFFEE_ODDS_), - item_coffee_machine_odds(ITEM_COFFEE_MACHINE_ODDS_) {} - }; + Helper() + : need_coffee(false), + need_coffee_machine(false), + need_power_ball(false), + counter(HELP_COUNTER_), + item_disk_odds(ITEM_POINTS_1_DISK_ODDS_), + item_gavina_odds(ITEM_POINTS_2_GAVINA_ODDS_), + item_pacmar_odds(ITEM_POINTS_3_PACMAR_ODDS_), + item_clock_odds(ITEM_CLOCK_ODDS_), + item_coffee_odds(ITEM_COFFEE_ODDS_), + item_coffee_machine_odds(ITEM_COFFEE_MACHINE_ODDS_) {} + }; - // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - Screen *screen_; // Objeto encargado de dibujar en pantalla - Input *input_; // Manejador de entrada - Scoreboard *scoreboard_; // Objeto para dibujar el marcador + // --- Objetos y punteros --- + SDL_Renderer *renderer_; // El renderizador de la ventana + Screen *screen_; // Objeto encargado de dibujar en pantalla + Input *input_; // Manejador de entrada + Scoreboard *scoreboard_; // Objeto para dibujar el marcador - std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego + std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego - SDL_Texture *canvas_; // Textura para dibujar la zona de juego + SDL_Texture *canvas_; // Textura para dibujar la zona de juego - std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores - std::vector<std::unique_ptr<Bullet>> bullets_; // Vector con las balas - std::vector<std::unique_ptr<Item>> items_; // Vector con los items - std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites - std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites + std::vector<std::shared_ptr<Player>> players_; // Vector con los jugadores + std::vector<std::unique_ptr<Bullet>> bullets_; // Vector con las balas + std::vector<std::unique_ptr<Item>> items_; // Vector con los items + std::vector<std::unique_ptr<SmartSprite>> smart_sprites_; // Vector con los smartsprites + std::vector<std::unique_ptr<PathSprite>> path_sprites_; // Vector con los pathsprites - std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items - std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_; // Vector con todas las texturas de los jugadores + std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items + std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_; // Vector con todas las texturas de los jugadores - std::vector<std::shared_ptr<Texture>> game_text_textures_; // Vector con las texturas para los sprites con textos + std::vector<std::shared_ptr<Texture>> game_text_textures_; // Vector con las texturas para los sprites con textos - std::vector<std::vector<std::string>> item_animations_; // Vector con las animaciones de los items - std::vector<std::vector<std::string>> player_animations_; // Vector con las animaciones del jugador + std::vector<std::vector<std::string>> item_animations_; // Vector con las animaciones de los items + std::vector<std::vector<std::string>> player_animations_; // Vector con las animaciones del jugador - std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades - std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades - std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos - std::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor - std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados + std::unique_ptr<Fade> fade_in_; // Objeto para renderizar fades + std::unique_ptr<Fade> fade_out_; // Objeto para renderizar fades + std::unique_ptr<BalloonManager> balloon_manager_; // Objeto para gestionar los globos + std::unique_ptr<Tabe> tabe_; // Objeto para gestionar el Tabe Volaor + std::vector<Path> paths_; // Vector con los recorridos precalculados almacenados - // --- Variables de estado --- - HiScoreEntry hi_score_ = HiScoreEntry( - Options::settings.hi_score_table[0].name, - Options::settings.hi_score_table[0].score); // Máxima puntuación y nombre de quien la ostenta + // --- Variables de estado --- + HiScoreEntry hi_score_ = HiScoreEntry( + Options::settings.hi_score_table[0].name, + Options::settings.hi_score_table[0].score); // Máxima puntuación y nombre de quien la ostenta - Demo demo_; // Variable con todas las variables relacionadas con el modo demo - Options::DifficultyCode difficulty_ = Options::settings.difficulty; // Dificultad del juego - Helper helper_; // Variable para gestionar las ayudas - Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa - bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego - bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima - bool paused_ = false; // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade) - // bool paused_by_service_menu_ = false; - float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad - int counter_ = 0; // Contador para el juego - int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos - int game_over_counter_ = GAME_OVER_COUNTER_; // Contador para el estado de fin de partida - int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido - int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases - int menace_current_ = 0; // Nivel de amenaza actual - int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos - GameState state_ = GameState::FADE_IN; // Estado - std::vector<std::shared_ptr<Player>> players_to_reorder; + Demo demo_; // Variable con todas las variables relacionadas con el modo demo + Options::DifficultyCode difficulty_ = Options::settings.difficulty; // Dificultad del juego + Helper helper_; // Variable para gestionar las ayudas + Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa + bool coffee_machine_enabled_ = false; // Indica si hay una máquina de café en el terreno de juego + bool hi_score_achieved_ = false; // Indica si se ha superado la puntuación máxima + bool paused_ = false; // Indica si el juego está pausado (no se deberia de poder utilizar en el modo arcade) + // bool paused_by_service_menu_ = false; + float difficulty_score_multiplier_; // Multiplicador de puntos en función de la dificultad + int counter_ = 0; // Contador para el juego + int game_completed_counter_ = 0; // Contador para el tramo final, cuando se ha completado la partida y ya no aparecen más enemigos + int game_over_counter_ = GAME_OVER_COUNTER_; // Contador para el estado de fin de partida + int time_stopped_counter_ = 0; // Temporizador para llevar la cuenta del tiempo detenido + int total_power_to_complete_game_; // La suma del poder necesario para completar todas las fases + int menace_current_ = 0; // Nivel de amenaza actual + int menace_threshold_ = 0; // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos + GameState state_ = GameState::FADE_IN; // Estado + std::vector<std::shared_ptr<Player>> players_to_reorder; #ifdef DEBUG - bool auto_pop_balloons_ = false; // Si es true, incrementa automaticamente los globos explotados + bool auto_pop_balloons_ = false; // Si es true, incrementa automaticamente los globos explotados - // Comprueba los eventos en el modo DEBUG - void checkDebugEvents(const SDL_Event &event); + // Comprueba los eventos en el modo DEBUG + void checkDebugEvents(const SDL_Event &event); #endif - // --- Métodos internos --- - void update(); // Actualiza el juego - void render(); // Dibuja el juego - void checkEvents(); // Comprueba los eventos que hay en cola - void setResources(); // Asigna texturas y animaciones - void updateHiScore(); // Actualiza el valor de HiScore en caso necesario - void updatePlayers(); // Actualiza las variables del jugador - void renderPlayers(); // Dibuja a los jugadores - void updateStage(); // Comprueba si hay cambio de fase y actualiza las variables - void updateGameStateGameOver(); // Actualiza el estado de fin de la partida - void destroyAllItems(); // Destruye todos los items - std::shared_ptr<Balloon> checkPlayerBalloonCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los globos activos - void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los items - void checkBulletCollision(); // Comprueba y procesa la colisión de las balas - void updateBullets(); // Mueve las balas activas - void renderBullets(); // Pinta las balas activas - void createBullet(int x, int y, BulletType kind, bool powered_up, int owner); // Crea un objeto bala - void freeBullets(); // Vacia el vector de balas - void updateItems(); // Actualiza los items - void renderItems(); // Pinta los items activos - ItemType dropItem(); // Devuelve un item en función del azar - void createItem(ItemType type, float x, float y); // Crea un objeto item - void freeItems(); // Vacia el vector de items - void createItemText(int x, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite - void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite - void freeSmartSprites(); // Vacia el vector de smartsprites - void freePathSprites(); // Vacia el vector de pathsprites - void throwCoffee(int x, int y); // Crea un SpriteSmart para arrojar el item café al recibir un impacto - void updateSmartSprites(); // Actualiza los SpriteSmarts - void renderSmartSprites(); // Pinta los SpriteSmarts activos - void updatePathSprites(); // Actualiza los PathSprites - void renderPathSprites(); // Pinta los PathSprites activos - void handlePlayerCollision(std::shared_ptr<Player> &player); // Acciones a realizar cuando el jugador colisiona con un globo - void updateTimeStopped(); // Actualiza y comprueba el valor de la variable - void updateBackground(); // Actualiza el fondo - void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos - void enableTimeStopItem(); // Habilita el efecto del item de detener el tiempo - void disableTimeStopItem(); // Deshabilita el efecto del item de detener el tiempo - void updateHelper(); // Actualiza las variables de ayuda - bool allPlayersAreWaitingOrGameOver(); // Comprueba si todos los jugadores han terminado de jugar - bool allPlayersAreGameOver(); // Comprueba si todos los jugadores han terminado de jugar - bool allPlayersAreNotPlaying(); // Comprueba si todos los jugadores han terminado de jugar - void updateScoreboard(); // Actualiza el marcador - void fillCanvas(); // Dibuja los elementos de la zona de juego en su textura - void pause(bool value); // Pausa el juego - void addScoreToScoreBoard(const std::shared_ptr<Player> &player); // Añade una puntuación a la tabla de records - void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Saca del estado de GAME OVER al jugador si el otro está activo - void checkPlayersStatusPlaying(); // Comprueba el estado de juego de los jugadores - std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id" - int getController(int playerId); // Obtiene un controlador a partir del "id" del jugador - void checkInput(); // Gestiona la entrada durante el juego - void checkPauseInput(); // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego. - void DEMO_handleInput(); // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos. - void DEMO_handlePassInput(); // Gestiona las entradas de los jugadores en el modo demo para saltarse la demo. - void DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa las entradas para un jugador específico durante el modo demo. - void handleFireInput(const std::shared_ptr<Player> &player, BulletType bulletType); // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos. - void handlePlayersInput(); // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo). - void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Maneja las entradas de movimiento y disparo para un jugador en modo normal. - void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex); // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. - void handlePlayerContinue(const std::shared_ptr<Player> &player); // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio. - void handleNameInput(const std::shared_ptr<Player> &player); // Procesa las entradas para la introducción del nombre del jugador. - void initDemo(int player_id); // Inicializa las variables para el modo DEMO - void setTotalPower(); // Calcula el poder total necesario para completar el juego - void initScoreboard(); // Inicializa el marcador - void initDifficultyVars(); // Inicializa las opciones relacionadas con la dificultad - void initPlayers(int player_id); // Inicializa los jugadores - void playMusic(); // Hace sonar la música - void stopMusic(); // Detiene la música - void playSound(const std::string &name); // Hace sonar un sonido - void updateDemo(); // Actualiza las variables durante el modo demo - void updateGameStateFadeIn(); // Actualiza las variables durante dicho estado - void updateGameStateEnteringPlayer(); // Actualiza las variables durante dicho estado - void updateGameStateShowingGetReadyMessage(); // Actualiza las variables durante dicho estado - void updateGameStatePlaying(); // Actualiza las variables durante el transcurso normal del juego - void updateGameStateCompleted(); // Gestiona eventos para el estado del final del juego - void checkState(); // Comprueba el estado del juego - void cleanVectors(); // Vacía los vectores de elementos deshabilitados - void updateMenace(); // Gestiona el nivel de amenaza - void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos - void checkAndUpdateBalloonSpeed(); // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase - void setState(GameState state); // Cambia el estado del juego - void movePlayersToFront(); // Organiza los jugadores para que los vivos se pinten sobre los muertos - void checkServiceMenu(); // Comprueba si está activo el menu de servicio para poner el juego en pausa + // --- Métodos internos --- + void update(); // Actualiza el juego + void render(); // Dibuja el juego + void checkEvents(); // Comprueba los eventos que hay en cola + void setResources(); // Asigna texturas y animaciones + void updateHiScore(); // Actualiza el valor de HiScore en caso necesario + void updatePlayers(); // Actualiza las variables del jugador + void renderPlayers(); // Dibuja a los jugadores + void updateStage(); // Comprueba si hay cambio de fase y actualiza las variables + void updateGameStateGameOver(); // Actualiza el estado de fin de la partida + void destroyAllItems(); // Destruye todos los items + std::shared_ptr<Balloon> checkPlayerBalloonCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los globos activos + void checkPlayerItemCollision(std::shared_ptr<Player> &player); // Comprueba la colisión entre el jugador y los items + void checkBulletCollision(); // Comprueba y procesa la colisión de las balas + void updateBullets(); // Mueve las balas activas + void renderBullets(); // Pinta las balas activas + void createBullet(int x, int y, BulletType kind, bool powered_up, int owner); // Crea un objeto bala + void freeBullets(); // Vacia el vector de balas + void updateItems(); // Actualiza los items + void renderItems(); // Pinta los items activos + ItemType dropItem(); // Devuelve un item en función del azar + void createItem(ItemType type, float x, float y); // Crea un objeto item + void freeItems(); // Vacia el vector de items + void createItemText(int x, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite + void createMessage(const std::vector<Path> &paths, std::shared_ptr<Texture> texture); // Crea un objeto PathSprite + void freeSmartSprites(); // Vacia el vector de smartsprites + void freePathSprites(); // Vacia el vector de pathsprites + void throwCoffee(int x, int y); // Crea un SpriteSmart para arrojar el item café al recibir un impacto + void updateSmartSprites(); // Actualiza los SpriteSmarts + void renderSmartSprites(); // Pinta los SpriteSmarts activos + void updatePathSprites(); // Actualiza los PathSprites + void renderPathSprites(); // Pinta los PathSprites activos + void handlePlayerCollision(std::shared_ptr<Player> &player); // Acciones a realizar cuando el jugador colisiona con un globo + void updateTimeStopped(); // Actualiza y comprueba el valor de la variable + void updateBackground(); // Actualiza el fondo + void initPaths(); // Inicializa las variables que contienen puntos de ruta para mover objetos + void enableTimeStopItem(); // Habilita el efecto del item de detener el tiempo + void disableTimeStopItem(); // Deshabilita el efecto del item de detener el tiempo + void updateHelper(); // Actualiza las variables de ayuda + bool allPlayersAreWaitingOrGameOver(); // Comprueba si todos los jugadores han terminado de jugar + bool allPlayersAreGameOver(); // Comprueba si todos los jugadores han terminado de jugar + bool allPlayersAreNotPlaying(); // Comprueba si todos los jugadores han terminado de jugar + void updateScoreboard(); // Actualiza el marcador + void fillCanvas(); // Dibuja los elementos de la zona de juego en su textura + void pause(bool value); // Pausa el juego + void addScoreToScoreBoard(const std::shared_ptr<Player> &player); // Añade una puntuación a la tabla de records + void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index); // Saca del estado de GAME OVER al jugador si el otro está activo + void checkPlayersStatusPlaying(); // Comprueba el estado de juego de los jugadores + std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id" + int getController(int playerId); // Obtiene un controlador a partir del "id" del jugador + void checkInput(); // Gestiona la entrada durante el juego + void checkPauseInput(); // Verifica si alguno de los controladores ha solicitado una pausa y actualiza el estado de pausa del juego. + void DEMO_handleInput(); // Gestiona las entradas de los jugadores en el modo demo, incluyendo movimientos y disparos automáticos. + void DEMO_handlePassInput(); // Gestiona las entradas de los jugadores en el modo demo para saltarse la demo. + void DEMO_handlePlayerInput(const std::shared_ptr<Player> &player, int index); // Procesa las entradas para un jugador específico durante el modo demo. + void handleFireInput(const std::shared_ptr<Player> &player, BulletType bulletType); // Maneja el disparo de un jugador, incluyendo la creación de balas y la gestión del tiempo de espera entre disparos. + void handlePlayersInput(); // Gestiona las entradas de todos los jugadores en el modo normal (fuera del modo demo). + void handleNormalPlayerInput(const std::shared_ptr<Player> &player); // Maneja las entradas de movimiento y disparo para un jugador en modo normal. + void handleFireInputs(const std::shared_ptr<Player> &player, bool autofire, int controllerIndex); // Procesa las entradas de disparo del jugador, permitiendo disparos automáticos si está habilitado. + void handlePlayerContinue(const std::shared_ptr<Player> &player); // Maneja la continuación del jugador cuando no está jugando, permitiendo que continúe si se pulsa el botón de inicio. + void handleNameInput(const std::shared_ptr<Player> &player); // Procesa las entradas para la introducción del nombre del jugador. + void initDemo(int player_id); // Inicializa las variables para el modo DEMO + void setTotalPower(); // Calcula el poder total necesario para completar el juego + void initScoreboard(); // Inicializa el marcador + void initDifficultyVars(); // Inicializa las opciones relacionadas con la dificultad + void initPlayers(int player_id); // Inicializa los jugadores + void playMusic(); // Hace sonar la música + void stopMusic(); // Detiene la música + void playSound(const std::string &name); // Hace sonar un sonido + void updateDemo(); // Actualiza las variables durante el modo demo + void updateGameStateFadeIn(); // Actualiza las variables durante dicho estado + void updateGameStateEnteringPlayer(); // Actualiza las variables durante dicho estado + void updateGameStateShowingGetReadyMessage(); // Actualiza las variables durante dicho estado + void updateGameStatePlaying(); // Actualiza las variables durante el transcurso normal del juego + void updateGameStateCompleted(); // Gestiona eventos para el estado del final del juego + void checkState(); // Comprueba el estado del juego + void cleanVectors(); // Vacía los vectores de elementos deshabilitados + void updateMenace(); // Gestiona el nivel de amenaza + void evaluateAndSetMenace(); // Calcula y establece el valor de amenaza en funcion de los globos activos + void checkAndUpdateBalloonSpeed(); // Actualiza la velocidad de los globos en funcion del poder acumulado de la fase + void setState(GameState state); // Cambia el estado del juego + void movePlayersToFront(); // Organiza los jugadores para que los vivos se pinten sobre los muertos + void checkServiceMenu(); // Comprueba si está activo el menu de servicio para poner el juego en pausa #ifdef RECORDING - void updateRecording(); // Actualiza las variables durante el modo de grabación + void updateRecording(); // Actualiza las variables durante el modo de grabación #endif }; \ No newline at end of file diff --git a/source/sections/hiscore_table.cpp b/source/sections/hiscore_table.cpp index 91949b3..4fd7c1b 100644 --- a/source/sections/hiscore_table.cpp +++ b/source/sections/hiscore_table.cpp @@ -1,432 +1,389 @@ #include "hiscore_table.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget -#include <stdlib.h> // Para rand, size_t -#include <algorithm> // Para max -#include <functional> // Para function -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget +#include <stdlib.h> // Para rand, size_t -#include "audio.h" // Para Audio -#include "background.h" // Para Background -#include "fade.h" // Para Fade, FadeMode, FadeType -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input -#include "lang.h" // Para getText -#include "manage_hiscore_table.h" // Para HiScoreEntry -#include "options.h" // Para SettingsOptions, settings -#include "param.h" // Para Param, param, ParamGame, ParamFade -#include "path_sprite.h" // Para PathSprite, Path, PathType -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options -#include "sprite.h" // Para Sprite -#include "text.h" // Para Text, TEXT_SHADOW, TEXT_COLOR -#include "texture.h" // Para Texture -#include "utils.h" // Para Color, easeOutQuint, NO_TEXT_COLOR +#include <algorithm> // Para max +#include <functional> // Para function +#include <vector> // Para vector + +#include "audio.h" // Para Audio +#include "background.h" // Para Background +#include "fade.h" // Para Fade, FadeMode, FadeType +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input +#include "lang.h" // Para getText +#include "manage_hiscore_table.h" // Para HiScoreEntry +#include "options.h" // Para SettingsOptions, settings +#include "param.h" // Para Param, param, ParamGame, ParamFade +#include "path_sprite.h" // Para PathSprite, Path, PathType +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options +#include "sprite.h" // Para Sprite +#include "text.h" // Para Text, TEXT_SHADOW, TEXT_COLOR +#include "texture.h" // Para Texture +#include "utils.h" // Para Color, easeOutQuint, NO_TEXT_COLOR // Constructor HiScoreTable::HiScoreTable() - : renderer_(Screen::get()->getRenderer()), - backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), - fade_(std::make_unique<Fade>()), - background_(std::make_unique<Background>()), - counter_(0), - ticks_(0), - view_area_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}), - fade_mode_(FadeMode::IN), - background_fade_color_(Color(0, 0, 0)) -{ - // Inicializa el resto - Section::name = Section::Name::HI_SCORE_TABLE; - SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); - initFade(); - initBackground(); - iniEntryColors(); - createSprites(); + : renderer_(Screen::get()->getRenderer()), + backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), + fade_(std::make_unique<Fade>()), + background_(std::make_unique<Background>()), + counter_(0), + ticks_(0), + view_area_(SDL_FRect{0, 0, static_cast<float>(param.game.width), static_cast<float>(param.game.height)}), + fade_mode_(FadeMode::IN), + background_fade_color_(Color(0, 0, 0)) { + // Inicializa el resto + Section::name = Section::Name::HI_SCORE_TABLE; + SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); + initFade(); + initBackground(); + iniEntryColors(); + createSprites(); } // Destructor -HiScoreTable::~HiScoreTable() -{ - SDL_DestroyTexture(backbuffer_); - Options::settings.clearLastHiScoreEntries(); +HiScoreTable::~HiScoreTable() { + SDL_DestroyTexture(backbuffer_); + Options::settings.clearLastHiScoreEntries(); } // Actualiza las variables -void HiScoreTable::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - // Actualiza el contador de ticks - ticks_ = SDL_GetTicks(); +void HiScoreTable::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + // Actualiza el contador de ticks + ticks_ = SDL_GetTicks(); - // Actualiza las posiciones de los sprites de texto - updateSprites(); + // Actualiza las posiciones de los sprites de texto + updateSprites(); - // Actualiza el fondo - background_->update(); + // Actualiza el fondo + background_->update(); - // Gestiona el fade - updateFade(); + // Gestiona el fade + updateFade(); - // Gestiona el contador y sus eventos - updateCounter(); + // Gestiona el contador y sus eventos + updateCounter(); - // Dibuja los sprites en la textura - fillTexture(); + // Dibuja los sprites en la textura + fillTexture(); - // Actualiza el objeto screen - Screen::get()->update(); + // Actualiza el objeto screen + Screen::get()->update(); - // Actualiza las variables de globalInputs - } + // Actualiza las variables de globalInputs + } } // Dibuja los sprites en la textura -void HiScoreTable::fillTexture() -{ - // Pinta en el backbuffer el texto y los sprites - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); - SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); - SDL_RenderClear(renderer_); +void HiScoreTable::fillTexture() { + // Pinta en el backbuffer el texto y los sprites + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); + SDL_RenderClear(renderer_); - // Escribe el texto: Mejores puntuaciones - header_->render(); + // Escribe el texto: Mejores puntuaciones + header_->render(); - // Escribe los nombres de la tabla de puntuaciones - for (auto const &entry : entry_names_) - { - entry->render(); - } + // Escribe los nombres de la tabla de puntuaciones + for (auto const &entry : entry_names_) { + entry->render(); + } - // Cambia el destino de renderizado - SDL_SetRenderTarget(renderer_, temp); + // Cambia el destino de renderizado + SDL_SetRenderTarget(renderer_, temp); } // Pinta en pantalla -void HiScoreTable::render() -{ - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); +void HiScoreTable::render() { + // Prepara para empezar a dibujar en la textura de juego + Screen::get()->start(); - // Limpia la pantalla - Screen::get()->clean(); + // Limpia la pantalla + Screen::get()->clean(); - // Pinta el fondo - background_->render(); + // Pinta el fondo + background_->render(); - // Establece la ventana del backbuffer - view_area_.y = std::max(0.0f, param.game.height - counter_ + 100); + // Establece la ventana del backbuffer + view_area_.y = std::max(0.0f, param.game.height - counter_ + 100); - // Copia el backbuffer al renderizador - SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_); + // Copia el backbuffer al renderizador + SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_area_); - // Renderiza el fade - fade_->render(); + // Renderiza el fade + fade_->render(); - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); + // Vuelca el contenido del renderizador en pantalla + Screen::get()->render(); } // Comprueba los eventos -void HiScoreTable::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { - GlobalEvents::check(event); - } +void HiScoreTable::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + GlobalEvents::check(event); + } } // Comprueba las entradas -void HiScoreTable::checkInput() -{ - Input::get()->update(); - GlobalInputs::check(); +void HiScoreTable::checkInput() { + Input::get()->update(); + GlobalInputs::check(); } // Bucle para la pantalla de instrucciones -void HiScoreTable::run() -{ - Audio::get()->playMusic("title.ogg"); - while (Section::name == Section::Name::HI_SCORE_TABLE) - { - checkInput(); - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } +void HiScoreTable::run() { + Audio::get()->playMusic("title.ogg"); + while (Section::name == Section::Name::HI_SCORE_TABLE) { + checkInput(); + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Gestiona el fade -void HiScoreTable::updateFade() -{ - fade_->update(); +void HiScoreTable::updateFade() { + fade_->update(); - if (fade_->hasEnded() && fade_mode_ == FadeMode::IN) - { - fade_->reset(); - fade_mode_ = FadeMode::OUT; - fade_->setMode(fade_mode_); - } + if (fade_->hasEnded() && fade_mode_ == FadeMode::IN) { + fade_->reset(); + fade_mode_ = FadeMode::OUT; + fade_->setMode(fade_mode_); + } - if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT) - { - Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING) - ? Section::Name::TITLE - : Section::Name::INSTRUCTIONS; - Section::options = Section::Options::NONE; - } + if (fade_->hasEnded() && fade_mode_ == FadeMode::OUT) { + Section::name = (Section::options == Section::Options::HI_SCORE_AFTER_PLAYING) + ? Section::Name::TITLE + : Section::Name::INSTRUCTIONS; + Section::options = Section::Options::NONE; + } } // Convierte un entero a un string con separadores de miles -std::string HiScoreTable::format(int number) -{ - const std::string separator = "."; - const std::string score = std::to_string(number); +std::string HiScoreTable::format(int number) { + const std::string separator = "."; + const std::string score = std::to_string(number); - auto index = (int)score.size() - 1; - std::string result; - auto i = 0; - while (index >= 0) - { - result = score.at(index) + result; - index--; - i++; - if (i == 3) - { - i = 0; - result = separator + result; - } - } + auto index = (int)score.size() - 1; + std::string result; + auto i = 0; + while (index >= 0) { + result = score.at(index) + result; + index--; + i++; + if (i == 3) { + i = 0; + result = separator + result; + } + } - return result; + return result; } // Crea los sprites con los textos -void HiScoreTable::createSprites() -{ - auto header_text = Resource::get()->getText("04b_25_grey"); - auto entry_text = Resource::get()->getText("smb2"); +void HiScoreTable::createSprites() { + auto header_text = Resource::get()->getText("04b_25_grey"); + auto entry_text = Resource::get()->getText("smb2"); - // Obtiene el tamaño de la textura - float backbuffer_width; - float backbuffer_height; - SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height); + // Obtiene el tamaño de la textura + float backbuffer_width; + float backbuffer_height; + SDL_GetTextureSize(backbuffer_, &backbuffer_width, &backbuffer_height); - constexpr int ENTRY_LENGHT = 22; - constexpr int MAX_NAMES = 10; - const int space_between_header = entry_text->getCharacterSize() * 4; - const int space_between_lines = entry_text->getCharacterSize() * 2; - const int size = space_between_header + space_between_lines * (MAX_NAMES - 1) + entry_text->getCharacterSize(); - const int first_line = (param.game.height - size) / 2; + constexpr int ENTRY_LENGHT = 22; + constexpr int MAX_NAMES = 10; + const int space_between_header = entry_text->getCharacterSize() * 4; + const int space_between_lines = entry_text->getCharacterSize() * 2; + const int size = space_between_header + space_between_lines * (MAX_NAMES - 1) + entry_text->getCharacterSize(); + const int first_line = (param.game.height - size) / 2; - // Crea el sprite para el texto de cabecera - header_ = std::make_unique<Sprite>(header_text->writeDXToTexture(TEXT_COLOR, Lang::getText("[HIGHSCORE_TABLE] CAPTION"), -2, background_fade_color_.inverse().lighten(25))); - header_->setPosition(param.game.game_area.center_x - (header_->getWidth() / 2), first_line); + // Crea el sprite para el texto de cabecera + header_ = std::make_unique<Sprite>(header_text->writeDXToTexture(TEXT_COLOR, Lang::getText("[HIGHSCORE_TABLE] CAPTION"), -2, background_fade_color_.inverse().lighten(25))); + header_->setPosition(param.game.game_area.center_x - (header_->getWidth() / 2), first_line); - // Crea los sprites para las entradas en la tabla de puntuaciones - const int animation = rand() % 4; - const std::string sample_line(ENTRY_LENGHT + 3, ' '); - auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(TEXT_SHADOW, sample_line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)); - const auto entry_width = sample_entry->getWidth(); - for (int i = 0; i < MAX_NAMES; ++i) - { - const auto table_position = format(i + 1) + ". "; - const auto score = format(Options::settings.hi_score_table.at(i).score); - const auto num_dots = ENTRY_LENGHT - Options::settings.hi_score_table.at(i).name.size() - score.size(); - const auto one_cc = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : ""; - std::string dots; - for (int j = 0; j < (int)num_dots; ++j) - { - dots = dots + "."; - } - const auto line = table_position + Options::settings.hi_score_table.at(i).name + dots + score + one_cc; + // Crea los sprites para las entradas en la tabla de puntuaciones + const int animation = rand() % 4; + const std::string sample_line(ENTRY_LENGHT + 3, ' '); + auto sample_entry = std::make_unique<Sprite>(entry_text->writeDXToTexture(TEXT_SHADOW, sample_line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR)); + const auto entry_width = sample_entry->getWidth(); + for (int i = 0; i < MAX_NAMES; ++i) { + const auto table_position = format(i + 1) + ". "; + const auto score = format(Options::settings.hi_score_table.at(i).score); + const auto num_dots = ENTRY_LENGHT - Options::settings.hi_score_table.at(i).name.size() - score.size(); + const auto one_cc = Options::settings.hi_score_table.at(i).one_credit_complete ? " }" : ""; + std::string dots; + for (int j = 0; j < (int)num_dots; ++j) { + dots = dots + "."; + } + const auto line = table_position + Options::settings.hi_score_table.at(i).name + dots + score + one_cc; - entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(TEXT_SHADOW, line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR))); - const int default_pos_x = (backbuffer_width - entry_width) / 2; - const int pos_x = (i < 9) ? default_pos_x : default_pos_x - entry_text->getCharacterSize(); - const int pos_y = (i * space_between_lines) + first_line + space_between_header; - constexpr int steps = 80; - switch (animation) - { - case 0: // Ambos lados alternativamente - { - if (i % 2 == 0) - { - entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); - entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0); - } - else - { - entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); - entry_names_.back()->setPosition(backbuffer_width, 0); - } - break; - } + entry_names_.emplace_back(std::make_shared<PathSprite>(entry_text->writeDXToTexture(TEXT_SHADOW, line, 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR))); + const int default_pos_x = (backbuffer_width - entry_width) / 2; + const int pos_x = (i < 9) ? default_pos_x : default_pos_x - entry_text->getCharacterSize(); + const int pos_y = (i * space_between_lines) + first_line + space_between_header; + constexpr int steps = 80; + switch (animation) { + case 0: // Ambos lados alternativamente + { + if (i % 2 == 0) { + entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); + entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0); + } else { + entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); + entry_names_.back()->setPosition(backbuffer_width, 0); + } + break; + } - case 1: // Entran por la izquierda - { - entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); - entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0); - break; - } + case 1: // Entran por la izquierda + { + entry_names_.back()->addPath(-entry_names_.back()->getWidth(), pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); + entry_names_.back()->setPosition(-entry_names_.back()->getWidth(), 0); + break; + } - case 2: // Entran por la derecha - { - entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); - entry_names_.back()->setPosition(backbuffer_width, 0); - break; - } + case 2: // Entran por la derecha + { + entry_names_.back()->addPath(backbuffer_width, pos_x, PathType::HORIZONTAL, pos_y, steps, easeOutQuint); + entry_names_.back()->setPosition(backbuffer_width, 0); + break; + } - case 3: // Entran desde la parte inferior - { - entry_names_.back()->addPath(backbuffer_height, pos_y, PathType::VERTICAL, pos_x, steps, easeOutQuint); - entry_names_.back()->setPosition(0, backbuffer_height); - } + case 3: // Entran desde la parte inferior + { + entry_names_.back()->addPath(backbuffer_height, pos_y, PathType::VERTICAL, pos_x, steps, easeOutQuint); + entry_names_.back()->setPosition(0, backbuffer_height); + } - default: - break; - } - } + default: + break; + } + } } // Actualiza las posiciones de los sprites de texto -void HiScoreTable::updateSprites() -{ - constexpr int init_counter = 190; - const int counter_between_entries = 16; - if (counter_ >= init_counter) - { - const int counter2 = counter_ - init_counter; - if (counter2 % counter_between_entries == 0) - { - int index = counter2 / counter_between_entries; - if (index < static_cast<int>(entry_names_.size())) - { - entry_names_.at(index)->enable(); - } - } - } - for (auto const &entry : entry_names_) - { - entry->update(); - } +void HiScoreTable::updateSprites() { + constexpr int init_counter = 190; + const int counter_between_entries = 16; + if (counter_ >= init_counter) { + const int counter2 = counter_ - init_counter; + if (counter2 % counter_between_entries == 0) { + int index = counter2 / counter_between_entries; + if (index < static_cast<int>(entry_names_.size())) { + entry_names_.at(index)->enable(); + } + } + } + for (auto const &entry : entry_names_) { + entry->update(); + } - glowEntryNames(); + glowEntryNames(); } // Inicializa el fade -void HiScoreTable::initFade() -{ - fade_->setColor(param.fade.color); - fade_->setType(FadeType::RANDOM_SQUARE); - fade_->setPostDuration(param.fade.post_duration); - fade_->setMode(fade_mode_); - fade_->activate(); +void HiScoreTable::initFade() { + fade_->setColor(param.fade.color); + fade_->setType(FadeType::RANDOM_SQUARE); + fade_->setPostDuration(param.fade.post_duration); + fade_->setMode(fade_mode_); + fade_->activate(); } // Inicializa el fondo -void HiScoreTable::initBackground() -{ - background_->setPos(param.game.game_area.rect); - background_->setCloudsSpeed(-0.1f); +void HiScoreTable::initBackground() { + background_->setPos(param.game.game_area.rect); + background_->setCloudsSpeed(-0.1f); - const int lucky = rand() % 3; - switch (lucky) - { - case 0: // Fondo verde - { - background_->setGradientNumber(2); - background_->setTransition(0.0f); - background_->setSunProgression(1.0f); - background_->setMoonProgression(0.0f); - background_fade_color_ = GREEN_SKY_COLOR; - break; - } + const int lucky = rand() % 3; + switch (lucky) { + case 0: // Fondo verde + { + background_->setGradientNumber(2); + background_->setTransition(0.0f); + background_->setSunProgression(1.0f); + background_->setMoonProgression(0.0f); + background_fade_color_ = GREEN_SKY_COLOR; + break; + } - case 1: // Fondo naranja - { - background_->setGradientNumber(1); - background_->setTransition(0.0f); - background_->setSunProgression(0.65f); - background_->setMoonProgression(0.0f); - background_fade_color_ = PINK_SKY_COLOR; - break; - } + case 1: // Fondo naranja + { + background_->setGradientNumber(1); + background_->setTransition(0.0f); + background_->setSunProgression(0.65f); + background_->setMoonProgression(0.0f); + background_fade_color_ = PINK_SKY_COLOR; + break; + } - case 2: // Fondo azul - { - background_->setGradientNumber(0); - background_->setTransition(0.0f); - background_->setSunProgression(0.0f); - background_->setMoonProgression(0.0f); - background_fade_color_ = BLUE_SKY_COLOR; - break; - } + case 2: // Fondo azul + { + background_->setGradientNumber(0); + background_->setTransition(0.0f); + background_->setSunProgression(0.0f); + background_->setMoonProgression(0.0f); + background_fade_color_ = BLUE_SKY_COLOR; + break; + } - default: - break; - } + default: + break; + } } // Obtiene un color del vector de colores de entradas -Color HiScoreTable::getEntryColor(int counter_) -{ - int cycle_length = entry_colors_.size() * 2 - 2; - size_t n = counter_ % cycle_length; +Color HiScoreTable::getEntryColor(int counter_) { + int cycle_length = entry_colors_.size() * 2 - 2; + size_t n = counter_ % cycle_length; - size_t index; - if (n < entry_colors_.size()) - { - index = n; // Avanza: 0,1,2,3 - } - else - { - index = 2 * (entry_colors_.size() - 1) - n; // Retrocede: 2,1 - } + size_t index; + if (n < entry_colors_.size()) { + index = n; // Avanza: 0,1,2,3 + } else { + index = 2 * (entry_colors_.size() - 1) - n; // Retrocede: 2,1 + } - return entry_colors_[index]; + return entry_colors_[index]; } // Inicializa los colores de las entradas -void HiScoreTable::iniEntryColors() -{ - entry_colors_.clear(); - entry_colors_.emplace_back(background_fade_color_.inverse().lighten(75)); - entry_colors_.emplace_back(background_fade_color_.inverse().lighten(50)); - entry_colors_.emplace_back(background_fade_color_.inverse().lighten(25)); - entry_colors_.emplace_back(background_fade_color_.inverse()); +void HiScoreTable::iniEntryColors() { + entry_colors_.clear(); + entry_colors_.emplace_back(background_fade_color_.inverse().lighten(75)); + entry_colors_.emplace_back(background_fade_color_.inverse().lighten(50)); + entry_colors_.emplace_back(background_fade_color_.inverse().lighten(25)); + entry_colors_.emplace_back(background_fade_color_.inverse()); } // Hace brillar los nombres de la tabla de records -void HiScoreTable::glowEntryNames() -{ - const Color entry_color = getEntryColor(counter_ / 5); - for (const auto &entry_index : Options::settings.last_hi_score_entry) - { - if (entry_index != -1) - { - entry_names_.at(entry_index)->getTexture()->setColor(entry_color); - } - } +void HiScoreTable::glowEntryNames() { + const Color entry_color = getEntryColor(counter_ / 5); + for (const auto &entry_index : Options::settings.last_hi_score_entry) { + if (entry_index != -1) { + entry_names_.at(entry_index)->getTexture()->setColor(entry_color); + } + } } // Gestiona el contador -void HiScoreTable::updateCounter() -{ - ++counter_; +void HiScoreTable::updateCounter() { + ++counter_; - if (counter_ == 150) - { - background_->setColor(background_fade_color_.darken()); - background_->setAlpha(96); - } + if (counter_ == 150) { + background_->setColor(background_fade_color_.darken()); + background_->setAlpha(96); + } - if (counter_ == COUNTER_END_) - { - fade_->activate(); - } + if (counter_ == COUNTER_END_) { + fade_->activate(); + } } \ No newline at end of file diff --git a/source/sections/hiscore_table.h b/source/sections/hiscore_table.h index 41404c0..23a5b98 100644 --- a/source/sections/hiscore_table.h +++ b/source/sections/hiscore_table.h @@ -1,11 +1,12 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint16, SDL_FRect, SDL_Renderer, SDL_Texture, Uint64, Uint8 -#include <memory> // Para unique_ptr, shared_ptr -#include <string> // Para string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para Uint16, SDL_FRect, SDL_Renderer, SDL_Texture, Uint64, Uint8 -#include "utils.h" // Para Color +#include <memory> // Para unique_ptr, shared_ptr +#include <string> // Para string +#include <vector> // Para vector + +#include "utils.h" // Para Color class Background; class Fade; @@ -15,64 +16,63 @@ enum class FadeMode : Uint8; struct Path; /* - Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones - más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura - sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista - para dar el efecto de que la textura se mueve sobre la pantalla. + Esta clase gestiona un estado del programa. Se encarga de mostrar la tabla con las puntuaciones + más altas. Para ello utiliza un objeto que se encarga de pintar el fondo y una textura + sobre la que escribe las puntuaciones. Esta textura se recorre modificando la ventana de vista + para dar el efecto de que la textura se mueve sobre la pantalla. - Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar - su atenuación. + Para mejorar la legibilidad de los textos, el objeto que dibuja el fondo es capaz de modificar + su atenuación. */ // Clase HiScoreTable -class HiScoreTable -{ -public: - // Constructor - HiScoreTable(); +class HiScoreTable { + public: + // Constructor + HiScoreTable(); - // Destructor - ~HiScoreTable(); + // Destructor + ~HiScoreTable(); - // Bucle principal - void run(); + // Bucle principal + void run(); -private: - // --- Constantes --- - static constexpr Uint16 COUNTER_END_ = 800; // Valor final para el contador + private: + // --- Constantes --- + static constexpr Uint16 COUNTER_END_ = 800; // Valor final para el contador - // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - SDL_Texture *backbuffer_; // Textura para usar como backbuffer + // --- Objetos y punteros --- + SDL_Renderer *renderer_; // El renderizador de la ventana + SDL_Texture *backbuffer_; // Textura para usar como backbuffer - std::unique_ptr<Fade> fade_; // Objeto para renderizar fades - std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego - std::unique_ptr<Sprite> header_; // Sprite con la cabecera del texto - std::vector<std::shared_ptr<PathSprite>> entry_names_; // Lista con los sprites de cada uno de los nombres de la tabla de records - std::vector<Path> paths_; // Vector con los recorridos precalculados + std::unique_ptr<Fade> fade_; // Objeto para renderizar fades + std::unique_ptr<Background> background_; // Objeto para dibujar el fondo del juego + std::unique_ptr<Sprite> header_; // Sprite con la cabecera del texto + std::vector<std::shared_ptr<PathSprite>> entry_names_; // Lista con los sprites de cada uno de los nombres de la tabla de records + std::vector<Path> paths_; // Vector con los recorridos precalculados - // --- Variables --- - Uint16 counter_ = 0; // Contador - Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa - SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla - FadeMode fade_mode_; // Modo de fade a utilizar - Color background_fade_color_; // Color de atenuación del fondo - std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla + // --- Variables --- + Uint16 counter_ = 0; // Contador + Uint64 ticks_; // Contador de ticks para ajustar la velocidad del programa + SDL_FRect view_area_; // Parte de la textura que se muestra en pantalla + FadeMode fade_mode_; // Modo de fade a utilizar + Color background_fade_color_; // Color de atenuación del fondo + std::vector<Color> entry_colors_; // Colores para destacar las entradas en la tabla - // --- Métodos internos --- - void update(); // Actualiza las variables - void render(); // Pinta en pantalla - void checkEvents(); // Comprueba los eventos - void checkInput(); // Comprueba las entradas - std::string format(int number); // Convierte un entero a un string con separadores de miles - void fillTexture(); // Dibuja los sprites en la textura - void updateFade(); // Gestiona el fade - void createSprites(); // Crea los sprites con los textos - void updateSprites(); // Actualiza las posiciones de los sprites de texto - void initFade(); // Inicializa el fade - void initBackground(); // Inicializa el fondo - Color getEntryColor(int counter_); // Obtiene un color del vector de colores de entradas - void iniEntryColors(); // Inicializa los colores de las entradas - void glowEntryNames(); // Hace brillar los nombres de la tabla de records - void updateCounter(); // Gestiona el contador + // --- Métodos internos --- + void update(); // Actualiza las variables + void render(); // Pinta en pantalla + void checkEvents(); // Comprueba los eventos + void checkInput(); // Comprueba las entradas + std::string format(int number); // Convierte un entero a un string con separadores de miles + void fillTexture(); // Dibuja los sprites en la textura + void updateFade(); // Gestiona el fade + void createSprites(); // Crea los sprites con los textos + void updateSprites(); // Actualiza las posiciones de los sprites de texto + void initFade(); // Inicializa el fade + void initBackground(); // Inicializa el fondo + Color getEntryColor(int counter_); // Obtiene un color del vector de colores de entradas + void iniEntryColors(); // Inicializa los colores de las entradas + void glowEntryNames(); // Hace brillar los nombres de la tabla de records + void updateCounter(); // Gestiona el contador }; \ No newline at end of file diff --git a/source/sections/instructions.cpp b/source/sections/instructions.cpp index 88fad5f..5caac11 100644 --- a/source/sections/instructions.cpp +++ b/source/sections/instructions.cpp @@ -1,382 +1,351 @@ #include "instructions.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget, SDL_Re... -#include <algorithm> // Para max -#include <array> // Para array -#include <string> // Para basic_string, string -#include <utility> // Para move -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderTarget, SDL_Re... -#include "audio.h" // Para Audio -#include "fade.h" // Para Fade, FadeMode, FadeType -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input -#include "lang.h" // Para getText -#include "param.h" // Para Param, param, ParamGame, ParamFade, Param... -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options -#include "sprite.h" // Para Sprite -#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR, TEXT_SHADOW -#include "tiled_bg.h" // Para TiledBG, TiledBGMode -#include "utils.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C... +#include <algorithm> // Para max +#include <array> // Para array +#include <string> // Para basic_string, string +#include <utility> // Para move +#include <vector> // Para vector + +#include "audio.h" // Para Audio +#include "fade.h" // Para Fade, FadeMode, FadeType +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input +#include "lang.h" // Para getText +#include "param.h" // Para Param, param, ParamGame, ParamFade, Param... +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options +#include "sprite.h" // Para Sprite +#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR, TEXT_SHADOW +#include "tiled_bg.h" // Para TiledBG, TiledBGMode +#include "utils.h" // Para Color, SHADOW_TEXT_COLOR, Zone, NO_TEXT_C... // Constructor Instructions::Instructions() - : renderer_(Screen::get()->getRenderer()), - texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), - backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), - text_(Resource::get()->getText("smb2")), - tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)), - fade_(std::make_unique<Fade>()) -{ - // Configura las texturas - SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); - SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); + : renderer_(Screen::get()->getRenderer()), + texture_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), + backbuffer_(SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, param.game.width, param.game.height)), + text_(Resource::get()->getText("smb2")), + tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::STATIC)), + fade_(std::make_unique<Fade>()) { + // Configura las texturas + SDL_SetTextureBlendMode(backbuffer_, SDL_BLENDMODE_BLEND); + SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); - // Inicializa variables - Section::name = Section::Name::INSTRUCTIONS; - view_ = param.game.game_area.rect; + // Inicializa variables + Section::name = Section::Name::INSTRUCTIONS; + view_ = param.game.game_area.rect; - // Inicializa objetos - tiled_bg_->setColor(param.title.bg_color); - fade_->setColor(param.fade.color); - fade_->setType(FadeType::FULLSCREEN); - fade_->setPostDuration(param.fade.post_duration); - fade_->setMode(FadeMode::IN); - fade_->activate(); + // Inicializa objetos + tiled_bg_->setColor(param.title.bg_color); + fade_->setColor(param.fade.color); + fade_->setType(FadeType::FULLSCREEN); + fade_->setPostDuration(param.fade.post_duration); + fade_->setMode(FadeMode::IN); + fade_->activate(); - // Inicializa las líneas con un retraso progresivo de 50 ms - lines_ = initializeLines(256); + // Inicializa las líneas con un retraso progresivo de 50 ms + lines_ = initializeLines(256); - // Rellena la textura de texto - fillTexture(); + // Rellena la textura de texto + fillTexture(); - // Inicializa los sprites de los items - iniSprites(); + // Inicializa los sprites de los items + iniSprites(); } // Destructor -Instructions::~Instructions() -{ - item_textures_.clear(); - sprites_.clear(); +Instructions::~Instructions() { + item_textures_.clear(); + sprites_.clear(); - SDL_DestroyTexture(backbuffer_); - SDL_DestroyTexture(texture_); + SDL_DestroyTexture(backbuffer_); + SDL_DestroyTexture(texture_); } // Inicializa los sprites de los items -void Instructions::iniSprites() -{ - // Inicializa las texturas - item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png")); - item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png")); +void Instructions::iniSprites() { + // Inicializa las texturas + item_textures_.emplace_back(Resource::get()->getTexture("item_points1_disk.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_points2_gavina.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_points3_pacmar.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_clock.png")); + item_textures_.emplace_back(Resource::get()->getTexture("item_coffee.png")); - // Inicializa los sprites - for (int i = 0; i < (int)item_textures_.size(); ++i) - { - auto sprite = std::make_unique<Sprite>(item_textures_[i], 0, 0, param.game.item_size, param.game.item_size); - sprite->setPosition((SDL_FPoint){sprite_pos_.x, sprite_pos_.y + ((param.game.item_size + item_space_) * i)}); - sprites_.push_back(std::move(sprite)); - } + // Inicializa los sprites + for (int i = 0; i < (int)item_textures_.size(); ++i) { + auto sprite = std::make_unique<Sprite>(item_textures_[i], 0, 0, param.game.item_size, param.game.item_size); + sprite->setPosition((SDL_FPoint){sprite_pos_.x, sprite_pos_.y + ((param.game.item_size + item_space_) * i)}); + sprites_.push_back(std::move(sprite)); + } } // Actualiza los sprites -void Instructions::updateSprites() -{ - SDL_FRect src_rect = {0, 0, param.game.item_size, param.game.item_size}; +void Instructions::updateSprites() { + SDL_FRect src_rect = {0, 0, param.game.item_size, param.game.item_size}; - // Disquito - src_rect.y = param.game.item_size * (((counter_ + 12) / 36) % 2); - sprites_[0]->setSpriteClip(src_rect); + // Disquito + src_rect.y = param.game.item_size * (((counter_ + 12) / 36) % 2); + sprites_[0]->setSpriteClip(src_rect); - // Gavina - src_rect.y = param.game.item_size * (((counter_ + 9) / 36) % 2); - sprites_[1]->setSpriteClip(src_rect); + // Gavina + src_rect.y = param.game.item_size * (((counter_ + 9) / 36) % 2); + sprites_[1]->setSpriteClip(src_rect); - // Pacmar - src_rect.y = param.game.item_size * (((counter_ + 6) / 36) % 2); - sprites_[2]->setSpriteClip(src_rect); + // Pacmar + src_rect.y = param.game.item_size * (((counter_ + 6) / 36) % 2); + sprites_[2]->setSpriteClip(src_rect); - // Time Stopper - src_rect.y = param.game.item_size * (((counter_ + 3) / 36) % 2); - sprites_[3]->setSpriteClip(src_rect); + // Time Stopper + src_rect.y = param.game.item_size * (((counter_ + 3) / 36) % 2); + sprites_[3]->setSpriteClip(src_rect); - // Coffee - src_rect.y = param.game.item_size * (((counter_ + 0) / 36) % 2); - sprites_[4]->setSpriteClip(src_rect); + // Coffee + src_rect.y = param.game.item_size * (((counter_ + 0) / 36) % 2); + sprites_[4]->setSpriteClip(src_rect); } // Rellena la textura de texto -void Instructions::fillTexture() -{ - const int desp_x = param.game.item_size + 8; +void Instructions::fillTexture() { + const int desp_x = param.game.item_size + 8; - // Modifica el renderizador para pintar en la textura - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, texture_); + // Modifica el renderizador para pintar en la textura + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, texture_); - // Limpia la textura - SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); - SDL_RenderClear(renderer_); + // Limpia la textura + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); + SDL_RenderClear(renderer_); - // Constantes - constexpr int num_lines = 4; - constexpr int num_item_lines = 4; - constexpr int num_post_headers = 2; - constexpr int num_pre_headers = 1; + // Constantes + constexpr int num_lines = 4; + constexpr int num_item_lines = 4; + constexpr int num_post_headers = 2; + constexpr int num_pre_headers = 1; - constexpr int space_post_header = 20; - constexpr int space_pre_header = 28; - const int space_between_lines = text_->getCharacterSize() * 1.5f; - const int space_between_item_lines = param.game.item_size + item_space_; - const int space_new_paragraph = space_between_lines * 0.5f; + constexpr int space_post_header = 20; + constexpr int space_pre_header = 28; + const int space_between_lines = text_->getCharacterSize() * 1.5f; + const int space_between_item_lines = param.game.item_size + item_space_; + const int space_new_paragraph = space_between_lines * 0.5f; - const int size = (num_lines * space_between_lines) + (num_item_lines * space_between_item_lines) + (num_post_headers * space_post_header) + (num_pre_headers * space_pre_header) + (space_new_paragraph); - const int first_line = (param.game.height - size) / 2; + const int size = (num_lines * space_between_lines) + (num_item_lines * space_between_item_lines) + (num_post_headers * space_post_header) + (num_pre_headers * space_pre_header) + (space_new_paragraph); + const int first_line = (param.game.height - size) / 2; - // Calcula cual es el texto más largo de las descripciones de los items - int lenght = 0; - const std::array<std::string, 5> ITEM_DESCRIPTIONS = { - Lang::getText("[INSTRUCTIONS] 07"), - Lang::getText("[INSTRUCTIONS] 08"), - Lang::getText("[INSTRUCTIONS] 09"), - Lang::getText("[INSTRUCTIONS] 10"), - Lang::getText("[INSTRUCTIONS] 11")}; - for (const auto &desc : ITEM_DESCRIPTIONS) - { - const int l = text_->lenght(desc); - lenght = l > lenght ? l : lenght; - } - const int ANCHOR_ITEM = (param.game.width - (lenght + desp_x)) / 2; + // Calcula cual es el texto más largo de las descripciones de los items + int lenght = 0; + const std::array<std::string, 5> ITEM_DESCRIPTIONS = { + Lang::getText("[INSTRUCTIONS] 07"), + Lang::getText("[INSTRUCTIONS] 08"), + Lang::getText("[INSTRUCTIONS] 09"), + Lang::getText("[INSTRUCTIONS] 10"), + Lang::getText("[INSTRUCTIONS] 11")}; + for (const auto &desc : ITEM_DESCRIPTIONS) { + const int l = text_->lenght(desc); + lenght = l > lenght ? l : lenght; + } + const int ANCHOR_ITEM = (param.game.width - (lenght + desp_x)) / 2; - constexpr Color ORANGE_COLOR = Color(0XFF, 0X7A, 0X00); + constexpr Color ORANGE_COLOR = Color(0XFF, 0X7A, 0X00); - // Escribe el texto de las instrucciones - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, first_line, Lang::getText("[INSTRUCTIONS] 01"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); + // Escribe el texto de las instrucciones + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, first_line, Lang::getText("[INSTRUCTIONS] 01"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); - const int anchor1 = first_line + space_post_header; - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 0, Lang::getText("[INSTRUCTIONS] 02"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 1, Lang::getText("[INSTRUCTIONS] 03"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 2, Lang::getText("[INSTRUCTIONS] 04"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 3, Lang::getText("[INSTRUCTIONS] 05"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); + const int anchor1 = first_line + space_post_header; + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 0, Lang::getText("[INSTRUCTIONS] 02"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_between_lines * 1, Lang::getText("[INSTRUCTIONS] 03"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 2, Lang::getText("[INSTRUCTIONS] 04"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor1 + space_new_paragraph + space_between_lines * 3, Lang::getText("[INSTRUCTIONS] 05"), 1, NO_TEXT_COLOR, 1, SHADOW_TEXT_COLOR); - // Escribe el texto de los objetos y sus puntos - const int anchor2 = anchor1 + space_pre_header + space_new_paragraph + space_between_lines * 3; - text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor2, Lang::getText("[INSTRUCTIONS] 06"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); + // Escribe el texto de los objetos y sus puntos + const int anchor2 = anchor1 + space_pre_header + space_new_paragraph + space_between_lines * 3; + text_->writeDX(TEXT_CENTER | TEXT_COLOR | TEXT_SHADOW, param.game.game_area.center_x, anchor2, Lang::getText("[INSTRUCTIONS] 06"), 1, ORANGE_COLOR, 1, SHADOW_TEXT_COLOR); - const int anchor3 = anchor2 + space_post_header; - text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 0, Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR); - text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 1, Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR); - text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 2, Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR); - text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 3, Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR); - text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 4, Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR); + const int anchor3 = anchor2 + space_post_header; + text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 0, Lang::getText("[INSTRUCTIONS] 07"), SHADOW_TEXT_COLOR); + text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 1, Lang::getText("[INSTRUCTIONS] 08"), SHADOW_TEXT_COLOR); + text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 2, Lang::getText("[INSTRUCTIONS] 09"), SHADOW_TEXT_COLOR); + text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 3, Lang::getText("[INSTRUCTIONS] 10"), SHADOW_TEXT_COLOR); + text_->writeShadowed(ANCHOR_ITEM + desp_x, anchor3 + space_between_item_lines * 4, Lang::getText("[INSTRUCTIONS] 11"), SHADOW_TEXT_COLOR); - // Deja el renderizador como estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador como estaba + SDL_SetRenderTarget(renderer_, temp); - // Da valor a la variable - sprite_pos_.x = ANCHOR_ITEM; - sprite_pos_.y = anchor3 - ((param.game.item_size - text_->getCharacterSize()) / 2); + // Da valor a la variable + sprite_pos_.x = ANCHOR_ITEM; + sprite_pos_.y = anchor3 - ((param.game.item_size - text_->getCharacterSize()) / 2); } // Rellena el backbuffer -void Instructions::fillBackbuffer() -{ - // Modifica el renderizador para pintar en la textura - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, backbuffer_); +void Instructions::fillBackbuffer() { + // Modifica el renderizador para pintar en la textura + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, backbuffer_); - // Limpia la textura - SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); - SDL_RenderClear(renderer_); + // Limpia la textura + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); + SDL_RenderClear(renderer_); - // Coloca el texto de fondo - SDL_RenderTexture(renderer_, texture_, nullptr, nullptr); + // Coloca el texto de fondo + SDL_RenderTexture(renderer_, texture_, nullptr, nullptr); - // Dibuja los sprites - for (auto &sprite : sprites_) - { - sprite->render(); - } + // Dibuja los sprites + for (auto &sprite : sprites_) { + sprite->render(); + } - // Deja el renderizador como estaba - SDL_SetRenderTarget(renderer_, temp); + // Deja el renderizador como estaba + SDL_SetRenderTarget(renderer_, temp); } // Actualiza las variables -void Instructions::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - // Actualiza el contador de ticks - ticks_ = SDL_GetTicks(); +void Instructions::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + // Actualiza el contador de ticks + ticks_ = SDL_GetTicks(); - // Actualiza el objeto screen - Screen::get()->update(); + // Actualiza el objeto screen + Screen::get()->update(); - // Incrementa el contador - counter_++; + // Incrementa el contador + counter_++; - // Actualiza los sprites - updateSprites(); + // Actualiza los sprites + updateSprites(); - // Gestiona la textura con los graficos - updateBackbuffer(); + // Gestiona la textura con los graficos + updateBackbuffer(); - // Actualiza el mosaico de fondo - tiled_bg_->update(); + // Actualiza el mosaico de fondo + tiled_bg_->update(); - // Actualiza el objeto "fade" - fade_->update(); + // Actualiza el objeto "fade" + fade_->update(); - // Rellena el backbuffer - fillBackbuffer(); - } + // Rellena el backbuffer + fillBackbuffer(); + } } // Pinta en pantalla -void Instructions::render() -{ - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); +void Instructions::render() { + // Prepara para empezar a dibujar en la textura de juego + Screen::get()->start(); - // Limpia la pantalla - Screen::get()->clean(); + // Limpia la pantalla + Screen::get()->clean(); - // Dibuja el mosacico de fondo - tiled_bg_->render(); + // Dibuja el mosacico de fondo + tiled_bg_->render(); - // Copia la textura y el backbuffer al renderizador - if (view_.y == 0) - renderLines(renderer_, backbuffer_, lines_); - else - SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_); + // Copia la textura y el backbuffer al renderizador + if (view_.y == 0) + renderLines(renderer_, backbuffer_, lines_); + else + SDL_RenderTexture(renderer_, backbuffer_, nullptr, &view_); - fade_->render(); + fade_->render(); - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); + // Vuelca el contenido del renderizador en pantalla + Screen::get()->render(); } // Comprueba los eventos -void Instructions::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { - GlobalEvents::check(event); - } +void Instructions::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + GlobalEvents::check(event); + } } // Comprueba las entradas -void Instructions::checkInput() -{ - Input::get()->update(); - GlobalInputs::check(); +void Instructions::checkInput() { + Input::get()->update(); + GlobalInputs::check(); } // Bucle para la pantalla de instrucciones -void Instructions::run() -{ - Audio::get()->playMusic("title.ogg"); - while (Section::name == Section::Name::INSTRUCTIONS) - { - checkInput(); - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } +void Instructions::run() { + Audio::get()->playMusic("title.ogg"); + while (Section::name == Section::Name::INSTRUCTIONS) { + checkInput(); + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Método para inicializar las líneas -std::vector<Line> Instructions::initializeLines(int height) -{ - std::vector<Line> lines; - for (int y = 0; y < height; y++) - { - int direction = (y % 2 == 0) ? -1 : 1; // Pares a la izquierda, impares a la derecha - lines.emplace_back(y, 0.0f, direction); - } - return lines; +std::vector<Line> Instructions::initializeLines(int height) { + std::vector<Line> lines; + for (int y = 0; y < height; y++) { + int direction = (y % 2 == 0) ? -1 : 1; // Pares a la izquierda, impares a la derecha + lines.emplace_back(y, 0.0f, direction); + } + return lines; } // Método para mover las líneas con suavizado -bool Instructions::moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay) -{ - Uint32 current_time = SDL_GetTicks(); - bool all_lines_off_screen = true; +bool Instructions::moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay) { + Uint32 current_time = SDL_GetTicks(); + bool all_lines_off_screen = true; - for (auto &line : lines) - { - // Establecer startTime en el primer cuadro de animación - if (line.startTime == 0) - { - line.startTime = current_time + line.y * startDelay; - } + for (auto &line : lines) { + // Establecer startTime en el primer cuadro de animación + if (line.startTime == 0) { + line.startTime = current_time + line.y * startDelay; + } - float elapsed_time = (current_time - line.startTime) / 1000.0f; // Convertir a segundos - if (elapsed_time < 0) - { - all_lines_off_screen = false; // Si aún no se debe mover esta línea, no están todas fuera de pantalla - continue; - } - if (elapsed_time >= duration) - { - continue; // Si la línea ha salido de los límites, no la muevas más - } + float elapsed_time = (current_time - line.startTime) / 1000.0f; // Convertir a segundos + if (elapsed_time < 0) { + all_lines_off_screen = false; // Si aún no se debe mover esta línea, no están todas fuera de pantalla + continue; + } + if (elapsed_time >= duration) { + continue; // Si la línea ha salido de los límites, no la muevas más + } - float t = elapsed_time / duration; - float smooth_factor = easeInOutQuint(t); - line.x = line.direction * smooth_factor * width; - all_lines_off_screen = false; // Si alguna línea aún se está moviendo, no están todas fuera de pantalla - } + float t = elapsed_time / duration; + float smooth_factor = easeInOutQuint(t); + line.x = line.direction * smooth_factor * width; + all_lines_off_screen = false; // Si alguna línea aún se está moviendo, no están todas fuera de pantalla + } - return all_lines_off_screen; + return all_lines_off_screen; } // Método para renderizar las líneas -void Instructions::renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines) -{ - for (const auto &LINE : lines) - { - SDL_FRect srcRect = {0, static_cast<float>(LINE.y), 320, 1}; - SDL_FRect dstRect = {static_cast<float>(LINE.x), static_cast<float>(LINE.y), 320, 1}; - SDL_RenderTexture(renderer, texture, &srcRect, &dstRect); - } +void Instructions::renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines) { + for (const auto &LINE : lines) { + SDL_FRect srcRect = {0, static_cast<float>(LINE.y), 320, 1}; + SDL_FRect dstRect = {static_cast<float>(LINE.x), static_cast<float>(LINE.y), 320, 1}; + SDL_RenderTexture(renderer, texture, &srcRect, &dstRect); + } } // Gestiona la textura con los graficos -void Instructions::updateBackbuffer() -{ - // Establece la ventana del backbuffer - view_.y = std::max(0.0f, param.game.height - counter_ + 100); +void Instructions::updateBackbuffer() { + // Establece la ventana del backbuffer + view_.y = std::max(0.0f, param.game.height - counter_ + 100); - // Verifica si view_.y == 0 y gestiona el temporizador - if (view_.y == 0) - { - if (!start_delay_triggered_) - { - // Activa el temporizador si no ha sido activado - start_delay_triggered_ = true; - start_delay_time_ = SDL_GetTicks(); - } - else if (SDL_GetTicks() - start_delay_time_ >= 4000) - { - // Han pasado tres segundos, mover líneas - all_lines_off_screen_ = moveLines(lines_, 320, 1.0f, 5); - } - } + // Verifica si view_.y == 0 y gestiona el temporizador + if (view_.y == 0) { + if (!start_delay_triggered_) { + // Activa el temporizador si no ha sido activado + start_delay_triggered_ = true; + start_delay_time_ = SDL_GetTicks(); + } else if (SDL_GetTicks() - start_delay_time_ >= 4000) { + // Han pasado tres segundos, mover líneas + all_lines_off_screen_ = moveLines(lines_, 320, 1.0f, 5); + } + } - // Comprueba si el contador ha llegado al final - if (all_lines_off_screen_) - { - Section::name = Section::Name::TITLE; - Section::options = Section::Options::TITLE_1; - } + // Comprueba si el contador ha llegado al final + if (all_lines_off_screen_) { + Section::name = Section::Name::TITLE; + Section::options = Section::Options::TITLE_1; + } } \ No newline at end of file diff --git a/source/sections/instructions.h b/source/sections/instructions.h index 089f145..1e8a25c 100644 --- a/source/sections/instructions.h +++ b/source/sections/instructions.h @@ -1,8 +1,9 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_Texture, Uint32, SDL_Renderer, SDL_FPoint, SDL_FRect, Uint64 -#include <memory> // Para unique_ptr, shared_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_Texture, Uint32, SDL_Renderer, SDL_FPoint, SDL_FRect, Uint64 + +#include <memory> // Para unique_ptr, shared_ptr +#include <vector> // Para vector class Fade; class Sprite; @@ -11,78 +12,76 @@ class Texture; class TiledBG; /* - Esta clase gestiona un estado del programa. Se encarga de poner en pantalla - un texto explicativo para entender cómo se juega. + Esta clase gestiona un estado del programa. Se encarga de poner en pantalla + un texto explicativo para entender cómo se juega. - Además muestra algunos items y explica para qué sirven. + Además muestra algunos items y explica para qué sirven. - Utiliza dos texturas de apoyo, una con el texto ya escrito y otra donde se combina - tanto el texto de la primera textura como los sprites de los items. + Utiliza dos texturas de apoyo, una con el texto ya escrito y otra donde se combina + tanto el texto de la primera textura como los sprites de los items. - Finalmente, una ventana recorre la textura para dar el efecto de que todo se desplaza - por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto). + Finalmente, una ventana recorre la textura para dar el efecto de que todo se desplaza + por la pantalla sobre el mosaico de fondo (gestionado por el correspondiente objeto). */ // Estructura para almacenar información de línea animada -struct Line -{ - int y; // Coordenada Y de la línea - float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado) - int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha - Uint32 startTime; // Tiempo de inicio del movimiento +struct Line { + int y; // Coordenada Y de la línea + float x; // Coordenada X inicial (usamos float para mayor precisión en el suavizado) + int direction; // Dirección de movimiento: -1 para izquierda, 1 para derecha + Uint32 startTime; // Tiempo de inicio del movimiento - // Constructor de Line - Line(int y, float x, int direction) - : y(y), x(x), direction(direction), startTime(0) {} + // Constructor de Line + Line(int y, float x, int direction) + : y(y), x(x), direction(direction), startTime(0) {} }; // Clase Instructions -class Instructions -{ -public: - // Constructor - Instructions(); +class Instructions { + public: + // Constructor + Instructions(); - // Destructor - ~Instructions(); + // Destructor + ~Instructions(); - // Bucle principal - void run(); + // Bucle principal + void run(); -private: - // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - SDL_Texture *texture_; // Textura fija con el texto - SDL_Texture *backbuffer_; // Textura para usar como backbuffer + private: + // --- Objetos y punteros --- + SDL_Renderer *renderer_; // El renderizador de la ventana + SDL_Texture *texture_; // Textura fija con el texto + SDL_Texture *backbuffer_; // Textura para usar como backbuffer - std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items - std::vector<std::unique_ptr<Sprite>> sprites_; // Vector con los sprites de los items - std::shared_ptr<Text> text_; // Objeto para escribir texto - std::unique_ptr<TiledBG> tiled_bg_; // Objeto para dibujar el mosaico animado de fondo - std::unique_ptr<Fade> fade_; // Objeto para renderizar fades + std::vector<std::shared_ptr<Texture>> item_textures_; // Vector con las texturas de los items + std::vector<std::unique_ptr<Sprite>> sprites_; // Vector con los sprites de los items + std::shared_ptr<Text> text_; // Objeto para escribir texto + std::unique_ptr<TiledBG> tiled_bg_; // Objeto para dibujar el mosaico animado de fondo + std::unique_ptr<Fade> fade_; // Objeto para renderizar fades - // --- Variables --- - int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones - Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa - SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla - SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista - float item_space_ = 2.0; // Espacio entre los items en pantalla - std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla - bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla - Uint32 start_delay_time_ = 0; // Tiempo de inicio del retraso para mover las líneas - bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado + // --- Variables --- + int counter_ = 0; // Contador para manejar el progreso en la pantalla de instrucciones + Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa + SDL_FRect view_; // Vista del backbuffer que se va a mostrar por pantalla + SDL_FPoint sprite_pos_ = {0, 0}; // Posición del primer sprite en la lista + float item_space_ = 2.0; // Espacio entre los items en pantalla + std::vector<Line> lines_; // Vector que contiene las líneas animadas en la pantalla + bool all_lines_off_screen_ = false; // Indica si todas las líneas han salido de la pantalla + Uint32 start_delay_time_ = 0; // Tiempo de inicio del retraso para mover las líneas + bool start_delay_triggered_ = false; // Bandera para determinar si el retraso ha comenzado - // --- Métodos internos --- - void update(); // Actualiza las variables - void render(); // Pinta en pantalla - void checkEvents(); // Comprueba los eventos - void checkInput(); // Comprueba las entradas - void fillTexture(); // Rellena la textura de texto - void fillBackbuffer(); // Rellena el backbuffer - void iniSprites(); // Inicializa los sprites de los items - void updateSprites(); // Actualiza los sprites - std::vector<Line> initializeLines(int height); // Inicializa las líneas animadas - bool moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay); // Mueve las líneas - void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas - void updateBackbuffer(); // Gestiona la textura con los gráficos + // --- Métodos internos --- + void update(); // Actualiza las variables + void render(); // Pinta en pantalla + void checkEvents(); // Comprueba los eventos + void checkInput(); // Comprueba las entradas + void fillTexture(); // Rellena la textura de texto + void fillBackbuffer(); // Rellena el backbuffer + void iniSprites(); // Inicializa los sprites de los items + void updateSprites(); // Actualiza los sprites + std::vector<Line> initializeLines(int height); // Inicializa las líneas animadas + bool moveLines(std::vector<Line> &lines, int width, float duration, Uint32 startDelay); // Mueve las líneas + void renderLines(SDL_Renderer *renderer, SDL_Texture *texture, const std::vector<Line> &lines); // Renderiza las líneas + void updateBackbuffer(); // Gestiona la textura con los gráficos }; \ No newline at end of file diff --git a/source/sections/intro.cpp b/source/sections/intro.cpp index 40f5b68..85c1a79 100644 --- a/source/sections/intro.cpp +++ b/source/sections/intro.cpp @@ -1,637 +1,578 @@ #include "intro.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL... -#include <array> // Para array -#include <functional> // Para function -#include <iostream> // Para basic_ostream, basic_ostream::operator<< -#include <string> // Para basic_string, char_traits, string -#include <utility> // Para move +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_SetRenderDrawColor, SDL... -#include "audio.h" // Para Audio -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input -#include "lang.h" // Para getText -#include "param.h" // Para Param, param, ParamGame, ParamIntro, Para... -#include "path_sprite.h" // Para PathSprite, PathType -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options -#include "text.h" // Para Text -#include "texture.h" // Para Texture -#include "tiled_bg.h" // Para TiledBG, TiledBGMode -#include "utils.h" // Para Color, Zone, easeInOutExpo, easeInElastic -#include "writer.h" // Para Writer +#include <array> // Para array +#include <functional> // Para function +#include <iostream> // Para basic_ostream, basic_ostream::operator<< +#include <string> // Para basic_string, char_traits, string +#include <utility> // Para move + +#include "audio.h" // Para Audio +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input +#include "lang.h" // Para getText +#include "param.h" // Para Param, param, ParamGame, ParamIntro, Para... +#include "path_sprite.h" // Para PathSprite, PathType +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options +#include "text.h" // Para Text +#include "texture.h" // Para Texture +#include "tiled_bg.h" // Para TiledBG, TiledBGMode +#include "utils.h" // Para Color, Zone, easeInOutExpo, easeInElastic +#include "writer.h" // Para Writer #ifdef DEBUG -#include <iomanip> // Para operator<<, setfill, setw +#include <iomanip> // Para operator<<, setfill, setw #endif // Constructor Intro::Intro() - : tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)) -{ - // Inicializa variables - Section::name = Section::Name::INTRO; - Section::options = Section::Options::NONE; + : tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::DIAGONAL)) { + // Inicializa variables + Section::name = Section::Name::INTRO; + Section::options = Section::Options::NONE; - // Inicializa las imagens - initSprites(); + // Inicializa las imagens + initSprites(); - // Inicializa los textos - initTexts(); + // Inicializa los textos + initTexts(); - // Configura el fondo - tiled_bg_->setSpeed(0.3f); - tiled_bg_->setColor(bg_color_); + // Configura el fondo + tiled_bg_->setSpeed(0.3f); + tiled_bg_->setColor(bg_color_); } // Comprueba los eventos -void Intro::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { +void Intro::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { #ifdef DEBUG - if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1) - { - static Color color = param.intro.bg_color; - switch (event.key.key) - { - case SDLK_A: - if (color.r < 255) - ++color.r; - break; + if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1) { + static Color color = param.intro.bg_color; + switch (event.key.key) { + case SDLK_A: + if (color.r < 255) + ++color.r; + break; - case SDLK_Z: - if (color.r > 0) - --color.r; - break; + case SDLK_Z: + if (color.r > 0) + --color.r; + break; - case SDLK_S: - if (color.g < 255) - ++color.g; - break; + case SDLK_S: + if (color.g < 255) + ++color.g; + break; - case SDLK_X: - if (color.g > 0) - --color.g; - break; + case SDLK_X: + if (color.g > 0) + --color.g; + break; - case SDLK_D: - if (color.b < 255) - ++color.b; - break; + case SDLK_D: + if (color.b < 255) + ++color.b; + break; - case SDLK_C: - if (color.b > 0) - --color.b; - break; + case SDLK_C: + if (color.b > 0) + --color.b; + break; - case SDLK_F: - if (color.r < 255) - ++color.r; - if (color.g < 255) - ++color.g; - if (color.b < 255) - ++color.b; - break; + case SDLK_F: + if (color.r < 255) + ++color.r; + if (color.g < 255) + ++color.g; + if (color.b < 255) + ++color.b; + break; - case SDLK_V: - if (color.r > 0) - --color.r; - if (color.g > 0) - --color.g; - if (color.b > 0) - --color.b; - break; + case SDLK_V: + if (color.r > 0) + --color.r; + if (color.g > 0) + --color.g; + if (color.b > 0) + --color.b; + break; - default: - break; - } - tiled_bg_->setColor(color); - std::cout << "#" - << std::hex << std::setw(2) << std::setfill('0') << (int)color.r - << std::setw(2) << std::setfill('0') << (int)color.g - << std::setw(2) << std::setfill('0') << (int)color.b - << std::endl; - } + default: + break; + } + tiled_bg_->setColor(color); + std::cout << "#" + << std::hex << std::setw(2) << std::setfill('0') << (int)color.r + << std::setw(2) << std::setfill('0') << (int)color.g + << std::setw(2) << std::setfill('0') << (int)color.b + << std::endl; + } #endif - GlobalEvents::check(event); - } + GlobalEvents::check(event); + } } // Comprueba las entradas -void Intro::checkInput() -{ - Input::get()->update(); - GlobalInputs::check(); +void Intro::checkInput() { + Input::get()->update(); + GlobalInputs::check(); } // Actualiza las escenas de la intro -void Intro::updateScenes() -{ - switch (scene_) - { - case 0: - { - // Primera imagen - UPV - card_sprites_.at(0)->enable(); - shadow_sprites_.at(0)->enable(); +void Intro::updateScenes() { + switch (scene_) { + case 0: { + // Primera imagen - UPV + card_sprites_.at(0)->enable(); + shadow_sprites_.at(0)->enable(); - // Primer texto de la primera imagen - if (card_sprites_.at(0)->hasFinished() && !texts_.at(0)->hasFinished()) - { - texts_.at(0)->setEnabled(true); - } + // Primer texto de la primera imagen + if (card_sprites_.at(0)->hasFinished() && !texts_.at(0)->hasFinished()) { + texts_.at(0)->setEnabled(true); + } - // Segundo texto de la primera imagen - if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) - { - texts_.at(0)->setEnabled(false); - texts_.at(1)->setEnabled(true); - } + // Segundo texto de la primera imagen + if (texts_.at(0)->hasFinished() && !texts_.at(1)->hasFinished()) { + texts_.at(0)->setEnabled(false); + texts_.at(1)->setEnabled(true); + } - // Tercer texto de la primera imagen - if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) - { - texts_.at(1)->setEnabled(false); - texts_.at(2)->setEnabled(true); - } + // Tercer texto de la primera imagen + if (texts_.at(1)->hasFinished() && !texts_.at(2)->hasFinished()) { + texts_.at(1)->setEnabled(false); + texts_.at(2)->setEnabled(true); + } - // Fin de la primera escena - if (texts_.at(2)->hasFinished()) - { - texts_.at(2)->setEnabled(false); - scene_++; - } - break; - } + // Fin de la primera escena + if (texts_.at(2)->hasFinished()) { + texts_.at(2)->setEnabled(false); + scene_++; + } + break; + } - case 1: - { - // Segunda imagen - Máquina - card_sprites_.at(1)->enable(); - shadow_sprites_.at(1)->enable(); + case 1: { + // Segunda imagen - Máquina + card_sprites_.at(1)->enable(); + shadow_sprites_.at(1)->enable(); - // Primer texto de la segunda imagen - if (card_sprites_.at(1)->hasFinished() && !texts_.at(3)->hasFinished()) - { - texts_.at(3)->setEnabled(true); - } + // Primer texto de la segunda imagen + if (card_sprites_.at(1)->hasFinished() && !texts_.at(3)->hasFinished()) { + texts_.at(3)->setEnabled(true); + } - // Fin de la segunda escena - if (texts_.at(3)->hasFinished()) - { - texts_.at(3)->setEnabled(false); - scene_++; - } - break; - } + // Fin de la segunda escena + if (texts_.at(3)->hasFinished()) { + texts_.at(3)->setEnabled(false); + scene_++; + } + break; + } - case 2: - { - // Tercera imagen junto con primer texto - GRITO - if (!texts_.at(4)->hasFinished()) - { - card_sprites_.at(2)->enable(); - shadow_sprites_.at(2)->enable(); + case 2: { + // Tercera imagen junto con primer texto - GRITO + if (!texts_.at(4)->hasFinished()) { + card_sprites_.at(2)->enable(); + shadow_sprites_.at(2)->enable(); - texts_.at(4)->setEnabled(true); - } + texts_.at(4)->setEnabled(true); + } - // Fin de la tercera escena - if (card_sprites_.at(2)->hasFinished() && texts_.at(4)->hasFinished()) - { - texts_.at(4)->setEnabled(false); - scene_++; - } - break; - } + // Fin de la tercera escena + if (card_sprites_.at(2)->hasFinished() && texts_.at(4)->hasFinished()) { + texts_.at(4)->setEnabled(false); + scene_++; + } + break; + } - case 3: - { - // Cuarta imagen junto con primer texto - Reflexión - card_sprites_.at(3)->enable(); - shadow_sprites_.at(3)->enable(); + case 3: { + // Cuarta imagen junto con primer texto - Reflexión + card_sprites_.at(3)->enable(); + shadow_sprites_.at(3)->enable(); - if (!texts_.at(5)->hasFinished()) - { - texts_.at(5)->setEnabled(true); - } + if (!texts_.at(5)->hasFinished()) { + texts_.at(5)->setEnabled(true); + } - // Segundo texto de la cuarta imagen - if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) - { - texts_.at(5)->setEnabled(false); - texts_.at(6)->setEnabled(true); - } + // Segundo texto de la cuarta imagen + if (texts_.at(5)->hasFinished() && !texts_.at(6)->hasFinished()) { + texts_.at(5)->setEnabled(false); + texts_.at(6)->setEnabled(true); + } - // Fin de la cuarta escena - if (card_sprites_.at(3)->hasFinished() && texts_.at(6)->hasFinished()) - { - texts_.at(6)->setEnabled(false); - scene_++; - } - break; - } + // Fin de la cuarta escena + if (card_sprites_.at(3)->hasFinished() && texts_.at(6)->hasFinished()) { + texts_.at(6)->setEnabled(false); + scene_++; + } + break; + } - case 4: - { - // Quinta imagen - Patada - card_sprites_.at(4)->enable(); - shadow_sprites_.at(4)->enable(); + case 4: { + // Quinta imagen - Patada + card_sprites_.at(4)->enable(); + shadow_sprites_.at(4)->enable(); - // Primer texto de la quinta imagen - if (!texts_.at(7)->hasFinished()) - { - texts_.at(7)->setEnabled(true); - } + // Primer texto de la quinta imagen + if (!texts_.at(7)->hasFinished()) { + texts_.at(7)->setEnabled(true); + } - // Fin de la quinta escena - if (card_sprites_.at(4)->hasFinished() && texts_.at(7)->hasFinished()) - { - texts_.at(7)->setEnabled(false); - scene_++; - } - break; - } + // Fin de la quinta escena + if (card_sprites_.at(4)->hasFinished() && texts_.at(7)->hasFinished()) { + texts_.at(7)->setEnabled(false); + scene_++; + } + break; + } - case 5: - { - // Sexta imagen junto con texto - Globos de café - card_sprites_.at(5)->enable(); - shadow_sprites_.at(5)->enable(); + case 5: { + // Sexta imagen junto con texto - Globos de café + card_sprites_.at(5)->enable(); + shadow_sprites_.at(5)->enable(); - if (!texts_.at(8)->hasFinished()) - { - texts_.at(8)->setEnabled(true); - } + if (!texts_.at(8)->hasFinished()) { + texts_.at(8)->setEnabled(true); + } - // Acaba el último texto - if (texts_.at(8)->hasFinished()) - { - texts_.at(8)->setEnabled(false); - } + // Acaba el último texto + if (texts_.at(8)->hasFinished()) { + texts_.at(8)->setEnabled(false); + } - // Acaba la ultima imagen - if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) - { - state_ = IntroState::POST; - state_start_time_ = SDL_GetTicks(); - } - break; - } + // Acaba la ultima imagen + if (card_sprites_.at(5)->hasFinished() && texts_.at(8)->hasFinished()) { + state_ = IntroState::POST; + state_start_time_ = SDL_GetTicks(); + } + break; + } - default: - break; - } + default: + break; + } } // Actualiza las variables del objeto -void Intro::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - // Actualiza el contador de ticks - ticks_ = SDL_GetTicks(); +void Intro::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + // Actualiza el contador de ticks + ticks_ = SDL_GetTicks(); - // Actualiza el fondo - tiled_bg_->update(); + // Actualiza el fondo + tiled_bg_->update(); - switch (state_) - { - case IntroState::SCENES: - updateSprites(); - updateTexts(); - updateScenes(); - break; + switch (state_) { + case IntroState::SCENES: + updateSprites(); + updateTexts(); + updateScenes(); + break; - case IntroState::POST: - updatePostState(); - break; - } + case IntroState::POST: + updatePostState(); + break; + } - // Actualiza el objeto screen - Screen::get()->update(); - } + // Actualiza el objeto screen + Screen::get()->update(); + } } // Dibuja el objeto en pantalla -void Intro::render() -{ - // Prepara para empezar a dibujar en la textura de juego - Screen::get()->start(); +void Intro::render() { + // Prepara para empezar a dibujar en la textura de juego + Screen::get()->start(); - // Limpia la pantalla - Screen::get()->clean(); + // Limpia la pantalla + Screen::get()->clean(); - // Dibuja el fondo - tiled_bg_->render(); + // Dibuja el fondo + tiled_bg_->render(); - switch (state_) - { - case IntroState::SCENES: - { - renderTextRect(); - renderSprites(); - renderTexts(); - break; - } - case IntroState::POST: - break; - } + switch (state_) { + case IntroState::SCENES: { + renderTextRect(); + renderSprites(); + renderTexts(); + break; + } + case IntroState::POST: + break; + } - // Vuelca el contenido del renderizador en pantalla - Screen::get()->render(); + // Vuelca el contenido del renderizador en pantalla + Screen::get()->render(); } // Bucle principal -void Intro::run() -{ - Audio::get()->playMusic("intro.ogg", 0); - while (Section::name == Section::Name::INTRO) - { - checkInput(); - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } +void Intro::run() { + Audio::get()->playMusic("intro.ogg", 0); + while (Section::name == Section::Name::INTRO) { + checkInput(); + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Inicializa las imagens -void Intro::initSprites() -{ - // Listado de imagenes a usar - const std::array<std::string, 6> TEXTURE_LIST = { - "intro1.png", - "intro2.png", - "intro3.png", - "intro4.png", - "intro5.png", - "intro6.png"}; +void Intro::initSprites() { + // Listado de imagenes a usar + const std::array<std::string, 6> TEXTURE_LIST = { + "intro1.png", + "intro2.png", + "intro3.png", + "intro4.png", + "intro5.png", + "intro6.png"}; - // Constantes - constexpr int TOTAL_SPRITES = TEXTURE_LIST.size(); - const float BORDER = 2.0f; + // Constantes + constexpr int TOTAL_SPRITES = TEXTURE_LIST.size(); + const float BORDER = 2.0f; - auto texture = Resource::get()->getTexture(TEXTURE_LIST.front()); - const float CARD_WIDTH = texture->getWidth() + (BORDER * 2); - const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2); + auto texture = Resource::get()->getTexture(TEXTURE_LIST.front()); + const float CARD_WIDTH = texture->getWidth() + (BORDER * 2); + const float CARD_HEIGHT = texture->getHeight() + (BORDER * 2); - // Crea las texturas para las tarjetas - std::vector<std::shared_ptr<Texture>> card_textures; + // Crea las texturas para las tarjetas + std::vector<std::shared_ptr<Texture>> card_textures; - for (int i = 0; i < TOTAL_SPRITES; ++i) - { - // Crea la textura - auto card_texture = std::make_shared<Texture>(Screen::get()->getRenderer()); - card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); - card_texture->setBlendMode(SDL_BLENDMODE_BLEND); + for (int i = 0; i < TOTAL_SPRITES; ++i) { + // Crea la textura + auto card_texture = std::make_shared<Texture>(Screen::get()->getRenderer()); + card_texture->createBlank(CARD_WIDTH, CARD_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); + card_texture->setBlendMode(SDL_BLENDMODE_BLEND); - // Apuntamos el renderizador a la textura - auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); - card_texture->setAsRenderTarget(Screen::get()->getRenderer()); + // Apuntamos el renderizador a la textura + auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); + card_texture->setAsRenderTarget(Screen::get()->getRenderer()); - // Limpia la textura - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); - SDL_RenderClear(Screen::get()->getRenderer()); + // Limpia la textura + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); + SDL_RenderClear(Screen::get()->getRenderer()); - // Pone color en el marco de la textura - auto color = param.intro.card_color; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a); - SDL_FRect rect1 = {1, 0, CARD_WIDTH - 2, CARD_HEIGHT}; - SDL_FRect rect2 = {0, 1, CARD_WIDTH, CARD_HEIGHT - 2}; - SDL_RenderRect(Screen::get()->getRenderer(), &rect1); - SDL_RenderRect(Screen::get()->getRenderer(), &rect2); + // Pone color en el marco de la textura + auto color = param.intro.card_color; + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, color.a); + SDL_FRect rect1 = {1, 0, CARD_WIDTH - 2, CARD_HEIGHT}; + SDL_FRect rect2 = {0, 1, CARD_WIDTH, CARD_HEIGHT - 2}; + SDL_RenderRect(Screen::get()->getRenderer(), &rect1); + SDL_RenderRect(Screen::get()->getRenderer(), &rect2); - // Copia la textura con la imagen dentro del marco - SDL_FRect dest = {BORDER, BORDER, CARD_WIDTH - (BORDER * 2), CARD_HEIGHT - (BORDER * 2)}; - SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest); + // Copia la textura con la imagen dentro del marco + SDL_FRect dest = {BORDER, BORDER, CARD_WIDTH - (BORDER * 2), CARD_HEIGHT - (BORDER * 2)}; + SDL_RenderTexture(Screen::get()->getRenderer(), Resource::get()->getTexture(TEXTURE_LIST.at(i))->getSDLTexture(), nullptr, &dest); - // Deja el renderizador como estaba y añade la textura a la lista - SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); - card_textures.push_back(card_texture); - } + // Deja el renderizador como estaba y añade la textura a la lista + SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); + card_textures.push_back(card_texture); + } - // Inicializa los sprites para las tarjetas - for (int i = 0; i < TOTAL_SPRITES; ++i) - { - auto sprite = std::make_unique<PathSprite>(card_textures.at(i)); - sprite->setWidth(CARD_WIDTH); - sprite->setHeight(CARD_HEIGHT); - sprite->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT); - card_sprites_.push_back(std::move(sprite)); - } + // Inicializa los sprites para las tarjetas + for (int i = 0; i < TOTAL_SPRITES; ++i) { + auto sprite = std::make_unique<PathSprite>(card_textures.at(i)); + sprite->setWidth(CARD_WIDTH); + sprite->setHeight(CARD_HEIGHT); + sprite->setSpriteClip(0, 0, CARD_WIDTH, CARD_HEIGHT); + card_sprites_.push_back(std::move(sprite)); + } - const float X_DEST = param.game.game_area.center_x - CARD_WIDTH / 2; - const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4); + const float X_DEST = param.game.game_area.center_x - CARD_WIDTH / 2; + const float Y_DEST = param.game.game_area.first_quarter_y - (CARD_HEIGHT / 4); - card_sprites_.at(0)->addPath(-CARD_WIDTH - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0); - card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeOutBounce, 0); - card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 40, easeOutQuint, 0); - card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, 300, easeInOutExpo, 0); - card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 70, easeOutElastic, 0); - card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 250, easeOutQuad, 450); - card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, 80, easeInElastic, 0); + card_sprites_.at(0)->addPath(-CARD_WIDTH - 10, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeInOutExpo, 0); + card_sprites_.at(1)->addPath(param.game.width, X_DEST, PathType::HORIZONTAL, Y_DEST, 100, easeOutBounce, 0); + card_sprites_.at(2)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 40, easeOutQuint, 0); + card_sprites_.at(3)->addPath(param.game.height, Y_DEST, PathType::VERTICAL, X_DEST, 300, easeInOutExpo, 0); + card_sprites_.at(4)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 70, easeOutElastic, 0); + card_sprites_.at(5)->addPath(-CARD_HEIGHT, Y_DEST, PathType::VERTICAL, X_DEST, 250, easeOutQuad, 450); + card_sprites_.at(5)->addPath(X_DEST, -CARD_WIDTH, PathType::HORIZONTAL, Y_DEST, 80, easeInElastic, 0); - // Constantes - const float DESP = 8; - const float SHADOW_SPRITE_WIDTH = CARD_WIDTH; - const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT; + // Constantes + const float DESP = 8; + const float SHADOW_SPRITE_WIDTH = CARD_WIDTH; + const float SHADOW_SPRITE_HEIGHT = CARD_HEIGHT; - // Crea la textura para las sombras de las tarjetas - auto shadow_texture = std::make_shared<Texture>(Screen::get()->getRenderer()); - shadow_texture->createBlank(SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); - shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND); + // Crea la textura para las sombras de las tarjetas + auto shadow_texture = std::make_shared<Texture>(Screen::get()->getRenderer()); + shadow_texture->createBlank(SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); + shadow_texture->setBlendMode(SDL_BLENDMODE_BLEND); - // Apuntamos el renderizador a la textura - auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); - shadow_texture->setAsRenderTarget(Screen::get()->getRenderer()); + // Apuntamos el renderizador a la textura + auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); + shadow_texture->setAsRenderTarget(Screen::get()->getRenderer()); - // Limpia la textura - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); - SDL_RenderClear(Screen::get()->getRenderer()); + // Limpia la textura + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 0); + SDL_RenderClear(Screen::get()->getRenderer()); - // Dibuja la sombra sobre la textura - auto color = param.intro.shadow_color; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, 255); - SDL_FRect rect1 = {1, 0, SHADOW_SPRITE_WIDTH - 2, SHADOW_SPRITE_HEIGHT}; - SDL_FRect rect2 = {0, 1, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT - 2}; - SDL_RenderFillRect(Screen::get()->getRenderer(), &rect1); - SDL_RenderFillRect(Screen::get()->getRenderer(), &rect2); + // Dibuja la sombra sobre la textura + auto color = param.intro.shadow_color; + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), color.r, color.g, color.b, 255); + SDL_FRect rect1 = {1, 0, SHADOW_SPRITE_WIDTH - 2, SHADOW_SPRITE_HEIGHT}; + SDL_FRect rect2 = {0, 1, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT - 2}; + SDL_RenderFillRect(Screen::get()->getRenderer(), &rect1); + SDL_RenderFillRect(Screen::get()->getRenderer(), &rect2); - // Deja el renderizador como estaba y añade la textura a la lista - SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); + // Deja el renderizador como estaba y añade la textura a la lista + SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); - // Inicializa los sprites para la sombras usando la texturas con la sombra - for (int i = 0; i < TOTAL_SPRITES; ++i) - { - auto color = param.intro.shadow_color; - auto sprite = std::make_unique<PathSprite>(shadow_texture); - sprite->setWidth(SHADOW_SPRITE_WIDTH); - sprite->setHeight(SHADOW_SPRITE_HEIGHT); - sprite->setSpriteClip(0, 0, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT); - sprite->getTexture()->setAlpha(color.a); - shadow_sprites_.push_back(std::move(sprite)); - } + // Inicializa los sprites para la sombras usando la texturas con la sombra + for (int i = 0; i < TOTAL_SPRITES; ++i) { + auto color = param.intro.shadow_color; + auto sprite = std::make_unique<PathSprite>(shadow_texture); + sprite->setWidth(SHADOW_SPRITE_WIDTH); + sprite->setHeight(SHADOW_SPRITE_HEIGHT); + sprite->setSpriteClip(0, 0, SHADOW_SPRITE_WIDTH, SHADOW_SPRITE_HEIGHT); + sprite->getTexture()->setAlpha(color.a); + shadow_sprites_.push_back(std::move(sprite)); + } - const float S_X_DEST = X_DEST + DESP; - const float S_Y_DEST = Y_DEST + DESP; + const float S_X_DEST = X_DEST + DESP; + const float S_Y_DEST = Y_DEST + DESP; - shadow_sprites_.at(0)->addPath(param.game.height + 10, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeInOutExpo, 0); - shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeOutBounce, 0); - shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 40, easeOutQuint, 0); - shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 300, easeInOutExpo, 0); - shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 70, easeOutElastic, 0); - shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 250, easeOutQuad, 450); - shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, 80, easeInElastic, 0); + shadow_sprites_.at(0)->addPath(param.game.height + 10, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeInOutExpo, 0); + shadow_sprites_.at(1)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 100, easeOutBounce, 0); + shadow_sprites_.at(2)->addPath(-SHADOW_SPRITE_WIDTH, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 40, easeOutQuint, 0); + shadow_sprites_.at(3)->addPath(-SHADOW_SPRITE_HEIGHT, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 300, easeInOutExpo, 0); + shadow_sprites_.at(4)->addPath(param.game.height, S_Y_DEST, PathType::VERTICAL, S_X_DEST, 70, easeOutElastic, 0); + shadow_sprites_.at(5)->addPath(param.game.width, S_X_DEST, PathType::HORIZONTAL, S_Y_DEST, 250, easeOutQuad, 450); + shadow_sprites_.at(5)->addPath(S_X_DEST, param.game.width, PathType::HORIZONTAL, S_Y_DEST, 80, easeInElastic, 0); } // Inicializa los textos -void Intro::initTexts() -{ - constexpr int TOTAL_TEXTS = 9; - for (int i = 0; i < TOTAL_TEXTS; ++i) - { - auto w = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal")); - w->setPosX(0); - w->setPosY(param.game.height - param.intro.text_distance_from_bottom); - w->setKerning(-2); - w->setEnabled(false); - w->setFinishedCounter(180); - texts_.push_back(std::move(w)); - } +void Intro::initTexts() { + constexpr int TOTAL_TEXTS = 9; + for (int i = 0; i < TOTAL_TEXTS; ++i) { + auto w = std::make_unique<Writer>(Resource::get()->getText("04b_25_metal")); + w->setPosX(0); + w->setPosY(param.game.height - param.intro.text_distance_from_bottom); + w->setKerning(-2); + w->setEnabled(false); + w->setFinishedCounter(180); + texts_.push_back(std::move(w)); + } - // Un dia qualsevol de l'any 2000 - texts_.at(0)->setCaption(Lang::getText("[INTRO] 1")); - texts_.at(0)->setSpeed(8); + // Un dia qualsevol de l'any 2000 + texts_.at(0)->setCaption(Lang::getText("[INTRO] 1")); + texts_.at(0)->setSpeed(8); - // Tot esta tranquil a la UPV - texts_.at(1)->setCaption(Lang::getText("[INTRO] 2")); - texts_.at(1)->setSpeed(8); + // Tot esta tranquil a la UPV + texts_.at(1)->setCaption(Lang::getText("[INTRO] 2")); + texts_.at(1)->setSpeed(8); - // Fins que un desaprensiu... - texts_.at(2)->setCaption(Lang::getText("[INTRO] 3")); - texts_.at(2)->setSpeed(12); + // Fins que un desaprensiu... + texts_.at(2)->setCaption(Lang::getText("[INTRO] 3")); + texts_.at(2)->setSpeed(12); - // HEY! ME ANE A FERME UN CORTAET... - texts_.at(3)->setCaption(Lang::getText("[INTRO] 4")); - texts_.at(3)->setSpeed(8); + // HEY! ME ANE A FERME UN CORTAET... + texts_.at(3)->setCaption(Lang::getText("[INTRO] 4")); + texts_.at(3)->setSpeed(8); - // UAAAAAAAAAAAAA!!! - texts_.at(4)->setCaption(Lang::getText("[INTRO] 5")); - texts_.at(4)->setSpeed(1); + // UAAAAAAAAAAAAA!!! + texts_.at(4)->setCaption(Lang::getText("[INTRO] 5")); + texts_.at(4)->setSpeed(1); - // Espera un moment... - texts_.at(5)->setCaption(Lang::getText("[INTRO] 6")); - texts_.at(5)->setSpeed(16); + // Espera un moment... + texts_.at(5)->setCaption(Lang::getText("[INTRO] 6")); + texts_.at(5)->setSpeed(16); - // Si resulta que no tinc solt! - texts_.at(6)->setCaption(Lang::getText("[INTRO] 7")); - texts_.at(6)->setSpeed(2); + // Si resulta que no tinc solt! + texts_.at(6)->setCaption(Lang::getText("[INTRO] 7")); + texts_.at(6)->setSpeed(2); - // MERDA DE MAQUINA! - texts_.at(7)->setCaption(Lang::getText("[INTRO] 8")); - texts_.at(7)->setSpeed(3); + // MERDA DE MAQUINA! + texts_.at(7)->setCaption(Lang::getText("[INTRO] 8")); + texts_.at(7)->setSpeed(3); - // Blop... blop... blop... - texts_.at(8)->setCaption(Lang::getText("[INTRO] 9")); - texts_.at(8)->setSpeed(20); + // Blop... blop... blop... + texts_.at(8)->setCaption(Lang::getText("[INTRO] 9")); + texts_.at(8)->setSpeed(20); - for (auto &text : texts_) - { - text->center(param.game.game_area.center_x); - } + for (auto &text : texts_) { + text->center(param.game.game_area.center_x); + } } // Actualiza los sprites -void Intro::updateSprites() -{ - for (auto &sprite : card_sprites_) - { - sprite->update(); - } +void Intro::updateSprites() { + for (auto &sprite : card_sprites_) { + sprite->update(); + } - for (auto &sprite : shadow_sprites_) - { - sprite->update(); - } + for (auto &sprite : shadow_sprites_) { + sprite->update(); + } } // Actualiza los textos -void Intro::updateTexts() -{ - for (auto &text : texts_) - { - text->update(); - } +void Intro::updateTexts() { + for (auto &text : texts_) { + text->update(); + } } // Dibuja los sprites -void Intro::renderSprites() -{ - shadow_sprites_.at(scene_)->render(); - card_sprites_.at(scene_)->render(); +void Intro::renderSprites() { + shadow_sprites_.at(scene_)->render(); + card_sprites_.at(scene_)->render(); } // Dibuja los textos -void Intro::renderTexts() -{ - for (const auto &text : texts_) - { - text->render(); - } +void Intro::renderTexts() { + for (const auto &text : texts_) { + text->render(); + } } // Actualiza el estado POST -void Intro::updatePostState() -{ - const Uint32 ELAPSED_TIME = SDL_GetTicks() - state_start_time_; +void Intro::updatePostState() { + const Uint32 ELAPSED_TIME = SDL_GetTicks() - state_start_time_; - switch (post_state_) - { - case IntroPostState::STOP_BG: - // EVENTO: Detiene el fondo después de 1 segundo - if (ELAPSED_TIME >= 1000) - { - tiled_bg_->stopGracefully(); + switch (post_state_) { + case IntroPostState::STOP_BG: + // EVENTO: Detiene el fondo después de 1 segundo + if (ELAPSED_TIME >= 1000) { + tiled_bg_->stopGracefully(); - if (!bg_color_.isEqualTo(param.title.bg_color)) - { - bg_color_ = bg_color_.approachTo(param.title.bg_color, 1); - } + if (!bg_color_.isEqualTo(param.title.bg_color)) { + bg_color_ = bg_color_.approachTo(param.title.bg_color, 1); + } - tiled_bg_->setColor(bg_color_); - } + tiled_bg_->setColor(bg_color_); + } - // Cambia de estado si el fondo se ha detenido y recuperado el color - if (tiled_bg_->isStopped() && bg_color_.isEqualTo(param.title.bg_color)) - { - post_state_ = IntroPostState::END; - state_start_time_ = SDL_GetTicks(); - } - break; + // Cambia de estado si el fondo se ha detenido y recuperado el color + if (tiled_bg_->isStopped() && bg_color_.isEqualTo(param.title.bg_color)) { + post_state_ = IntroPostState::END; + state_start_time_ = SDL_GetTicks(); + } + break; - case IntroPostState::END: - // Finaliza la intro después de 1 segundo - if (ELAPSED_TIME >= 1000) - { - Audio::get()->stopMusic(); - Section::name = Section::Name::TITLE; - Section::options = Section::Options::TITLE_1; - } - break; + case IntroPostState::END: + // Finaliza la intro después de 1 segundo + if (ELAPSED_TIME >= 1000) { + Audio::get()->stopMusic(); + Section::name = Section::Name::TITLE; + Section::options = Section::Options::TITLE_1; + } + break; - default: - break; - } + default: + break; + } } -void Intro::renderTextRect() -{ - static const float HEIGHT = Resource::get()->getText("04b_25_metal")->getCharacterSize(); - static SDL_FRect rect = {0.0f, param.game.height - param.intro.text_distance_from_bottom - HEIGHT, param.game.width, HEIGHT * 3}; - SDL_SetRenderDrawColor(Screen::get()->getRenderer(), param.intro.shadow_color.r, param.intro.shadow_color.g, param.intro.shadow_color.b, param.intro.shadow_color.a); - SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); +void Intro::renderTextRect() { + static const float HEIGHT = Resource::get()->getText("04b_25_metal")->getCharacterSize(); + static SDL_FRect rect = {0.0f, param.game.height - param.intro.text_distance_from_bottom - HEIGHT, param.game.width, HEIGHT * 3}; + SDL_SetRenderDrawColor(Screen::get()->getRenderer(), param.intro.shadow_color.r, param.intro.shadow_color.g, param.intro.shadow_color.b, param.intro.shadow_color.a); + SDL_RenderFillRect(Screen::get()->getRenderer(), &rect); } \ No newline at end of file diff --git a/source/sections/intro.h b/source/sections/intro.h index 360c6e6..bd9f822 100644 --- a/source/sections/intro.h +++ b/source/sections/intro.h @@ -1,74 +1,72 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32, Uint64 -#include <memory> // Para unique_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para Uint32, Uint64 -#include "param.h" // Para Param, ParamIntro, param -#include "path_sprite.h" // Para PathSprite -#include "tiled_bg.h" // Para TiledBG -#include "utils.h" // Para Color -#include "writer.h" // Para Writer +#include <memory> // Para unique_ptr +#include <vector> // Para vector + +#include "param.h" // Para Param, ParamIntro, param +#include "path_sprite.h" // Para PathSprite +#include "tiled_bg.h" // Para TiledBG +#include "utils.h" // Para Color +#include "writer.h" // Para Writer /* - Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia - de introducción. + Esta clase gestiona un estado del programa. Se encarga de mostrar la secuencia + de introducción. */ // Clase Intro -class Intro -{ -public: - // Constructor - Intro(); +class Intro { + public: + // Constructor + Intro(); - // Destructor - ~Intro() = default; + // Destructor + ~Intro() = default; - // Bucle principal - void run(); + // Bucle principal + void run(); -private: - // --- Estados internos --- - enum class IntroState - { - SCENES, - POST, - }; + private: + // --- Estados internos --- + enum class IntroState { + SCENES, + POST, + }; - enum class IntroPostState - { - STOP_BG, - END, - }; + enum class IntroPostState { + STOP_BG, + END, + }; - // --- Objetos --- - std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro - std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras - std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro - std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico - // std::unique_ptr<Sprite> shadow_square_for_text_; // Sprite + // --- Objetos --- + std::vector<std::unique_ptr<PathSprite>> card_sprites_; // Vector con los sprites inteligentes para los dibujos de la intro + std::vector<std::unique_ptr<PathSprite>> shadow_sprites_; // Vector con los sprites inteligentes para las sombras + std::vector<std::unique_ptr<Writer>> texts_; // Textos de la intro + std::unique_ptr<TiledBG> tiled_bg_; // Fondo en mosaico + // std::unique_ptr<Sprite> shadow_square_for_text_; // Sprite - // --- Variables --- - Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa - int scene_ = 0; // Indica qué escena está activa - IntroState state_ = IntroState::SCENES; // Estado principal de la intro - IntroPostState post_state_ = IntroPostState::STOP_BG; // Estado POST - Uint32 state_start_time_; // Tiempo de inicio del estado actual - Color bg_color_ = param.intro.bg_color; // Color de fondo + // --- Variables --- + Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa + int scene_ = 0; // Indica qué escena está activa + IntroState state_ = IntroState::SCENES; // Estado principal de la intro + IntroPostState post_state_ = IntroPostState::STOP_BG; // Estado POST + Uint32 state_start_time_; // Tiempo de inicio del estado actual + Color bg_color_ = param.intro.bg_color; // Color de fondo - // --- Métodos internos --- - void update(); // Actualiza las variables del objeto - void render(); // Dibuja el objeto en pantalla - void checkEvents(); // Comprueba los eventos - void checkInput(); // Comprueba las entradas - void updateScenes(); // Actualiza las escenas de la intro - void initSprites(); // Inicializa las imágenes - void initTexts(); // Inicializa los textos - void updateSprites(); // Actualiza los sprites - void updateTexts(); // Actualiza los textos - void renderSprites(); // Dibuja los sprites - void renderTexts(); // Dibuja los textos - void renderTextRect(); // Dibuja el rectangulo de fondo del texto; - void updatePostState(); // Actualiza el estado POST + // --- Métodos internos --- + void update(); // Actualiza las variables del objeto + void render(); // Dibuja el objeto en pantalla + void checkEvents(); // Comprueba los eventos + void checkInput(); // Comprueba las entradas + void updateScenes(); // Actualiza las escenas de la intro + void initSprites(); // Inicializa las imágenes + void initTexts(); // Inicializa los textos + void updateSprites(); // Actualiza los sprites + void updateTexts(); // Actualiza los textos + void renderSprites(); // Dibuja los sprites + void renderTexts(); // Dibuja los textos + void renderTextRect(); // Dibuja el rectangulo de fondo del texto; + void updatePostState(); // Actualiza el estado POST }; diff --git a/source/sections/logo.cpp b/source/sections/logo.cpp index d1ac91e..5793776 100644 --- a/source/sections/logo.cpp +++ b/source/sections/logo.cpp @@ -1,211 +1,181 @@ #include "logo.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_PollEvent, SDL_Event -#include <string> // Para basic_string -#include <utility> // Para move +#include <SDL3/SDL.h> // Para SDL_GetTicks, SDL_PollEvent, SDL_Event -#include "audio.h" // Para Audio -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input -#include "param.h" // Para Param, ParamGame, param -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name -#include "sprite.h" // Para Sprite -#include "texture.h" // Para Texture -#include "utils.h" // Para Color, Zone +#include <string> // Para basic_string +#include <utility> // Para move + +#include "audio.h" // Para Audio +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input +#include "param.h" // Para Param, ParamGame, param +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name +#include "sprite.h" // Para Sprite +#include "texture.h" // Para Texture +#include "utils.h" // Para Color, Zone // Constructor Logo::Logo() - : since_texture_(Resource::get()->getTexture("logo_since_1998.png")), - since_sprite_(std::make_unique<Sprite>(since_texture_)), - jail_texture_(Resource::get()->getTexture("logo_jailgames.png")) -{ + : since_texture_(Resource::get()->getTexture("logo_since_1998.png")), + since_sprite_(std::make_unique<Sprite>(since_texture_)), + jail_texture_(Resource::get()->getTexture("logo_jailgames.png")) { + // Inicializa variables + Section::name = Section::Name::LOGO; + dest_.x = param.game.game_area.center_x - jail_texture_->getWidth() / 2; + dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2; + since_sprite_->setPosition(SDL_FRect{ + static_cast<float>((param.game.width - since_texture_->getWidth()) / 2), + static_cast<float>(83 + jail_texture_->getHeight() + 5), + static_cast<float>(since_texture_->getWidth()), + static_cast<float>(since_texture_->getHeight())}); + since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5); + since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight()); + since_texture_->setColor(0x00, 0x00, 0x00); - // Inicializa variables - Section::name = Section::Name::LOGO; - dest_.x = param.game.game_area.center_x - jail_texture_->getWidth() / 2; - dest_.y = param.game.game_area.center_y - jail_texture_->getHeight() / 2; - since_sprite_->setPosition(SDL_FRect{ - static_cast<float>((param.game.width - since_texture_->getWidth()) / 2), - static_cast<float>(83 + jail_texture_->getHeight() + 5), - static_cast<float>(since_texture_->getWidth()), - static_cast<float>(since_texture_->getHeight())}); - since_sprite_->setY(dest_.y + jail_texture_->getHeight() + 5); - since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight()); - since_texture_->setColor(0x00, 0x00, 0x00); + // Crea los sprites de cada linea + for (int i = 0; i < jail_texture_->getHeight(); ++i) { + auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1); + temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1); + const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3); + temp->setX(POS_X); + temp->setY(dest_.y + i); + jail_sprite_.push_back(std::move(temp)); + } - // Crea los sprites de cada linea - for (int i = 0; i < jail_texture_->getHeight(); ++i) - { - auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), 1); - temp->setSpriteClip(0, i, jail_texture_->getWidth(), 1); - const int POS_X = (i % 2 == 0) ? param.game.width + (i * 3) : -jail_texture_->getWidth() - (i * 3); - temp->setX(POS_X); - temp->setY(dest_.y + i); - jail_sprite_.push_back(std::move(temp)); - } - - // Inicializa el vector de colores - color_.push_back(Color(0x00, 0x00, 0x00)); // Black - color_.push_back(Color(0x00, 0x00, 0xd8)); // Blue - color_.push_back(Color(0xd8, 0x00, 0x00)); // Red - color_.push_back(Color(0xd8, 0x00, 0xd8)); // Magenta - color_.push_back(Color(0x00, 0xd8, 0x00)); // Green - color_.push_back(Color(0x00, 0xd8, 0xd8)); // Cyan - color_.push_back(Color(0xd8, 0xd8, 0x00)); // Yellow - color_.push_back(Color(0xFF, 0xFF, 0xFF)); // Bright white + // Inicializa el vector de colores + color_.push_back(Color(0x00, 0x00, 0x00)); // Black + color_.push_back(Color(0x00, 0x00, 0xd8)); // Blue + color_.push_back(Color(0xd8, 0x00, 0x00)); // Red + color_.push_back(Color(0xd8, 0x00, 0xd8)); // Magenta + color_.push_back(Color(0x00, 0xd8, 0x00)); // Green + color_.push_back(Color(0x00, 0xd8, 0xd8)); // Cyan + color_.push_back(Color(0xd8, 0xd8, 0x00)); // Yellow + color_.push_back(Color(0xFF, 0xFF, 0xFF)); // Bright white } // Destructor -Logo::~Logo() -{ - jail_texture_->setColor(255, 255, 255); - since_texture_->setColor(255, 255, 255); - Audio::get()->stopAllSounds(); - Audio::get()->stopMusic(); +Logo::~Logo() { + jail_texture_->setColor(255, 255, 255); + since_texture_->setColor(255, 255, 255); + Audio::get()->stopAllSounds(); + Audio::get()->stopMusic(); } // Comprueba el manejador de eventos -void Logo::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { - GlobalEvents::check(event); - } +void Logo::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { + GlobalEvents::check(event); + } } // Comprueba las entradas -void Logo::checkInput() -{ - Input::get()->update(); - GlobalInputs::check(); +void Logo::checkInput() { + Input::get()->update(); + GlobalInputs::check(); } // Gestiona el logo de JAILGAMES -void Logo::updateJAILGAMES() -{ - if (counter_ == 30) - { - Audio::get()->playSound("logo.wav"); - } +void Logo::updateJAILGAMES() { + if (counter_ == 30) { + Audio::get()->playSound("logo.wav"); + } - if (counter_ > 30) - { - for (int i = 0; i < (int)jail_sprite_.size(); ++i) - { - if (jail_sprite_[i]->getX() != dest_.x) - { - if (i % 2 == 0) - { - jail_sprite_[i]->incX(-SPEED); - if (jail_sprite_[i]->getX() < dest_.x) - { - jail_sprite_[i]->setX(dest_.x); - } - } - else - { - jail_sprite_[i]->incX(SPEED); - if (jail_sprite_[i]->getX() > dest_.x) - { - jail_sprite_[i]->setX(dest_.x); - } - } - } - } - } + if (counter_ > 30) { + for (int i = 0; i < (int)jail_sprite_.size(); ++i) { + if (jail_sprite_[i]->getX() != dest_.x) { + if (i % 2 == 0) { + jail_sprite_[i]->incX(-SPEED); + if (jail_sprite_[i]->getX() < dest_.x) { + jail_sprite_[i]->setX(dest_.x); + } + } else { + jail_sprite_[i]->incX(SPEED); + if (jail_sprite_[i]->getX() > dest_.x) { + jail_sprite_[i]->setX(dest_.x); + } + } + } + } + } - // Comprueba si ha terminado el logo - if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION) - { - Section::name = Section::Name::INTRO; - } + // Comprueba si ha terminado el logo + if (counter_ == END_LOGO_COUNTER_MARK + POST_LOGO_DURATION) { + Section::name = Section::Name::INTRO; + } } // Gestiona el color de las texturas -void Logo::updateTextureColors() -{ - constexpr int inc = 4; +void Logo::updateTextureColors() { + constexpr int inc = 4; - // Manejo de 'sinceTexture' - for (int i = 0; i <= 7; ++i) - { - if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + inc * i) - { - since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b); - } - } + // Manejo de 'sinceTexture' + for (int i = 0; i <= 7; ++i) { + if (counter_ == SHOW_SINCE_SPRITE_COUNTER_MARK + inc * i) { + since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b); + } + } - // Manejo de 'jailTexture' y 'sinceTexture' en el fade - for (int i = 0; i <= 6; ++i) - { - if (counter_ == INIT_FADE_COUNTER_MARK + inc * i) - { - jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); - since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); - } - } + // Manejo de 'jailTexture' y 'sinceTexture' en el fade + for (int i = 0; i <= 6; ++i) { + if (counter_ == INIT_FADE_COUNTER_MARK + inc * i) { + jail_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); + since_texture_->setColor(color_[6 - i].r, color_[6 - i].g, color_[6 - i].b); + } + } } // Actualiza las variables -void Logo::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - // Actualiza el contador de ticks - ticks_ = SDL_GetTicks(); +void Logo::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + // Actualiza el contador de ticks + ticks_ = SDL_GetTicks(); - // Actualiza el objeto screen - Screen::get()->update(); + // Actualiza el objeto screen + Screen::get()->update(); - // Comprueba las entradas - checkInput(); + // Comprueba las entradas + checkInput(); - updateJAILGAMES(); - updateTextureColors(); + updateJAILGAMES(); + updateTextureColors(); - // Gestiona el contador - ++counter_; - } + // Gestiona el contador + ++counter_; + } } // Dibuja en pantalla -void Logo::render() -{ - Screen::get()->start(); - Screen::get()->clean(); +void Logo::render() { + Screen::get()->start(); + Screen::get()->clean(); - renderJAILGAMES(); + renderJAILGAMES(); - Screen::get()->render(); + Screen::get()->render(); } // Bucle para el logo del juego -void Logo::run() -{ - while (Section::name == Section::Name::LOGO) - { - checkInput(); - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } +void Logo::run() { + while (Section::name == Section::Name::LOGO) { + checkInput(); + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Renderiza el logo de JAILGAMES -void Logo::renderJAILGAMES() -{ - // Dibuja los sprites - for (auto &sprite : jail_sprite_) - { - sprite->render(); - } +void Logo::renderJAILGAMES() { + // Dibuja los sprites + for (auto &sprite : jail_sprite_) { + sprite->render(); + } - if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK) - { - since_sprite_->render(); - } + if (counter_ >= SHOW_SINCE_SPRITE_COUNTER_MARK) { + since_sprite_->render(); + } } \ No newline at end of file diff --git a/source/sections/logo.h b/source/sections/logo.h index c011a5f..b30df1c 100644 --- a/source/sections/logo.h +++ b/source/sections/logo.h @@ -1,60 +1,60 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FPoint, Uint64 -#include <memory> // Para shared_ptr, unique_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FPoint, Uint64 + +#include <memory> // Para shared_ptr, unique_ptr +#include <vector> // Para vector class Sprite; class Texture; struct Color; /* - Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el - logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por - cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una - modulación de color sobre la textura para simular un fade to black al estilo - ZX Spectrum. + Esta clase gestiona un estado del programa. Se encarga de dibujar por pantalla el + logo de "JAILGAMES" utilizando un sencillo efecto consistente en generar un sprite por + cada línea del bitmap que forma la palabra "JAILGAMES". Posteriormente realiza una + modulación de color sobre la textura para simular un fade to black al estilo + ZX Spectrum. */ // --- Clase Logo --- -class Logo -{ -public: - // Constructor - Logo(); +class Logo { + public: + // Constructor + Logo(); - // Destructor - ~Logo(); + // Destructor + ~Logo(); - // Bucle principal - void run(); + // Bucle principal + void run(); -private: - // --- Constantes --- - static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998" - static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro - static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo - static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo - static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea + private: + // --- Constantes --- + static constexpr int SHOW_SINCE_SPRITE_COUNTER_MARK = 70; // Tiempo del contador en el que empieza a verse el sprite de "SINCE 1998" + static constexpr int INIT_FADE_COUNTER_MARK = 300; // Tiempo del contador cuando inicia el fade a negro + static constexpr int END_LOGO_COUNTER_MARK = 400; // Tiempo del contador para terminar el logo + static constexpr int POST_LOGO_DURATION = 20; // Tiempo que dura el logo con el fade al máximo + static constexpr int SPEED = 8; // Velocidad de desplazamiento de cada línea - // --- Objetos y punteros --- - std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998" - std::unique_ptr<Sprite> since_sprite_; // Sprite para manejar la since_texture - std::shared_ptr<Texture> jail_texture_; // Textura con los gráficos "JAILGAMES" - std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES + // --- Objetos y punteros --- + std::shared_ptr<Texture> since_texture_; // Textura con los gráficos "Since 1998" + std::unique_ptr<Sprite> since_sprite_; // Sprite para manejar la since_texture + std::shared_ptr<Texture> jail_texture_; // Textura con los gráficos "JAILGAMES" + std::vector<std::unique_ptr<Sprite>> jail_sprite_; // Vector con los sprites de cada línea que forman el bitmap JAILGAMES - // --- Variables --- - std::vector<Color> color_; // Vector con los colores para el fade - int counter_ = 0; // Contador - Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa - SDL_FPoint dest_; // Posición donde dibujar el logo + // --- Variables --- + std::vector<Color> color_; // Vector con los colores para el fade + int counter_ = 0; // Contador + Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad del programa + SDL_FPoint dest_; // Posición donde dibujar el logo - // --- Métodos internos --- - void update(); // Actualiza las variables - void render(); // Dibuja en pantalla - void checkEvents(); // Comprueba el manejador de eventos - void checkInput(); // Comprueba las entradas - void updateJAILGAMES(); // Gestiona el logo de JAILGAMES - void renderJAILGAMES(); // Renderiza el logo de JAILGAMES - void updateTextureColors(); // Gestiona el color de las texturas + // --- Métodos internos --- + void update(); // Actualiza las variables + void render(); // Dibuja en pantalla + void checkEvents(); // Comprueba el manejador de eventos + void checkInput(); // Comprueba las entradas + void updateJAILGAMES(); // Gestiona el logo de JAILGAMES + void renderJAILGAMES(); // Renderiza el logo de JAILGAMES + void updateTextureColors(); // Gestiona el color de las texturas }; diff --git a/source/sections/title.cpp b/source/sections/title.cpp index 47719c9..c83751d 100644 --- a/source/sections/title.cpp +++ b/source/sections/title.cpp @@ -1,579 +1,523 @@ #include "title.h" -#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_EventType -#include <stddef.h> // Para size_t -#include <algorithm> // Para find_if -#include <iostream> // Para basic_ostream, basic_ostream::operator<< -#include <string> // Para basic_string, char_traits, operator+ -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_GetTicks, Uint32, SDL_EventType +#include <stddef.h> // Para size_t -#include "audio.h" // Para Audio -#include "define_buttons.h" // Para DefineButtons -#include "fade.h" // Para Fade, FadeType -#include "game_logo.h" // Para GameLogo -#include "global_events.h" // Para check -#include "global_inputs.h" // Para check -#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input... -#include "lang.h" // Para getText -#include "notifier.h" // Para Notifier -#include "options.h" // Para GamepadOptions, controllers, getPlayerW... -#include "param.h" // Para Param, param, ParamGame, ParamTitle -#include "player.h" // Para Player, PlayerState -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options, AttractMode -#include "sprite.h" // Para Sprite -#include "text.h" // Para TEXT_CENTER, TEXT_SHADOW, Text -#include "tiled_bg.h" // Para TiledBG, TiledBGMode -#include "ui/service_menu.h" // Para ServiceMenu -#include "utils.h" // Para Color, Zone, NO_TEXT_COLOR, TITLE_SHADO... +#include <algorithm> // Para find_if +#include <iostream> // Para basic_ostream, basic_ostream::operator<< +#include <string> // Para basic_string, char_traits, operator+ +#include <vector> // Para vector + +#include "audio.h" // Para Audio +#include "define_buttons.h" // Para DefineButtons +#include "fade.h" // Para Fade, FadeType +#include "game_logo.h" // Para GameLogo +#include "global_events.h" // Para check +#include "global_inputs.h" // Para check +#include "input.h" // Para Input, INPUT_DO_NOT_ALLOW_REPEAT, Input... +#include "lang.h" // Para getText +#include "notifier.h" // Para Notifier +#include "options.h" // Para GamepadOptions, controllers, getPlayerW... +#include "param.h" // Para Param, param, ParamGame, ParamTitle +#include "player.h" // Para Player, PlayerState +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options, AttractMode +#include "sprite.h" // Para Sprite +#include "text.h" // Para TEXT_CENTER, TEXT_SHADOW, Text +#include "tiled_bg.h" // Para TiledBG, TiledBGMode +#include "ui/service_menu.h" // Para ServiceMenu +#include "utils.h" // Para Color, Zone, NO_TEXT_COLOR, TITLE_SHADO... class Texture; #ifdef DEBUG -#include <iomanip> // Para operator<<, setfill, setw +#include <iomanip> // Para operator<<, setfill, setw #endif // Constructor Title::Title() - : text_(Resource::get()->getText("smb2_grad")), - fade_(std::make_unique<Fade>()), - tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)), - game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)), - mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), - define_buttons_(std::make_unique<DefineButtons>()), - num_controllers_(Input::get()->getNumControllers()), - state_(TitleState::LOGO_ANIMATING) -{ - // Configura objetos - tiled_bg_->setColor(param.title.bg_color); - game_logo_->enable(); - mini_logo_sprite_->setX(param.game.game_area.center_x - mini_logo_sprite_->getWidth() / 2); - fade_->setColor(param.fade.color); - fade_->setType(FadeType::RANDOM_SQUARE); - fade_->setPostDuration(param.fade.post_duration); - initPlayers(); + : text_(Resource::get()->getText("smb2_grad")), + fade_(std::make_unique<Fade>()), + tiled_bg_(std::make_unique<TiledBG>(param.game.game_area.rect, TiledBGMode::RANDOM)), + game_logo_(std::make_unique<GameLogo>(param.game.game_area.center_x, param.title.title_c_c_position)), + mini_logo_sprite_(std::make_unique<Sprite>(Resource::get()->getTexture("logo_jailgames_mini.png"))), + define_buttons_(std::make_unique<DefineButtons>()), + num_controllers_(Input::get()->getNumControllers()), + state_(TitleState::LOGO_ANIMATING) { + // Configura objetos + tiled_bg_->setColor(param.title.bg_color); + game_logo_->enable(); + mini_logo_sprite_->setX(param.game.game_area.center_x - mini_logo_sprite_->getWidth() / 2); + fade_->setColor(param.fade.color); + fade_->setType(FadeType::RANDOM_SQUARE); + fade_->setPostDuration(param.fade.post_duration); + initPlayers(); - // Asigna valores a otras variables - Section::options = Section::Options::TITLE_1; - const bool IS_TITLE_TO_DEMO = (Section::attract_mode == Section::AttractMode::TITLE_TO_DEMO); - next_section_ = IS_TITLE_TO_DEMO ? Section::Name::GAME_DEMO : Section::Name::LOGO; - Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO; + // Asigna valores a otras variables + Section::options = Section::Options::TITLE_1; + const bool IS_TITLE_TO_DEMO = (Section::attract_mode == Section::AttractMode::TITLE_TO_DEMO); + next_section_ = IS_TITLE_TO_DEMO ? Section::Name::GAME_DEMO : Section::Name::LOGO; + Section::attract_mode = IS_TITLE_TO_DEMO ? Section::AttractMode::TITLE_TO_LOGO : Section::AttractMode::TITLE_TO_DEMO; - // Define los anclajes de los elementos - anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK; - mini_logo_sprite_->setY(anchor_.mini_logo); - anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3; + // Define los anclajes de los elementos + anchor_.mini_logo = (param.game.height / 5 * 4) + BLOCK; + mini_logo_sprite_->setY(anchor_.mini_logo); + anchor_.copyright_text = anchor_.mini_logo + mini_logo_sprite_->getHeight() + 3; } // Destructor -Title::~Title() -{ - Audio::get()->stopAllSounds(); - if (Section::name == Section::Name::LOGO) - { - Audio::get()->fadeOutMusic(300); - } +Title::~Title() { + Audio::get()->stopAllSounds(); + if (Section::name == Section::Name::LOGO) { + Audio::get()->fadeOutMusic(300); + } } // Actualiza las variables del objeto -void Title::update() -{ - if (SDL_GetTicks() - ticks_ > param.game.speed) - { - ticks_ = SDL_GetTicks(); - updateFade(); - updateState(); - updateStartPrompt(); - updatePlayers(); - Screen::get()->update(); - } +void Title::update() { + if (SDL_GetTicks() - ticks_ > param.game.speed) { + ticks_ = SDL_GetTicks(); + updateFade(); + updateState(); + updateStartPrompt(); + updatePlayers(); + Screen::get()->update(); + } } // Dibuja el objeto en pantalla -void Title::render() -{ - Screen::get()->start(); - Screen::get()->clean(); +void Title::render() { + Screen::get()->start(); + Screen::get()->clean(); - tiled_bg_->render(); - game_logo_->render(); - renderPlayers(); - renderStartPrompt(); - renderCopyright(); + tiled_bg_->render(); + game_logo_->render(); + renderPlayers(); + renderStartPrompt(); + renderCopyright(); - define_buttons_->render(); - fade_->render(); + define_buttons_->render(); + fade_->render(); - Screen::get()->render(); + Screen::get()->render(); } // Comprueba los eventos -void Title::checkEvents() -{ - SDL_Event event; - while (SDL_PollEvent(&event)) - { +void Title::checkEvents() { + SDL_Event event; + while (SDL_PollEvent(&event)) { #ifdef DEBUG - if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1) - { - static Color color = param.title.bg_color; - switch (event.key.key) - { - case SDLK_A: - if (color.r < 255) - ++color.r; - break; + if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 1) { + static Color color = param.title.bg_color; + switch (event.key.key) { + case SDLK_A: + if (color.r < 255) + ++color.r; + break; - case SDLK_Z: - if (color.r > 0) - --color.r; - break; + case SDLK_Z: + if (color.r > 0) + --color.r; + break; - case SDLK_S: - if (color.g < 255) - ++color.g; - break; + case SDLK_S: + if (color.g < 255) + ++color.g; + break; - case SDLK_X: - if (color.g > 0) - --color.g; - break; + case SDLK_X: + if (color.g > 0) + --color.g; + break; - case SDLK_D: - if (color.b < 255) - ++color.b; - break; + case SDLK_D: + if (color.b < 255) + ++color.b; + break; - case SDLK_C: - if (color.b > 0) - --color.b; - break; + case SDLK_C: + if (color.b > 0) + --color.b; + break; - case SDLK_F: - if (color.r < 255) - ++color.r; - if (color.g < 255) - ++color.g; - if (color.b < 255) - ++color.b; - break; + case SDLK_F: + if (color.r < 255) + ++color.r; + if (color.g < 255) + ++color.g; + if (color.b < 255) + ++color.b; + break; - case SDLK_V: - if (color.r > 0) - --color.r; - if (color.g > 0) - --color.g; - if (color.b > 0) - --color.b; - break; + case SDLK_V: + if (color.r > 0) + --color.r; + if (color.g > 0) + --color.g; + if (color.b > 0) + --color.b; + break; - default: - break; - } - counter_ = 0; - tiled_bg_->setColor(color); - std::cout << "#" - << std::hex << std::setw(2) << std::setfill('0') << (int)color.r - << std::setw(2) << std::setfill('0') << (int)color.g - << std::setw(2) << std::setfill('0') << (int)color.b - << std::endl; - } + default: + break; + } + counter_ = 0; + tiled_bg_->setColor(color); + std::cout << "#" + << std::hex << std::setw(2) << std::setfill('0') << (int)color.r + << std::setw(2) << std::setfill('0') << (int)color.g + << std::setw(2) << std::setfill('0') << (int)color.b + << std::endl; + } #endif - if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) - { - switch (event.key.key) - { - case SDLK_1: // Redefine los botones del mando #0 - define_buttons_->enable(0); - resetCounter(); - break; + if (event.type == SDL_EVENT_KEY_DOWN && event.key.repeat == 0) { + switch (event.key.key) { + case SDLK_1: // Redefine los botones del mando #0 + define_buttons_->enable(0); + resetCounter(); + break; - case SDLK_2: // Redefine los botones del mando #1 - define_buttons_->enable(1); - resetCounter(); - break; + case SDLK_2: // Redefine los botones del mando #1 + define_buttons_->enable(1); + resetCounter(); + break; - case SDLK_3: // Intercambia los mandos entre los dos jugadores - swapControllers(); - resetCounter(); - break; + case SDLK_3: // Intercambia los mandos entre los dos jugadores + swapControllers(); + resetCounter(); + break; - case SDLK_4: // Intercambia la asignación del teclado - swapKeyboard(); - resetCounter(); - break; + case SDLK_4: // Intercambia la asignación del teclado + swapKeyboard(); + resetCounter(); + break; - case SDLK_5: // Muestra la asignación de mandos y teclado - showControllers(); - resetCounter(); - break; + case SDLK_5: // Muestra la asignación de mandos y teclado + showControllers(); + resetCounter(); + break; - default: - break; - } - } + default: + break; + } + } - GlobalEvents::check(event); - define_buttons_->checkEvents(event); - } + GlobalEvents::check(event); + define_buttons_->checkEvents(event); + } } // Comprueba las entradas -void Title::checkInput() -{ - // Comprueba las entradas solo si no se estan definiendo los botones - if (define_buttons_->isEnabled()) - return; +void Title::checkInput() { + // Comprueba las entradas solo si no se estan definiendo los botones + if (define_buttons_->isEnabled()) + return; - Input::get()->update(); + Input::get()->update(); - if (!ServiceMenu::get()->isEnabled()) - { - // Comprueba todos los métodos de control - for (const auto &CONTROLLER : Options::controllers) - { - // Boton START - if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) - { - if ((state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP)) - { - if (CONTROLLER.player_id == 1) - { - if (!player1_start_pressed_) - { - player1_start_pressed_ = true; - getPlayer(1)->setPlayingState(PlayerState::TITLE_ANIMATION); - setState(TitleState::START_HAS_BEEN_PRESSED); - counter_ = 0; - } - } + if (!ServiceMenu::get()->isEnabled()) { + // Comprueba todos los métodos de control + for (const auto &CONTROLLER : Options::controllers) { + // Boton START + if (Input::get()->checkInput(InputAction::START, INPUT_DO_NOT_ALLOW_REPEAT, CONTROLLER.type, CONTROLLER.index)) { + if ((state_ != TitleState::LOGO_ANIMATING || ALLOW_TITLE_ANIMATION_SKIP)) { + if (CONTROLLER.player_id == 1) { + if (!player1_start_pressed_) { + player1_start_pressed_ = true; + getPlayer(1)->setPlayingState(PlayerState::TITLE_ANIMATION); + setState(TitleState::START_HAS_BEEN_PRESSED); + counter_ = 0; + } + } - if (CONTROLLER.player_id == 2) - { - if (!player2_start_pressed_) - { - player2_start_pressed_ = true; - getPlayer(2)->setPlayingState(PlayerState::TITLE_ANIMATION); - setState(TitleState::START_HAS_BEEN_PRESSED); - counter_ = 0; - } - } - } - } - } - } + if (CONTROLLER.player_id == 2) { + if (!player2_start_pressed_) { + player2_start_pressed_ = true; + getPlayer(2)->setPlayingState(PlayerState::TITLE_ANIMATION); + setState(TitleState::START_HAS_BEEN_PRESSED); + counter_ = 0; + } + } + } + } + } + } - // Comprueba los inputs que se pueden introducir en cualquier sección del juego - GlobalInputs::check(); + // Comprueba los inputs que se pueden introducir en cualquier sección del juego + GlobalInputs::check(); } // Bucle para el titulo del juego -void Title::run() -{ - while (Section::name == Section::Name::TITLE) - { - checkInput(); - update(); - checkEvents(); // Tiene que ir antes del render - render(); - } +void Title::run() { + while (Section::name == Section::Name::TITLE) { + checkInput(); + update(); + checkEvents(); // Tiene que ir antes del render + render(); + } } // Reinicia el contador interno void Title::resetCounter() { counter_ = 0; } // Intercambia la asignación de mandos a los jugadores -void Title::swapControllers() -{ - if (Input::get()->getNumControllers() == 0) - return; +void Title::swapControllers() { + if (Input::get()->getNumControllers() == 0) + return; - Options::swapControllers(); - showControllers(); + Options::swapControllers(); + showControllers(); } // Intercambia el teclado de jugador -void Title::swapKeyboard() -{ - Options::swapKeyboard(); - std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::getPlayerWhoUsesKeyboard()) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD"); - Notifier::get()->show({text}); +void Title::swapKeyboard() { + Options::swapKeyboard(); + std::string text = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(Options::getPlayerWhoUsesKeyboard()) + ": " + Lang::getText("[DEFINE_BUTTONS] KEYBOARD"); + Notifier::get()->show({text}); } // Muestra información sobre los controles y los jugadores -void Title::showControllers() -{ - // Crea vectores de texto vacíos para un número máximo de mandos - constexpr size_t NUM_CONTROLLERS = 2; - std::vector<std::string> text(NUM_CONTROLLERS); - std::vector<int> player_controller_index(NUM_CONTROLLERS, -1); +void Title::showControllers() { + // Crea vectores de texto vacíos para un número máximo de mandos + constexpr size_t NUM_CONTROLLERS = 2; + std::vector<std::string> text(NUM_CONTROLLERS); + std::vector<int> player_controller_index(NUM_CONTROLLERS, -1); - // Obtiene de cada jugador el índice del mando que tiene asignado - for (size_t i = 0; i < NUM_CONTROLLERS; ++i) - { - // Ejemplo: el jugador 1 tiene el mando 2 - player_controller_index.at(Options::controllers.at(i).player_id - 1) = i; - } + // Obtiene de cada jugador el índice del mando que tiene asignado + for (size_t i = 0; i < NUM_CONTROLLERS; ++i) { + // Ejemplo: el jugador 1 tiene el mando 2 + player_controller_index.at(Options::controllers.at(i).player_id - 1) = i; + } - // Genera el texto correspondiente - for (size_t i = 0; i < NUM_CONTROLLERS; ++i) - { - const size_t index = player_controller_index.at(i); - if (Options::controllers.at(index).plugged) - { - text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(index).name; - } - } + // Genera el texto correspondiente + for (size_t i = 0; i < NUM_CONTROLLERS; ++i) { + const size_t index = player_controller_index.at(i); + if (Options::controllers.at(index).plugged) { + text.at(i) = Lang::getText("[DEFINE_BUTTONS] PLAYER") + std::to_string(i + 1) + ": " + Options::controllers.at(index).name; + } + } - // Muestra la notificación - Notifier::get()->show({text.at(0), text.at(1)}); + // Muestra la notificación + Notifier::get()->show({text.at(0), text.at(1)}); } // Actualiza el fade -void Title::updateFade() -{ - fade_->update(); - if (fade_->hasEnded()) - { - const int COMBO = (player1_start_pressed_ ? 1 : 0) | (player2_start_pressed_ ? 2 : 0); +void Title::updateFade() { + fade_->update(); + if (fade_->hasEnded()) { + const int COMBO = (player1_start_pressed_ ? 1 : 0) | (player2_start_pressed_ ? 2 : 0); - switch (COMBO) - { - case 0: // Ningún jugador ha pulsado Start - Section::name = next_section_; - break; + switch (COMBO) { + case 0: // Ningún jugador ha pulsado Start + Section::name = next_section_; + break; - case 1: // Solo el jugador 1 ha pulsado Start - Section::name = Section::Name::GAME; - Section::options = Section::Options::GAME_PLAY_1P; - Audio::get()->stopMusic(); - break; + case 1: // Solo el jugador 1 ha pulsado Start + Section::name = Section::Name::GAME; + Section::options = Section::Options::GAME_PLAY_1P; + Audio::get()->stopMusic(); + break; - case 2: // Solo el jugador 2 ha pulsado Start - Section::name = Section::Name::GAME; - Section::options = Section::Options::GAME_PLAY_2P; - Audio::get()->stopMusic(); - break; + case 2: // Solo el jugador 2 ha pulsado Start + Section::name = Section::Name::GAME; + Section::options = Section::Options::GAME_PLAY_2P; + Audio::get()->stopMusic(); + break; - case 3: // Ambos jugadores han pulsado Start - Section::name = Section::Name::GAME; - Section::options = Section::Options::GAME_PLAY_BOTH; - Audio::get()->stopMusic(); - break; - } - } + case 3: // Ambos jugadores han pulsado Start + Section::name = Section::Name::GAME; + Section::options = Section::Options::GAME_PLAY_BOTH; + Audio::get()->stopMusic(); + break; + } + } } // Actualiza el estado -void Title::updateState() -{ - // Establece la lógica según el estado - switch (state_) - { - case TitleState::LOGO_ANIMATING: - { - game_logo_->update(); - if (game_logo_->hasFinished()) - { - setState(TitleState::LOGO_FINISHED); - } - break; - } - case TitleState::LOGO_FINISHED: - { - // El contador solo sube si no estamos definiendo botones - counter_ = define_buttons_->isEnabled() ? 0 : counter_ + 1; +void Title::updateState() { + // Establece la lógica según el estado + switch (state_) { + case TitleState::LOGO_ANIMATING: { + game_logo_->update(); + if (game_logo_->hasFinished()) { + setState(TitleState::LOGO_FINISHED); + } + break; + } + case TitleState::LOGO_FINISHED: { + // El contador solo sube si no estamos definiendo botones + counter_ = define_buttons_->isEnabled() ? 0 : counter_ + 1; - // Actualiza el logo con el título del juego - game_logo_->update(); + // Actualiza el logo con el título del juego + game_logo_->update(); - // Actualiza el mosaico de fondo - tiled_bg_->update(); + // Actualiza el mosaico de fondo + tiled_bg_->update(); - if (counter_ == param.title.title_duration) - { - // El menu ha hecho time out - fade_->setPostDuration(0); - fade_->activate(); - selection_ = Section::Options::TITLE_TIME_OUT; - } + if (counter_ == param.title.title_duration) { + // El menu ha hecho time out + fade_->setPostDuration(0); + fade_->activate(); + selection_ = Section::Options::TITLE_TIME_OUT; + } - break; - } - case TitleState::START_HAS_BEEN_PRESSED: - { - // Actualiza el logo con el título del juego - game_logo_->update(); + break; + } + case TitleState::START_HAS_BEEN_PRESSED: { + // Actualiza el logo con el título del juego + game_logo_->update(); - // Actualiza el mosaico de fondo - tiled_bg_->update(); + // Actualiza el mosaico de fondo + tiled_bg_->update(); - if (counter_ == 100) - { - fade_->activate(); - } - ++counter_; - break; - } + if (counter_ == 100) { + fade_->activate(); + } + ++counter_; + break; + } - default: - break; - } + default: + break; + } } -void Title::updateStartPrompt() -{ - constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos - constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250 +void Title::updateStartPrompt() { + constexpr Uint32 LOGO_BLINK_PERIOD = 833; // milisegundos + constexpr Uint32 LOGO_BLINK_ON_TIME = 583; // 833 - 250 - constexpr Uint32 START_BLINK_PERIOD = 167; - constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83 + constexpr Uint32 START_BLINK_PERIOD = 167; + constexpr Uint32 START_BLINK_ON_TIME = 83; // 167 - 83 - Uint32 time_ms = SDL_GetTicks(); - bool condition_met = false; + Uint32 time_ms = SDL_GetTicks(); + bool condition_met = false; - if (!define_buttons_->isEnabled()) - { - switch (state_) - { - case TitleState::LOGO_FINISHED: - condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME); - break; + if (!define_buttons_->isEnabled()) { + switch (state_) { + case TitleState::LOGO_FINISHED: + condition_met = (time_ms % LOGO_BLINK_PERIOD) >= (LOGO_BLINK_PERIOD - LOGO_BLINK_ON_TIME); + break; - case TitleState::START_HAS_BEEN_PRESSED: - condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME); - break; + case TitleState::START_HAS_BEEN_PRESSED: + condition_met = (time_ms % START_BLINK_PERIOD) >= (START_BLINK_PERIOD - START_BLINK_ON_TIME); + break; - default: - break; - } - } + default: + break; + } + } - should_render_start_prompt = condition_met; + should_render_start_prompt = condition_met; } -void Title::renderStartPrompt() -{ - if (should_render_start_prompt) - { - text_->writeDX(TEXT_CENTER | TEXT_SHADOW, - param.game.game_area.center_x, - param.title.press_start_position, - Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"), - 1, - NO_TEXT_COLOR, - 1, - TITLE_SHADOW_TEXT_COLOR); - } +void Title::renderStartPrompt() { + if (should_render_start_prompt) { + text_->writeDX(TEXT_CENTER | TEXT_SHADOW, + param.game.game_area.center_x, + param.title.press_start_position, + Lang::getText("[TITLE] PRESS_BUTTON_TO_PLAY"), + 1, + NO_TEXT_COLOR, + 1, + TITLE_SHADOW_TEXT_COLOR); + } } -void Title::renderCopyright() -{ - if (state_ != TitleState::LOGO_ANIMATING) - { - // Mini logo - mini_logo_sprite_->render(); +void Title::renderCopyright() { + if (state_ != TitleState::LOGO_ANIMATING) { + // Mini logo + mini_logo_sprite_->render(); - // Texto con el copyright - text_->writeDX(TEXT_CENTER | TEXT_SHADOW, - param.game.game_area.center_x, - anchor_.copyright_text, - TEXT_COPYRIGHT, - 1, - NO_TEXT_COLOR, - 1, - TITLE_SHADOW_TEXT_COLOR); - } + // Texto con el copyright + text_->writeDX(TEXT_CENTER | TEXT_SHADOW, + param.game.game_area.center_x, + anchor_.copyright_text, + TEXT_COPYRIGHT, + 1, + NO_TEXT_COLOR, + 1, + TITLE_SHADOW_TEXT_COLOR); + } } // Cambia el estado -void Title::setState(TitleState state) -{ - if (state_ == state) - return; +void Title::setState(TitleState state) { + if (state_ == state) + return; - state_ = state; - switch (state_) - { - case TitleState::LOGO_ANIMATING: - break; - case TitleState::LOGO_FINISHED: - Audio::get()->playMusic("title.ogg"); - break; - case TitleState::START_HAS_BEEN_PRESSED: - Audio::get()->fadeOutMusic(1500); - break; - } + state_ = state; + switch (state_) { + case TitleState::LOGO_ANIMATING: + break; + case TitleState::LOGO_FINISHED: + Audio::get()->playMusic("title.ogg"); + break; + case TitleState::START_HAS_BEEN_PRESSED: + Audio::get()->fadeOutMusic(1500); + break; + } } // Inicializa los jugadores -void Title::initPlayers() -{ - std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores; - std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador +void Title::initPlayers() { + std::vector<std::vector<std::shared_ptr<Texture>>> player_textures; // Vector con todas las texturas de los jugadores; + std::vector<std::vector<std::string>> player_animations; // Vector con las animaciones del jugador - // Texturas - Player1 - { - std::vector<std::shared_ptr<Texture>> player_texture; - player_texture.emplace_back(Resource::get()->getTexture("player1.gif")); - player_texture.emplace_back(Resource::get()->getTexture("player1_power.png")); - player_textures.push_back(player_texture); - } + // Texturas - Player1 + { + std::vector<std::shared_ptr<Texture>> player_texture; + player_texture.emplace_back(Resource::get()->getTexture("player1.gif")); + player_texture.emplace_back(Resource::get()->getTexture("player1_power.png")); + player_textures.push_back(player_texture); + } - // Texturas - Player2 - { - std::vector<std::shared_ptr<Texture>> player_texture; - player_texture.emplace_back(Resource::get()->getTexture("player2.gif")); - player_texture.emplace_back(Resource::get()->getTexture("player2_power.png")); - player_textures.push_back(player_texture); - } + // Texturas - Player2 + { + std::vector<std::shared_ptr<Texture>> player_texture; + player_texture.emplace_back(Resource::get()->getTexture("player2.gif")); + player_texture.emplace_back(Resource::get()->getTexture("player2_power.png")); + player_textures.push_back(player_texture); + } - // Animaciones -- Jugador - { - player_animations.emplace_back(Resource::get()->getAnimation("player.ani")); - player_animations.emplace_back(Resource::get()->getAnimation("player_power.ani")); - } + // Animaciones -- Jugador + { + player_animations.emplace_back(Resource::get()->getAnimation("player.ani")); + player_animations.emplace_back(Resource::get()->getAnimation("player_power.ani")); + } - // Crea los dos jugadores - constexpr int PLAYER_WIDTH = 32; - constexpr int PLAYER_HEIGHT = 32; - const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2); - constexpr bool DEMO = false; - players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations)); - players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN); + // Crea los dos jugadores + constexpr int PLAYER_WIDTH = 32; + constexpr int PLAYER_HEIGHT = 32; + const int Y = param.title.press_start_position - (PLAYER_HEIGHT / 2); + constexpr bool DEMO = false; + players_.emplace_back(std::make_unique<Player>(1, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(0), player_animations)); + players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN); - players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations)); - players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN); + players_.emplace_back(std::make_unique<Player>(2, param.game.game_area.center_x - (PLAYER_WIDTH / 2), Y, DEMO, param.game.play_area.rect, player_textures.at(1), player_animations)); + players_.back()->setPlayingState(PlayerState::TITLE_HIDDEN); } // Actualza los jugadores -void Title::updatePlayers() -{ - for (auto &player : players_) - { - player->update(); - } +void Title::updatePlayers() { + for (auto &player : players_) { + player->update(); + } } // Renderiza los jugadores -void Title::renderPlayers() -{ - for (auto const &player : players_) - { - player->render(); - } +void Title::renderPlayers() { + for (auto const &player : players_) { + player->render(); + } } // Obtiene un jugador a partir de su "id" -std::shared_ptr<Player> Title::getPlayer(int id) -{ - auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) - { return player->getId() == id; }); +std::shared_ptr<Player> Title::getPlayer(int id) { + auto it = std::find_if(players_.begin(), players_.end(), [id](const auto &player) { return player->getId() == id; }); - if (it != players_.end()) - { - return *it; - } - return nullptr; + if (it != players_.end()) { + return *it; + } + return nullptr; } \ No newline at end of file diff --git a/source/sections/title.h b/source/sections/title.h index f893d24..bcb8dfb 100644 --- a/source/sections/title.h +++ b/source/sections/title.h @@ -1,10 +1,11 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32 -#include <memory> // Para unique_ptr, shared_ptr +#include <SDL3/SDL.h> // Para Uint32 + +#include <memory> // Para unique_ptr, shared_ptr #include <vector> -#include "section.h" // Para Options +#include "section.h" // Para Options class DefineButtons; class Fade; @@ -21,78 +22,75 @@ constexpr const char TEXT_COPYRIGHT[] = "@2020,2025 JailDesigner"; constexpr bool ALLOW_TITLE_ANIMATION_SKIP = false; /* - Clase que gestiona el estado de título/menú principal del juego. - Responsable de mostrar el logo, el fondo animado y gestionar la entrada para comenzar la partida. - No permite saltar la animación del título salvo que se cambie el define. + Clase que gestiona el estado de título/menú principal del juego. + Responsable de mostrar el logo, el fondo animado y gestionar la entrada para comenzar la partida. + No permite saltar la animación del título salvo que se cambie el define. */ // Clase Title -class Title -{ -public: - // --- Constructores y destructor --- - Title(); - ~Title(); +class Title { + public: + // --- Constructores y destructor --- + Title(); + ~Title(); - // --- Método principal --- - void run(); // Bucle para el título del juego + // --- Método principal --- + void run(); // Bucle para el título del juego -private: - // --- Enumeraciones --- - enum class TitleState - { - LOGO_ANIMATING, // El logo está animándose - LOGO_FINISHED, // El logo ha terminado de animarse - START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start - }; + private: + // --- Enumeraciones --- + enum class TitleState { + LOGO_ANIMATING, // El logo está animándose + LOGO_FINISHED, // El logo ha terminado de animarse + START_HAS_BEEN_PRESSED, // Se ha pulsado el botón de start + }; - // --- Estructura para definir anclas --- - struct Anchor - { - int mini_logo; - int copyright_text; - }; + // --- Estructura para definir anclas --- + struct Anchor { + int mini_logo; + int copyright_text; + }; - // --- Objetos y punteros --- - std::shared_ptr<Text> text_; // Objeto de texto para escribir en pantalla - std::unique_ptr<Fade> fade_; // Fundido en pantalla - std::unique_ptr<TiledBG> tiled_bg_; // Fondo animado de tiles - std::unique_ptr<GameLogo> game_logo_; // Logo del juego - std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini - std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick - std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores + // --- Objetos y punteros --- + std::shared_ptr<Text> text_; // Objeto de texto para escribir en pantalla + std::unique_ptr<Fade> fade_; // Fundido en pantalla + std::unique_ptr<TiledBG> tiled_bg_; // Fondo animado de tiles + std::unique_ptr<GameLogo> game_logo_; // Logo del juego + std::unique_ptr<Sprite> mini_logo_sprite_; // Logo JailGames mini + std::unique_ptr<DefineButtons> define_buttons_; // Definición de botones del joystick + std::vector<std::shared_ptr<Player>> players_; // Vector de jugadores - // --- Variables de estado --- - int counter_ = 0; // Temporizador para la pantalla de título - Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad - Section::Name next_section_; // Siguiente sección a cargar - Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título - int num_controllers_; // Número de mandos conectados - TitleState state_; // Estado actual de la sección - bool should_render_start_prompt = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY - bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1 - bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2 + // --- Variables de estado --- + int counter_ = 0; // Temporizador para la pantalla de título + Uint64 ticks_ = 0; // Contador de ticks para ajustar la velocidad + Section::Name next_section_; // Siguiente sección a cargar + Section::Options selection_ = Section::Options::TITLE_TIME_OUT; // Opción elegida en el título + int num_controllers_; // Número de mandos conectados + TitleState state_; // Estado actual de la sección + bool should_render_start_prompt = false; // Indica si se muestra o no el texto de PRESS START BUTTON TO PLAY + bool player1_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 1 + bool player2_start_pressed_ = false; // Indica si se ha pulsdo el boton de empezar a jugar para el jugador 2 - // -- Variables de diseño --- - Anchor anchor_; // Anclas para definir la posición de los elementos del titulo + // -- Variables de diseño --- + Anchor anchor_; // Anclas para definir la posición de los elementos del titulo - // --- Métodos internos --- - void update(); // Actualiza las variables del objeto - void render(); // Dibuja el objeto en pantalla - void checkEvents(); // Comprueba los eventos - void checkInput(); // Comprueba las entradas - void resetCounter(); // Reinicia el contador interno - void swapControllers(); // Intercambia la asignación de mandos a los jugadores - void swapKeyboard(); // Intercambia el teclado de jugador - void showControllers(); // Muestra información sobre los controles y los jugadores - void updateFade(); // Actualiza el efecto de fundido (fade in/out) - void updateState(); // Actualiza el estado actual del título - void updateStartPrompt(); // Actualiza el mensaje de "Pulsa Start" - void renderStartPrompt(); // Dibuja el mensaje de "Pulsa Start" en pantalla - void renderCopyright(); // Dibuja el aviso de copyright - void setState(TitleState state); // Cambia el estado del título - void initPlayers(); // Inicializa los jugadores - void renderPlayers(); // Renderiza los jugadores - void updatePlayers(); // Actualza los jugadores - std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id" + // --- Métodos internos --- + void update(); // Actualiza las variables del objeto + void render(); // Dibuja el objeto en pantalla + void checkEvents(); // Comprueba los eventos + void checkInput(); // Comprueba las entradas + void resetCounter(); // Reinicia el contador interno + void swapControllers(); // Intercambia la asignación de mandos a los jugadores + void swapKeyboard(); // Intercambia el teclado de jugador + void showControllers(); // Muestra información sobre los controles y los jugadores + void updateFade(); // Actualiza el efecto de fundido (fade in/out) + void updateState(); // Actualiza el estado actual del título + void updateStartPrompt(); // Actualiza el mensaje de "Pulsa Start" + void renderStartPrompt(); // Dibuja el mensaje de "Pulsa Start" en pantalla + void renderCopyright(); // Dibuja el aviso de copyright + void setState(TitleState state); // Cambia el estado del título + void initPlayers(); // Inicializa los jugadores + void renderPlayers(); // Renderiza los jugadores + void updatePlayers(); // Actualza los jugadores + std::shared_ptr<Player> getPlayer(int id); // Obtiene un jugador a partir de su "id" }; \ No newline at end of file diff --git a/source/smart_sprite.cpp b/source/smart_sprite.cpp index e890ca1..e73f57a 100644 --- a/source/smart_sprite.cpp +++ b/source/smart_sprite.cpp @@ -1,104 +1,86 @@ #include "smart_sprite.h" -#include "moving_sprite.h" // Para MovingSprite +#include "moving_sprite.h" // Para MovingSprite // Actualiza la posición y comprueba si ha llegado a su destino -void SmartSprite::update() -{ - if (enabled_) - { - MovingSprite::update(); - checkMove(); - checkFinished(); - } +void SmartSprite::update() { + if (enabled_) { + MovingSprite::update(); + checkMove(); + checkFinished(); + } } // Dibuja el sprite -void SmartSprite::render() -{ - if (enabled_) - { - MovingSprite::render(); - } +void SmartSprite::render() { + if (enabled_) { + MovingSprite::render(); + } } // Comprueba el movimiento -void SmartSprite::checkMove() -{ - // Comprueba si se desplaza en el eje X hacia la derecha - if (getAccelX() > 0 || getVelX() > 0) - { - // Comprueba si ha llegado al destino - if (getPosX() > dest_x_) - { - // Lo coloca en posición - setPosX(dest_x_); +void SmartSprite::checkMove() { + // Comprueba si se desplaza en el eje X hacia la derecha + if (getAccelX() > 0 || getVelX() > 0) { + // Comprueba si ha llegado al destino + if (getPosX() > dest_x_) { + // Lo coloca en posición + setPosX(dest_x_); - // Lo detiene - setVelX(0.0f); - setAccelX(0.0f); - } - } - // Comprueba si se desplaza en el eje X hacia la izquierda - else if (getAccelX() < 0 || getVelX() < 0) - { - // Comprueba si ha llegado al destino - if (getPosX() < dest_x_) - { - // Lo coloca en posición - setPosX(dest_x_); + // Lo detiene + setVelX(0.0f); + setAccelX(0.0f); + } + } + // Comprueba si se desplaza en el eje X hacia la izquierda + else if (getAccelX() < 0 || getVelX() < 0) { + // Comprueba si ha llegado al destino + if (getPosX() < dest_x_) { + // Lo coloca en posición + setPosX(dest_x_); - // Lo detiene - setVelX(0.0f); - setAccelX(0.0f); - } - } + // Lo detiene + setVelX(0.0f); + setAccelX(0.0f); + } + } - // Comprueba si se desplaza en el eje Y hacia abajo - if (getAccelY() > 0 || getVelY() > 0) - { - // Comprueba si ha llegado al destino - if (getPosY() > dest_y_) - { - // Lo coloca en posición - setPosY(dest_y_); + // Comprueba si se desplaza en el eje Y hacia abajo + if (getAccelY() > 0 || getVelY() > 0) { + // Comprueba si ha llegado al destino + if (getPosY() > dest_y_) { + // Lo coloca en posición + setPosY(dest_y_); - // Lo detiene - setVelY(0.0f); - setAccelY(0.0f); - } - } - // Comprueba si se desplaza en el eje Y hacia arriba - else if (getAccelY() < 0 || getVelY() < 0) - { - // Comprueba si ha llegado al destino - if (getPosY() < dest_y_) - { - // Lo coloca en posición - setPosY(dest_y_); + // Lo detiene + setVelY(0.0f); + setAccelY(0.0f); + } + } + // Comprueba si se desplaza en el eje Y hacia arriba + else if (getAccelY() < 0 || getVelY() < 0) { + // Comprueba si ha llegado al destino + if (getPosY() < dest_y_) { + // Lo coloca en posición + setPosY(dest_y_); - // Lo detiene - setVelY(0.0f); - setAccelY(0.0f); - } - } + // Lo detiene + setVelY(0.0f); + setAccelY(0.0f); + } + } } // Comprueba si ha terminado -void SmartSprite::checkFinished() -{ - // Comprueba si ha llegado a su destino - on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_); +void SmartSprite::checkFinished() { + // Comprueba si ha llegado a su destino + on_destination_ = (getPosX() == dest_x_ && getPosY() == dest_y_); - if (on_destination_) - { - if (finished_counter_ == 0) - { - finished_ = true; - } - else - { - --finished_counter_; - } - } + if (on_destination_) { + if (finished_counter_ == 0) { + finished_ = true; + } else { + --finished_counter_; + } + } } \ No newline at end of file diff --git a/source/smart_sprite.h b/source/smart_sprite.h index 4fcdaca..f3ea3b8 100644 --- a/source/smart_sprite.h +++ b/source/smart_sprite.h @@ -1,46 +1,45 @@ #pragma once -#include <memory> // Para shared_ptr +#include <memory> // Para shared_ptr -#include "animated_sprite.h" // Para AnimatedSprite +#include "animated_sprite.h" // Para AnimatedSprite class Texture; // Clase SmartSprite: Sprite animado que se mueve hacia un destino y puede deshabilitarse automáticamente -class SmartSprite : public AnimatedSprite -{ -public: - // --- Constructor y destructor --- - explicit SmartSprite(std::shared_ptr<Texture> texture) - : AnimatedSprite(texture) {} - ~SmartSprite() override = default; +class SmartSprite : public AnimatedSprite { + public: + // --- Constructor y destructor --- + explicit SmartSprite(std::shared_ptr<Texture> texture) + : AnimatedSprite(texture) {} + ~SmartSprite() override = default; - // --- Métodos principales --- - void update() override; // Actualiza la posición y comprueba si ha llegado a su destino - void render() override; // Dibuja el sprite + // --- Métodos principales --- + void update() override; // Actualiza la posición y comprueba si ha llegado a su destino + void render() override; // Dibuja el sprite - // --- Getters --- - int getDestX() const { return dest_x_; } // Obtiene la posición de destino en X - int getDestY() const { return dest_y_; } // Obtiene la posición de destino en Y - bool isOnDestination() const { return on_destination_; } // Indica si está en el destino - bool hasFinished() const { return finished_; } // Indica si ya ha terminado + // --- Getters --- + int getDestX() const { return dest_x_; } // Obtiene la posición de destino en X + int getDestY() const { return dest_y_; } // Obtiene la posición de destino en Y + bool isOnDestination() const { return on_destination_; } // Indica si está en el destino + bool hasFinished() const { return finished_; } // Indica si ya ha terminado - // --- Setters --- - void setFinishedCounter(int value) { finished_counter_ = value; } // Establece el contador para deshabilitarlo - void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X - void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y - void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto + // --- Setters --- + void setFinishedCounter(int value) { finished_counter_ = value; } // Establece el contador para deshabilitarlo + void setDestX(int x) { dest_x_ = x; } // Establece la posición de destino en X + void setDestY(int y) { dest_y_ = y; } // Establece la posición de destino en Y + void setEnabled(bool value) { enabled_ = value; } // Habilita o deshabilita el objeto -private: - // --- Variables internas --- - bool on_destination_ = false; // Indica si está en el destino - int dest_x_ = 0; // Posición de destino en el eje X - int dest_y_ = 0; // Posición de destino en el eje Y - int finished_counter_ = 0; // Contador para deshabilitarlo - bool finished_ = false; // Indica si ya ha terminado - bool enabled_ = false; // Indica si el objeto está habilitado + private: + // --- Variables internas --- + bool on_destination_ = false; // Indica si está en el destino + int dest_x_ = 0; // Posición de destino en el eje X + int dest_y_ = 0; // Posición de destino en el eje Y + int finished_counter_ = 0; // Contador para deshabilitarlo + bool finished_ = false; // Indica si ya ha terminado + bool enabled_ = false; // Indica si el objeto está habilitado - // --- Métodos internos --- - void checkFinished(); // Comprueba si ha terminado - void checkMove(); // Comprueba el movimiento + // --- Métodos internos --- + void checkFinished(); // Comprueba si ha terminado + void checkMove(); // Comprueba el movimiento }; \ No newline at end of file diff --git a/source/sprite.cpp b/source/sprite.cpp index 3a535b7..ab6d0e8 100644 --- a/source/sprite.cpp +++ b/source/sprite.cpp @@ -1,46 +1,42 @@ #include "sprite.h" -#include "texture.h" // Para Texture +#include "texture.h" // Para Texture // Constructor Sprite::Sprite(std::shared_ptr<Texture> texture, float x, float y, float w, float h) - : texture_(texture), - pos_((SDL_FRect){x, y, w, h}), - sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} + : texture_(texture), + pos_((SDL_FRect){x, y, w, h}), + sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} Sprite::Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect) - : texture_(texture), - pos_(rect), - sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} + : texture_(texture), + pos_(rect), + sprite_clip_((SDL_FRect){0, 0, pos_.w, pos_.h}) {} Sprite::Sprite(std::shared_ptr<Texture> texture) - : texture_(texture), - pos_(SDL_FRect{0, 0, static_cast<float>(texture_->getWidth()), static_cast<float>(texture_->getHeight())}), - sprite_clip_(pos_) {} + : texture_(texture), + pos_(SDL_FRect{0, 0, static_cast<float>(texture_->getWidth()), static_cast<float>(texture_->getHeight())}), + sprite_clip_(pos_) {} // Muestra el sprite por pantalla -void Sprite::render() -{ - texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_, zoom_); +void Sprite::render() { + texture_->render(pos_.x, pos_.y, &sprite_clip_, zoom_, zoom_); } // Establece la posición del objeto -void Sprite::setPosition(float x, float y) -{ - pos_.x = x; - pos_.y = y; +void Sprite::setPosition(float x, float y) { + pos_.x = x; + pos_.y = y; } // Establece la posición del objeto -void Sprite::setPosition(SDL_FPoint p) -{ - pos_.x = p.x; - pos_.y = p.y; +void Sprite::setPosition(SDL_FPoint p) { + pos_.x = p.x; + pos_.y = p.y; } // Reinicia las variables a cero -void Sprite::clear() -{ - pos_ = {0, 0, 0, 0}; - sprite_clip_ = {0, 0, 0, 0}; +void Sprite::clear() { + pos_ = {0, 0, 0, 0}; + sprite_clip_ = {0, 0, 0, 0}; } \ No newline at end of file diff --git a/source/sprite.h b/source/sprite.h index 472ef52..933c00d 100644 --- a/source/sprite.h +++ b/source/sprite.h @@ -1,14 +1,14 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint -#include <memory> // Para shared_ptr +#include <SDL3/SDL.h> // Para SDL_FRect, SDL_FPoint + +#include <memory> // Para shared_ptr class Texture; // Clase Sprite: representa un objeto gráfico básico con posición, tamaño y textura -class Sprite -{ -public: +class Sprite { + public: // --- Constructores y destructor --- Sprite(std::shared_ptr<Texture> texture, float x, float y, float w, float h); Sprite(std::shared_ptr<Texture> texture, SDL_FRect rect); @@ -16,8 +16,8 @@ public: virtual ~Sprite() = default; // --- Renderizado y control --- - virtual void render(); // Muestra el sprite por pantalla - virtual void clear(); // Reinicia las variables a cero + virtual void render(); // Muestra el sprite por pantalla + virtual void clear(); // Reinicia las variables a cero // --- Getters de posición y tamaño --- float getX() const { return pos_.x; } @@ -52,10 +52,10 @@ public: std::shared_ptr<Texture> getTexture() const { return texture_; } void setTexture(std::shared_ptr<Texture> texture) { texture_ = texture; } -protected: + protected: // --- Variables internas --- - std::shared_ptr<Texture> texture_; // Textura donde están todos los dibujos del sprite - SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite - SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla - double zoom_ = 1.0f; // Zoom aplicado a la textura + std::shared_ptr<Texture> texture_; // Textura donde están todos los dibujos del sprite + SDL_FRect pos_; // Posición y tamaño donde dibujar el sprite + SDL_FRect sprite_clip_; // Rectángulo de origen de la textura que se dibujará en pantalla + double zoom_ = 1.0f; // Zoom aplicado a la textura }; \ No newline at end of file diff --git a/source/stage.cpp b/source/stage.cpp index 903c916..d61152a 100644 --- a/source/stage.cpp +++ b/source/stage.cpp @@ -1,47 +1,43 @@ #include "stage.h" -#include <algorithm> // Para min -#include <vector> // Para vector +#include <algorithm> // Para min +#include <vector> // Para vector -namespace Stage -{ +namespace Stage { - std::vector<Stage> stages; // Variable con los datos de cada pantalla - int power = 0; // Poder acumulado en la fase - int total_power = 0; // Poder total necesario para completar el juego - int number = 0; // Fase actual - bool power_can_be_added = true; // Habilita la recolecta de poder +std::vector<Stage> stages; // Variable con los datos de cada pantalla +int power = 0; // Poder acumulado en la fase +int total_power = 0; // Poder total necesario para completar el juego +int number = 0; // Fase actual +bool power_can_be_added = true; // Habilita la recolecta de poder - // Devuelve una fase - Stage get(int index) { return stages.at(std::min(9, index)); } +// Devuelve una fase +Stage get(int index) { return stages.at(std::min(9, index)); } - // Inicializa las variables del namespace Stage - void init() - { - stages.clear(); - stages.emplace_back(Stage(200, 7 + (4 * 1), 7 + (4 * 3))); - stages.emplace_back(Stage(300, 7 + (4 * 2), 7 + (4 * 4))); - stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5))); - stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5))); - stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6))); - stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6))); - stages.emplace_back(Stage(650, 7 + (4 * 5), 7 + (4 * 7))); - stages.emplace_back(Stage(750, 7 + (4 * 5), 7 + (4 * 7))); - stages.emplace_back(Stage(850, 7 + (4 * 6), 7 + (4 * 8))); - stages.emplace_back(Stage(950, 7 + (4 * 7), 7 + (4 * 10))); +// Inicializa las variables del namespace Stage +void init() { + stages.clear(); + stages.emplace_back(Stage(200, 7 + (4 * 1), 7 + (4 * 3))); + stages.emplace_back(Stage(300, 7 + (4 * 2), 7 + (4 * 4))); + stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5))); + stages.emplace_back(Stage(600, 7 + (4 * 3), 7 + (4 * 5))); + stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6))); + stages.emplace_back(Stage(600, 7 + (4 * 4), 7 + (4 * 6))); + stages.emplace_back(Stage(650, 7 + (4 * 5), 7 + (4 * 7))); + stages.emplace_back(Stage(750, 7 + (4 * 5), 7 + (4 * 7))); + stages.emplace_back(Stage(850, 7 + (4 * 6), 7 + (4 * 8))); + stages.emplace_back(Stage(950, 7 + (4 * 7), 7 + (4 * 10))); - power = 0; - total_power = 0; - number = 0; + power = 0; + total_power = 0; + number = 0; +} + +// Añade poder +void addPower(int amount) { + if (power_can_be_added) { + power += amount; + total_power += amount; } - - // Añade poder - void addPower(int amount) - { - if (power_can_be_added) - { - power += amount; - total_power += amount; - } - } -} \ No newline at end of file +} +} // namespace Stage \ No newline at end of file diff --git a/source/stage.h b/source/stage.h index 764d25c..7cd6e29 100644 --- a/source/stage.h +++ b/source/stage.h @@ -7,29 +7,27 @@ Permite consultar y modificar el poder necesario, la amenaza y el estado de cada fase. */ -namespace Stage -{ - // --- Estructura con los datos de una fase --- - struct Stage - { - int power_to_complete; // Cantidad de poder que se necesita para completar la fase - int min_menace; // Umbral mínimo de amenaza de la fase - int max_menace; // Umbral máximo de amenaza de la fase +namespace Stage { +// --- Estructura con los datos de una fase --- +struct Stage { + int power_to_complete; // Cantidad de poder que se necesita para completar la fase + int min_menace; // Umbral mínimo de amenaza de la fase + int max_menace; // Umbral máximo de amenaza de la fase - // Constructor - Stage(int power_to_complete, int min_menace, int max_menace) - : power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {} - }; + // Constructor + Stage(int power_to_complete, int min_menace, int max_menace) + : power_to_complete(power_to_complete), min_menace(min_menace), max_menace(max_menace) {} +}; - // --- Variables globales del estado de las fases --- - extern std::vector<Stage> stages; // Vector con los datos de cada pantalla - extern int power; // Poder acumulado en la fase actual - extern int total_power; // Poder total necesario para completar el juego - extern int number; // Índice de la fase actual - extern bool power_can_be_added; // Indica si se puede añadir poder a la fase +// --- Variables globales del estado de las fases --- +extern std::vector<Stage> stages; // Vector con los datos de cada pantalla +extern int power; // Poder acumulado en la fase actual +extern int total_power; // Poder total necesario para completar el juego +extern int number; // Índice de la fase actual +extern bool power_can_be_added; // Indica si se puede añadir poder a la fase - // --- Funciones principales --- - Stage get(int index); // Devuelve una fase por índice - void init(); // Inicializa las variables del namespace Stage - void addPower(int amount); // Añade poder a la fase actual -} \ No newline at end of file +// --- Funciones principales --- +Stage get(int index); // Devuelve una fase por índice +void init(); // Inicializa las variables del namespace Stage +void addPower(int amount); // Añade poder a la fase actual +} // namespace Stage \ No newline at end of file diff --git a/source/tabe.cpp b/source/tabe.cpp index 58de023..2cac0ae 100644 --- a/source/tabe.cpp +++ b/source/tabe.cpp @@ -1,16 +1,17 @@ // IWYU pragma: no_include <bits/std_abs.h> #include "tabe.h" -#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_GetTicks -#include <stdlib.h> // Para rand, abs +#include <SDL3/SDL.h> // Para SDL_FlipMode, SDL_GetTicks +#include <stdlib.h> // Para rand, abs + #include <algorithm> // Para max #include <cmath> // Para abs #include <string> // Para basic_string -#include "audio.h" // Para Audio -#include "param.h" // Para Param, ParamGame, param -#include "resource.h" // Para Resource -#include "utils.h" // Para Zone +#include "audio.h" // Para Audio +#include "param.h" // Para Param, ParamGame, param +#include "resource.h" // Para Resource +#include "utils.h" // Para Zone // Constructor Tabe::Tabe() @@ -18,33 +19,27 @@ Tabe::Tabe() timer_(TabeTimer(2.5f, 4.0f)) {} // Actualiza la lógica -void Tabe::update() -{ - if (enabled_) - { +void Tabe::update() { + if (enabled_) { sprite_->update(); move(); updateState(); } timer_.update(); - if (timer_.should_spawn()) - { + if (timer_.should_spawn()) { enable(); } } // Dibuja el objeto -void Tabe::render() -{ - if (enabled_) - { +void Tabe::render() { + if (enabled_) { sprite_->render(); } } // Mueve el objeto -void Tabe::move() -{ +void Tabe::move() { const int x = static_cast<int>(x_); speed_ += accel_; x_ += speed_; @@ -53,48 +48,37 @@ void Tabe::move() // Comprueba si sale por los bordes const float min_x = param.game.game_area.rect.x - WIDTH_; const float max_x = param.game.game_area.rect.x + param.game.game_area.rect.w; - switch (destiny_) - { - case TabeDirection::TO_THE_LEFT: - { - if (x_ < min_x) - { - disable(); + switch (destiny_) { + case TabeDirection::TO_THE_LEFT: { + if (x_ < min_x) { + disable(); + } + if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_ && direction_ == TabeDirection::TO_THE_RIGHT) { + setRandomFlyPath(TabeDirection::TO_THE_LEFT, 80); + x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_; + } + break; } - if (x_ > param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_ && direction_ == TabeDirection::TO_THE_RIGHT) - { - setRandomFlyPath(TabeDirection::TO_THE_LEFT, 80); - x_ = param.game.game_area.rect.x + param.game.game_area.rect.w - WIDTH_; + + case TabeDirection::TO_THE_RIGHT: { + if (x_ > max_x) { + disable(); + } + if (x_ < param.game.game_area.rect.x && direction_ == TabeDirection::TO_THE_LEFT) { + setRandomFlyPath(TabeDirection::TO_THE_RIGHT, 80); + x_ = param.game.game_area.rect.x; + } + break; } - break; + default: + break; } - case TabeDirection::TO_THE_RIGHT: - { - if (x_ > max_x) - { - disable(); - } - if (x_ < param.game.game_area.rect.x && direction_ == TabeDirection::TO_THE_LEFT) - { - setRandomFlyPath(TabeDirection::TO_THE_RIGHT, 80); - x_ = param.game.game_area.rect.x; - } - break; - } - default: - break; - } - - if (fly_distance_ <= 0) - { - if (waiting_counter_ > 0) - { + if (fly_distance_ <= 0) { + if (waiting_counter_ > 0) { accel_ = speed_ = 0.0f; --waiting_counter_; - } - else - { + } else { constexpr int CHOICES = 4; const TabeDirection left[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT}; const TabeDirection right[CHOICES] = {TabeDirection::TO_THE_LEFT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT, TabeDirection::TO_THE_RIGHT}; @@ -107,10 +91,8 @@ void Tabe::move() } // Habilita el objeto -void Tabe::enable() -{ - if (!enabled_) - { +void Tabe::enable() { + if (!enabled_) { enabled_ = true; has_bonus_ = true; hit_counter_ = 0; @@ -130,8 +112,7 @@ void Tabe::enable() } // Establece un vuelo aleatorio -void Tabe::setRandomFlyPath(TabeDirection direction, int lenght) -{ +void Tabe::setRandomFlyPath(TabeDirection direction, int lenght) { direction_ = direction; fly_distance_ = lenght; waiting_counter_ = 5 + rand() % 15; @@ -139,72 +120,61 @@ void Tabe::setRandomFlyPath(TabeDirection direction, int lenght) constexpr float SPEED = 2.0f; - switch (direction) - { - case TabeDirection::TO_THE_LEFT: - { - speed_ = -1.0f * SPEED; - accel_ = -1.0f * (1 + rand() % 10) / 30.0f; - sprite_->setFlip(SDL_FLIP_NONE); - break; - } + switch (direction) { + case TabeDirection::TO_THE_LEFT: { + speed_ = -1.0f * SPEED; + accel_ = -1.0f * (1 + rand() % 10) / 30.0f; + sprite_->setFlip(SDL_FLIP_NONE); + break; + } - case TabeDirection::TO_THE_RIGHT: - { - speed_ = SPEED; - accel_ = (1 + rand() % 10) / 30.0f; - sprite_->setFlip(SDL_FLIP_HORIZONTAL); - break; - } + case TabeDirection::TO_THE_RIGHT: { + speed_ = SPEED; + accel_ = (1 + rand() % 10) / 30.0f; + sprite_->setFlip(SDL_FLIP_HORIZONTAL); + break; + } - default: - break; + default: + break; } } // Establece el estado -void Tabe::setState(TabeState state) -{ - if (enabled_) - { +void Tabe::setState(TabeState state) { + if (enabled_) { state_ = state; - switch (state) - { - case TabeState::FLY: - sprite_->setCurrentAnimation("fly"); - break; + switch (state) { + case TabeState::FLY: + sprite_->setCurrentAnimation("fly"); + break; - case TabeState::HIT: - sprite_->setCurrentAnimation("hit"); - hit_counter_ = 5; - ++number_of_hits_; - break; + case TabeState::HIT: + sprite_->setCurrentAnimation("hit"); + hit_counter_ = 5; + ++number_of_hits_; + break; - default: - break; + default: + break; } } } // Actualiza el estado -void Tabe::updateState() -{ - if (state_ == TabeState::HIT) - { +void Tabe::updateState() { + if (state_ == TabeState::HIT) { --hit_counter_; - if (hit_counter_ == 0) - { + if (hit_counter_ == 0) { setState(TabeState::FLY); } } } // Intenta obtener el bonus -bool Tabe::tryToGetBonus() -{ - if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0) - { +bool Tabe::tryToGetBonus() { + if (has_bonus_ && rand() % std::max(1, 15 - number_of_hits_) == 0) { has_bonus_ = false; return true; } @@ -212,16 +182,14 @@ bool Tabe::tryToGetBonus() } // Actualiza el temporizador -void Tabe::updateTimer() -{ +void Tabe::updateTimer() { timer_.current_time = SDL_GetTicks(); timer_.delta_time = timer_.current_time - timer_.last_time; timer_.last_time = timer_.current_time; } // Deshabilita el objeto -void Tabe::disable() -{ +void Tabe::disable() { enabled_ = false; timer_.reset(); } \ No newline at end of file diff --git a/source/tabe.h b/source/tabe.h index cf492ce..43227a7 100644 --- a/source/tabe.h +++ b/source/tabe.h @@ -1,122 +1,111 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint32, SDL_GetTicks, SDL_FRect -#include <stdlib.h> // Para rand -#include <memory> // Para unique_ptr +#include <SDL3/SDL.h> // Para Uint32, SDL_GetTicks, SDL_FRect +#include <stdlib.h> // Para rand -#include "animated_sprite.h" // Para AnimatedSprite +#include <memory> // Para unique_ptr + +#include "animated_sprite.h" // Para AnimatedSprite // --- Enumeraciones para dirección y estado --- -enum class TabeDirection : int -{ +enum class TabeDirection : int { TO_THE_LEFT = 0, TO_THE_RIGHT = 1, }; -enum class TabeState : int -{ +enum class TabeState : int { FLY = 0, HIT = 1, }; // --- Estructura para el temporizador del Tabe --- -struct TabeTimer -{ - Uint32 time_until_next_spawn; // Tiempo restante para la próxima aparición - Uint32 min_spawn_time; // Tiempo mínimo entre apariciones - Uint32 max_spawn_time; // Tiempo máximo entre apariciones - Uint32 current_time; // Tiempo actual - Uint32 delta_time; // Diferencia de tiempo desde la última actualización - Uint32 last_time; // Tiempo de la última actualización +struct TabeTimer { + Uint32 time_until_next_spawn; // Tiempo restante para la próxima aparición + Uint32 min_spawn_time; // Tiempo mínimo entre apariciones + Uint32 max_spawn_time; // Tiempo máximo entre apariciones + Uint32 current_time; // Tiempo actual + Uint32 delta_time; // Diferencia de tiempo desde la última actualización + Uint32 last_time; // Tiempo de la última actualización // Constructor TabeTimer(float minTime, float maxTime) - : min_spawn_time(minTime * 60000), max_spawn_time(maxTime * 60000), - current_time(SDL_GetTicks()) - { + : min_spawn_time(minTime * 60000), max_spawn_time(maxTime * 60000), current_time(SDL_GetTicks()) { reset(); } // Restablece el temporizador con un nuevo tiempo hasta la próxima aparición - void reset() - { + void reset() { Uint32 range = max_spawn_time - min_spawn_time; time_until_next_spawn = min_spawn_time + rand() % (range + 1); last_time = SDL_GetTicks(); } // Actualiza el temporizador, decrementando el tiempo hasta la próxima aparición - void update() - { + void update() { current_time = SDL_GetTicks(); delta_time = current_time - last_time; last_time = current_time; - if (time_until_next_spawn > delta_time) - { + if (time_until_next_spawn > delta_time) { time_until_next_spawn -= delta_time; - } - else - { + } else { time_until_next_spawn = 0; } } // Indica si el temporizador ha finalizado - bool should_spawn() const - { + bool should_spawn() const { return time_until_next_spawn == 0; } }; // --- Clase Tabe --- -class Tabe -{ -public: +class Tabe { + public: // --- Constructores y destructor --- Tabe(); ~Tabe() = default; // --- Métodos principales --- - void update(); // Actualiza la lógica - void render(); // Dibuja el objeto - void enable(); // Habilita el objeto - void setState(TabeState state); // Establece el estado - bool tryToGetBonus(); // Intenta obtener el bonus + void update(); // Actualiza la lógica + void render(); // Dibuja el objeto + void enable(); // Habilita el objeto + void setState(TabeState state); // Establece el estado + bool tryToGetBonus(); // Intenta obtener el bonus // --- Getters --- - SDL_FRect &getCollider() { return sprite_->getRect(); } // Obtiene el área de colisión - bool isEnabled() const { return enabled_; } // Indica si el objeto está activo + SDL_FRect &getCollider() { return sprite_->getRect(); } // Obtiene el área de colisión + bool isEnabled() const { return enabled_; } // Indica si el objeto está activo -private: + private: // --- Constantes --- static constexpr int WIDTH_ = 32; static constexpr int HEIGHT_ = 32; // --- Objetos y punteros --- - std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos y animaciones + std::unique_ptr<AnimatedSprite> sprite_; // Sprite con los gráficos y animaciones // --- Variables de estado --- - float x_ = 0; // Posición X - float y_ = 0; // Posición Y - float speed_ = 0.0f; // Velocidad de movimiento - float accel_ = 0.0f; // Aceleración - int fly_distance_ = 0; // Distancia de vuelo - int waiting_counter_ = 0; // Tiempo que pasa quieto - bool enabled_ = false; // Indica si el objeto está activo - TabeDirection direction_ = TabeDirection::TO_THE_LEFT; // Dirección actual - TabeDirection destiny_ = TabeDirection::TO_THE_LEFT; // Destino - TabeState state_ = TabeState::FLY; // Estado actual - int hit_counter_ = 0; // Contador para el estado HIT - int number_of_hits_ = 0; // Cantidad de disparos recibidos - bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar - TabeTimer timer_; // Temporizador para gestionar la aparición + float x_ = 0; // Posición X + float y_ = 0; // Posición Y + float speed_ = 0.0f; // Velocidad de movimiento + float accel_ = 0.0f; // Aceleración + int fly_distance_ = 0; // Distancia de vuelo + int waiting_counter_ = 0; // Tiempo que pasa quieto + bool enabled_ = false; // Indica si el objeto está activo + TabeDirection direction_ = TabeDirection::TO_THE_LEFT; // Dirección actual + TabeDirection destiny_ = TabeDirection::TO_THE_LEFT; // Destino + TabeState state_ = TabeState::FLY; // Estado actual + int hit_counter_ = 0; // Contador para el estado HIT + int number_of_hits_ = 0; // Cantidad de disparos recibidos + bool has_bonus_ = true; // Indica si aún tiene el bonus para soltar + TabeTimer timer_; // Temporizador para gestionar la aparición // --- Métodos internos --- - void move(); // Mueve el objeto - void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite - void setRandomFlyPath(TabeDirection direction, int lenght); // Establece un vuelo aleatorio - void updateState(); // Actualiza el estado - void updateTimer(); // Actualiza el temporizador - void disable(); // Deshabilita el objeto + void move(); // Mueve el objeto + void shiftSprite() { sprite_->setPos(x_, y_); } // Actualiza la posición del sprite + void setRandomFlyPath(TabeDirection direction, int lenght); // Establece un vuelo aleatorio + void updateState(); // Actualiza el estado + void updateTimer(); // Actualiza el temporizador + void disable(); // Deshabilita el objeto }; \ No newline at end of file diff --git a/source/text.cpp b/source/text.cpp index e358d4f..08efa8d 100644 --- a/source/text.cpp +++ b/source/text.cpp @@ -1,290 +1,258 @@ #include "text.h" -#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_GetRenderTarget, Uint8 -#include <stddef.h> // Para size_t -#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream -#include <iostream> // Para cerr +#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_GetRenderTarget, Uint8 +#include <stddef.h> // Para size_t + +#include <fstream> // Para basic_ifstream, basic_istream, basic_ostream +#include <iostream> // Para cerr #include <stdexcept> // Para runtime_error -#include "screen.h" // Para Screen -#include "sprite.h" // Para Sprite -#include "texture.h" // Para Texture -#include "utils.h" // Para Color, getFileName, printWithDots +#include "screen.h" // Para Screen +#include "sprite.h" // Para Sprite +#include "texture.h" // Para Texture +#include "utils.h" // Para Color, getFileName, printWithDots // Llena una estructuta TextFile desde un fichero -std::shared_ptr<TextFile> loadTextFile(const std::string &file_path) -{ - auto tf = std::make_shared<TextFile>(); +std::shared_ptr<TextFile> loadTextFile(const std::string &file_path) { + auto tf = std::make_shared<TextFile>(); - // Inicializa a cero el vector con las coordenadas - for (int i = 0; i < 128; ++i) - { - tf->offset[i].x = 0; - tf->offset[i].y = 0; - tf->offset[i].w = 0; - tf->box_width = 0; - tf->box_height = 0; - } + // Inicializa a cero el vector con las coordenadas + for (int i = 0; i < 128; ++i) { + tf->offset[i].x = 0; + tf->offset[i].y = 0; + tf->offset[i].w = 0; + tf->box_width = 0; + tf->box_height = 0; + } - // Abre el fichero para leer los valores - std::ifstream file(file_path); + // Abre el fichero para leer los valores + std::ifstream file(file_path); - if (file.is_open() && file.good()) - { - std::string buffer; + if (file.is_open() && file.good()) { + std::string buffer; - // Lee los dos primeros valores del fichero - std::getline(file, buffer); - std::getline(file, buffer); - tf->box_width = std::stoi(buffer); + // Lee los dos primeros valores del fichero + std::getline(file, buffer); + std::getline(file, buffer); + tf->box_width = std::stoi(buffer); - std::getline(file, buffer); - std::getline(file, buffer); - tf->box_height = std::stoi(buffer); + std::getline(file, buffer); + std::getline(file, buffer); + tf->box_height = std::stoi(buffer); - // lee el resto de datos del fichero - auto index = 32; - auto line_read = 0; - while (std::getline(file, buffer)) - { - // Almacena solo las lineas impares - if (line_read % 2 == 1) - tf->offset[index++].w = std::stoi(buffer); + // lee el resto de datos del fichero + auto index = 32; + auto line_read = 0; + while (std::getline(file, buffer)) { + // Almacena solo las lineas impares + if (line_read % 2 == 1) + tf->offset[index++].w = std::stoi(buffer); - // Limpia el buffer - buffer.clear(); - line_read++; - }; + // Limpia el buffer + buffer.clear(); + line_read++; + }; - // Cierra el fichero - printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]"); - file.close(); - } + // Cierra el fichero + printWithDots("Text File : ", getFileName(file_path), "[ LOADED ]"); + file.close(); + } - // El fichero no se puede abrir - else - { - std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl; - throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); - } + // El fichero no se puede abrir + else { + std::cerr << "Error: Fichero no encontrado " << getFileName(file_path) << std::endl; + throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); + } - // Establece las coordenadas para cada caracter ascii de la cadena y su ancho - for (int i = 32; i < 128; ++i) - { - tf->offset[i].x = ((i - 32) % 15) * tf->box_width; - tf->offset[i].y = ((i - 32) / 15) * tf->box_height; - } + // Establece las coordenadas para cada caracter ascii de la cadena y su ancho + for (int i = 32; i < 128; ++i) { + tf->offset[i].x = ((i - 32) % 15) * tf->box_width; + tf->offset[i].y = ((i - 32) / 15) * tf->box_height; + } - return tf; + return tf; } // Constructor -Text::Text(std::shared_ptr<Texture> texture, const std::string &text_file) -{ - // Carga los offsets desde el fichero - auto tf = loadTextFile(text_file); +Text::Text(std::shared_ptr<Texture> texture, const std::string &text_file) { + // Carga los offsets desde el fichero + auto tf = loadTextFile(text_file); - // Inicializa variables desde la estructura - box_height_ = tf->box_height; - box_width_ = tf->box_width; - for (int i = 0; i < 128; ++i) - { - offset_[i].x = tf->offset[i].x; - offset_[i].y = tf->offset[i].y; - offset_[i].w = tf->offset[i].w; - } + // Inicializa variables desde la estructura + box_height_ = tf->box_height; + box_width_ = tf->box_width; + for (int i = 0; i < 128; ++i) { + offset_[i].x = tf->offset[i].x; + offset_[i].y = tf->offset[i].y; + offset_[i].w = tf->offset[i].w; + } - // Crea los objetos - sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)}); + // Crea los objetos + sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)}); - // Inicializa variables - fixed_width_ = false; + // Inicializa variables + fixed_width_ = false; } // Constructor -Text::Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file) -{ - // Inicializa variables desde la estructura - box_height_ = text_file->box_height; - box_width_ = text_file->box_width; - for (int i = 0; i < 128; ++i) - { - offset_[i].x = text_file->offset[i].x; - offset_[i].y = text_file->offset[i].y; - offset_[i].w = text_file->offset[i].w; - } +Text::Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file) { + // Inicializa variables desde la estructura + box_height_ = text_file->box_height; + box_width_ = text_file->box_width; + for (int i = 0; i < 128; ++i) { + offset_[i].x = text_file->offset[i].x; + offset_[i].y = text_file->offset[i].y; + offset_[i].w = text_file->offset[i].w; + } - // Crea los objetos - sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)}); + // Crea los objetos + sprite_ = std::make_unique<Sprite>(texture, (SDL_FRect){0, 0, static_cast<float>(box_width_), static_cast<float>(box_height_)}); - // Inicializa variables - fixed_width_ = false; + // Inicializa variables + fixed_width_ = false; } // Escribe texto en pantalla -void Text::write(int x, int y, const std::string &text, int kerning, int lenght) -{ - int shift = 0; +void Text::write(int x, int y, const std::string &text, int kerning, int lenght) { + int shift = 0; - if (lenght == -1) - lenght = text.length(); + if (lenght == -1) + lenght = text.length(); - sprite_->setY(y); - for (int i = 0; i < lenght; ++i) - { - auto index = static_cast<int>(text[i]); - sprite_->setSpriteClip(offset_[index].x, offset_[index].y, box_width_, box_height_); - sprite_->setX(x + shift); - sprite_->render(); - shift += offset_[static_cast<int>(text[i])].w + kerning; - } + sprite_->setY(y); + for (int i = 0; i < lenght; ++i) { + auto index = static_cast<int>(text[i]); + sprite_->setSpriteClip(offset_[index].x, offset_[index].y, box_width_, box_height_); + sprite_->setX(x + shift); + sprite_->render(); + shift += offset_[static_cast<int>(text[i])].w + kerning; + } } // Escribe texto en pantalla -void Text::write2X(int x, int y, const std::string &text, int kerning) -{ - int shift = 0; - for (size_t i = 0; i < text.length(); ++i) - { - auto index = static_cast<size_t>(text[i]); - SDL_FRect rect = {static_cast<float>(offset_[index].x), static_cast<float>(offset_[index].y), static_cast<float>(box_width_), static_cast<float>(box_height_)}; - sprite_->getTexture()->render(x + shift, y, &rect, 2.0f, 2.0f); - shift += (offset_[index].w + kerning) * 2; - } +void Text::write2X(int x, int y, const std::string &text, int kerning) { + int shift = 0; + for (size_t i = 0; i < text.length(); ++i) { + auto index = static_cast<size_t>(text[i]); + SDL_FRect rect = {static_cast<float>(offset_[index].x), static_cast<float>(offset_[index].y), static_cast<float>(box_width_), static_cast<float>(box_height_)}; + sprite_->getTexture()->render(x + shift, y, &rect, 2.0f, 2.0f); + shift += (offset_[index].w + kerning) * 2; + } } // Escribe el texto en una textura -std::shared_ptr<Texture> Text::writeToTexture(const std::string &text, int zoom, int kerning) -{ - auto renderer = Screen::get()->getRenderer(); - auto texture = std::make_shared<Texture>(renderer); - auto width = lenght(text, kerning) * zoom; - auto height = box_height_ * zoom; - auto temp = SDL_GetRenderTarget(renderer); - texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); - texture->setBlendMode(SDL_BLENDMODE_BLEND); - texture->setAsRenderTarget(renderer); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - zoom == 1 ? write(0, 0, text, kerning) : write2X(0, 0, text, kerning); - SDL_SetRenderTarget(renderer, temp); +std::shared_ptr<Texture> Text::writeToTexture(const std::string &text, int zoom, int kerning) { + auto renderer = Screen::get()->getRenderer(); + auto texture = std::make_shared<Texture>(renderer); + auto width = lenght(text, kerning) * zoom; + auto height = box_height_ * zoom; + auto temp = SDL_GetRenderTarget(renderer); + texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); + texture->setBlendMode(SDL_BLENDMODE_BLEND); + texture->setAsRenderTarget(renderer); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + zoom == 1 ? write(0, 0, text, kerning) : write2X(0, 0, text, kerning); + SDL_SetRenderTarget(renderer, temp); - return texture; + return texture; } // Escribe el texto con extras en una textura -std::shared_ptr<Texture> Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) -{ - auto renderer = Screen::get()->getRenderer(); - auto texture = std::make_shared<Texture>(renderer); - auto width = Text::lenght(text, kerning) + shadow_distance; - auto height = box_height_ + shadow_distance; - auto temp = SDL_GetRenderTarget(renderer); - texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); - texture->setBlendMode(SDL_BLENDMODE_BLEND); - texture->setAsRenderTarget(renderer); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - writeDX(flags, 0, 0, text, kerning, textColor, shadow_distance, shadow_color, lenght); - SDL_SetRenderTarget(renderer, temp); +std::shared_ptr<Texture> Text::writeDXToTexture(Uint8 flags, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) { + auto renderer = Screen::get()->getRenderer(); + auto texture = std::make_shared<Texture>(renderer); + auto width = Text::lenght(text, kerning) + shadow_distance; + auto height = box_height_ + shadow_distance; + auto temp = SDL_GetRenderTarget(renderer); + texture->createBlank(width, height, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET); + texture->setBlendMode(SDL_BLENDMODE_BLEND); + texture->setAsRenderTarget(renderer); + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + writeDX(flags, 0, 0, text, kerning, textColor, shadow_distance, shadow_color, lenght); + SDL_SetRenderTarget(renderer, temp); - return texture; + return texture; } // Escribe el texto con colores -void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int lenght) -{ - sprite_->getTexture()->setColor(color.r, color.g, color.b); - write(x, y, text, kerning, lenght); - sprite_->getTexture()->setColor(255, 255, 255); +void Text::writeColored(int x, int y, const std::string &text, Color color, int kerning, int lenght) { + sprite_->getTexture()->setColor(color.r, color.g, color.b); + write(x, y, text, kerning, lenght); + sprite_->getTexture()->setColor(255, 255, 255); } // Escribe el texto con sombra -void Text::writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance, int kerning, int lenght) -{ - sprite_->getTexture()->setColor(color.r, color.g, color.b); - write(x + shadow_distance, y + shadow_distance, text, kerning, lenght); - sprite_->getTexture()->setColor(255, 255, 255); - write(x, y, text, kerning, lenght); +void Text::writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance, int kerning, int lenght) { + sprite_->getTexture()->setColor(color.r, color.g, color.b); + write(x + shadow_distance, y + shadow_distance, text, kerning, lenght); + sprite_->getTexture()->setColor(255, 255, 255); + write(x, y, text, kerning, lenght); } // Escribe el texto centrado en un punto x -void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght) -{ - x -= (Text::lenght(text, kerning) / 2); - write(x, y, text, kerning, lenght); +void Text::writeCentered(int x, int y, const std::string &text, int kerning, int lenght) { + x -= (Text::lenght(text, kerning) / 2); + write(x, y, text, kerning, lenght); } // Escribe texto con extras -void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) -{ - const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER); - const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW); - const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR); - const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE); +void Text::writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning, Color textColor, Uint8 shadow_distance, Color shadow_color, int lenght) { + const auto centered = ((flags & TEXT_CENTER) == TEXT_CENTER); + const auto shadowed = ((flags & TEXT_SHADOW) == TEXT_SHADOW); + const auto colored = ((flags & TEXT_COLOR) == TEXT_COLOR); + const auto stroked = ((flags & TEXT_STROKE) == TEXT_STROKE); - if (centered) - { - x -= (Text::lenght(text, kerning) / 2); - } + if (centered) { + x -= (Text::lenght(text, kerning) / 2); + } - if (shadowed) - { - writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght); - } + if (shadowed) { + writeColored(x + shadow_distance, y + shadow_distance, text, shadow_color, kerning, lenght); + } - if (stroked) - { - for (int dist = 1; dist <= shadow_distance; ++dist) - { - for (int dy = -dist; dy <= dist; ++dy) - { - for (int dx = -dist; dx <= dist; ++dx) - { - writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght); - } - } - } - } + if (stroked) { + for (int dist = 1; dist <= shadow_distance; ++dist) { + for (int dy = -dist; dy <= dist; ++dy) { + for (int dx = -dist; dx <= dist; ++dx) { + writeColored(x + dx, y + dy, text, shadow_color, kerning, lenght); + } + } + } + } - if (colored) - { - writeColored(x, y, text, textColor, kerning, lenght); - } - else - { - write(x, y, text, kerning, lenght); - } + if (colored) { + writeColored(x, y, text, textColor, kerning, lenght); + } else { + write(x, y, text, kerning, lenght); + } } // Obtiene la longitud en pixels de una cadena -int Text::lenght(const std::string &text, int kerning) const -{ - int shift = 0; - for (size_t i = 0; i < text.length(); ++i) - shift += (offset_[static_cast<int>(text[i])].w + kerning); +int Text::lenght(const std::string &text, int kerning) const { + int shift = 0; + for (size_t i = 0; i < text.length(); ++i) + shift += (offset_[static_cast<int>(text[i])].w + kerning); - // Descuenta el kerning del último caracter - return shift - kerning; + // Descuenta el kerning del último caracter + return shift - kerning; } // Devuelve el valor de la variable -int Text::getCharacterSize() const -{ - return box_width_; +int Text::getCharacterSize() const { + return box_width_; } // Establece si se usa un tamaño fijo de letra -void Text::setFixedWidth(bool value) -{ - fixed_width_ = value; +void Text::setFixedWidth(bool value) { + fixed_width_ = value; } // Establece una paleta -void Text::setPalette(int number) -{ - auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); - SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr); - sprite_->getTexture()->setPalette(number); - SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); +void Text::setPalette(int number) { + auto temp = SDL_GetRenderTarget(Screen::get()->getRenderer()); + SDL_SetRenderTarget(Screen::get()->getRenderer(), nullptr); + sprite_->getTexture()->setPalette(number); + SDL_SetRenderTarget(Screen::get()->getRenderer(), temp); } \ No newline at end of file diff --git a/source/text.h b/source/text.h index 67e7f5e..965bfd4 100644 --- a/source/text.h +++ b/source/text.h @@ -1,11 +1,12 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint8 -#include <memory> // Para unique_ptr, shared_ptr -#include <string> // Para string +#include <SDL3/SDL.h> // Para Uint8 -#include "sprite.h" // Para Sprite -#include "utils.h" // Para Color +#include <memory> // Para unique_ptr, shared_ptr +#include <string> // Para string + +#include "sprite.h" // Para Sprite +#include "utils.h" // Para Color class Texture; @@ -16,59 +17,56 @@ constexpr int TEXT_CENTER = 4; constexpr int TEXT_STROKE = 8; // --- Estructuras auxiliares --- -struct TextOffset -{ - int x, y, w; +struct TextOffset { + int x, y, w; }; -struct TextFile -{ - int box_width; // Anchura de la caja de cada caracter en el png - int box_height; // Altura de la caja de cada caracter en el png - TextOffset offset[128]; // Vector con las posiciones y ancho de cada letra +struct TextFile { + int box_width; // Anchura de la caja de cada caracter en el png + int box_height; // Altura de la caja de cada caracter en el png + TextOffset offset[128]; // Vector con las posiciones y ancho de cada letra }; // Llena una estructura TextFile desde un fichero std::shared_ptr<TextFile> loadTextFile(const std::string &file_path); // --- Clase Text: pinta texto en pantalla a partir de un bitmap --- -class Text -{ -public: - // --- Constructores y destructor --- - Text(std::shared_ptr<Texture> texture, const std::string &text_file); - Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file); - ~Text() = default; +class Text { + public: + // --- Constructores y destructor --- + Text(std::shared_ptr<Texture> texture, const std::string &text_file); + Text(std::shared_ptr<Texture> texture, std::shared_ptr<TextFile> text_file); + ~Text() = default; - // --- Métodos de escritura en pantalla --- - void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto en pantalla - void write2X(int x, int y, const std::string &text, int kerning = 1); // Escribe el texto al doble de tamaño + // --- Métodos de escritura en pantalla --- + void write(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto en pantalla + void write2X(int x, int y, const std::string &text, int kerning = 1); // Escribe el texto al doble de tamaño - // --- Escritura en textura --- - std::shared_ptr<Texture> writeToTexture(const std::string &text, int zoom = 1, int kerning = 1); // Escribe el texto en una textura - std::shared_ptr<Texture> writeDXToTexture(Uint8 flags, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe el texto con extras en una textura + // --- Escritura en textura --- + std::shared_ptr<Texture> writeToTexture(const std::string &text, int zoom = 1, int kerning = 1); // Escribe el texto en una textura + std::shared_ptr<Texture> writeDXToTexture(Uint8 flags, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe el texto con extras en una textura - // --- Métodos de escritura avanzada --- - void writeColored(int x, int y, const std::string &text, Color color, int kerning = 1, int lenght = -1); // Escribe el texto con colores - void writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1); // Escribe el texto con sombra - void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto centrado en un punto x - void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe texto con extras + // --- Métodos de escritura avanzada --- + void writeColored(int x, int y, const std::string &text, Color color, int kerning = 1, int lenght = -1); // Escribe el texto con colores + void writeShadowed(int x, int y, const std::string &text, Color color, Uint8 shadow_distance = 1, int kerning = 1, int lenght = -1); // Escribe el texto con sombra + void writeCentered(int x, int y, const std::string &text, int kerning = 1, int lenght = -1); // Escribe el texto centrado en un punto x + void writeDX(Uint8 flags, int x, int y, const std::string &text, int kerning = 1, Color textColor = Color(), Uint8 shadow_distance = 1, Color shadow_color = Color(), int lenght = -1); // Escribe texto con extras - // --- Utilidades --- - int lenght(const std::string &text, int kerning = 1) const; // Obtiene la longitud en pixels de una cadena - int getCharacterSize() const; // Devuelve el tamaño de caracter actual + // --- Utilidades --- + int lenght(const std::string &text, int kerning = 1) const; // Obtiene la longitud en pixels de una cadena + int getCharacterSize() const; // Devuelve el tamaño de caracter actual - // --- Configuración --- - void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra - void setPalette(int number); // Establece una paleta + // --- Configuración --- + void setFixedWidth(bool value); // Establece si se usa un tamaño fijo de letra + void setPalette(int number); // Establece una paleta -private: - // --- Objetos y punteros --- - std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto + private: + // --- Objetos y punteros --- + std::unique_ptr<Sprite> sprite_ = nullptr; // Objeto con los gráficos para el texto - // --- Variables --- - int box_width_ = 0; // Anchura de la caja de cada caracter en el png - int box_height_ = 0; // Altura de la caja de cada caracter en el png - bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras - TextOffset offset_[128] = {}; // Vector con las posiciones y ancho de cada letra + // --- Variables --- + int box_width_ = 0; // Anchura de la caja de cada caracter en el png + int box_height_ = 0; // Altura de la caja de cada caracter en el png + bool fixed_width_ = false; // Indica si el texto se ha de escribir con longitud fija en todas las letras + TextOffset offset_[128] = {}; // Vector con las posiciones y ancho de cada letra }; \ No newline at end of file diff --git a/source/texture.cpp b/source/texture.cpp index badfc39..22e375f 100644 --- a/source/texture.cpp +++ b/source/texture.cpp @@ -1,438 +1,381 @@ #define STB_IMAGE_IMPLEMENTATION #include "texture.h" -#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_... -#include <stdint.h> // Para uint32_t -#include <cstring> // Para memcpy -#include <fstream> // Para basic_ifstream, basic_istream, basic_ios -#include <sstream> // Para basic_istringstream -#include <stdexcept> // Para runtime_error -#include <string> // Para basic_string, char_traits, operator+, string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_LogError, SDL_LogCategory, Uint8, SDL_... +#include <stdint.h> // Para uint32_t -#include "external/gif.h" // Para Gif -#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha -#include "utils.h" // Para getFileName, Color, printWithDots +#include <cstring> // Para memcpy +#include <fstream> // Para basic_ifstream, basic_istream, basic_ios +#include <sstream> // Para basic_istringstream +#include <stdexcept> // Para runtime_error +#include <string> // Para basic_string, char_traits, operator+, string +#include <vector> // Para vector + +#include "external/gif.h" // Para Gif +#include "stb_image.h" // Para stbi_image_free, stbi_load, STBI_rgb_alpha +#include "utils.h" // Para getFileName, Color, printWithDots // Constructor Texture::Texture(SDL_Renderer *renderer, const std::string &path) - : renderer_(renderer), - path_(path) -{ - // Carga el fichero en la textura - if (!path_.empty()) - { - // Obtiene la extensión - const std::string extension = path_.substr(path_.find_last_of(".") + 1); + : renderer_(renderer), + path_(path) { + // Carga el fichero en la textura + if (!path_.empty()) { + // Obtiene la extensión + const std::string extension = path_.substr(path_.find_last_of(".") + 1); - // .png - if (extension == "png") - { - loadFromFile(path_); - } + // .png + if (extension == "png") { + loadFromFile(path_); + } - // .gif - else if (extension == "gif") - { - // Crea la surface desde un fichero - surface_ = loadSurface(path_); + // .gif + else if (extension == "gif") { + // Crea la surface desde un fichero + surface_ = loadSurface(path_); - // Añade la propia paleta del fichero a la lista - addPaletteFromGifFile(path_); + // Añade la propia paleta del fichero a la lista + addPaletteFromGifFile(path_); - // Crea la textura, establece el BlendMode y copia la surface a la textura - createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING); - SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); - flipSurface(); - } - } + // Crea la textura, establece el BlendMode y copia la surface a la textura + createBlank(width_, height_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING); + SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_BLEND); + flipSurface(); + } + } } // Destructor -Texture::~Texture() -{ - unloadTexture(); - unloadSurface(); - palettes_.clear(); +Texture::~Texture() { + unloadTexture(); + unloadSurface(); + palettes_.clear(); } // Carga una imagen desde un fichero -bool Texture::loadFromFile(const std::string &file_path) -{ - if (file_path.empty()) - return false; +bool Texture::loadFromFile(const std::string &file_path) { + if (file_path.empty()) + return false; - int req_format = STBI_rgb_alpha; - int width, height, orig_format; - unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format); - if (!data) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str()); - throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); - } - else - { - printWithDots("Texture : ", getFileName(file_path), "[ LOADED ]"); - } + int req_format = STBI_rgb_alpha; + int width, height, orig_format; + unsigned char *data = stbi_load(file_path.c_str(), &width, &height, &orig_format, req_format); + if (!data) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", getFileName(file_path).c_str()); + throw std::runtime_error("Fichero no encontrado: " + getFileName(file_path)); + } else { + printWithDots("Texture : ", getFileName(file_path), "[ LOADED ]"); + } - int pitch; - SDL_PixelFormat pixel_format; + int pitch; + SDL_PixelFormat pixel_format; - // STBI_rgb_alpha (RGBA) - pitch = 4 * width; - pixel_format = SDL_PIXELFORMAT_RGBA32; + // STBI_rgb_alpha (RGBA) + pitch = 4 * width; + pixel_format = SDL_PIXELFORMAT_RGBA32; - // Limpia - unloadTexture(); + // Limpia + unloadTexture(); - // La textura final - SDL_Texture *new_texture = nullptr; + // La textura final + SDL_Texture *new_texture = nullptr; - // Carga la imagen desde una ruta específica - auto loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void *>(data), pitch); - if (loaded_surface == nullptr) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load image %s", file_path.c_str()); - } - else - { - // Crea la textura desde los pixels de la surface - new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface); - if (new_texture == nullptr) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture from %s! SDL Error: %s", file_path.c_str(), SDL_GetError()); - } - else - { - // Obtiene las dimensiones de la imagen - width_ = loaded_surface->w; - height_ = loaded_surface->h; - } + // Carga la imagen desde una ruta específica + auto loaded_surface = SDL_CreateSurfaceFrom(width, height, pixel_format, static_cast<void *>(data), pitch); + if (loaded_surface == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load image %s", file_path.c_str()); + } else { + // Crea la textura desde los pixels de la surface + new_texture = SDL_CreateTextureFromSurface(renderer_, loaded_surface); + if (new_texture == nullptr) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create texture from %s! SDL Error: %s", file_path.c_str(), SDL_GetError()); + } else { + // Obtiene las dimensiones de la imagen + width_ = loaded_surface->w; + height_ = loaded_surface->h; + } - // Elimina la textura cargada - SDL_DestroySurface(loaded_surface); - } + // Elimina la textura cargada + SDL_DestroySurface(loaded_surface); + } - // Return success - stbi_image_free(data); - texture_ = new_texture; - return texture_ != nullptr; + // Return success + stbi_image_free(data); + texture_ = new_texture; + return texture_ != nullptr; } // Crea una textura en blanco -bool Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_TextureAccess access) -{ - // Crea una textura sin inicializar - texture_ = SDL_CreateTexture(renderer_, format, access, width, height); - if (!texture_) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError()); - } - else - { - width_ = width; - height_ = height; - } +bool Texture::createBlank(int width, int height, SDL_PixelFormat format, SDL_TextureAccess access) { + // Crea una textura sin inicializar + texture_ = SDL_CreateTexture(renderer_, format, access, width, height); + if (!texture_) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create blank texture! SDL Error: %s", SDL_GetError()); + } else { + width_ = width; + height_ = height; + } - return texture_ != nullptr; + return texture_ != nullptr; } // Libera la memoria de la textura -void Texture::unloadTexture() -{ - // Libera la textura - if (texture_) - { - SDL_DestroyTexture(texture_); - texture_ = nullptr; - width_ = 0; - height_ = 0; - } +void Texture::unloadTexture() { + // Libera la textura + if (texture_) { + SDL_DestroyTexture(texture_); + texture_ = nullptr; + width_ = 0; + height_ = 0; + } } // Establece el color para la modulacion -void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue) -{ - SDL_SetTextureColorMod(texture_, red, green, blue); +void Texture::setColor(Uint8 red, Uint8 green, Uint8 blue) { + SDL_SetTextureColorMod(texture_, red, green, blue); } -void Texture::setColor(Color color) -{ - SDL_SetTextureColorMod(texture_, color.r, color.g, color.b); +void Texture::setColor(Color color) { + SDL_SetTextureColorMod(texture_, color.r, color.g, color.b); } // Establece el blending -void Texture::setBlendMode(SDL_BlendMode blending) -{ - SDL_SetTextureBlendMode(texture_, blending); +void Texture::setBlendMode(SDL_BlendMode blending) { + SDL_SetTextureBlendMode(texture_, blending); } // Establece el alpha para la modulación -void Texture::setAlpha(Uint8 alpha) -{ - SDL_SetTextureAlphaMod(texture_, alpha); +void Texture::setAlpha(Uint8 alpha) { + SDL_SetTextureAlphaMod(texture_, alpha); } // Renderiza la textura en un punto específico -void Texture::render(int x, int y, SDL_FRect *clip, float zoomW, float zoomH, double angle, SDL_FPoint *center, SDL_FlipMode flip) -{ - // Establece el destino de renderizado en la pantalla - SDL_FRect renderQuad = {static_cast<float>(x), static_cast<float>(y), static_cast<float>(width_), static_cast<float>(height_)}; +void Texture::render(int x, int y, SDL_FRect *clip, float zoomW, float zoomH, double angle, SDL_FPoint *center, SDL_FlipMode flip) { + // Establece el destino de renderizado en la pantalla + SDL_FRect renderQuad = {static_cast<float>(x), static_cast<float>(y), static_cast<float>(width_), static_cast<float>(height_)}; - // Obtiene las dimesiones del clip de renderizado - if (clip != nullptr) - { - renderQuad.w = clip->w; - renderQuad.h = clip->h; - } + // Obtiene las dimesiones del clip de renderizado + if (clip != nullptr) { + renderQuad.w = clip->w; + renderQuad.h = clip->h; + } - // Calcula el zoom y las coordenadas - if (zoomH != 1.0f || zoomW != 1.0f) - { - renderQuad.x = renderQuad.x + (renderQuad.w / 2); - renderQuad.y = renderQuad.y + (renderQuad.h / 2); - renderQuad.w = renderQuad.w * zoomW; - renderQuad.h = renderQuad.h * zoomH; - renderQuad.x = renderQuad.x - (renderQuad.w / 2); - renderQuad.y = renderQuad.y - (renderQuad.h / 2); - } + // Calcula el zoom y las coordenadas + if (zoomH != 1.0f || zoomW != 1.0f) { + renderQuad.x = renderQuad.x + (renderQuad.w / 2); + renderQuad.y = renderQuad.y + (renderQuad.h / 2); + renderQuad.w = renderQuad.w * zoomW; + renderQuad.h = renderQuad.h * zoomH; + renderQuad.x = renderQuad.x - (renderQuad.w / 2); + renderQuad.y = renderQuad.y - (renderQuad.h / 2); + } - // Renderiza a pantalla - SDL_RenderTextureRotated(renderer_, texture_, clip, &renderQuad, angle, center, flip); + // Renderiza a pantalla + SDL_RenderTextureRotated(renderer_, texture_, clip, &renderQuad, angle, center, flip); } // Establece la textura como objetivo de renderizado -void Texture::setAsRenderTarget(SDL_Renderer *renderer) -{ - SDL_SetRenderTarget(renderer, texture_); +void Texture::setAsRenderTarget(SDL_Renderer *renderer) { + SDL_SetRenderTarget(renderer, texture_); } // Obtiene el ancho de la imagen -int Texture::getWidth() -{ - return width_; +int Texture::getWidth() { + return width_; } // Obtiene el alto de la imagen -int Texture::getHeight() -{ - return height_; +int Texture::getHeight() { + return height_; } // Recarga la textura -bool Texture::reLoad() -{ - return loadFromFile(path_); +bool Texture::reLoad() { + return loadFromFile(path_); } // Obtiene la textura -SDL_Texture *Texture::getSDLTexture() -{ - return texture_; +SDL_Texture *Texture::getSDLTexture() { + return texture_; } // Desencadenar la superficie actual -void Texture::unloadSurface() -{ - surface_.reset(); // Resetea el shared_ptr - width_ = 0; - height_ = 0; +void Texture::unloadSurface() { + surface_.reset(); // Resetea el shared_ptr + width_ = 0; + height_ = 0; } // Crea una surface desde un fichero .gif -std::shared_ptr<Surface> Texture::loadSurface(const std::string &file_path) -{ - // Libera la superficie actual - unloadSurface(); +std::shared_ptr<Surface> Texture::loadSurface(const std::string &file_path) { + // Libera la superficie actual + unloadSurface(); - // Abrir el archivo usando std::ifstream para manejo automático del recurso - std::ifstream file(file_path, std::ios::binary | std::ios::ate); - if (!file) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); - throw std::runtime_error("Fichero no encontrado: " + file_path); - } + // Abrir el archivo usando std::ifstream para manejo automático del recurso + std::ifstream file(file_path, std::ios::binary | std::ios::ate); + if (!file) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); + throw std::runtime_error("Fichero no encontrado: " + file_path); + } - // Obtener el tamaño del archivo - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); + // Obtener el tamaño del archivo + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); - // Leer el contenido del archivo en un buffer - std::vector<Uint8> buffer(size); - if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str()); - throw std::runtime_error("Error al leer el fichero: " + file_path); - } + // Leer el contenido del archivo en un buffer + std::vector<Uint8> buffer(size); + if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al leer el fichero %s", file_path.c_str()); + throw std::runtime_error("Error al leer el fichero: " + file_path); + } - // Crear un objeto Gif y llamar a la función loadGif - GIF::Gif gif; - Uint16 w = 0, h = 0; - std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h); - if (rawPixels.empty()) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo cargar el GIF %s", file_path.c_str()); - return nullptr; - } + // Crear un objeto Gif y llamar a la función loadGif + GIF::Gif gif; + Uint16 w = 0, h = 0; + std::vector<Uint8> rawPixels = gif.loadGif(buffer.data(), w, h); + if (rawPixels.empty()) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo cargar el GIF %s", file_path.c_str()); + return nullptr; + } - // Si el constructor de Surface espera un std::shared_ptr<Uint8[]>: - size_t pixelCount = rawPixels.size(); - auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixelCount], std::default_delete<Uint8[]>()); - std::memcpy(pixels.get(), rawPixels.data(), pixelCount); + // Si el constructor de Surface espera un std::shared_ptr<Uint8[]>: + size_t pixelCount = rawPixels.size(); + auto pixels = std::shared_ptr<Uint8[]>(new Uint8[pixelCount], std::default_delete<Uint8[]>()); + std::memcpy(pixels.get(), rawPixels.data(), pixelCount); - auto surface = std::make_shared<Surface>(w, h, pixels); + auto surface = std::make_shared<Surface>(w, h, pixels); - // Actualizar las dimensiones - width_ = w; - height_ = h; + // Actualizar las dimensiones + width_ = w; + height_ = h; - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF %s cargado correctamente.", file_path.c_str()); - return surface; + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "GIF %s cargado correctamente.", file_path.c_str()); + return surface; } // Vuelca la surface en la textura -void Texture::flipSurface() -{ - // Limpia la textura - auto temp = SDL_GetRenderTarget(renderer_); - SDL_SetRenderTarget(renderer_, texture_); - SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); - SDL_RenderClear(renderer_); - SDL_SetRenderTarget(renderer_, temp); +void Texture::flipSurface() { + // Limpia la textura + auto temp = SDL_GetRenderTarget(renderer_); + SDL_SetRenderTarget(renderer_, texture_); + SDL_SetRenderDrawColor(renderer_, 0, 0, 0, 0); + SDL_RenderClear(renderer_); + SDL_SetRenderTarget(renderer_, temp); - // Vuelca los datos - Uint32 *pixels; - int pitch; - SDL_LockTexture(texture_, nullptr, reinterpret_cast<void **>(&pixels), &pitch); - for (int i = 0; i < width_ * height_; ++i) - { - pixels[i] = palettes_[current_palette_][surface_->data[i]]; - } - SDL_UnlockTexture(texture_); + // Vuelca los datos + Uint32 *pixels; + int pitch; + SDL_LockTexture(texture_, nullptr, reinterpret_cast<void **>(&pixels), &pitch); + for (int i = 0; i < width_ * height_; ++i) { + pixels[i] = palettes_[current_palette_][surface_->data[i]]; + } + SDL_UnlockTexture(texture_); } // Establece un color de la paleta -void Texture::setPaletteColor(int palette, int index, Uint32 color) -{ - palettes_.at(palette)[index] = color; +void Texture::setPaletteColor(int palette, int index, Uint32 color) { + palettes_.at(palette)[index] = color; } // Carga una paleta desde un fichero -Palette Texture::loadPaletteFromFile(const std::string &file_path) -{ - Palette palette; +Palette Texture::loadPaletteFromFile(const std::string &file_path) { + Palette palette; - // Abrir el archivo GIF - std::ifstream file(file_path, std::ios::binary | std::ios::ate); - if (!file) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); - throw std::runtime_error("Fichero no encontrado: " + file_path); - } - else - { - printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]"); - } + // Abrir el archivo GIF + std::ifstream file(file_path, std::ios::binary | std::ios::ate); + if (!file) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); + throw std::runtime_error("Fichero no encontrado: " + file_path); + } else { + printWithDots("Palette : ", getFileName(file_path), "[ LOADED ]"); + } - // Obtener el tamaño del archivo y leerlo en un buffer - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); + // Obtener el tamaño del archivo y leerlo en un buffer + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); - std::vector<Uint8> buffer(size); - if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) - { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str()); - throw std::runtime_error("Error al leer el fichero: " + file_path); - } + std::vector<Uint8> buffer(size); + if (!file.read(reinterpret_cast<char *>(buffer.data()), size)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: No se pudo leer completamente el fichero %s", file_path.c_str()); + throw std::runtime_error("Error al leer el fichero: " + file_path); + } - // Usar la nueva función loadPalette, que devuelve un vector<uint32_t> - GIF::Gif gif; - std::vector<uint32_t> pal = gif.loadPalette(buffer.data()); - if (pal.empty()) - { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Advertencia: No se encontró paleta en el archivo %s", file_path.c_str()); - return palette; // Devuelve un vector vacío si no hay paleta - } + // Usar la nueva función loadPalette, que devuelve un vector<uint32_t> + GIF::Gif gif; + std::vector<uint32_t> pal = gif.loadPalette(buffer.data()); + if (pal.empty()) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Advertencia: No se encontró paleta en el archivo %s", file_path.c_str()); + return palette; // Devuelve un vector vacío si no hay paleta + } - // Modificar la conversión para obtener formato RGBA (0xRRGGBBAA) - for (size_t i = 0; i < pal.size() && i < palette.size(); ++i) - { - palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA - } + // Modificar la conversión para obtener formato RGBA (0xRRGGBBAA) + for (size_t i = 0; i < pal.size() && i < palette.size(); ++i) { + palette[i] = (pal[i] << 8) | 0xFF; // Resultado: 0xRRGGBBAA + } - SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Paleta cargada correctamente desde %s", file_path.c_str()); - return palette; + SDL_LogInfo(SDL_LOG_CATEGORY_TEST, "Paleta cargada correctamente desde %s", file_path.c_str()); + return palette; } // Añade una paleta a la lista -void Texture::addPaletteFromGifFile(const std::string &path) -{ - palettes_.emplace_back(loadPaletteFromFile(path)); - setPaletteColor(palettes_.size() - 1, 0, 0x00000000); +void Texture::addPaletteFromGifFile(const std::string &path) { + palettes_.emplace_back(loadPaletteFromFile(path)); + setPaletteColor(palettes_.size() - 1, 0, 0x00000000); } // Añade una paleta a la lista -void Texture::addPaletteFromPalFile(const std::string &path) -{ - palettes_.emplace_back(readPalFile(path)); - setPaletteColor(palettes_.size() - 1, 0, 0x00000000); +void Texture::addPaletteFromPalFile(const std::string &path) { + palettes_.emplace_back(readPalFile(path)); + setPaletteColor(palettes_.size() - 1, 0, 0x00000000); } // Cambia la paleta de la textura -void Texture::setPalette(size_t palette) -{ - if (palette < palettes_.size()) - { - current_palette_ = palette; - flipSurface(); - } +void Texture::setPalette(size_t palette) { + if (palette < palettes_.size()) { + current_palette_ = palette; + flipSurface(); + } } // Obtiene el renderizador SDL_Renderer *Texture::getRenderer() { return renderer_; } // Carga una paleta desde un archivo .pal -Palette Texture::readPalFile(const std::string &file_path) -{ - Palette palette{}; - palette.fill(0); // Inicializar todo con 0 (transparente por defecto) +Palette Texture::readPalFile(const std::string &file_path) { + Palette palette{}; + palette.fill(0); // Inicializar todo con 0 (transparente por defecto) - std::ifstream file(file_path); - if (!file.is_open()) - { - throw std::runtime_error("No se pudo abrir el archivo .pal"); - } + std::ifstream file(file_path); + if (!file.is_open()) { + throw std::runtime_error("No se pudo abrir el archivo .pal"); + } - std::string line; - int line_number = 0; - int color_index = 0; + std::string line; + int line_number = 0; + int color_index = 0; - while (std::getline(file, line)) - { - ++line_number; + while (std::getline(file, line)) { + ++line_number; - // Ignorar las tres primeras líneas del archivo - if (line_number <= 3) - { - continue; - } + // Ignorar las tres primeras líneas del archivo + if (line_number <= 3) { + continue; + } - // Procesar las líneas restantes con valores RGB - std::istringstream ss(line); - int r, g, b; - if (ss >> r >> g >> b) - { - // Construir el color RGBA (A = 255 por defecto) - Uint32 color = (r << 24) | (g << 16) | (b << 8) | 255; - palette[color_index++] = color; + // Procesar las líneas restantes con valores RGB + std::istringstream ss(line); + int r, g, b; + if (ss >> r >> g >> b) { + // Construir el color RGBA (A = 255 por defecto) + Uint32 color = (r << 24) | (g << 16) | (b << 8) | 255; + palette[color_index++] = color; - // Limitar a un máximo de 256 colores (opcional) - if (color_index >= 256) - { - break; - } - } - } + // Limitar a un máximo de 256 colores (opcional) + if (color_index >= 256) { + break; + } + } + } - file.close(); - return palette; + file.close(); + return palette; } \ No newline at end of file diff --git a/source/texture.h b/source/texture.h index 6df34b2..c66fb54 100644 --- a/source/texture.h +++ b/source/texture.h @@ -1,11 +1,12 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint8, SDL_Renderer, Uint16, SDL_FlipMode, SDL_PixelFormat, SDL_TextureAccess, SDL_Texture, Uint32, SDL_BlendMode, SDL_FPoint, SDL_FRect -#include <stddef.h> // Para size_t -#include <array> // Para array -#include <memory> // Para shared_ptr -#include <string> // Para string, basic_string -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para Uint8, SDL_Renderer, Uint16, SDL_FlipMode, SDL_PixelFormat, SDL_TextureAccess, SDL_Texture, Uint32, SDL_BlendMode, SDL_FPoint, SDL_FRect +#include <stddef.h> // Para size_t + +#include <array> // Para array +#include <memory> // Para shared_ptr +#include <string> // Para string, basic_string +#include <vector> // Para vector struct Color; @@ -13,8 +14,7 @@ struct Color; using Palette = std::array<Uint32, 256>; // Definición de Surface para imágenes con paleta -struct Surface -{ +struct Surface { std::shared_ptr<Uint8[]> data; Uint16 w, h; @@ -24,58 +24,57 @@ struct Surface }; // Clase Texture: gestiona texturas, paletas y renderizado -class Texture -{ -public: +class Texture { + public: // --- Constructores y destructor --- explicit Texture(SDL_Renderer *renderer, const std::string &path = std::string()); ~Texture(); // --- Carga y creación --- - bool loadFromFile(const std::string &path); // Carga una imagen desde un fichero - bool createBlank(int width, int height, SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess access = SDL_TEXTUREACCESS_STREAMING); // Crea una textura en blanco - bool reLoad(); // Recarga la textura + bool loadFromFile(const std::string &path); // Carga una imagen desde un fichero + bool createBlank(int width, int height, SDL_PixelFormat format = SDL_PIXELFORMAT_RGBA8888, SDL_TextureAccess access = SDL_TEXTUREACCESS_STREAMING); // Crea una textura en blanco + bool reLoad(); // Recarga la textura // --- Renderizado --- - void render(int x, int y, SDL_FRect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_FPoint *center = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); // Renderiza la textura en un punto específico - void setAsRenderTarget(SDL_Renderer *renderer); // Establece la textura como objetivo de renderizado + void render(int x, int y, SDL_FRect *clip = nullptr, float zoomW = 1, float zoomH = 1, double angle = 0.0, SDL_FPoint *center = nullptr, SDL_FlipMode flip = SDL_FLIP_NONE); // Renderiza la textura en un punto específico + void setAsRenderTarget(SDL_Renderer *renderer); // Establece la textura como objetivo de renderizado // --- Modificadores de color y blending --- - void setColor(Uint8 red, Uint8 green, Uint8 blue); // Establece el color para la modulación - void setColor(Color color); // Establece el color para la modulación - void setBlendMode(SDL_BlendMode blending); // Establece el blending - void setAlpha(Uint8 alpha); // Establece el alpha para la modulación + void setColor(Uint8 red, Uint8 green, Uint8 blue); // Establece el color para la modulación + void setColor(Color color); // Establece el color para la modulación + void setBlendMode(SDL_BlendMode blending); // Establece el blending + void setAlpha(Uint8 alpha); // Establece el alpha para la modulación // --- Paletas --- - void addPaletteFromGifFile(const std::string &path); // Añade una paleta a la lista - void addPaletteFromPalFile(const std::string &path); // Añade una paleta a la lista - void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta - void setPalette(size_t palette); // Cambia la paleta de la textura + void addPaletteFromGifFile(const std::string &path); // Añade una paleta a la lista + void addPaletteFromPalFile(const std::string &path); // Añade una paleta a la lista + void setPaletteColor(int palette, int index, Uint32 color); // Establece un color de la paleta + void setPalette(size_t palette); // Cambia la paleta de la textura // --- Getters --- - int getWidth(); // Obtiene el ancho de la imagen - int getHeight(); // Obtiene el alto de la imagen - SDL_Texture *getSDLTexture(); // Obtiene la textura SDL - SDL_Renderer *getRenderer(); // Obtiene el renderizador + int getWidth(); // Obtiene el ancho de la imagen + int getHeight(); // Obtiene el alto de la imagen + SDL_Texture *getSDLTexture(); // Obtiene la textura SDL + SDL_Renderer *getRenderer(); // Obtiene el renderizador -private: + private: // --- Objetos y punteros --- - SDL_Renderer *renderer_; // Renderizador donde dibujar la textura - SDL_Texture *texture_ = nullptr; // La textura - std::shared_ptr<Surface> surface_ = nullptr; // Surface para usar imágenes en formato gif con paleta + SDL_Renderer *renderer_; // Renderizador donde dibujar la textura + SDL_Texture *texture_ = nullptr; // La textura + std::shared_ptr<Surface> surface_ = nullptr; // Surface para usar imágenes en formato gif con paleta // --- Variables --- - std::string path_; // Ruta de la imagen de la textura - int width_ = 0; // Ancho de la imagen - int height_ = 0; // Alto de la imagen - std::vector<Palette> palettes_; // Vector con las diferentes paletas - int current_palette_ = 0; // Índice de la paleta en uso + std::string path_; // Ruta de la imagen de la textura + int width_ = 0; // Ancho de la imagen + int height_ = 0; // Alto de la imagen + std::vector<Palette> palettes_; // Vector con las diferentes paletas + int current_palette_ = 0; // Índice de la paleta en uso // --- Métodos internos --- - std::shared_ptr<Surface> loadSurface(const std::string &file_name); // Crea una surface desde un fichero .gif - void flipSurface(); // Vuelca la surface en la textura - Palette loadPaletteFromFile(const std::string &file_name); // Carga una paleta desde un fichero - void unloadTexture(); // Libera la memoria de la textura - void unloadSurface(); // Libera la surface actual - Palette readPalFile(const std::string &file_path); // Carga una paleta desde un archivo .pal + std::shared_ptr<Surface> loadSurface(const std::string &file_name); // Crea una surface desde un fichero .gif + void flipSurface(); // Vuelca la surface en la textura + Palette loadPaletteFromFile(const std::string &file_name); // Carga una paleta desde un fichero + void unloadTexture(); // Libera la memoria de la textura + void unloadSurface(); // Libera la surface actual + Palette readPalFile(const std::string &file_path); // Carga una paleta desde un archivo .pal }; \ No newline at end of file diff --git a/source/tiled_bg.cpp b/source/tiled_bg.cpp index 2d36bb2..74e77ed 100644 --- a/source/tiled_bg.cpp +++ b/source/tiled_bg.cpp @@ -1,21 +1,21 @@ #include "tiled_bg.h" -#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_De... -#include <stdlib.h> // Para rand -#include <cmath> // Para sin -#include <memory> // Para unique_ptr, make_unique -#include <string> // Para basic_string +#include <SDL3/SDL.h> // Para SDL_SetRenderTarget, SDL_CreateTexture, SDL_De... +#include <stdlib.h> // Para rand -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "sprite.h" // Para Sprite +#include <cmath> // Para sin +#include <memory> // Para unique_ptr, make_unique +#include <string> // Para basic_string + +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "sprite.h" // Para Sprite // Constructor TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode) : renderer_(Screen::get()->getRenderer()), pos_(pos), - mode_(mode == TiledBGMode::RANDOM ? static_cast<TiledBGMode>(rand() % 2) : mode) -{ + mode_(mode == TiledBGMode::RANDOM ? static_cast<TiledBGMode>(rand() % 2) : mode) { // Crea la textura para el mosaico de fondo canvas_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, pos_.w * 2, pos_.h * 2); @@ -23,39 +23,35 @@ TiledBG::TiledBG(SDL_FRect pos, TiledBGMode mode) fillTexture(); // Inicializa variables - switch (mode_) - { - case TiledBGMode::STATIC: - window_ = {0, 0, pos_.w, pos_.h}; - speed_ = 0.0f; - break; - case TiledBGMode::DIAGONAL: - window_ = {0, 0, pos_.w, pos_.h}; - break; - case TiledBGMode::CIRCLE: - window_ = {128, 128, pos_.w, pos_.h}; - break; - default: - window_ = {0, 0, pos_.w, pos_.h}; - break; + switch (mode_) { + case TiledBGMode::STATIC: + window_ = {0, 0, pos_.w, pos_.h}; + speed_ = 0.0f; + break; + case TiledBGMode::DIAGONAL: + window_ = {0, 0, pos_.w, pos_.h}; + break; + case TiledBGMode::CIRCLE: + window_ = {128, 128, pos_.w, pos_.h}; + break; + default: + window_ = {0, 0, pos_.w, pos_.h}; + break; } // Inicializa los valores del vector con los valores del seno - for (int i = 0; i < 360; ++i) - { - sin_[i] = std::sin(i * 3.14159 / 180.0); // Convierte grados a radianes y calcula el seno + for (int i = 0; i < 360; ++i) { + sin_[i] = std::sin(i * 3.14159 / 180.0); // Convierte grados a radianes y calcula el seno } } // Destructor -TiledBG::~TiledBG() -{ +TiledBG::~TiledBG() { SDL_DestroyTexture(canvas_); } // Rellena la textura con el contenido -void TiledBG::fillTexture() -{ +void TiledBG::fillTexture() { // Crea los objetos para pintar en la textura de fondo auto tile = std::make_unique<Sprite>(Resource::get()->getTexture("title_bg_tile.png"), (SDL_FRect){0, 0, TILE_WIDTH_, TILE_HEIGHT_}); @@ -67,10 +63,8 @@ void TiledBG::fillTexture() const auto i_max = pos_.w * 2 / TILE_WIDTH_; const auto j_max = pos_.h * 2 / TILE_HEIGHT_; tile->setSpriteClip(0, 0, TILE_WIDTH_, TILE_HEIGHT_); - for (int i = 0; i < i_max; ++i) - { - for (int j = 0; j < j_max; ++j) - { + for (int i = 0; i < i_max; ++i) { + for (int j = 0; j < j_max; ++j) { tile->setX(i * TILE_WIDTH_); tile->setY(j * TILE_HEIGHT_); tile->render(); @@ -82,65 +76,55 @@ void TiledBG::fillTexture() } // Pinta la clase en pantalla -void TiledBG::render() -{ +void TiledBG::render() { SDL_RenderTexture(renderer_, canvas_, &window_, &pos_); } // Actualiza la lógica de la clase -void TiledBG::update() -{ +void TiledBG::update() { updateDesp(); updateStop(); - switch (mode_) - { - case TiledBGMode::DIAGONAL: - { - // El tileado de fondo se desplaza en diagonal - window_.x = static_cast<int>(desp_) % TILE_WIDTH_; - window_.y = static_cast<int>(desp_) % TILE_HEIGHT_; + switch (mode_) { + case TiledBGMode::DIAGONAL: { + // El tileado de fondo se desplaza en diagonal + window_.x = static_cast<int>(desp_) % TILE_WIDTH_; + window_.y = static_cast<int>(desp_) % TILE_HEIGHT_; - break; - } - case TiledBGMode::CIRCLE: - { - // El tileado de fondo se desplaza en circulo - const int INDEX = static_cast<int>(desp_) % 360; + break; + } + case TiledBGMode::CIRCLE: { + // El tileado de fondo se desplaza en circulo + const int INDEX = static_cast<int>(desp_) % 360; - window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128)); - window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96)); - break; - } - default: - break; + window_.x = 128 + (static_cast<int>(sin_[(INDEX + 270) % 360] * 128)); + window_.y = 128 + (static_cast<int>(sin_[(360 - INDEX) % 360] * 96)); + break; + } + default: + break; } } // Detiene el desplazamiento de forma ordenada -void TiledBG::updateStop() -{ - if (stopping_) - { - const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada +void TiledBG::updateStop() { + if (stopping_) { + const int UMBRAL = 20 * speed_; // Ajusta este valor según la precisión deseada // Desacelerar si estamos cerca de completar el ciclo (ventana a punto de regresar a 0) - if (window_.x >= TILE_WIDTH_ - UMBRAL) - { - speed_ /= 1.05f; // Reduce gradualmente la velocidad + if (window_.x >= TILE_WIDTH_ - UMBRAL) { + speed_ /= 1.05f; // Reduce gradualmente la velocidad // Asegura que no baje demasiado - if (speed_ < 0.1f) - { + if (speed_ < 0.1f) { speed_ = 0.1f; } } // Si estamos en 0, detener - if (window_.x == 0) - { + if (window_.x == 0) { speed_ = 0.0f; - stopping_ = false; // Desactivamos el estado de "stopping" + stopping_ = false; // Desactivamos el estado de "stopping" } } } \ No newline at end of file diff --git a/source/tiled_bg.h b/source/tiled_bg.h index 9439b71..8bb280e 100644 --- a/source/tiled_bg.h +++ b/source/tiled_bg.h @@ -1,12 +1,11 @@ #pragma once -#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 "utils.h" // Para Color +#include "utils.h" // Para Color // Modos de funcionamiento para el tileado de fondo -enum class TiledBGMode : int -{ +enum class TiledBGMode : int { CIRCLE = 0, DIAGONAL = 1, RANDOM = 2, @@ -20,45 +19,44 @@ enum class TiledBGMode : int */ // Clase TiledBG -class TiledBG -{ -public: +class TiledBG { + public: // --- Constructores y destructor --- TiledBG(SDL_FRect pos, TiledBGMode mode); ~TiledBG(); // --- Métodos principales --- - void render(); // Pinta la clase en pantalla - void update(); // Actualiza la lógica de la clase + void render(); // Pinta la clase en pantalla + void update(); // Actualiza la lógica de la clase // --- Configuración --- - void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad - void stopGracefully() { stopping_ = true; } // Detiene el desplazamiento de forma ordenada - void setColor(Color color) { SDL_SetTextureColorMod(canvas_, color.r, color.g, color.b); } // Cambia el color de la textura + void setSpeed(float speed) { speed_ = speed; } // Establece la velocidad + void stopGracefully() { stopping_ = true; } // Detiene el desplazamiento de forma ordenada + void setColor(Color color) { SDL_SetTextureColorMod(canvas_, color.r, color.g, color.b); } // Cambia el color de la textura // --- Getters --- - bool isStopped() const { return speed_ == 0.0f; } // Indica si está parado + bool isStopped() const { return speed_ == 0.0f; } // Indica si está parado -private: + private: // --- Constantes --- - static constexpr int TILE_WIDTH_ = 64; // Ancho del tile - static constexpr int TILE_HEIGHT_ = 64; // Alto del tile + static constexpr int TILE_WIDTH_ = 64; // Ancho del tile + static constexpr int TILE_HEIGHT_ = 64; // Alto del tile // --- Objetos y punteros --- - SDL_Renderer *renderer_; // El renderizador de la ventana - SDL_Texture *canvas_; // Textura donde dibujar el fondo formado por tiles + SDL_Renderer *renderer_; // El renderizador de la ventana + SDL_Texture *canvas_; // Textura donde dibujar el fondo formado por tiles // --- Variables de estado --- - SDL_FRect pos_; // Posición y tamaño del mosaico - SDL_FRect window_; // Ventana visible para la textura de fondo del título - TiledBGMode mode_; // Tipo de movimiento del mosaico - double sin_[360]; // Vector con los valores del seno precalculados - float desp_ = 0.0f; // Desplazamiento aplicado - float speed_ = 1.0f; // Incremento que se añade al desplazamiento a cada bucle - bool stopping_ = false; // Indica si se está deteniendo + SDL_FRect pos_; // Posición y tamaño del mosaico + SDL_FRect window_; // Ventana visible para la textura de fondo del título + TiledBGMode mode_; // Tipo de movimiento del mosaico + double sin_[360]; // Vector con los valores del seno precalculados + float desp_ = 0.0f; // Desplazamiento aplicado + float speed_ = 1.0f; // Incremento que se añade al desplazamiento a cada bucle + bool stopping_ = false; // Indica si se está deteniendo // --- Métodos internos --- - void fillTexture(); // Rellena la textura con el contenido - void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento - void updateStop(); // Detiene el desplazamiento de forma ordenada + void fillTexture(); // Rellena la textura con el contenido + void updateDesp() { desp_ += speed_; } // Actualiza el desplazamiento + void updateStop(); // Detiene el desplazamiento de forma ordenada }; \ No newline at end of file diff --git a/source/ui/menu_option.h b/source/ui/menu_option.h index 1cddc9b..74f39a5 100644 --- a/source/ui/menu_option.h +++ b/source/ui/menu_option.h @@ -1,23 +1,22 @@ #pragma once -#include <string> -#include <vector> +#include <algorithm> // para std::clamp #include <functional> #include <memory> -#include <algorithm> // para std::clamp -#include "ui/service_menu.h" // Necesitamos las enums como SettingsGroup -#include "options.h" // Para acceder a las variables de configuración -#include "lang.h" // Para las traducciones -#include "section.h" // Para las acciones como Quit o Reset -#include "text.h" // Para poder calcular el ancho del texto +#include <string> +#include <vector> + +#include "lang.h" // Para las traducciones +#include "options.h" // Para acceder a las variables de configuración +#include "section.h" // Para las acciones como Quit o Reset +#include "text.h" // Para poder calcular el ancho del texto +#include "ui/service_menu.h" // Necesitamos las enums como SettingsGroup // --- Interfaz Base para todas las Opciones del Menú --- -class MenuOption -{ -public: - enum class Behavior - { +class MenuOption { + public: + enum class Behavior { ADJUST, SELECT }; @@ -41,7 +40,7 @@ public: // Método virtual para que cada opción calcule el ancho de su valor más largo. virtual int getMaxValueWidth(Text *text_renderer) const { return 0; } -protected: + protected: std::string caption_; ServiceMenu::SettingsGroup group_; bool hidden_; @@ -49,52 +48,44 @@ protected: // --- Clases Derivadas --- -class BoolOption : public MenuOption -{ -public: +class BoolOption : public MenuOption { + public: BoolOption(const std::string &cap, ServiceMenu::SettingsGroup grp, bool *var) : MenuOption(cap, grp), linked_variable_(var) {} Behavior getBehavior() const override { return Behavior::ADJUST; } - std::string getValueAsString() const override - { + std::string getValueAsString() const override { return *linked_variable_ ? Lang::getText("[SERVICE_MENU] ON") : Lang::getText("[SERVICE_MENU] OFF"); } - void adjustValue(bool /*adjust_up*/) override - { + void adjustValue(bool /*adjust_up*/) override { *linked_variable_ = !*linked_variable_; } - int getMaxValueWidth(Text *text_renderer) const override - { + int getMaxValueWidth(Text *text_renderer) const override { return std::max( text_renderer->lenght(Lang::getText("[SERVICE_MENU] ON"), -2), text_renderer->lenght(Lang::getText("[SERVICE_MENU] OFF"), -2)); } -private: + private: bool *linked_variable_; }; -class IntOption : public MenuOption -{ -public: +class IntOption : public MenuOption { + public: IntOption(const std::string &cap, ServiceMenu::SettingsGroup grp, int *var, int min, int max, int step) : MenuOption(cap, grp), linked_variable_(var), min_value_(min), max_value_(max), step_value_(step) {} Behavior getBehavior() const override { return Behavior::ADJUST; } std::string getValueAsString() const override { return std::to_string(*linked_variable_); } - void adjustValue(bool adjust_up) override - { + void adjustValue(bool adjust_up) override { int newValue = *linked_variable_ + (adjust_up ? step_value_ : -step_value_); *linked_variable_ = std::clamp(newValue, min_value_, max_value_); } - int getMaxValueWidth(Text *text_renderer) const override - { + int getMaxValueWidth(Text *text_renderer) const override { int max_width = 0; // Iterar por todos los valores posibles en el rango - for (int value = min_value_; value <= max_value_; value += step_value_) - { + for (int value = min_value_; value <= max_value_; value += step_value_) { int width = text_renderer->lenght(std::to_string(value), -2); max_width = std::max(max_width, width); } @@ -102,34 +93,26 @@ public: return max_width; } -private: + private: int *linked_variable_; int min_value_, max_value_, step_value_; }; -class ListOption : public MenuOption -{ -public: - ListOption(const std::string &cap, ServiceMenu::SettingsGroup grp, - std::vector<std::string> values, - std::function<std::string()> current_value_getter, - std::function<void(const std::string &)> new_value_setter) +class ListOption : public MenuOption { + public: + ListOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::vector<std::string> values, std::function<std::string()> current_value_getter, std::function<void(const std::string &)> new_value_setter) : MenuOption(cap, grp), value_list_(std::move(values)), getter_(std::move(current_value_getter)), setter_(std::move(new_value_setter)), - list_index_(0) - { + list_index_(0) { sync(); } - void sync() - { + void sync() { std::string current_value = getter_(); - for (size_t i = 0; i < value_list_.size(); ++i) - { - if (value_list_[i] == current_value) - { + for (size_t i = 0; i < value_list_.size(); ++i) { + if (value_list_[i] == current_value) { list_index_ = i; return; } @@ -137,12 +120,10 @@ public: } Behavior getBehavior() const override { return Behavior::ADJUST; } - std::string getValueAsString() const override - { + std::string getValueAsString() const override { return value_list_.empty() ? "" : value_list_[list_index_]; } - void adjustValue(bool adjust_up) override - { + void adjustValue(bool adjust_up) override { if (value_list_.empty()) return; size_t size = value_list_.size(); @@ -150,49 +131,44 @@ public: : (list_index_ + size - 1) % size; setter_(value_list_[list_index_]); } - int getMaxValueWidth(Text *text_renderer) const override - { + int getMaxValueWidth(Text *text_renderer) const override { int max_w = 0; - for (const auto &val : value_list_) - { + for (const auto &val : value_list_) { max_w = std::max(max_w, text_renderer->lenght(val, -2)); } return max_w; } -private: + private: std::vector<std::string> value_list_; std::function<std::string()> getter_; std::function<void(const std::string &)> setter_; size_t list_index_; }; -class FolderOption : public MenuOption -{ -public: +class FolderOption : public MenuOption { + public: FolderOption(const std::string &cap, ServiceMenu::SettingsGroup grp, ServiceMenu::SettingsGroup target) : MenuOption(cap, grp), target_group_(target) {} Behavior getBehavior() const override { return Behavior::SELECT; } ServiceMenu::SettingsGroup getTargetGroup() const override { return target_group_; } -private: + private: ServiceMenu::SettingsGroup target_group_; }; -class ActionOption : public MenuOption -{ -public: +class ActionOption : public MenuOption { + public: ActionOption(const std::string &cap, ServiceMenu::SettingsGroup grp, std::function<void()> action, bool hidden = false) : MenuOption(cap, grp, hidden), action_(std::move(action)) {} Behavior getBehavior() const override { return Behavior::SELECT; } - void executeAction() override - { + void executeAction() override { if (action_) action_(); } -private: + private: std::function<void()> action_; }; diff --git a/source/ui/menu_renderer.cpp b/source/ui/menu_renderer.cpp index 64d50c6..118b773 100644 --- a/source/ui/menu_renderer.cpp +++ b/source/ui/menu_renderer.cpp @@ -1,22 +1,20 @@ #include "menu_renderer.h" -#include <algorithm> // Para max -#include <string> // Para basic_string -#include <utility> // Para pair, move +#include <algorithm> // Para max +#include <string> // Para basic_string +#include <utility> // Para pair, move -#include "menu_option.h" // Para MenuOption -#include "param.h" // Para Param, param, ParamServiceMenu, ParamGame -#include "screen.h" // Para Screen -#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR +#include "menu_option.h" // Para MenuOption +#include "param.h" // Para Param, param, ParamServiceMenu, ParamGame +#include "screen.h" // Para Screen +#include "text.h" // Para Text, TEXT_CENTER, TEXT_COLOR MenuRenderer::MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text) : element_text_(std::move(element_text)), title_text_(std::move(title_text)) {} -void MenuRenderer::render(const ServiceMenu *menu_state) -{ +void MenuRenderer::render(const ServiceMenu *menu_state) { // Dibuja la sombra - if (param.service_menu.drop_shadow) - { + if (param.service_menu.drop_shadow) { SDL_FRect shadowRect = {rect_.x + 5, rect_.y + 5, rect_.w, rect_.h}; SDL_SetRenderDrawColor(Screen::get()->getRenderer(), 0, 0, 0, 64); SDL_RenderFillRect(Screen::get()->getRenderer(), &shadowRect); @@ -44,59 +42,48 @@ void MenuRenderer::render(const ServiceMenu *menu_state) // Dibuja las opciones y = options_y_; const auto &option_pairs = menu_state->getOptionPairs(); - for (size_t i = 0; i < option_pairs.size(); ++i) - { + for (size_t i = 0; i < option_pairs.size(); ++i) { const bool is_selected = (i == menu_state->getSelectedIndex()); const Color ¤t_color = is_selected ? param.service_menu.selected_color : param.service_menu.text_color; - if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) - { + if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { element_text_->writeColored(rect_.x + ServiceMenu::OPTIONS_HORIZONTAL_PADDING_, y, option_pairs.at(i).first, current_color, -2); const int X = rect_.x + rect_.w - ServiceMenu::OPTIONS_HORIZONTAL_PADDING_ - element_text_->lenght(option_pairs.at(i).second, -2); element_text_->writeColored(X, y, option_pairs.at(i).second, current_color, -2); - } - else - { + } else { element_text_->writeDX(TEXT_CENTER | TEXT_COLOR, rect_.x + rect_.w / 2, y, option_pairs.at(i).first, -2, current_color); } y += options_height_ + options_padding_; } } -void MenuRenderer::update(const ServiceMenu *menu_state) -{ - if (resizing_) - { +void MenuRenderer::update(const ServiceMenu *menu_state) { + if (resizing_) { updateResizeAnimation(); } updateColorCounter(); param.service_menu.selected_color = getAnimatedSelectedColor(); } -void MenuRenderer::onLayoutChanged(const ServiceMenu *menu_state) -{ +void MenuRenderer::onLayoutChanged(const ServiceMenu *menu_state) { // Cuando la lógica del menú notifica un cambio, el renderer recalcula su layout precalculateMenuWidths(menu_state->getAllOptions(), menu_state); setAnchors(menu_state); resize(menu_state); } -void MenuRenderer::setLayout(const ServiceMenu *menu_state) -{ +void MenuRenderer::setLayout(const ServiceMenu *menu_state) { // Cuando la lógica del menú notifica un cambio, el renderer recalcula su layout precalculateMenuWidths(menu_state->getAllOptions(), menu_state); setAnchors(menu_state); setSize(menu_state); } -void MenuRenderer::setAnchors(const ServiceMenu *menu_state) -{ +void MenuRenderer::setAnchors(const ServiceMenu *menu_state) { size_t max_entries = 0; - for (int i = 0; i < 5; ++i) - { + for (int i = 0; i < 5; ++i) { size_t count = menu_state->countOptionsInGroup(static_cast<ServiceMenu::SettingsGroup>(i)); - if (count > max_entries) - { + if (count > max_entries) { max_entries = count; } } @@ -113,8 +100,7 @@ void MenuRenderer::setAnchors(const ServiceMenu *menu_state) height_ = upper_height_ + lower_height_; } -SDL_FRect MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) -{ +SDL_FRect MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) { width_ = getMenuWidthForGroup(menu_state->getCurrentGroup()); const auto &display_options = menu_state->getDisplayOptions(); lower_height_ = ((display_options.size() > 0 ? display_options.size() - 1 : 0) * (options_height_ + options_padding_)) + options_height_ + (lower_padding_ * 2); @@ -123,40 +109,33 @@ SDL_FRect MenuRenderer::calculateNewRect(const ServiceMenu *menu_state) return {(param.game.width - width_) / 2.0f, (param.game.height - height_) / 2.0f, (float)width_, (float)height_}; } -void MenuRenderer::resize(const ServiceMenu *menu_state) -{ +void MenuRenderer::resize(const ServiceMenu *menu_state) { SDL_FRect new_rect = calculateNewRect(menu_state); - if (rect_.x != new_rect.x || rect_.y != new_rect.y || rect_.w != new_rect.w || rect_.h != new_rect.h) - { + if (rect_.x != new_rect.x || rect_.y != new_rect.y || rect_.w != new_rect.w || rect_.h != new_rect.h) { rect_anim_from_ = rect_; rect_anim_to_ = new_rect; resize_anim_step_ = 0; resizing_ = true; - } - else - { + } else { rect_ = setRect(new_rect); resizing_ = false; } options_y_ = new_rect.y + upper_height_ + lower_padding_; } -void MenuRenderer::setSize(const ServiceMenu *menu_state) -{ +void MenuRenderer::setSize(const ServiceMenu *menu_state) { rect_ = setRect(calculateNewRect(menu_state)); resizing_ = false; options_y_ = rect_.y + upper_height_ + lower_padding_; } -void MenuRenderer::updateResizeAnimation() -{ +void MenuRenderer::updateResizeAnimation() { if (!resizing_) return; ++resize_anim_step_; float t = static_cast<float>(resize_anim_step_) / resize_anim_steps_; - if (t >= 1.0f) - { + if (t >= 1.0f) { rect_ = setRect(rect_anim_to_); resizing_ = false; return; @@ -164,64 +143,54 @@ void MenuRenderer::updateResizeAnimation() float ease = 1 - (1 - t) * (1 - t); SDL_FRect rect = {rect_anim_from_.x + (rect_anim_to_.x - rect_anim_from_.x) * ease, - rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease, - rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease, - rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease}; + rect_anim_from_.y + (rect_anim_to_.y - rect_anim_from_.y) * ease, + rect_anim_from_.w + (rect_anim_to_.w - rect_anim_from_.w) * ease, + rect_anim_from_.h + (rect_anim_to_.h - rect_anim_from_.h) * ease}; rect_ = setRect(rect); } -void MenuRenderer::precalculateMenuWidths(const std::vector<std::unique_ptr<MenuOption>> &all_options, const ServiceMenu *menu_state) -{ +void MenuRenderer::precalculateMenuWidths(const std::vector<std::unique_ptr<MenuOption>> &all_options, const ServiceMenu *menu_state) { for (int &w : group_menu_widths_) w = ServiceMenu::MIN_WIDTH_; - for (int group = 0; group < 5; ++group) - { + for (int group = 0; group < 5; ++group) { auto sg = static_cast<ServiceMenu::SettingsGroup>(group); int max_option_width = 0; int max_value_width = 0; - for (const auto &option : all_options) - { + for (const auto &option : all_options) { if (option->getGroup() != sg) continue; max_option_width = std::max(max_option_width, element_text_->lenght(option->getCaption(), -2)); - if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) - { + if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { max_value_width = std::max(max_value_width, option->getMaxValueWidth(element_text_.get())); } } size_t total_width = max_option_width + (ServiceMenu::OPTIONS_HORIZONTAL_PADDING_ * 2); - if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) - { + if (menu_state->getCurrentGroupAlignment() == ServiceMenu::GroupAlignment::LEFT) { total_width += ServiceMenu::MIN_GAP_OPTION_VALUE_ + max_value_width; } group_menu_widths_[group] = std::max((int)ServiceMenu::MIN_WIDTH_, (int)total_width); } } -int MenuRenderer::getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const -{ +int MenuRenderer::getMenuWidthForGroup(ServiceMenu::SettingsGroup group) const { return group_menu_widths_[static_cast<int>(group)]; } -void MenuRenderer::updateColorCounter() -{ +void MenuRenderer::updateColorCounter() { static Uint64 lastUpdate = SDL_GetTicks(); Uint64 currentTicks = SDL_GetTicks(); - if (currentTicks - lastUpdate >= 50) - { + if (currentTicks - lastUpdate >= 50) { color_counter_++; lastUpdate = currentTicks; } } -Color MenuRenderer::getAnimatedSelectedColor() -{ +Color MenuRenderer::getAnimatedSelectedColor() { static auto colorCycle = generateMirroredCycle(param.service_menu.selected_color, ColorCycleStyle::HueWave); return colorCycle.at(color_counter_ % colorCycle.size()); } -SDL_FRect MenuRenderer::setRect(SDL_FRect rect) -{ +SDL_FRect MenuRenderer::setRect(SDL_FRect rect) { border_rect_ = {rect.x - 1, rect.y + 1, rect.w + 2, rect.h - 2}; return rect; } \ No newline at end of file diff --git a/source/ui/menu_renderer.h b/source/ui/menu_renderer.h index 5624685..44e24f2 100644 --- a/source/ui/menu_renderer.h +++ b/source/ui/menu_renderer.h @@ -1,20 +1,20 @@ #pragma once -#include <SDL3/SDL.h> // Para SDL_FRect, Uint32 -#include <stddef.h> // Para size_t -#include <memory> // Para shared_ptr, unique_ptr -#include <vector> // Para vector +#include <SDL3/SDL.h> // Para SDL_FRect, Uint32 +#include <stddef.h> // Para size_t -#include "ui/service_menu.h" // Para ServiceMenu -#include "utils.h" // Para Color +#include <memory> // Para shared_ptr, unique_ptr +#include <vector> // Para vector + +#include "ui/service_menu.h" // Para ServiceMenu +#include "utils.h" // Para Color class MenuOption; // Forward declarations class Text; -class MenuRenderer -{ -public: +class MenuRenderer { + public: MenuRenderer(const ServiceMenu *menu_state, std::shared_ptr<Text> element_text, std::shared_ptr<Text> title_text); // Métodos principales de la vista @@ -28,7 +28,7 @@ public: // Getters const SDL_FRect &getRect() const { return rect_; } -private: + private: // --- Referencias a los renderizadores de texto --- std::shared_ptr<Text> element_text_; std::shared_ptr<Text> title_text_; diff --git a/source/ui/service_menu.cpp b/source/ui/service_menu.cpp index b128a09..c8f24b8 100644 --- a/source/ui/service_menu.cpp +++ b/source/ui/service_menu.cpp @@ -1,16 +1,16 @@ #include "ui/service_menu.h" -#include "audio.h" // Para Audio -#include "lang.h" // Para getText, getCodeFromName, getNameFromCode -#include "menu_option.h" // Para MenuOption, BoolOption, ActionOption, Int... -#include "menu_renderer.h" // Para MenuRenderer -#include "options.h" // Para PendingChanges, VideoOptions, pending_cha... -#include "param.h" // Para Param, param, ParamGame, ParamServiceMenu -#include "resource.h" // Para Resource -#include "screen.h" // Para Screen -#include "section.h" // Para Name, name, Options, options -#include "ui/ui_message.h" // Para UIMessage -#include "utils.h" // Para Zone +#include "audio.h" // Para Audio +#include "lang.h" // Para getText, getCodeFromName, getNameFromCode +#include "menu_option.h" // Para MenuOption, BoolOption, ActionOption, Int... +#include "menu_renderer.h" // Para MenuRenderer +#include "options.h" // Para PendingChanges, VideoOptions, pending_cha... +#include "param.h" // Para Param, param, ParamGame, ParamServiceMenu +#include "resource.h" // Para Resource +#include "screen.h" // Para Screen +#include "section.h" // Para Name, name, Options, options +#include "ui/ui_message.h" // Para UIMessage +#include "utils.h" // Para Zone // Singleton ServiceMenu *ServiceMenu::instance_ = nullptr; @@ -21,8 +21,7 @@ ServiceMenu *ServiceMenu::get() { return ServiceMenu::instance_; } // Constructor ServiceMenu::ServiceMenu() : current_settings_group_(SettingsGroup::MAIN), - previous_settings_group_(current_settings_group_) -{ + previous_settings_group_(current_settings_group_) { auto element_text = Resource::get()->getText("04b_25_flat"); auto title_text = Resource::get()->getText("04b_25_flat_2x"); @@ -32,18 +31,15 @@ ServiceMenu::ServiceMenu() reset(); } -void ServiceMenu::toggle() -{ +void ServiceMenu::toggle() { playBackSound(); enabled_ = !enabled_; - if (!enabled_) - { + if (!enabled_) { reset(); } } -void ServiceMenu::render() -{ +void ServiceMenu::render() { if (!enabled_) return; renderer_->render(this); @@ -55,89 +51,76 @@ void ServiceMenu::render() restart_message_ui_->render(); } -void ServiceMenu::update() -{ +void ServiceMenu::update() { if (!enabled_) return; renderer_->update(this); bool now_pending = Options::pending_changes.has_pending_changes; - if (now_pending != last_pending_changes_) - { + if (now_pending != last_pending_changes_) { now_pending ? restart_message_ui_->show() : restart_message_ui_->hide(); last_pending_changes_ = now_pending; } restart_message_ui_->update(); } -void ServiceMenu::reset() -{ +void ServiceMenu::reset() { selected_ = 0; main_menu_selected_ = 0; current_settings_group_ = SettingsGroup::MAIN; previous_settings_group_ = SettingsGroup::MAIN; initializeOptions(); updateMenu(); - renderer_->setLayout(this); // Notifica al renderer para que calcule el layout inicial + renderer_->setLayout(this); // Notifica al renderer para que calcule el layout inicial } // --- Lógica de Navegación --- -void ServiceMenu::setSelectorUp() -{ +void ServiceMenu::setSelectorUp() { if (display_options_.empty()) return; selected_ = (selected_ > 0) ? selected_ - 1 : display_options_.size() - 1; playMoveSound(); } -void ServiceMenu::setSelectorDown() -{ +void ServiceMenu::setSelectorDown() { if (display_options_.empty()) return; selected_ = (selected_ + 1) % display_options_.size(); playMoveSound(); } -void ServiceMenu::adjustOption(bool adjust_up) -{ +void ServiceMenu::adjustOption(bool adjust_up) { if (display_options_.empty()) return; auto &selected_option = display_options_.at(selected_); - if (selected_option->getBehavior() == MenuOption::Behavior::ADJUST) - { + if (selected_option->getBehavior() == MenuOption::Behavior::ADJUST) { selected_option->adjustValue(adjust_up); applySettings(); playAdjustSound(); } } -void ServiceMenu::selectOption() -{ +void ServiceMenu::selectOption() { if (display_options_.empty()) return; if (current_settings_group_ == SettingsGroup::MAIN) main_menu_selected_ = selected_; auto &selected_option = display_options_.at(selected_); - if (auto folder = dynamic_cast<FolderOption *>(selected_option)) - { + if (auto folder = dynamic_cast<FolderOption *>(selected_option)) { previous_settings_group_ = current_settings_group_; current_settings_group_ = folder->getTargetGroup(); selected_ = 0; updateMenu(); - } - else if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) - { + } else if (selected_option->getBehavior() == MenuOption::Behavior::SELECT) { selected_option->executeAction(); } playSelectSound(); } -void ServiceMenu::moveBack() -{ +void ServiceMenu::moveBack() { playBackSound(); - if (current_settings_group_ == SettingsGroup::MAIN) - { + if (current_settings_group_ == SettingsGroup::MAIN) { enabled_ = false; return; } @@ -148,30 +131,24 @@ void ServiceMenu::moveBack() // --- Lógica Interna --- -void ServiceMenu::updateDisplayOptions() -{ +void ServiceMenu::updateDisplayOptions() { display_options_.clear(); - for (auto &option : options_) - { - if (option->getGroup() == current_settings_group_ && !option->isHidden()) - { + for (auto &option : options_) { + if (option->getGroup() == current_settings_group_ && !option->isHidden()) { display_options_.push_back(option.get()); } } updateOptionPairs(); } -void ServiceMenu::updateOptionPairs() -{ +void ServiceMenu::updateOptionPairs() { option_pairs_.clear(); - for (const auto &option : display_options_) - { + for (const auto &option : display_options_) { option_pairs_.emplace_back(option->getCaption(), option->getValueAsString()); } } -void ServiceMenu::updateMenu() -{ +void ServiceMenu::updateMenu() { title_ = settingsGroupToString(current_settings_group_); AdjustListValues(); @@ -182,8 +159,7 @@ void ServiceMenu::updateMenu() renderer_->onLayoutChanged(this); } -void ServiceMenu::applySettings() -{ +void ServiceMenu::applySettings() { if (current_settings_group_ == SettingsGroup::VIDEO) applyVideoSettings(); if (current_settings_group_ == SettingsGroup::AUDIO) @@ -195,28 +171,22 @@ void ServiceMenu::applySettings() updateOptionPairs(); } -void ServiceMenu::applyVideoSettings() -{ +void ServiceMenu::applyVideoSettings() { Screen::get()->applySettings(); setHiddenOptions(); } -void ServiceMenu::applyAudioSettings() -{ +void ServiceMenu::applyAudioSettings() { Audio::get()->applySettings(); } -void ServiceMenu::applySettingsSettings() -{ +void ServiceMenu::applySettingsSettings() { setHiddenOptions(); } -MenuOption *ServiceMenu::getOptionByCaption(const std::string &caption) const -{ - for (const auto &option : options_) - { - if (option->getCaption() == caption) - { +MenuOption *ServiceMenu::getOptionByCaption(const std::string &caption) const { + for (const auto &option : options_) { + if (option->getCaption() == caption) { return option.get(); } } @@ -225,26 +195,21 @@ MenuOption *ServiceMenu::getOptionByCaption(const std::string &caption) const // --- Getters y otros --- -ServiceMenu::GroupAlignment ServiceMenu::getCurrentGroupAlignment() const -{ - switch (current_settings_group_) - { - case SettingsGroup::VIDEO: - case SettingsGroup::AUDIO: - case SettingsGroup::SETTINGS: - return GroupAlignment::LEFT; - default: - return GroupAlignment::CENTERED; +ServiceMenu::GroupAlignment ServiceMenu::getCurrentGroupAlignment() const { + switch (current_settings_group_) { + case SettingsGroup::VIDEO: + case SettingsGroup::AUDIO: + case SettingsGroup::SETTINGS: + return GroupAlignment::LEFT; + default: + return GroupAlignment::CENTERED; } } -size_t ServiceMenu::countOptionsInGroup(SettingsGroup group) const -{ +size_t ServiceMenu::countOptionsInGroup(SettingsGroup group) const { size_t count = 0; - for (const auto &option : options_) - { - if (option->getGroup() == group && !option->isHidden()) - { + for (const auto &option : options_) { + if (option->getGroup() == group && !option->isHidden()) { count++; } } @@ -252,8 +217,7 @@ size_t ServiceMenu::countOptionsInGroup(SettingsGroup group) const } // Inicializa todas las opciones del menú -void ServiceMenu::initializeOptions() -{ +void ServiceMenu::initializeOptions() { options_.clear(); // --- Video --- @@ -271,21 +235,14 @@ void ServiceMenu::initializeOptions() // --- Settings --- options_.push_back(std::make_unique<BoolOption>(Lang::getText("[SERVICE_MENU] AUTOFIRE"), SettingsGroup::SETTINGS, &Options::settings.autofire)); - options_.push_back(std::make_unique<ListOption>(Lang::getText("[SERVICE_MENU] LANGUAGE"), SettingsGroup::SETTINGS, std::vector<std::string>{Lang::getText("[SERVICE_MENU] LANG_ES"), Lang::getText("[SERVICE_MENU] LANG_BA"), Lang::getText("[SERVICE_MENU] LANG_EN")}, []() - { return Lang::getNameFromCode(Options::pending_changes.new_language); }, [](const std::string &val) - { Options::pending_changes.new_language = Lang::getCodeFromName(val); Options::checkPendingChanges(); })); - options_.push_back(std::make_unique<ListOption>(Lang::getText("[SERVICE_MENU] DIFFICULTY"), SettingsGroup::SETTINGS, std::vector<std::string>{Lang::getText("[SERVICE_MENU] EASY"), Lang::getText("[SERVICE_MENU] NORMAL"), Lang::getText("[SERVICE_MENU] HARD")}, []() - { return Options::getDifficultyNameFromCode(Options::pending_changes.new_difficulty); }, [](const std::string &val) - { Options::pending_changes.new_difficulty = Options::getDifficultyCodeFromName(val); Options::checkPendingChanges(); })); + options_.push_back(std::make_unique<ListOption>(Lang::getText("[SERVICE_MENU] LANGUAGE"), SettingsGroup::SETTINGS, std::vector<std::string>{Lang::getText("[SERVICE_MENU] LANG_ES"), Lang::getText("[SERVICE_MENU] LANG_BA"), Lang::getText("[SERVICE_MENU] LANG_EN")}, []() { return Lang::getNameFromCode(Options::pending_changes.new_language); }, [](const std::string &val) { Options::pending_changes.new_language = Lang::getCodeFromName(val); Options::checkPendingChanges(); })); + options_.push_back(std::make_unique<ListOption>(Lang::getText("[SERVICE_MENU] DIFFICULTY"), SettingsGroup::SETTINGS, std::vector<std::string>{Lang::getText("[SERVICE_MENU] EASY"), Lang::getText("[SERVICE_MENU] NORMAL"), Lang::getText("[SERVICE_MENU] HARD")}, []() { return Options::getDifficultyNameFromCode(Options::pending_changes.new_difficulty); }, [](const std::string &val) { Options::pending_changes.new_difficulty = Options::getDifficultyCodeFromName(val); Options::checkPendingChanges(); })); options_.push_back(std::make_unique<BoolOption>(Lang::getText("[SERVICE_MENU] ENABLE_SHUTDOWN"), SettingsGroup::SETTINGS, &Options::settings.shutdown_enabled)); // --- System --- - options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, [this]() - { Section::name = Section::Name::RESET; toggle(); })); - options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] QUIT"), SettingsGroup::SYSTEM, []() - { Section::name = Section::Name::QUIT; Section::options = Section::Options::NONE; })); - options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] SHUTDOWN"), SettingsGroup::SYSTEM, []() - { Section::name = Section::Name::QUIT; Section::options = Section::Options::SHUTDOWN; }, !Options::settings.shutdown_enabled)); + options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] RESET"), SettingsGroup::SYSTEM, [this]() { Section::name = Section::Name::RESET; toggle(); })); + options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] QUIT"), SettingsGroup::SYSTEM, []() { Section::name = Section::Name::QUIT; Section::options = Section::Options::NONE; })); + options_.push_back(std::make_unique<ActionOption>(Lang::getText("[SERVICE_MENU] SHUTDOWN"), SettingsGroup::SYSTEM, []() { Section::name = Section::Name::QUIT; Section::options = Section::Options::SHUTDOWN; }, !Options::settings.shutdown_enabled)); // --- Main Menu --- options_.push_back(std::make_unique<FolderOption>(Lang::getText("[SERVICE_MENU] VIDEO"), SettingsGroup::MAIN, SettingsGroup::VIDEO)); @@ -297,12 +254,9 @@ void ServiceMenu::initializeOptions() } // Sincroniza los valores de las opciones tipo lista -void ServiceMenu::AdjustListValues() -{ - for (auto &option : options_) - { - if (auto list_option = dynamic_cast<ListOption *>(option.get())) - { +void ServiceMenu::AdjustListValues() { + for (auto &option : options_) { + if (auto list_option = dynamic_cast<ListOption *>(option.get())) { list_option->sync(); } } @@ -315,28 +269,25 @@ void ServiceMenu::playSelectSound() { Audio::get()->playSound("service_menu_sele void ServiceMenu::playBackSound() { Audio::get()->playSound("service_menu_select.wav", Audio::Group::INTERFACE); } // Devuelve el nombre del grupo como string para el título -std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const -{ - switch (group) - { - case SettingsGroup::MAIN: - return Lang::getText("[SERVICE_MENU] TITLE"); - case SettingsGroup::VIDEO: - return Lang::getText("[SERVICE_MENU] VIDEO"); - case SettingsGroup::AUDIO: - return Lang::getText("[SERVICE_MENU] AUDIO"); - case SettingsGroup::SETTINGS: - return Lang::getText("[SERVICE_MENU] SETTINGS"); - case SettingsGroup::SYSTEM: - return Lang::getText("[SERVICE_MENU] SYSTEM"); - default: - return Lang::getText("[SERVICE_MENU] TITLE"); +std::string ServiceMenu::settingsGroupToString(SettingsGroup group) const { + switch (group) { + case SettingsGroup::MAIN: + return Lang::getText("[SERVICE_MENU] TITLE"); + case SettingsGroup::VIDEO: + return Lang::getText("[SERVICE_MENU] VIDEO"); + case SettingsGroup::AUDIO: + return Lang::getText("[SERVICE_MENU] AUDIO"); + case SettingsGroup::SETTINGS: + return Lang::getText("[SERVICE_MENU] SETTINGS"); + case SettingsGroup::SYSTEM: + return Lang::getText("[SERVICE_MENU] SYSTEM"); + default: + return Lang::getText("[SERVICE_MENU] TITLE"); } } // Establece el estado de oculto de ciertas opciones -void ServiceMenu::setHiddenOptions() -{ +void ServiceMenu::setHiddenOptions() { { auto option = getOptionByCaption(Lang::getText("[SERVICE_MENU] WINDOW_SIZE")); if (option) @@ -349,5 +300,5 @@ void ServiceMenu::setHiddenOptions() option->setHidden(!Options::settings.shutdown_enabled); } - updateMenu(); // El menú debe refrescarse si algo se oculta + updateMenu(); // El menú debe refrescarse si algo se oculta } \ No newline at end of file diff --git a/source/ui/service_menu.h b/source/ui/service_menu.h index b7b36d9..d36ec95 100644 --- a/source/ui/service_menu.h +++ b/source/ui/service_menu.h @@ -1,21 +1,20 @@ #pragma once -#include <stddef.h> // Para size_t +#include <stddef.h> // Para size_t + #include <memory> // Para unique_ptr #include <string> // Para basic_string, string #include <utility> // Para pair #include <vector> // Para vector -#include "ui_message.h" // Para UIMessage +#include "ui_message.h" // Para UIMessage class MenuOption; -class MenuRenderer; // <-- Nuevo +class MenuRenderer; // <-- Nuevo -class ServiceMenu -{ -public: - enum class SettingsGroup - { +class ServiceMenu { + public: + enum class SettingsGroup { VIDEO, AUDIO, SETTINGS, @@ -23,8 +22,7 @@ public: MAIN }; - enum class GroupAlignment - { + enum class GroupAlignment { CENTERED, LEFT }; @@ -61,7 +59,7 @@ public: const std::vector<std::pair<std::string, std::string>> &getOptionPairs() const { return option_pairs_; } size_t countOptionsInGroup(SettingsGroup group) const; -private: + private: // --- Lógica de estado del menú (Modelo) --- bool enabled_ = false; std::vector<std::unique_ptr<MenuOption>> options_; diff --git a/source/ui/ui_message.cpp b/source/ui/ui_message.cpp index a57dc1f..5d4a28e 100644 --- a/source/ui/ui_message.cpp +++ b/source/ui/ui_message.cpp @@ -1,21 +1,20 @@ #include "ui_message.h" -#include <cmath> // Para pow +#include <cmath> // Para pow -#include "text.h" // Para TEXT_CENTER, TEXT_COLOR, Text +#include "text.h" // Para TEXT_CENTER, TEXT_COLOR, Text // Constructor: inicializa el renderizador, el texto y el color del mensaje UIMessage::UIMessage(std::shared_ptr<Text> text_renderer, const std::string &message_text, const Color &color) : text_renderer_(text_renderer), text_(message_text), color_(color) {} // Muestra el mensaje en la posición base_x, base_y con animación de entrada desde arriba -void UIMessage::show() -{ +void UIMessage::show() { if (visible_ && target_y_ == 0.0f) - return; // Ya está visible y quieto + return; // Ya está visible y quieto - start_y_ = DESP_; // Empieza 8 píxeles arriba de la posición base - target_y_ = 0.0f; // La posición final es la base + start_y_ = DESP_; // Empieza 8 píxeles arriba de la posición base + target_y_ = 0.0f; // La posición final es la base y_offset_ = start_y_; anim_step_ = 0; animating_ = true; @@ -23,47 +22,39 @@ void UIMessage::show() } // Oculta el mensaje con animación de salida hacia arriba -void UIMessage::hide() -{ +void UIMessage::hide() { if (!visible_) return; - start_y_ = y_offset_; // Comienza desde la posición actual - target_y_ = DESP_; // Termina 8 píxeles arriba de la base + start_y_ = y_offset_; // Comienza desde la posición actual + target_y_ = DESP_; // Termina 8 píxeles arriba de la base anim_step_ = 0; animating_ = true; } // Actualiza el estado de la animación (debe llamarse cada frame) -void UIMessage::update() -{ - if (animating_) - { +void UIMessage::update() { + if (animating_) { updateAnimation(); } } // Interpola la posición vertical del mensaje usando ease out cubic -void UIMessage::updateAnimation() -{ +void UIMessage::updateAnimation() { anim_step_++; float t = static_cast<float>(anim_step_) / ANIMATION_STEPS; - if (target_y_ > start_y_) - { + if (target_y_ > start_y_) { // Animación de entrada (ease out cubic) t = 1 - pow(1 - t, 3); - } - else - { + } else { // Animación de salida (ease in cubic) t = pow(t, 3); } y_offset_ = start_y_ + (target_y_ - start_y_) * t; - if (anim_step_ >= ANIMATION_STEPS) - { + if (anim_step_ >= ANIMATION_STEPS) { y_offset_ = target_y_; animating_ = false; if (target_y_ < 0.0f) @@ -72,10 +63,8 @@ void UIMessage::updateAnimation() } // Dibuja el mensaje en pantalla si está visible -void UIMessage::render() -{ - if (visible_) - { +void UIMessage::render() { + if (visible_) { text_renderer_->writeDX( TEXT_COLOR | TEXT_CENTER, base_x_, @@ -87,14 +76,12 @@ void UIMessage::render() } // Devuelve true si el mensaje está visible actualmente -bool UIMessage::isVisible() const -{ +bool UIMessage::isVisible() const { return visible_; } // Permite actualizar la posición del mensaje (por ejemplo, si el menú se mueve) -void UIMessage::setPosition(float new_base_x, float new_base_y) -{ +void UIMessage::setPosition(float new_base_x, float new_base_y) { base_x_ = new_base_x; base_y_ = new_base_y; } \ No newline at end of file diff --git a/source/ui/ui_message.h b/source/ui/ui_message.h index 5da7e9d..5e40474 100644 --- a/source/ui/ui_message.h +++ b/source/ui/ui_message.h @@ -1,16 +1,15 @@ #pragma once -#include <memory> // Para shared_ptr -#include <string> // Para string, basic_string +#include <memory> // Para shared_ptr +#include <string> // Para string, basic_string -#include "utils.h" // Para Color +#include "utils.h" // Para Color class Text; // Clase para mostrar mensajes animados en la interfaz de usuario -class UIMessage -{ -public: +class UIMessage { + public: // Constructor: recibe el renderizador de texto, el mensaje y el color UIMessage(std::shared_ptr<Text> text_renderer, const std::string &message_text, const Color &color); @@ -32,25 +31,25 @@ public: // Permite actualizar la posición del mensaje (por ejemplo, si el menú se mueve) void setPosition(float new_base_x, float new_base_y); -private: + private: // --- Configuración --- - std::shared_ptr<Text> text_renderer_; // Renderizador de texto - std::string text_; // Texto del mensaje a mostrar - Color color_; // Color del texto + std::shared_ptr<Text> text_renderer_; // Renderizador de texto + std::string text_; // Texto del mensaje a mostrar + Color color_; // Color del texto // --- Estado --- - bool visible_ = false; // Indica si el mensaje está visible - bool animating_ = false; // Indica si el mensaje está en proceso de animación - float base_x_ = 0.0f; // Posición X base donde se muestra el mensaje - float base_y_ = 0.0f; // Posición Y base donde se muestra el mensaje - float y_offset_ = 0.0f; // Desplazamiento vertical actual del mensaje (para animación) + bool visible_ = false; // Indica si el mensaje está visible + bool animating_ = false; // Indica si el mensaje está en proceso de animación + float base_x_ = 0.0f; // Posición X base donde se muestra el mensaje + float base_y_ = 0.0f; // Posición Y base donde se muestra el mensaje + float y_offset_ = 0.0f; // Desplazamiento vertical actual del mensaje (para animación) // --- Animación --- - float start_y_ = 0.0f; // Posición Y inicial de la animación - float target_y_ = 0.0f; // Posición Y objetivo de la animación - int anim_step_ = 0; // Paso actual de la animación - static constexpr int ANIMATION_STEPS = 8; // Número total de pasos de la animación - static constexpr float DESP_ = -8.0f; // Distancia a desplazarse + float start_y_ = 0.0f; // Posición Y inicial de la animación + float target_y_ = 0.0f; // Posición Y objetivo de la animación + int anim_step_ = 0; // Paso actual de la animación + static constexpr int ANIMATION_STEPS = 8; // Número total de pasos de la animación + static constexpr float DESP_ = -8.0f; // Distancia a desplazarse // Actualiza la interpolación de la animación (ease out/in cubic) void updateAnimation(); diff --git a/source/utils.cpp b/source/utils.cpp index 5e28bbc..022275a 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -1,50 +1,45 @@ #define _USE_MATH_DEFINES #include "utils.h" -#include <SDL3/SDL.h> // Para SDL_RenderPoint, SDL_FRect, SDL_CloseIO, SDL_I... -#include <algorithm> // Para min, clamp, find_if_not, find, transform -#include <cctype> // Para tolower, isspace -#include <cmath> // Para pow, sinf, fmaxf, fminf, M_PI, fmodf, roundf, sin -#include <compare> // Para operator< -#include <filesystem> // Para path -#include <stdexcept> // Para runtime_error -#include <string> // Para basic_string, allocator, string, char_traits +#include <SDL3/SDL.h> // Para SDL_RenderPoint, SDL_FRect, SDL_CloseIO, SDL_I... -#include "lang.h" // Para getText +#include <algorithm> // Para min, clamp, find_if_not, find, transform +#include <cctype> // Para tolower, isspace +#include <cmath> // Para pow, sinf, fmaxf, fminf, M_PI, fmodf, roundf, sin +#include <compare> // Para operator< +#include <filesystem> // Para path +#include <stdexcept> // Para runtime_error +#include <string> // Para basic_string, allocator, string, char_traits + +#include "lang.h" // Para getText // Variables Overrides overrides = Overrides(); // Obtiene un color del vector de colores imitando al Coche Fantástico -Color getColorLikeKnightRider(const std::vector<Color> &colors, int counter_) -{ +Color getColorLikeKnightRider(const std::vector<Color> &colors, int counter_) { int cycle_length = colors.size() * 2 - 2; size_t n = counter_ % cycle_length; size_t index; - if (n < colors.size()) - { - index = n; // Avanza: 0,1,2,3 - } - else - { - index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1 + if (n < colors.size()) { + index = n; // Avanza: 0,1,2,3 + } else { + index = 2 * (colors.size() - 1) - n; // Retrocede: 2,1 } return colors[index]; } // Calcula el cuadrado de la distancia entre dos puntos -double distanceSquared(int x1, int y1, int x2, int y2) -{ +double distanceSquared(int x1, int y1, int x2, int y2) { const int delta_x = x2 - x1; const int delta_y = y2 - y1; return delta_x * delta_x + delta_y * delta_y; } // Detector de colisiones entre dos circulos -bool checkCollision(const Circle &a, const Circle &b) -{ +bool checkCollision(const Circle &a, const Circle &b) { // Calcula el radio total al cuadrado int total_radius_squared = (a.r + b.r) * (a.r + b.r); @@ -53,8 +48,7 @@ bool checkCollision(const Circle &a, const Circle &b) } // Detector de colisiones entre un circulo y un rectangulo -bool checkCollision(const Circle &a, const SDL_FRect &b) -{ +bool checkCollision(const Circle &a, const SDL_FRect &b) { // Encuentra el punto más cercano en el rectángulo float cX = std::clamp(static_cast<float>(a.x), b.x, b.x + b.w); float cY = std::clamp(static_cast<float>(a.y), b.y, b.y + b.h); @@ -64,8 +58,7 @@ bool checkCollision(const Circle &a, const SDL_FRect &b) } // Detector de colisiones entre dos rectangulos -bool checkCollision(const SDL_FRect &a, const SDL_FRect &b) -{ +bool checkCollision(const SDL_FRect &a, const SDL_FRect &b) { const int leftA = a.x, rightA = a.x + a.w, topA = a.y, bottomA = a.y + a.h; const int leftB = b.x, rightB = b.x + b.w, topB = b.y, bottomB = b.y + b.h; @@ -82,8 +75,7 @@ bool checkCollision(const SDL_FRect &a, const SDL_FRect &b) } // Detector de colisiones entre un punto y un rectangulo -bool checkCollision(const SDL_FPoint &p, const SDL_FRect &r) -{ +bool checkCollision(const SDL_FPoint &p, const SDL_FRect &r) { if (p.x < r.x || p.x > r.x + r.w) return false; if (p.y < r.y || p.y > r.y + r.h) @@ -92,37 +84,30 @@ bool checkCollision(const SDL_FPoint &p, const SDL_FRect &r) } // Convierte una cadena en un valor booleano -bool stringToBool(const std::string &str) -{ +bool stringToBool(const std::string &str) { std::string s = trim(toLower(str)); return (s == "true" || s == "1" || s == "yes" || s == "on"); } // Convierte un valor booleano en una cadena -std::string boolToString(bool value) -{ +std::string boolToString(bool value) { return value ? "true" : "false"; } // Convierte un valor booleano en una cadena "on" o "off" -std::string boolToOnOff(bool value) -{ +std::string boolToOnOff(bool value) { return value ? Lang::getText("[NOTIFICATIONS] 06") : Lang::getText("[NOTIFICATIONS] 07"); } // Convierte una cadena a minusculas -std::string toLower(const std::string &str) -{ +std::string toLower(const std::string &str) { std::string result = str; - std::transform(result.begin(), result.end(), result.begin(), - [](unsigned char c) - { return std::tolower(c); }); + std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return std::tolower(c); }); return result; } // Dibuja un circulo -void DrawCircle(SDL_Renderer *renderer, int32_t centerX, int32_t centerY, int32_t radius) -{ +void DrawCircle(SDL_Renderer *renderer, int32_t centerX, int32_t centerY, int32_t radius) { const int32_t diameter = (radius * 2); int32_t x = (radius - 1); @@ -131,8 +116,7 @@ void DrawCircle(SDL_Renderer *renderer, int32_t centerX, int32_t centerY, int32_ int32_t ty = 1; int32_t error = (tx - diameter); - while (x >= y) - { + while (x >= y) { // Each of the following renders an octant of the circle SDL_RenderPoint(renderer, centerX + x, centerY - y); SDL_RenderPoint(renderer, centerX + x, centerY + y); @@ -143,15 +127,13 @@ void DrawCircle(SDL_Renderer *renderer, int32_t centerX, int32_t centerY, int32_ SDL_RenderPoint(renderer, centerX - y, centerY - x); SDL_RenderPoint(renderer, centerX - y, centerY + x); - if (error <= 0) - { + if (error <= 0) { ++y; error += ty; ty += 2; } - if (error > 0) - { + if (error > 0) { --x; tx += 2; error += (tx - diameter); @@ -160,8 +142,7 @@ void DrawCircle(SDL_Renderer *renderer, int32_t centerX, int32_t centerY, int32_ } // Aclara el color -Color lightenColor(const Color &color, int amount) -{ +Color lightenColor(const Color &color, int amount) { Color newColor; newColor.r = std::min(255, color.r + amount); newColor.g = std::min(255, color.g + amount); @@ -170,8 +151,7 @@ Color lightenColor(const Color &color, int amount) } // Oscurece el color -Color DarkenColor(const Color &color, int amount) -{ +Color DarkenColor(const Color &color, int amount) { Color newColor; newColor.r = std::min(255, color.r - +amount); newColor.g = std::min(255, color.g - +amount); @@ -180,128 +160,104 @@ Color DarkenColor(const Color &color, int amount) } // Quita los espacioes en un string -std::string trim(const std::string &str) -{ +std::string trim(const std::string &str) { auto start = std::find_if_not(str.begin(), str.end(), ::isspace); auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace).base(); return (start < end ? std::string(start, end) : std::string()); } // Función de suavizado -double easeOutQuint(double t) -{ +double easeOutQuint(double t) { return 1 - std::pow(1 - t, 5); } // Función de suavizado -double easeInQuint(double t) -{ +double easeInQuint(double t) { return pow(t, 5); } // Función de suavizado -double easeInOutQuint(double t) -{ +double easeInOutQuint(double t) { return t < 0.5 ? 16 * pow(t, 5) : 1 - pow(-2 * t + 2, 5) / 2; } // Función de suavizado -double easeInQuad(double t) -{ +double easeInQuad(double t) { return t * t; } // Función de suavizado -double easeOutQuad(double t) -{ +double easeOutQuad(double t) { return 1 - (1 - t) * (1 - t); } // Función de suavizado -double easeInOutSine(double t) -{ +double easeInOutSine(double t) { return -0.5 * (std::cos(M_PI * t) - 1); } // Función de suavizado -double easeInOut(double t) -{ +double easeInOut(double t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; } // Función de suavizado -double easeInOutExpo(double t) -{ +double easeInOutExpo(double t) { return t == 0 ? 0 : (t == 1 ? 1 : (t < 0.5 ? pow(2, 20 * t - 10) / 2 : (2 - pow(2, -20 * t + 10)) / 2)); } // Función de suavizado (easeInElastic) -double easeInElastic(double t) -{ +double easeInElastic(double t) { return t == 0 ? 0 : (t == 1 ? 1 : -pow(2, 10 * t - 10) * sin((t * 10 - 10.75) * (2 * M_PI) / 3)); } // Función de suavizado -double easeOutBounce(double t) -{ - if (t < 1 / 2.75) - { +double easeOutBounce(double t) { + if (t < 1 / 2.75) { return 7.5625 * t * t; - } - else if (t < 2 / 2.75) - { + } else if (t < 2 / 2.75) { t -= 1.5 / 2.75; return 7.5625 * t * t + 0.75; - } - else if (t < 2.5 / 2.75) - { + } else if (t < 2.5 / 2.75) { t -= 2.25 / 2.75; return 7.5625 * t * t + 0.9375; - } - else - { + } else { t -= 2.625 / 2.75; return 7.5625 * t * t + 0.984375; } } // Función de suavizado -double easeOutElastic(double t) -{ - const double c4 = (2 * M_PI) / 3; // Constante para controlar la elasticidad +double easeOutElastic(double t) { + const double c4 = (2 * M_PI) / 3; // Constante para controlar la elasticidad return t == 0 - ? 0 - : (t == 1 - ? 1 - : pow(2, -10 * t) * sin((t * 10 - 0.75) * c4) + 1); + ? 0 + : (t == 1 + ? 1 + : pow(2, -10 * t) * sin((t * 10 - 0.75) * c4) + 1); } // Comprueba si una vector contiene una cadena -bool stringInVector(const std::vector<std::string> &vec, const std::string &str) -{ +bool stringInVector(const std::vector<std::string> &vec, const std::string &str) { return std::find(vec.begin(), vec.end(), str) != vec.end(); } // Imprime por pantalla una línea de texto de tamaño fijo rellena con puntos -void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3) -{ +void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3) { constexpr size_t TOTAL_WIDTH = 52; // Calcula el ancho del campo para text2 restando la longitud de text1 y text3 size_t field_width = TOTAL_WIDTH > (text1.size() + text3.size()) - ? TOTAL_WIDTH - text1.size() - text3.size() - : 0; + ? TOTAL_WIDTH - text1.size() - text3.size() + : 0; // Prepara el bloque a imprimir a partir de text2 std::string field_text; - if (text2.size() < field_width) - { + if (text2.size() < field_width) { // Si text2 es más corto, lo rellenamos a la derecha con puntos field_text = text2 + std::string(field_width - text2.size(), '.'); - } - else - { + } else { // Si es demasiado largo, lo cortamos field_text = text2.substr(0, field_width); } @@ -314,24 +270,19 @@ void printWithDots(const std::string &text1, const std::string &text2, const std } // Carga el fichero de datos para la demo -DemoData loadDemoDataFromFile(const std::string &file_path) -{ +DemoData loadDemoDataFromFile(const std::string &file_path) { DemoData dd; // Indicador de éxito en la carga auto file = SDL_IOFromFile(file_path.c_str(), "r+b"); - if (!file) - { + if (!file) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Fichero no encontrado %s", file_path.c_str()); throw std::runtime_error("Fichero no encontrado: " + file_path); - } - else - { + } else { printWithDots("DemoData : ", getFileName(file_path), "[ LOADED ]"); // Lee todos los datos del fichero y los deja en el destino - for (int i = 0; i < TOTAL_DEMO_DATA; ++i) - { + for (int i = 0; i < TOTAL_DEMO_DATA; ++i) { DemoKeys dk = DemoKeys(); SDL_ReadIO(file, &dk, sizeof(DemoKeys)); dd.push_back(dk); @@ -346,55 +297,45 @@ DemoData loadDemoDataFromFile(const std::string &file_path) #ifdef RECORDING // Guarda el fichero de datos para la demo -bool saveDemoFile(const std::string &file_path, const DemoData &dd) -{ +bool saveDemoFile(const std::string &file_path, const DemoData &dd) { auto success = true; auto file = SDL_IOFromFile(file_path.c_str(), "w+b"); - if (file) - { + if (file) { // Guarda los datos - for (const auto &data : dd) - { - if (SDL_RWwrite(file, &data, sizeof(DemoKeys), 1) != 1) - { + for (const auto &data : dd) { + if (SDL_RWwrite(file, &data, sizeof(DemoKeys), 1) != 1) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error al escribir el fichero %s", getFileName(file_path).c_str()); success = false; break; } } - if (success) - { + if (success) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file %s", getFileName(file_path).c_str()); } // Cierra el fichero SDL_CloseIO(file); - } - else - { + } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: Unable to save %s file! %s", getFileName(file_path).c_str(), SDL_GetError()); success = false; } return success; } -#endif // RECORDING +#endif // RECORDING // Obtiene el nombre de un fichero a partir de una ruta completa -std::string getFileName(const std::string &path) -{ +std::string getFileName(const std::string &path) { return std::filesystem::path(path).filename().string(); } // Obtiene la ruta eliminando el nombre del fichero -std::string getPath(const std::string &full_path) -{ +std::string getPath(const std::string &full_path) { std::filesystem::path path(full_path); return path.parent_path().string(); } -constexpr HSV rgbToHsv(Color color) -{ +constexpr HSV rgbToHsv(Color color) { float r = color.r / 255.0f; float g = color.g / 255.0f; float b = color.b / 255.0f; @@ -404,8 +345,7 @@ constexpr HSV rgbToHsv(Color color) float delta = max - min; float h = 0.0f; - if (delta > 0.00001f) - { + if (delta > 0.00001f) { if (max == r) h = fmodf((g - b) / delta, 6.0f); else if (max == g) @@ -423,46 +363,34 @@ constexpr HSV rgbToHsv(Color color) return {h, s, v}; } -constexpr Color hsvToRgb(HSV hsv) -{ +constexpr Color hsvToRgb(HSV hsv) { float c = hsv.v * hsv.s; float x = c * (1 - std::abs(std::fmod(hsv.h / 60.0f, 2) - 1)); float m = hsv.v - c; float r = 0, g = 0, b = 0; - if (hsv.h < 60) - { + if (hsv.h < 60) { r = c; g = x; b = 0; - } - else if (hsv.h < 120) - { + } else if (hsv.h < 120) { r = x; g = c; b = 0; - } - else if (hsv.h < 180) - { + } else if (hsv.h < 180) { r = 0; g = c; b = x; - } - else if (hsv.h < 240) - { + } else if (hsv.h < 240) { r = 0; g = x; b = c; - } - else if (hsv.h < 300) - { + } else if (hsv.h < 300) { r = x; g = 0; b = c; - } - else - { + } else { r = c; g = 0; b = x; @@ -474,47 +402,44 @@ constexpr Color hsvToRgb(HSV hsv) static_cast<uint8_t>(roundf((b + m) * 255))); } -ColorCycle generateMirroredCycle(Color base, ColorCycleStyle style) -{ +ColorCycle generateMirroredCycle(Color base, ColorCycleStyle style) { ColorCycle result{}; HSV baseHSV = rgbToHsv(base); - for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) - { - float t = static_cast<float>(i) / (COLOR_CYCLE_SIZE - 1); // 0 → 1 + for (size_t i = 0; i < COLOR_CYCLE_SIZE; ++i) { + float t = static_cast<float>(i) / (COLOR_CYCLE_SIZE - 1); // 0 → 1 float hueShift = 0.0f; float satShift = 0.0f; float valShift = 0.0f; - switch (style) - { - case ColorCycleStyle::SubtlePulse: - // Solo brillo suave - valShift = 0.07f * sinf(t * M_PI); - break; + switch (style) { + case ColorCycleStyle::SubtlePulse: + // Solo brillo suave + valShift = 0.07f * sinf(t * M_PI); + break; - case ColorCycleStyle::HueWave: - // Oscilación leve de tono - hueShift = 15.0f * (t - 0.5f) * 2.0f; - valShift = 0.05f * sinf(t * M_PI); - break; + case ColorCycleStyle::HueWave: + // Oscilación leve de tono + hueShift = 15.0f * (t - 0.5f) * 2.0f; + valShift = 0.05f * sinf(t * M_PI); + break; - case ColorCycleStyle::Vibrant: - // Cambios fuertes en tono y brillo - hueShift = 35.0f * sinf(t * M_PI); - valShift = 0.2f * sinf(t * M_PI); - satShift = -0.2f * sinf(t * M_PI); - break; + case ColorCycleStyle::Vibrant: + // Cambios fuertes en tono y brillo + hueShift = 35.0f * sinf(t * M_PI); + valShift = 0.2f * sinf(t * M_PI); + satShift = -0.2f * sinf(t * M_PI); + break; - case ColorCycleStyle::DarkenGlow: - // Se oscurece al centro - valShift = -0.15f * sinf(t * M_PI); - break; + case ColorCycleStyle::DarkenGlow: + // Se oscurece al centro + valShift = -0.15f * sinf(t * M_PI); + break; - case ColorCycleStyle::LightFlash: - // Se ilumina al centro - valShift = 0.25f * sinf(t * M_PI); - break; + case ColorCycleStyle::LightFlash: + // Se ilumina al centro + valShift = 0.25f * sinf(t * M_PI); + break; } HSV adjusted = { @@ -524,7 +449,7 @@ ColorCycle generateMirroredCycle(Color base, ColorCycleStyle style) Color c = hsvToRgb(adjusted); result[i] = c; - result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo + result[2 * COLOR_CYCLE_SIZE - 1 - i] = c; // espejo } return result; diff --git a/source/utils.h b/source/utils.h index 96f6118..54fff54 100644 --- a/source/utils.h +++ b/source/utils.h @@ -1,187 +1,169 @@ #pragma once -#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_FPoint, SDL_Renderer -#include <stdint.h> // Para int32_t +#include <SDL3/SDL.h> // Para Uint8, SDL_FRect, SDL_FPoint, SDL_Renderer +#include <stdint.h> // Para int32_t + #include <algorithm> // Para max, min -#include <array> // Para array -#include <cctype> // Para isxdigit -#include <cstdlib> // Para abs, size_t +#include <array> // Para array +#include <cctype> // Para isxdigit +#include <cstdlib> // Para abs, size_t #include <stdexcept> // Para invalid_argument -#include <string> // Para string, basic_string, stoi -#include <vector> // Para vector +#include <string> // Para string, basic_string, stoi +#include <vector> // Para vector // --- Constantes --- constexpr int BLOCK = 8; constexpr int TOTAL_DEMO_DATA = 2000; -constexpr size_t COLOR_CYCLE_SIZE = 6; // Mitad del ciclo espejado +constexpr size_t COLOR_CYCLE_SIZE = 6; // Mitad del ciclo espejado // --- Estructuras y tipos --- -struct Overrides -{ - std::string param_file; // Fichero de parametros a utilizar - bool clear_hi_score_table; // Reinicia la tabla de records +struct Overrides { + std::string param_file; // Fichero de parametros a utilizar + bool clear_hi_score_table; // Reinicia la tabla de records - Overrides() - : param_file(""), clear_hi_score_table(false) {} + Overrides() + : param_file(""), clear_hi_score_table(false) {} }; extern Overrides overrides; // Estructura para definir un circulo -struct Circle -{ - int x, y, r; - Circle() : x(0), y(0), r(0) {} - Circle(int xCoord, int yCoord, int radius) - : x(xCoord), y(yCoord), r(radius) {} +struct Circle { + int x, y, r; + Circle() : x(0), y(0), r(0) {} + Circle(int xCoord, int yCoord, int radius) + : x(xCoord), y(yCoord), r(radius) {} }; // Estructura para definir un color RGBA -struct Color -{ - Uint8 r, g, b, a; - constexpr Color() : r(0), g(0), b(0), a(255) {} - explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = 255) : r(red), g(green), b(blue), a(alpha) {} +struct Color { + Uint8 r, g, b, a; + constexpr Color() : r(0), g(0), b(0), a(255) {} + explicit constexpr Color(Uint8 red, Uint8 green, Uint8 blue, Uint8 alpha = 255) : r(red), g(green), b(blue), a(alpha) {} - constexpr Color inverse() const { return Color(255 - r, 255 - g, 255 - b, a); } - constexpr Color lighten(int amount = 50) const - { - return Color( - std::min(255, r + amount), - std::min(255, g + amount), - std::min(255, b + amount), - a); - } - constexpr Color darken(int amount = 50) const - { - return Color( - std::max(0, r - amount), - std::max(0, g - amount), - std::max(0, b - amount), - a); - } + constexpr Color inverse() const { return Color(255 - r, 255 - g, 255 - b, a); } + constexpr Color lighten(int amount = 50) const { + return Color( + std::min(255, r + amount), + std::min(255, g + amount), + std::min(255, b + amount), + a); + } + constexpr Color darken(int amount = 50) const { + return Color( + std::max(0, r - amount), + std::max(0, g - amount), + std::max(0, b - amount), + a); + } - // Método estático para crear Color desde string hexadecimal - static Color fromHex(const std::string &hexStr) - { - std::string hex = hexStr; + // Método estático para crear Color desde string hexadecimal + static Color fromHex(const std::string &hexStr) { + std::string hex = hexStr; - // Quitar '#' si existe - if (!hex.empty() && hex[0] == '#') - { - hex = hex.substr(1); - } + // Quitar '#' si existe + if (!hex.empty() && hex[0] == '#') { + hex = hex.substr(1); + } - // Verificar longitud válida (6 para RGB o 8 para RGBA) - if (hex.length() != 6 && hex.length() != 8) - { - throw std::invalid_argument("String hexadecimal debe tener 6 o 8 caracteres"); - } + // Verificar longitud válida (6 para RGB o 8 para RGBA) + if (hex.length() != 6 && hex.length() != 8) { + throw std::invalid_argument("String hexadecimal debe tener 6 o 8 caracteres"); + } - // Verificar que todos los caracteres sean hexadecimales válidos - for (char c : hex) - { - if (!std::isxdigit(c)) - { - throw std::invalid_argument("String contiene caracteres no hexadecimales"); - } - } + // Verificar que todos los caracteres sean hexadecimales válidos + for (char c : hex) { + if (!std::isxdigit(c)) { + throw std::invalid_argument("String contiene caracteres no hexadecimales"); + } + } - // Convertir cada par de caracteres a valores RGB(A) - Uint8 r = static_cast<Uint8>(std::stoi(hex.substr(0, 2), nullptr, 16)); - Uint8 g = static_cast<Uint8>(std::stoi(hex.substr(2, 2), nullptr, 16)); - Uint8 b = static_cast<Uint8>(std::stoi(hex.substr(4, 2), nullptr, 16)); - Uint8 a = 255; // Alpha por defecto + // Convertir cada par de caracteres a valores RGB(A) + Uint8 r = static_cast<Uint8>(std::stoi(hex.substr(0, 2), nullptr, 16)); + Uint8 g = static_cast<Uint8>(std::stoi(hex.substr(2, 2), nullptr, 16)); + Uint8 b = static_cast<Uint8>(std::stoi(hex.substr(4, 2), nullptr, 16)); + Uint8 a = 255; // Alpha por defecto - // Si tiene 8 caracteres, extraer el alpha - if (hex.length() == 8) - { - a = static_cast<Uint8>(std::stoi(hex.substr(6, 2), nullptr, 16)); - } + // Si tiene 8 caracteres, extraer el alpha + if (hex.length() == 8) { + a = static_cast<Uint8>(std::stoi(hex.substr(6, 2), nullptr, 16)); + } - return Color(r, g, b, a); - } + return Color(r, g, b, a); + } - constexpr bool isEqualTo(const Color &other) const - { - return r == other.r && g == other.g && b == other.b && a == other.a; - } + constexpr bool isEqualTo(const Color &other) const { + return r == other.r && g == other.g && b == other.b && a == other.a; + } - constexpr Color approachTo(const Color &target, int step = 1) const - { - Uint8 newR = (std::abs(r - target.r) <= step) ? target.r : (r < target.r ? r + step : r - step); - Uint8 newG = (std::abs(g - target.g) <= step) ? target.g : (g < target.g ? g + step : g - step); - Uint8 newB = (std::abs(b - target.b) <= step) ? target.b : (b < target.b ? b + step : b - step); - Uint8 newA = (std::abs(a - target.a) <= step) ? target.a : (a < target.a ? a + step : a - step); + constexpr Color approachTo(const Color &target, int step = 1) const { + Uint8 newR = (std::abs(r - target.r) <= step) ? target.r : (r < target.r ? r + step : r - step); + Uint8 newG = (std::abs(g - target.g) <= step) ? target.g : (g < target.g ? g + step : g - step); + Uint8 newB = (std::abs(b - target.b) <= step) ? target.b : (b < target.b ? b + step : b - step); + Uint8 newA = (std::abs(a - target.a) <= step) ? target.a : (a < target.a ? a + step : a - step); - return Color(newR, newG, newB, newA); - } + return Color(newR, newG, newB, newA); + } }; // Estructura para definir un color HSV -struct HSV -{ - float h, s, v; +struct HSV { + float h, s, v; }; // Estructura para definir el ciclo de color -enum class ColorCycleStyle -{ - SubtlePulse, // Variación leve en brillo (por defecto) - HueWave, // Variación suave en tono (sin verde) - Vibrant, // Cambios agresivos en tono y brillo - DarkenGlow, // Oscurece hacia el centro y regresa - LightFlash // Ilumina hacia el centro y regresa +enum class ColorCycleStyle { + SubtlePulse, // Variación leve en brillo (por defecto) + HueWave, // Variación suave en tono (sin verde) + Vibrant, // Cambios agresivos en tono y brillo + DarkenGlow, // Oscurece hacia el centro y regresa + LightFlash // Ilumina hacia el centro y regresa }; // Posiciones de las notificaciones -enum class NotifyPosition -{ - TOP, - BOTTOM, - LEFT, - MIDDLE, - RIGHT, +enum class NotifyPosition { + TOP, + BOTTOM, + LEFT, + MIDDLE, + RIGHT, }; // Estructura para datos de la demo -struct DemoKeys -{ - Uint8 left; - Uint8 right; - Uint8 no_input; - Uint8 fire; - Uint8 fire_left; - Uint8 fire_right; +struct DemoKeys { + Uint8 left; + Uint8 right; + Uint8 no_input; + Uint8 fire; + Uint8 fire_left; + Uint8 fire_right; - explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0) - : left(l), right(r), no_input(ni), fire(f), fire_left(fl), fire_right(fr) {} + explicit DemoKeys(Uint8 l = 0, Uint8 r = 0, Uint8 ni = 0, Uint8 f = 0, Uint8 fl = 0, Uint8 fr = 0) + : left(l), right(r), no_input(ni), fire(f), fire_left(fl), fire_right(fr) {} }; using DemoData = std::vector<DemoKeys>; -struct Demo -{ - bool enabled; // Indica si está activo el modo demo - bool recording; // Indica si está activado el modo para grabar la demo - int counter; // Contador para el modo demo - DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo - std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo +struct Demo { + bool enabled; // Indica si está activo el modo demo + bool recording; // Indica si está activado el modo para grabar la demo + int counter; // Contador para el modo demo + DemoKeys keys; // Variable con las pulsaciones de teclas del modo demo + std::vector<DemoData> data; // Vector con diferentes sets de datos con los movimientos para la demo - Demo() : enabled(false), recording(false), counter(0), keys(), data() {} - Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d) - : enabled(e), recording(r), counter(c), keys(k), data(d) {} + Demo() : enabled(false), recording(false), counter(0), keys(), data() {} + Demo(bool e, bool r, int c, const DemoKeys &k, const std::vector<DemoData> &d) + : enabled(e), recording(r), counter(c), keys(k), data(d) {} }; // Posiciones dentro de un rectangulo -struct Zone -{ - SDL_FRect rect; // Rectangulo que define la zona - float center_x; // Anclaje al 50% del eje X - float first_quarter_x; // Anclaje al 25% del eje X - float third_quarter_x; // Anclaje al 75% del eje X - float center_y; // Anclaje al 50% del eje Y - float first_quarter_y; // Anclaje al 25% del eje Y - float third_quarter_y; // Anclaje al 75% del eje Y +struct Zone { + SDL_FRect rect; // Rectangulo que define la zona + float center_x; // Anclaje al 50% del eje X + float first_quarter_x; // Anclaje al 25% del eje X + float third_quarter_x; // Anclaje al 75% del eje X + float center_y; // Anclaje al 50% del eje Y + float first_quarter_y; // Anclaje al 25% del eje Y + float third_quarter_y; // Anclaje al 75% del eje Y }; // --- Alias --- @@ -241,8 +223,8 @@ double easeOutElastic(double t); double easeInElastic(double t); // Utilidades varias -bool stringInVector(const std::vector<std::string> &vec, const std::string &str); // Comprueba si un vector contiene una cadena -void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3); // Imprime una línea con puntos +bool stringInVector(const std::vector<std::string> &vec, const std::string &str); // Comprueba si un vector contiene una cadena +void printWithDots(const std::string &text1, const std::string &text2, const std::string &text3); // Imprime una línea con puntos // Demo DemoData loadDemoDataFromFile(const std::string &file_path); @@ -252,5 +234,5 @@ bool saveDemoFile(const std::string &file_path, const DemoData &dd); #endif // Ficheros y rutas -std::string getFileName(const std::string &path); // Obtiene el nombre de un fichero a partir de una ruta -std::string getPath(const std::string &full_path); // Obtiene la ruta eliminando el nombre del fichero \ No newline at end of file +std::string getFileName(const std::string &path); // Obtiene el nombre de un fichero a partir de una ruta +std::string getPath(const std::string &full_path); // Obtiene la ruta eliminando el nombre del fichero \ No newline at end of file diff --git a/source/writer.cpp b/source/writer.cpp index b33340e..9e6e386 100644 --- a/source/writer.cpp +++ b/source/writer.cpp @@ -1,109 +1,87 @@ #include "writer.h" -#include "text.h" // Para Text +#include "text.h" // Para Text // Actualiza el objeto -void Writer::update() -{ - if (enabled_) - { - if (!completed_) - { - // No completado - if (writing_counter_ > 0) - { - writing_counter_--; - } - else - { - index_++; - writing_counter_ = speed_; - } +void Writer::update() { + if (enabled_) { + if (!completed_) { + // No completado + if (writing_counter_ > 0) { + writing_counter_--; + } else { + index_++; + writing_counter_ = speed_; + } - if (index_ == lenght_) - { - completed_ = true; - } - } - else - { - // Completado - finished_ = enabled_counter_ <= 0; - if (!finished_) - { - enabled_counter_--; - } - } - } + if (index_ == lenght_) { + completed_ = true; + } + } else { + // Completado + finished_ = enabled_counter_ <= 0; + if (!finished_) { + enabled_counter_--; + } + } + } } // Dibuja el objeto en pantalla -void Writer::render() const -{ - if (enabled_) - { - text_->write(pos_x_, pos_y_, caption_, kerning_, index_); - } +void Writer::render() const { + if (enabled_) { + text_->write(pos_x_, pos_y_, caption_, kerning_, index_); + } } // Establece el valor de la variable -void Writer::setPosX(int value) -{ - pos_x_ = value; +void Writer::setPosX(int value) { + pos_x_ = value; } // Establece el valor de la variable -void Writer::setPosY(int value) -{ - pos_y_ = value; +void Writer::setPosY(int value) { + pos_y_ = value; } // Establece el valor de la variable -void Writer::setKerning(int value) -{ - kerning_ = value; +void Writer::setKerning(int value) { + kerning_ = value; } // Establece el valor de la variable -void Writer::setCaption(const std::string &text) -{ - caption_ = text; - lenght_ = text.length(); +void Writer::setCaption(const std::string &text) { + caption_ = text; + lenght_ = text.length(); } // Establece el valor de la variable -void Writer::setSpeed(int value) -{ - speed_ = value; - writing_counter_ = value; +void Writer::setSpeed(int value) { + speed_ = value; + writing_counter_ = value; } // Establece el valor de la variable -void Writer::setEnabled(bool value) -{ - enabled_ = value; +void Writer::setEnabled(bool value) { + enabled_ = value; } // Obtiene el valor de la variable -bool Writer::IsEnabled() const -{ - return enabled_; +bool Writer::IsEnabled() const { + return enabled_; } // Establece el valor de la variable -void Writer::setFinishedCounter(int time) -{ - enabled_counter_ = time; +void Writer::setFinishedCounter(int time) { + enabled_counter_ = time; } // Centra la cadena de texto a un punto X -void Writer::center(int x) -{ - setPosX(x - (text_->lenght(caption_, kerning_) / 2)); +void Writer::center(int x) { + setPosX(x - (text_->lenght(caption_, kerning_) / 2)); } // Obtiene el valor de la variable -bool Writer::hasFinished() const -{ - return finished_; +bool Writer::hasFinished() const { + return finished_; } \ No newline at end of file diff --git a/source/writer.h b/source/writer.h index bf42ed7..90ab0de 100644 --- a/source/writer.h +++ b/source/writer.h @@ -1,58 +1,57 @@ #pragma once -#include <memory> // Para shared_ptr -#include <string> // Para string +#include <memory> // Para shared_ptr +#include <string> // Para string class Text; // Clase Writer. Pinta texto en pantalla letra a letra a partir de una cadena y un objeto Text -class Writer -{ -public: - // Constructor - explicit Writer(std::shared_ptr<Text> text) - : text_(text) {} +class Writer { + public: + // Constructor + explicit Writer(std::shared_ptr<Text> text) + : text_(text) {} - // Destructor - ~Writer() = default; + // Destructor + ~Writer() = default; - // Actualiza el objeto - void update(); + // Actualiza el objeto + void update(); - // Dibuja el objeto en pantalla - void render() const; + // Dibuja el objeto en pantalla + void render() const; - // Setters - void setPosX(int value); // Establece la posición X - void setPosY(int value); // Establece la posición Y - void setKerning(int value); // Establece el kerning (espaciado entre caracteres) - void setCaption(const std::string &text); // Establece el texto a escribir - void setSpeed(int value); // Establece la velocidad de escritura - void setEnabled(bool value); // Habilita o deshabilita el objeto - void setFinishedCounter(int time); // Establece el temporizador para deshabilitar el objeto + // Setters + void setPosX(int value); // Establece la posición X + void setPosY(int value); // Establece la posición Y + void setKerning(int value); // Establece el kerning (espaciado entre caracteres) + void setCaption(const std::string &text); // Establece el texto a escribir + void setSpeed(int value); // Establece la velocidad de escritura + void setEnabled(bool value); // Habilita o deshabilita el objeto + void setFinishedCounter(int time); // Establece el temporizador para deshabilitar el objeto - // Centra la cadena de texto a un punto X - void center(int x); + // Centra la cadena de texto a un punto X + void center(int x); - // Getters - bool IsEnabled() const; // Indica si el objeto está habilitado - bool hasFinished() const; // Indica si ya ha terminado + // Getters + bool IsEnabled() const; // Indica si el objeto está habilitado + bool hasFinished() const; // Indica si ya ha terminado -private: - // --- Objetos y punteros --- - std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto + private: + // --- Objetos y punteros --- + std::shared_ptr<Text> text_; // Objeto encargado de escribir el texto - // --- Variables de estado --- - int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto - int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto - int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres - std::string caption_ = std::string(); // El texto para escribir - int speed_ = 0; // Velocidad de escritura - int writing_counter_ = 0; // Temporizador de escritura para cada caracter - int index_ = 0; // Posición del texto que se está escribiendo - int lenght_ = 0; // Longitud de la cadena a escribir - bool completed_ = false; // Indica si se ha escrito todo el texto - bool enabled_ = false; // Indica si el objeto está habilitado - int enabled_counter_ = 0; // Temporizador para deshabilitar el objeto - bool finished_ = false; // Indica si ya ha terminado + // --- Variables de estado --- + int pos_x_ = 0; // Posición en el eje X donde empezar a escribir el texto + int pos_y_ = 0; // Posición en el eje Y donde empezar a escribir el texto + int kerning_ = 0; // Kerning del texto, es decir, espaciado entre caracteres + std::string caption_ = std::string(); // El texto para escribir + int speed_ = 0; // Velocidad de escritura + int writing_counter_ = 0; // Temporizador de escritura para cada caracter + int index_ = 0; // Posición del texto que se está escribiendo + int lenght_ = 0; // Longitud de la cadena a escribir + bool completed_ = false; // Indica si se ha escrito todo el texto + bool enabled_ = false; // Indica si el objeto está habilitado + int enabled_counter_ = 0; // Temporizador para deshabilitar el objeto + bool finished_ = false; // Indica si ya ha terminado }; \ No newline at end of file