diff --git a/Libraries/SDLLib b/Libraries/SDLLib index 07b5658..5e27624 160000 --- a/Libraries/SDLLib +++ b/Libraries/SDLLib @@ -1 +1 @@ -Subproject commit 07b5658c0056d2ebd65d51a97d4fb994aaee6fe1 +Subproject commit 5e27624003015d9e80f92e9ccee291b189da0ab3 diff --git a/Makefile b/Makefile index c530ee2..614cdbb 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE # my local pkg-config and I don't feel like dealing with CMake for all of this right now. CFLAGS := $(INCLUDE) -D__SWITCH__ `sdl2-config --cflags` `curl-config --cflags`\ -g -Wall -O3 -Os -ffunction-sections -ffast-math -fmax-errors=1 \ - $(ARCH) $(DEFINES) + -I$(PORTLIBS)/include/freetype2 $(ARCH) $(DEFINES) CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=c++23 diff --git a/source/JKSV.cpp b/source/JKSV.cpp index 23d285c..2feb045 100644 --- a/source/JKSV.cpp +++ b/source/JKSV.cpp @@ -112,7 +112,7 @@ JKSV::~JKSV() config::save(); curl::exit(); JKSV::exit_services(); - sdl::text::exit(); + sdl::text::SystemFont::exit(); sdl::exit(); appletSetCpuBoostMode(ApmCpuBoostMode_Normal); @@ -187,15 +187,17 @@ bool JKSV::initialize_sdl() { // Initialize SDL, freetype and the system font. bool sdlInit = sdl::initialize("JKSV", graphics::SCREEN_WIDTH, graphics::SCREEN_HEIGHT); - sdlInit = sdlInit && sdl::text::initialize(); + sdlInit = sdlInit && sdl::text::SystemFont::initialize(); + if (!sdlInit) { return false; } // Load the icon in the top left. m_headerIcon = sdl::TextureManager::load("headerIcon", "romfs:/Textures/HeaderIcon.png"); + if (!m_headerIcon) { return false; } // Push the color changing characters. JKSV::add_color_chars(); - return sdlInit && m_headerIcon; + return true; } bool JKSV::create_directories() diff --git a/source/appstates/BackupMenuState.cpp b/source/appstates/BackupMenuState.cpp index 69ff816..ed9567d 100644 --- a/source/appstates/BackupMenuState.cpp +++ b/source/appstates/BackupMenuState.cpp @@ -80,14 +80,22 @@ void BackupMenuState::update() void BackupMenuState::render() { + // Line render coords. + static constexpr int LINE_X = 10; + static constexpr int LINE_A_Y = 42; + static constexpr int LINE_B_Y = 648; + + static constexpr int CONTROL_X = 32; + static constexpr int CONTROL_Y = 673; + const bool hasFocus = BaseState::has_focus(); sdl::SharedTexture &target = sm_slidePanel->get_target(); sm_slidePanel->clear_target(); - sdl::render_line(target, 10, 42, sm_panelWidth - 10, 42, colors::WHITE); - sdl::render_line(target, 10, 648, sm_panelWidth - 10, 648, colors::WHITE); - sdl::text::render(target, 32, 673, 22, sdl::text::NO_WRAP, colors::WHITE, m_controlGuide); + sdl::render_line(target, LINE_X, LINE_A_Y, sm_panelWidth - LINE_X, LINE_A_Y, colors::WHITE); + sdl::render_line(target, LINE_X, LINE_B_Y, sm_panelWidth - LINE_X, LINE_B_Y, colors::WHITE); + sdl::text::render(target, CONTROL_X, CONTROL_Y, 22, sdl::text::NO_WRAP, colors::WHITE, m_controlGuide); sm_menuRenderTarget->clear(colors::TRANSPARENT); { @@ -144,7 +152,6 @@ void BackupMenuState::save_data_written() if (!m_saveHasData) { m_saveHasData = true; } } - // ---- Private functions ---- void BackupMenuState::initialize_static_members() diff --git a/source/stringutil.cpp b/source/stringutil.cpp index ee50033..052b804 100644 --- a/source/stringutil.cpp +++ b/source/stringutil.cpp @@ -1,40 +1,23 @@ #include "stringutil.hpp" #include -#include #include #include #include #include #include #include +#include +#include -namespace -{ - // Size limit for formatted strings. - constexpr size_t VA_BUFFER_SIZE = 0x1000; - - // These characters get replaced by spaces when path is sanitized. - constexpr std::array FORBIDDEN_PATH_CHARACTERS = - {L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'}; - - /// @brief This is a table for replacing accented characters and "look alike" unicode characters. - constexpr std::array, 78> REPLACEMENT_TABLE = { - {{L'Á', "A"}, {L'À', "A"}, {L'Â', "A"}, {L'Ä', "A"}, {L'Ã', "A"}, {L'Å', "A"}, {L'á', "a"}, {L'à', "a"}, - {L'â', "a"}, {L'ä', "a"}, {L'ã', "a"}, {L'å', "a"}, {L'É', "E"}, {L'È', "E"}, {L'Ê', "E"}, {L'Ë', "E"}, - {L'é', "e"}, {L'è', "e"}, {L'ê', "e"}, {L'ë', "e"}, {L'Í', "I"}, {L'Ì', "I"}, {L'Î', "I"}, {L'Ï', "I"}, - {L'í', "i"}, {L'ì', "i"}, {L'î', "i"}, {L'ï', "i"}, {L'Ó', "O"}, {L'Ò', "O"}, {L'Ô', "O"}, {L'Ö', "O"}, - {L'Õ', "O"}, {L'Ø', "O"}, {L'ó', "o"}, {L'ò', "o"}, {L'ô', "o"}, {L'ö', "o"}, {L'õ', "o"}, {L'ø', "o"}, - {L'Ú', "U"}, {L'Ù', "U"}, {L'Û', "U"}, {L'Ü', "U"}, {L'ú', "u"}, {L'ù', "u"}, {L'û', "u"}, {L'ü', "u"}, - {L'Ñ', "N"}, {L'ñ', "n"}, {L'Ç', "C"}, {L'ç', "c"}, {L'ẞ', "Ss"}, {L'ß', "ss"}, {L'Œ', "OE"}, {L'œ', "oe"}, - {L'Æ', "AE"}, {L'æ', "ae"}, {L'‐', "-"}, {L'–', "-"}, {L'—', "-"}, {L'―', "-"}, {L' ', " "}, {L' ', " "}, - {L' ', " "}, {L'Ⅰ', "I"}, {L'Ⅱ', "II"}, {L'Ⅲ', "III"}, {L'Ⅳ', "IV"}, {L'Ⅴ', "V"}, {L'※', "*"}, {L'×', "x"}, - {L'‘', "'"}, {L'’', "'"}, {L'‛', "'"}, {L'′', "'"}, {L'ʼ', "'"}}}; - -} // namespace +// Defined at bottom. +static std::unordered_set &get_forbidden_characters(); +static std::unordered_map &get_replacement_table(); std::string stringutil::get_formatted_string(const char *format, ...) { + static constexpr size_t VA_BUFFER_SIZE = 0x1000; + std::array vaBuffer = {0}; std::va_list vaList; @@ -67,7 +50,9 @@ void stringutil::strip_character(char c, std::string &target) bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, size_t stringOutSize) { uint32_t codepoint{}; - const int length = std::char_traits::length(stringIn); + const int length = std::char_traits::length(stringIn); + auto &forbiddenChars = get_forbidden_characters(); + auto &replacementTable = get_replacement_table(); for (int i = 0, outOffset = 0; i < length;) { @@ -76,8 +61,7 @@ bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, if (count <= 0 || outOffset + count >= static_cast(stringOutSize)) { return false; } // If it's forbidden, skip. - const bool isForbidden = std::find(FORBIDDEN_PATH_CHARACTERS.begin(), FORBIDDEN_PATH_CHARACTERS.end(), codepoint) != - FORBIDDEN_PATH_CHARACTERS.end(); + const bool isForbidden = forbiddenChars.find(codepoint) != forbiddenChars.end(); if (isForbidden) { i += count; @@ -85,10 +69,8 @@ bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, } // Check for replacing. - const auto &replace = std::find_if(REPLACEMENT_TABLE.begin(), - REPLACEMENT_TABLE.end(), - [codepoint](const auto &replacePair) { return replacePair.first == codepoint; }); - if (replace != REPLACEMENT_TABLE.end()) + const auto replace = replacementTable.find(codepoint); + if (replace != replacementTable.end()) { const auto &[tablePoint, replacement] = *replace; const size_t replacementLength = replacement.length(); @@ -124,26 +106,53 @@ bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, std::string stringutil::get_date_string(stringutil::DateFormat format) { - char stringBuffer[0x80] = {0}; + static constexpr size_t STRING_BUFFER_SIZE = 0x80; + + char stringBuffer[STRING_BUFFER_SIZE] = {0}; std::time_t timer{}; std::time(&timer); - const std::tm *localTime = std::localtime(&timer); + const std::tm localTime = *std::localtime(&timer); switch (format) { case stringutil::DateFormat::YearMonthDay: { - std::strftime(stringBuffer, 0x80, "%Y-%m-%d_%H-%M-%S", localTime); + std::strftime(stringBuffer, STRING_BUFFER_SIZE, "%Y-%m-%d_%H-%M-%S", &localTime); } break; case stringutil::DateFormat::YearDayMonth: { - std::strftime(stringBuffer, 0x80, "%Y-%d-%m_%H-%M-%S", localTime); + std::strftime(stringBuffer, STRING_BUFFER_SIZE, "%Y-%d-%m_%H-%M-%S", &localTime); } break; } return std::string(stringBuffer); } + +static std::unordered_set &get_forbidden_characters() +{ + static std::unordered_set forbidden = + {L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'}; + + return forbidden; +} + +static std::unordered_map &get_replacement_table() +{ + static std::unordered_map replacementTable = { + {L'Á', "A"}, {L'À', "A"}, {L'Â', "A"}, {L'Ä', "A"}, {L'Ã', "A"}, {L'Å', "A"}, {L'á', "a"}, {L'à', "a"}, + {L'â', "a"}, {L'ä', "a"}, {L'ã', "a"}, {L'å', "a"}, {L'É', "E"}, {L'È', "E"}, {L'Ê', "E"}, {L'Ë', "E"}, + {L'é', "e"}, {L'è', "e"}, {L'ê', "e"}, {L'ë', "e"}, {L'Í', "I"}, {L'Ì', "I"}, {L'Î', "I"}, {L'Ï', "I"}, + {L'í', "i"}, {L'ì', "i"}, {L'î', "i"}, {L'ï', "i"}, {L'Ó', "O"}, {L'Ò', "O"}, {L'Ô', "O"}, {L'Ö', "O"}, + {L'Õ', "O"}, {L'Ø', "O"}, {L'ó', "o"}, {L'ò', "o"}, {L'ô', "o"}, {L'ö', "o"}, {L'õ', "o"}, {L'ø', "o"}, + {L'Ú', "U"}, {L'Ù', "U"}, {L'Û', "U"}, {L'Ü', "U"}, {L'ú', "u"}, {L'ù', "u"}, {L'û', "u"}, {L'ü', "u"}, + {L'Ñ', "N"}, {L'ñ', "n"}, {L'Ç', "C"}, {L'ç', "c"}, {L'ẞ', "Ss"}, {L'ß', "ss"}, {L'Œ', "OE"}, {L'œ', "oe"}, + {L'Æ', "AE"}, {L'æ', "ae"}, {L'‐', "-"}, {L'–', "-"}, {L'—', "-"}, {L'―', "-"}, {L' ', " "}, {L' ', " "}, + {L' ', " "}, {L'Ⅰ', "I"}, {L'Ⅱ', "II"}, {L'Ⅲ', "III"}, {L'Ⅳ', "IV"}, {L'Ⅴ', "V"}, {L'※', "*"}, {L'×', "x"}, + {L'‘', "'"}, {L'’', "'"}, {L'‛', "'"}, {L'′', "'"}, {L'ʼ', "'"}}; + + return replacementTable; +}