diff --git a/include/appstates/BaseTask.hpp b/include/appstates/BaseTask.hpp index 242f009..5813b60 100644 --- a/include/appstates/BaseTask.hpp +++ b/include/appstates/BaseTask.hpp @@ -15,15 +15,11 @@ class BaseTask : public BaseState /// @brief Runs the update routine for rendering the loading glyph animation. /// @param - void update() override; + virtual void update() = 0; /// @brief Virtual render function. virtual void render() = 0; - /// @brief This function renders the loading glyph in the bottom left corner. - /// @note This is mostly just so users don't think JKSV has frozen when operations take a long time. - void render_loading_glyph(); - protected: /// @brief Underlying system task. This needs to be allocated by the derived classes. std::unique_ptr m_task{}; @@ -31,6 +27,13 @@ class BaseTask : public BaseState /// @brief Updates the loading glyph animation. void update_loading_glyph(); + /// @brief Displays the "can't quit JKSV" when plus is pressed. + void pop_on_plus(); + + /// @brief This function renders the loading glyph in the bottom left corner. + /// @note This is mostly just so users don't think JKSV has frozen when operations take a long time. + void render_loading_glyph(); + /// @brief This is the font size used for displaying text during tasks. static inline constexpr int FONT_SIZE = 20; diff --git a/include/appstates/DataLoadingState.hpp b/include/appstates/DataLoadingState.hpp index ecf3673..e685c17 100644 --- a/include/appstates/DataLoadingState.hpp +++ b/include/appstates/DataLoadingState.hpp @@ -3,6 +3,7 @@ #include "appstates/BaseTask.hpp" #include "data/DataContext.hpp" #include "sdl.hpp" +#include "sys/OpTimer.hpp" #include #include diff --git a/include/fs/MiniUnzip.hpp b/include/fs/MiniUnzip.hpp index 92639ba..e2d4c74 100644 --- a/include/fs/MiniUnzip.hpp +++ b/include/fs/MiniUnzip.hpp @@ -18,7 +18,7 @@ namespace fs ~MiniUnzip(); /// @brief Returns whether or not the unzFile was successfully opened. - bool is_open() const; + bool is_open() const noexcept; /// @brief Attempts to open the path passed as a ZIP file. bool open(const fslib::Path &path); @@ -42,13 +42,13 @@ namespace fs ssize_t read(void *buffer, size_t bufferSize); /// @brief Returns the name of the current file. - const char *get_filename(); + const char *get_filename() const noexcept; /// @brief Returns the compressed size of the currently open file. - uint64_t get_compressed_size() const; + uint64_t get_compressed_size() const noexcept; /// @brief Returns the uncompressed size of the the currently open file. - uint64_t get_uncompressed_size() const; + uint64_t get_uncompressed_size() const noexcept; private: /// @brief Underlying unzFile. diff --git a/include/fs/MiniZip.hpp b/include/fs/MiniZip.hpp index 616e2f1..480c30b 100644 --- a/include/fs/MiniZip.hpp +++ b/include/fs/MiniZip.hpp @@ -20,7 +20,7 @@ namespace fs /// @brief Returns whether or not the zip file was successfully opened. /// @return - bool is_open() const; + bool is_open() const noexcept; /// @brief Opens a Zip file at path bool open(const fslib::Path &path); diff --git a/include/sys/OpTimer.hpp b/include/sys/OpTimer.hpp new file mode 100644 index 0000000..b9d50ff --- /dev/null +++ b/include/sys/OpTimer.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +namespace sys +{ + class OpTimer final + { + public: + /// @brief Starts the operation timer. + OpTimer(const std::source_location &location = std::source_location::current()) noexcept; + + /// @brief Ends the timer and + ~OpTimer() noexcept; + + private: + // Stores a reference to the location passed. + const std::source_location m_location; + + /// @brief Stores the time the timer was started. + std::chrono::high_resolution_clock::time_point m_begin{}; + + /// @brief Returns a string_view containing just the function name. No return type. + std::string_view get_function_name() const noexcept; + }; +} \ No newline at end of file diff --git a/include/ui/Menu.hpp b/include/ui/Menu.hpp index 9bd0bbc..18bb0ff 100644 --- a/include/ui/Menu.hpp +++ b/include/ui/Menu.hpp @@ -99,6 +99,9 @@ namespace ui /// @brief The target Y coordinate the menu should be rendered at. double m_targetY{}; + /// @brief Maximum number of display options render target can show. + int m_maxDisplayOptions{}; + /// @brief How many options before scrolling happens. int m_scrollLength{}; @@ -114,15 +117,15 @@ namespace ui /// @brief Vertical size of the destination render target in pixels. int m_renderTargetHeight{}; - /// @brief Maximum number of display options render target can show. - int m_maxDisplayOptions{}; - /// @brief Vector of options. std::vector m_options{}; /// @brief Text scroll for when the current option is too long to on screen. std::shared_ptr m_optionScroll{}; + /// @brief Keeps track of the current menu ID for render target. + static inline int sm_menuID{}; + /// @brief Updates the text scroll for the currently highlighted option. void update_scroll_text(); diff --git a/include/ui/SlideOutPanel.hpp b/include/ui/SlideOutPanel.hpp index 37e8e60..104143d 100644 --- a/include/ui/SlideOutPanel.hpp +++ b/include/ui/SlideOutPanel.hpp @@ -108,6 +108,9 @@ namespace ui /// @brief Vector of elements. std::vector> m_elements{}; + /// @brief Tracks the internal ID of render targets for panels. + static inline int sm_targetID{}; + /// @brief Handles sliding out logic. void slide_out() noexcept; diff --git a/source/JKSV.cpp b/source/JKSV.cpp index 08c10e2..a270582 100644 --- a/source/JKSV.cpp +++ b/source/JKSV.cpp @@ -16,6 +16,7 @@ #include "sdl.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" +#include "sys/OpTimer.hpp" #include "ui/PopMessageManager.hpp" #include diff --git a/source/appstates/BaseState.cpp b/source/appstates/BaseState.cpp index c7f1d20..d307432 100644 --- a/source/appstates/BaseState.cpp +++ b/source/appstates/BaseState.cpp @@ -8,13 +8,13 @@ BaseState::BaseState(bool isClosable) : m_isClosable(isClosable) { if (m_isClosable) { return; } - error::libnx(appletBeginBlockingHomeButton(0)); + error::libnx(appletBeginBlockingHomeButtonShortAndLongPressed(0)); } BaseState::~BaseState() { if (m_isClosable) { return; } - error::libnx(appletEndBlockingHomeButton()); + error::libnx(appletEndBlockingHomeButtonShortAndLongPressed()); } void BaseState::deactivate() { m_isActive = false; } diff --git a/source/appstates/BaseTask.cpp b/source/appstates/BaseTask.cpp index 0cac3e1..c790fa8 100644 --- a/source/appstates/BaseTask.cpp +++ b/source/appstates/BaseTask.cpp @@ -13,29 +13,8 @@ namespace BaseTask::BaseTask() : BaseState(false) - , m_popUnableExit(strings::get_by_name(strings::names::GENERAL_POPS, 0)) -{ - m_frameTimer.start(TICKS_GLYPH_TRIGGER); -} - -void BaseTask::update() -{ - const bool plusPressed = input::button_pressed(HidNpadButton_Plus); - - if (!m_task->is_running()) - { - BaseState::deactivate(); - return; - } - else if (plusPressed) { ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_TICKS, m_popUnableExit); } - - BaseTask::update_loading_glyph(); -} - -void BaseTask::render_loading_glyph() -{ - sdl::text::render(sdl::Texture::Null, 56, 673, 32, sdl::text::NO_WRAP, m_colorMod, sm_glyphArray[m_currentFrame]); -} + , m_frameTimer(TICKS_GLYPH_TRIGGER) + , m_popUnableExit(strings::get_by_name(strings::names::GENERAL_POPS, 0)) {}; void BaseTask::update_loading_glyph() { @@ -44,3 +23,15 @@ void BaseTask::update_loading_glyph() if (!m_frameTimer.is_triggered()) { return; } if (++m_currentFrame % 8 == 0) { m_currentFrame = 0; } } + +void BaseTask::pop_on_plus() +{ + const bool plusPressed = input::button_pressed(HidNpadButton_Plus); + + if (plusPressed) { ui::PopMessageManager::push_message(ui::PopMessageManager::DEFAULT_TICKS, m_popUnableExit); } +} + +void BaseTask::render_loading_glyph() +{ + sdl::text::render(sdl::Texture::Null, 56, 673, 32, sdl::text::NO_WRAP, m_colorMod, sm_glyphArray[m_currentFrame]); +} diff --git a/source/appstates/FadeState.cpp b/source/appstates/FadeState.cpp index 7a0fd47..9d54232 100644 --- a/source/appstates/FadeState.cpp +++ b/source/appstates/FadeState.cpp @@ -14,10 +14,9 @@ FadeState::FadeState(sdl::Color baseColor, uint8_t startAlpha, uint8_t endAlpha, : m_baseColor(baseColor) , m_alpha(startAlpha) , m_endAlpha(endAlpha) + , m_direction(m_endAlpha < m_alpha ? FadeState::Direction::In : FadeState::Direction::Out) , m_nextState(nextState) { - m_direction = m_endAlpha < m_alpha ? FadeState::Direction::In : FadeState::Direction::Out; - FadeState::find_divisor(); m_fadeTimer.start(TICKS_TIMER_TRIGGER); } diff --git a/source/appstates/ProgressState.cpp b/source/appstates/ProgressState.cpp index 3728be7..41d2776 100644 --- a/source/appstates/ProgressState.cpp +++ b/source/appstates/ProgressState.cpp @@ -27,6 +27,8 @@ void ProgressState::update() const double current = task->get_progress(); BaseTask::update_loading_glyph(); + BaseTask::pop_on_plus(); + if (!m_task->is_running()) { ProgressState::deactivate_state(); } m_progressBarWidth = std::round(SIZE_BAR_WIDTH * current); diff --git a/source/appstates/TaskState.cpp b/source/appstates/TaskState.cpp index d13edd9..93c9e42 100644 --- a/source/appstates/TaskState.cpp +++ b/source/appstates/TaskState.cpp @@ -10,6 +10,7 @@ void TaskState::update() { BaseTask::update_loading_glyph(); + BaseTask::pop_on_plus(); if (!m_task->is_running()) { TaskState::deactivate_state(); } } diff --git a/source/data/TitleInfo.cpp b/source/data/TitleInfo.cpp index 95d266b..8d99e88 100644 --- a/source/data/TitleInfo.cpp +++ b/source/data/TitleInfo.cpp @@ -41,10 +41,9 @@ data::TitleInfo::TitleInfo(uint64_t applicationID) noexcept // To do: Make this safer... data::TitleInfo::TitleInfo(uint64_t applicationID, NsApplicationControlData &controlData) noexcept : m_applicationID(applicationID) + , m_data(controlData) + , m_hasData(true) { - m_hasData = true; - m_data = controlData; - const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data.nacp, &m_entry)); if (entryError) { diff --git a/source/data/User.cpp b/source/data/User.cpp index 3e888b2..088f394 100644 --- a/source/data/User.cpp +++ b/source/data/User.cpp @@ -33,8 +33,8 @@ namespace static bool sort_user_data(const data::UserDataEntry &entryA, const data::UserDataEntry &entryB); data::User::User(AccountUid accountID, FsSaveDataType saveType) noexcept - : m_accountID{accountID} - , m_saveType{saveType} + : m_accountID(accountID) + , m_saveType(saveType) { AccountProfile profile{}; AccountProfileBase profileBase{}; @@ -50,11 +50,11 @@ data::User::User(AccountUid accountID, std::string_view nickname, std::string_view pathSafeNickname, FsSaveDataType saveType) noexcept - : m_accountID{accountID} - , m_saveType{saveType} + : m_accountID(accountID) + , m_saveType(saveType) { - std::memcpy(m_nickname, nickname.data(), nickname.length()); - std::memcpy(m_pathSafeNickname, pathSafeNickname.data(), pathSafeNickname.length()); + std::strncpy(m_nickname, nickname.data(), nickname.length()); + std::strncpy(m_pathSafeNickname, pathSafeNickname.data(), pathSafeNickname.length()); } data::User::User(data::User &&user) noexcept { *this = std::move(user); } @@ -72,9 +72,7 @@ data::User &data::User::operator=(data::User &&user) noexcept user.m_accountID = {0}; user.m_saveType = static_cast(0); - std::memset(user.m_nickname, 0x00, SIZE_NICKNAME); - std::memset(user.m_pathSafeNickname, 0x00, SIZE_NICKNAME); - user.m_icon = nullptr; + user.m_icon = nullptr; return *this; } diff --git a/source/fs/MiniUnzip.cpp b/source/fs/MiniUnzip.cpp index e1e90fe..bdb6f16 100644 --- a/source/fs/MiniUnzip.cpp +++ b/source/fs/MiniUnzip.cpp @@ -7,7 +7,7 @@ fs::MiniUnzip::MiniUnzip(const fslib::Path &path) { MiniUnzip::open(path); } fs::MiniUnzip::~MiniUnzip() { MiniUnzip::close(); } -bool fs::MiniUnzip::is_open() const { return m_isOpen; } +bool fs::MiniUnzip::is_open() const noexcept { return m_isOpen; } bool fs::MiniUnzip::open(const fslib::Path &path) { @@ -59,8 +59,8 @@ bool fs::MiniUnzip::reset() ssize_t fs::MiniUnzip::read(void *buffer, size_t bufferSize) { return unzReadCurrentFile(m_unz, buffer, bufferSize); } -const char *fs::MiniUnzip::get_filename() { return m_filename; } +const char *fs::MiniUnzip::get_filename() const noexcept { return m_filename; } -uint64_t fs::MiniUnzip::get_compressed_size() const { return m_fileInfo.compressed_size; } +uint64_t fs::MiniUnzip::get_compressed_size() const noexcept { return m_fileInfo.compressed_size; } -uint64_t fs::MiniUnzip::get_uncompressed_size() const { return m_fileInfo.uncompressed_size; } +uint64_t fs::MiniUnzip::get_uncompressed_size() const noexcept { return m_fileInfo.uncompressed_size; } diff --git a/source/fs/MiniZip.cpp b/source/fs/MiniZip.cpp index 10d4899..6d85bc2 100644 --- a/source/fs/MiniZip.cpp +++ b/source/fs/MiniZip.cpp @@ -17,7 +17,7 @@ fs::MiniZip::MiniZip(const fslib::Path &path) fs::MiniZip::~MiniZip() { MiniZip::close(); } -bool fs::MiniZip::is_open() const { return m_isOpen; } +bool fs::MiniZip::is_open() const noexcept { return m_isOpen; } bool fs::MiniZip::open(const fslib::Path &path) { diff --git a/source/strings/strings.cpp b/source/strings/strings.cpp index 80aa112..8063308 100644 --- a/source/strings/strings.cpp +++ b/source/strings/strings.cpp @@ -7,6 +7,7 @@ #include "logging/logger.hpp" #include "stringutil.hpp" +#include #include #include #include @@ -16,25 +17,24 @@ namespace // This is the actual map where the strings are. std::map, std::string> s_stringMap; - // This map is for matching files to the language value - std::unordered_map s_fileMap = {{SetLanguage_JA, "JA.json"}, - {SetLanguage_ENUS, "ENUS.json"}, - {SetLanguage_FR, "FR.json"}, - {SetLanguage_DE, "DE.json"}, - {SetLanguage_IT, "IT.json"}, - {SetLanguage_ES, "ES.json"}, - {SetLanguage_ZHCN, "ZHCN.json"}, - {SetLanguage_KO, "KO.json"}, - {SetLanguage_NL, "NL.json"}, - {SetLanguage_PT, "PT.json"}, - {SetLanguage_RU, "RU.json"}, - {SetLanguage_ZHTW, "ZHTW.json"}, - {SetLanguage_ENGB, "ENGB.json"}, - {SetLanguage_FRCA, "FRCA.json"}, - {SetLanguage_ES419, "ES419.json"}, - {SetLanguage_ZHHANS, "ZHCN.json"}, - {SetLanguage_ZHHANT, "ZHTW.json"}, - {SetLanguage_PTBR, "PTBR.json"}}; + const std::array PATH_ARRAY = {"JA.json", + "ENUS.json", + "FR.json", + "DE.json", + "IT.json", + "ES.json", + "ZHCN.json", + "KO.json", + "NL.json", + "PT.json", + "RU.json", + "ZHTW.json", + "ENGB.json", + "FRCA.json", + "ES419.json", + "ZHCN.json", + "ZHTW.json", + "PTBR.json"}; } // namespace // Definitions at bottom. @@ -84,7 +84,7 @@ const char *strings::get_by_name(std::string_view name, int index) noexcept const auto findPair = s_stringMap.find(mapPair); if (findPair == s_stringMap.end()) { return nullptr; } - return s_stringMap.at(mapPair).c_str(); + return findPair->second.c_str(); } static fslib::Path get_file_path() @@ -98,8 +98,8 @@ static fslib::Path get_file_path() const bool forceEnglish = config::get_by_key(config::keys::FORCE_ENGLISH); const bool codeError = error::libnx(setGetLanguageCode(&languageCode)); const bool langError = !codeError && error::libnx(setMakeLanguage(languageCode, &language)); - if (forceEnglish || codeError || langError) { returnPath /= s_fileMap[SetLanguage_ENUS]; } - else { returnPath /= s_fileMap[language]; } + if (forceEnglish || codeError || langError) { returnPath /= PATH_ARRAY[SetLanguage_ENUS]; } + else { returnPath /= PATH_ARRAY[language]; } return returnPath; } diff --git a/source/sys/OpTimer.cpp b/source/sys/OpTimer.cpp new file mode 100644 index 0000000..fd80792 --- /dev/null +++ b/source/sys/OpTimer.cpp @@ -0,0 +1,25 @@ +#include "sys/OpTimer.hpp" + +#include "logging/logger.hpp" + +sys::OpTimer::OpTimer(const std::source_location &location) noexcept + : m_location(location) + , m_begin(std::chrono::high_resolution_clock::now()) {}; + +sys::OpTimer::~OpTimer() noexcept +{ + const auto end = std::chrono::high_resolution_clock::now(); + const auto diff = std::chrono::duration_cast(end - m_begin); + + std::string_view functionName = OpTimer::get_function_name(); + logger::log("%s took %lli microseconds.", functionName.data(), diff.count()); +} + +std::string_view sys::OpTimer::get_function_name() const noexcept +{ + std::string_view function = m_location.function_name(); + const size_t nameBegin = function.find_first_of(' '); + if (nameBegin != function.npos) { function = function.substr(nameBegin + 1); } + + return function; +} \ No newline at end of file diff --git a/source/ui/Menu.cpp b/source/ui/Menu.cpp index 213ce9b..e845031 100644 --- a/source/ui/Menu.cpp +++ b/source/ui/Menu.cpp @@ -12,27 +12,19 @@ ui::Menu::Menu(int x, int y, int width, int fontSize, int renderTargetHeight) : m_x(x) , m_y(y) , m_optionHeight(std::round(static_cast(fontSize) * 1.8f)) + , m_optionTarget( + sdl::TextureManager::load("MENU_" + std::to_string(sm_menuID++), width, m_optionHeight, SDL_TEXTUREACCESS_TARGET)) + , m_boundingBox(ui::BoundingBox::create(0, 0, width + 12, m_optionHeight + 12)) , m_originalY(y) , m_targetY(y) + , m_maxDisplayOptions((renderTargetHeight - m_originalY) / m_optionHeight) + , m_scrollLength(std::floor(static_cast(m_maxDisplayOptions) / 2)) , m_width(width) , m_fontSize(fontSize) , m_textY((m_optionHeight / 2) - (m_fontSize / 2)) // This seems to be the best alignment. , m_renderTargetHeight(renderTargetHeight) , m_optionScroll( - ui::TextScroll::create("", 16, 0, m_width, m_optionHeight, m_fontSize, colors::BLUE_GREEN, colors::TRANSPARENT)) -{ - // Create render target for options - static int MENU_ID = 0; - const std::string menuTargetName = "MENU_" + std::to_string(MENU_ID++); - m_optionTarget = sdl::TextureManager::load(menuTargetName, m_width, m_optionHeight, SDL_TEXTUREACCESS_TARGET); - - // Outside the initializer list because I'm tired and don't wanna deal with the headache. - m_boundingBox = ui::BoundingBox::create(0, 0, m_width + 12, m_optionHeight + 12); - - // Calculate around how many options can be shown on the render target at once. - m_maxDisplayOptions = (renderTargetHeight - m_originalY) / m_optionHeight; - m_scrollLength = std::floor(static_cast(m_maxDisplayOptions) / 2.0f); -} + ui::TextScroll::create({}, 16, 0, m_width, m_optionHeight, m_fontSize, colors::BLUE_GREEN, colors::TRANSPARENT)) {}; void ui::Menu::update(bool hasFocus) { diff --git a/source/ui/SlideOutPanel.cpp b/source/ui/SlideOutPanel.cpp index b6bf9aa..092223c 100644 --- a/source/ui/SlideOutPanel.cpp +++ b/source/ui/SlideOutPanel.cpp @@ -17,11 +17,8 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side) , m_width(width) , m_targetX(side == Side::Left ? 0.0f : static_cast(SCREEN_WIDTH) - m_width) , m_side(side) -{ - static int targetID = 0; - std::string targetName = "panelTarget_" + std::to_string(targetID++); - m_renderTarget = sdl::TextureManager::load(targetName, width, 720, SDL_TEXTUREACCESS_TARGET); -} + , m_renderTarget( + sdl::TextureManager::load("PANEL_" + std::to_string(sm_targetID++), m_width, 720, SDL_TEXTUREACCESS_TARGET)) {}; void ui::SlideOutPanel::update(bool hasFocus) {