diff --git a/source/core/rendering/surface.cpp b/source/core/rendering/surface.cpp index 484bf81..2663508 100644 --- a/source/core/rendering/surface.cpp +++ b/source/core/rendering/surface.cpp @@ -62,7 +62,7 @@ Surface::Surface(const std::string& file_path) { palette_[i] = Rendering::OPAQUE_ALPHA | rgb[i]; } - printWithDots("Surface", getFileName(file_path), "[ LOADED ]"); + printStatus("Surface", getFileName(file_path), StatusKind::SUCCESS); } void Surface::clear(Uint8 color) { diff --git a/source/core/rendering/text.cpp b/source/core/rendering/text.cpp index 13b29c9..bb84eff 100644 --- a/source/core/rendering/text.cpp +++ b/source/core/rendering/text.cpp @@ -101,7 +101,7 @@ auto Text::loadTextFile(const std::string& file_path) -> std::shared_ptr { } } - printWithDots("Text File", getFileName(file_path), "[ LOADED ]"); + printStatus("Text File", getFileName(file_path), StatusKind::SUCCESS); return tf; } diff --git a/source/core/resources/resource_cache.cpp b/source/core/resources/resource_cache.cpp index 02b0a6b..69f1fbb 100644 --- a/source/core/resources/resource_cache.cpp +++ b/source/core/resources/resource_cache.cpp @@ -37,13 +37,13 @@ namespace Resource { throw std::runtime_error("Cache: cannot load animation " + file_path); } animations_.emplace_back(AnimationResource{.name = name, .yaml_data = std::move(bytes)}); - printWithDots("Animation", getFileName(file_path), "[ LOADED ]"); + printStatus("Animation", getFileName(file_path), StatusKind::SUCCESS); } void Cache::loadPalette(const std::string& name, const std::string& file_path) { auto palette = PaletteLoader::loadFromFile(file_path); palettes_.emplace_back(PaletteResource{.name = name, .palette = palette}); - printWithDots("Palette", getFileName(file_path), "[ LOADED ]"); + printStatus("Palette", getFileName(file_path), StatusKind::SUCCESS); } auto Cache::getSurface(const std::string& name) -> std::shared_ptr { @@ -83,7 +83,7 @@ namespace Resource { throw std::runtime_error("Cache: Ja::loadMusic failed for " + file_path); } musics_.emplace_back(MusicResource{.name = name, .music = std::unique_ptr(raw)}); - printWithDots("Music", getFileName(file_path), "[ LOADED ]"); + printStatus("Music", getFileName(file_path), StatusKind::SUCCESS); } void Cache::loadSound(const std::string& name, const std::string& file_path) { @@ -96,7 +96,7 @@ namespace Resource { throw std::runtime_error("Cache: Ja::loadSound failed for " + file_path); } sounds_.emplace_back(SoundResource{.name = name, .sound = std::unique_ptr(raw)}); - printWithDots("Sound", getFileName(file_path), "[ LOADED ]"); + printStatus("Sound", getFileName(file_path), StatusKind::SUCCESS); } auto Cache::getMusic(const std::string& name) -> Ja::Music* { diff --git a/source/utils/log.cpp b/source/utils/log.cpp index 0eab89f..310d12c 100644 --- a/source/utils/log.cpp +++ b/source/utils/log.cpp @@ -130,6 +130,11 @@ namespace Log { SDL_LogMessage(categoryFor(tag), toSDL(lvl), "%s", BODY.c_str()); } + void logPrefixed(Level lvl, std::string_view tag, std::string_view prefix, std::string_view msg) { + const std::string BODY = std::format("{} {}: {}", prefix, tag, msg); + SDL_LogMessage(categoryFor(tag), toSDL(lvl), "%s", BODY.c_str()); + } + void raw(Level lvl, std::string_view msg) { const std::string TEXT{msg}; SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, toSDL(lvl), "%s", TEXT.c_str()); diff --git a/source/utils/log.hpp b/source/utils/log.hpp index 193a4c6..82f986d 100644 --- a/source/utils/log.hpp +++ b/source/utils/log.hpp @@ -32,6 +32,12 @@ namespace Log { // entrega a SDL_LogMessage amb la categoria registrada per al tag. void log(Level lvl, std::string_view tag, std::string_view msg); + // Com `log`, però antepossa un prefix arbitrari abans del tag, donant + // "prefix tag: msg". Pensat per a marcadors d'estat (p. ex. claudàtors + // de càrrega "[ LOADED ]"). El routing per categoria/filtre via AEE_LOG + // continua actiu (es decideix pel `tag`, no pel prefix). + void logPrefixed(Level lvl, std::string_view tag, std::string_view prefix, std::string_view msg); + // Variant sense prefix de tag — ús reservat per a marcadors literals // (p. ex. "Game start" / "Bye!"). Cau a SDL_LOG_CATEGORY_APPLICATION, // així AEE_LOG global encara els pot silenciar. diff --git a/source/utils/utils.cpp b/source/utils/utils.cpp index f1d5bd4..56f32bb 100644 --- a/source/utils/utils.cpp +++ b/source/utils/utils.cpp @@ -1,7 +1,16 @@ #include "utils/utils.hpp" +#include +#include #include #include +#include + +#ifndef _WIN32 +#include + +#include +#endif #include "utils/log.hpp" @@ -9,14 +18,75 @@ auto getFileName(const std::string& path) -> std::string { return std::filesystem::path(path).filename().string(); } -void printWithDots(const std::string& tag, const std::string& filename, const std::string& status) { - // Reprodueix l'alineació històrica (amplada total ≈ 50). El logger anteposa - // automàticament "tag: ", per això aquí només componem el cos. - constexpr std::size_t TOTAL_WIDTH = 50; - const std::size_t PREFIX_LEN = tag.size() + 2; // "{tag}: " - const std::size_t USED = PREFIX_LEN + status.size(); - const std::size_t FIELD = (USED < TOTAL_WIDTH) ? (TOTAL_WIDTH - USED) : 0; - std::string body = filename; - if (body.size() < FIELD) { body.resize(FIELD, '.'); } - Log::info(tag, "{}{}", body, status); +namespace { + + constexpr std::size_t BRACKET_INNER_WIDTH = 8; + + auto defaultLabel(StatusKind kind) -> std::string_view { + switch (kind) { + case StatusKind::SUCCESS: + return "LOADED"; + case StatusKind::INFO: + return "INFO"; + case StatusKind::ERROR: + return "ERROR"; + } + return {}; + } + + auto colorCode(StatusKind kind) -> std::string_view { + switch (kind) { + case StatusKind::SUCCESS: + return "\033[1;32m"; + case StatusKind::INFO: + return "\033[1;34m"; + case StatusKind::ERROR: + return "\033[1;31m"; + } + return {}; + } + + // Decideix una sola vegada per execució: stderr ha d'admetre ANSI? + // A Windows només Git Bash (MSYSTEM) el suporta sense habilitar VT. + auto useColor() -> bool { + static const bool ENABLED = [] { +#ifdef _WIN32 + return std::getenv("MSYSTEM") != nullptr; +#else + return isatty(fileno(stderr)) != 0; +#endif + }(); + return ENABLED; + } + + // Construeix "[ ]" amb el contingut interior centrat dins + // d'una amplada mínima de BRACKET_INNER_WIDTH. Si `with_color` és true, + // només l'interior porta codis ANSI; els claudàtors queden sense color + // (mateix patró que `mini`: `[...]`). + auto buildBracket(std::string_view label, StatusKind kind, bool with_color) -> std::string { + const std::size_t INNER = std::max(BRACKET_INNER_WIDTH, label.size()); + const std::size_t PAD = INNER - label.size(); + const std::size_t LEFT = PAD / 2; + const std::size_t RIGHT = PAD - LEFT; + std::string out; + out.push_back('['); + if (with_color) { out.append(colorCode(kind)); } + out.append(LEFT, ' '); + out.append(label); + out.append(RIGHT, ' '); + if (with_color) { out.append("\033[0m"); } + out.push_back(']'); + return out; + } + +} // namespace + +void printStatus(const std::string& tag, + const std::string& filename, + StatusKind kind, + std::string_view label) { + const std::string_view EFFECTIVE_LABEL = label.empty() ? defaultLabel(kind) : label; + const std::string BRACKET = buildBracket(EFFECTIVE_LABEL, kind, useColor()); + const Log::Level LVL = (kind == StatusKind::ERROR) ? Log::Level::ERROR : Log::Level::INFO; + Log::logPrefixed(LVL, tag, BRACKET, filename); } diff --git a/source/utils/utils.hpp b/source/utils/utils.hpp index ce6561a..cbaf480 100644 --- a/source/utils/utils.hpp +++ b/source/utils/utils.hpp @@ -1,8 +1,20 @@ #pragma once #include +#include + +enum class StatusKind : unsigned char { SUCCESS, + INFO, + ERROR }; auto getFileName(const std::string& path) -> std::string; -// Línia de càrrega amb alineació de punts: "{tag}: filename..........{status}". -// Embolcalla `Log::info`, amb la qual cosa respecta `AEE_LOG`. -void printWithDots(const std::string& tag, const std::string& filename, const std::string& status); + +// Línia d'estat amb claudàtor centrat al davant: "{tag}: [ LABEL ] filename". +// Amplada interna del claudàtor fixa a 8; si la `label` supera 8 chars, +// el claudàtor s'allarga per encabir-la. Color del claudàtor (verd/blau/roig) +// quan stderr és un terminal; a Windows només sota Git Bash (MSYSTEM). +// Embolcalla `Log::info`/`Log::error` segons `kind`, respectant `AEE_LOG`. +void printStatus(const std::string& tag, + const std::string& filename, + StatusKind kind, + std::string_view label = {});