diff --git a/Text/Files/DE.json b/Assets/Text/DE.json similarity index 100% rename from Text/Files/DE.json rename to Assets/Text/DE.json diff --git a/Text/Files/ENGB.json b/Assets/Text/ENGB.json similarity index 100% rename from Text/Files/ENGB.json rename to Assets/Text/ENGB.json diff --git a/Text/Files/ENUS.json b/Assets/Text/ENUS.json similarity index 100% rename from Text/Files/ENUS.json rename to Assets/Text/ENUS.json diff --git a/Text/Files/ES.json b/Assets/Text/ES.json similarity index 100% rename from Text/Files/ES.json rename to Assets/Text/ES.json diff --git a/Text/Files/ES419.json b/Assets/Text/ES419.json similarity index 100% rename from Text/Files/ES419.json rename to Assets/Text/ES419.json diff --git a/Text/Files/FR.json b/Assets/Text/FR.json similarity index 100% rename from Text/Files/FR.json rename to Assets/Text/FR.json diff --git a/Text/Files/FRCA.json b/Assets/Text/FRCA.json similarity index 100% rename from Text/Files/FRCA.json rename to Assets/Text/FRCA.json diff --git a/Text/Files/IT.json b/Assets/Text/IT.json similarity index 100% rename from Text/Files/IT.json rename to Assets/Text/IT.json diff --git a/Text/Files/JA.json b/Assets/Text/JA.json similarity index 100% rename from Text/Files/JA.json rename to Assets/Text/JA.json diff --git a/Text/Files/KO.json b/Assets/Text/KO.json similarity index 100% rename from Text/Files/KO.json rename to Assets/Text/KO.json diff --git a/Text/Files/NL.json b/Assets/Text/NL.json similarity index 100% rename from Text/Files/NL.json rename to Assets/Text/NL.json diff --git a/Text/Files/PT.json b/Assets/Text/PT.json similarity index 100% rename from Text/Files/PT.json rename to Assets/Text/PT.json diff --git a/Text/Files/PTBR.json b/Assets/Text/PTBR.json similarity index 100% rename from Text/Files/PTBR.json rename to Assets/Text/PTBR.json diff --git a/Text/Files/RU.json b/Assets/Text/RU.json similarity index 100% rename from Text/Files/RU.json rename to Assets/Text/RU.json diff --git a/Text/Files/ZHCN.json b/Assets/Text/ZHCN.json similarity index 100% rename from Text/Files/ZHCN.json rename to Assets/Text/ZHCN.json diff --git a/Text/Files/ZHTW.json b/Assets/Text/ZHTW.json similarity index 100% rename from Text/Files/ZHTW.json rename to Assets/Text/ZHTW.json diff --git a/Assets/compress_assets.py b/Assets/compress_assets.py new file mode 100644 index 0000000..f529f6f --- /dev/null +++ b/Assets/compress_assets.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# coding: utf-8 + +import zlib +import os +import sys +import shutil + +def ensure_fresh_dir(path: str): + + if os.path.exists(path): + shutil.rmtree(path) + + os.mkdir(path) + + +def compress_to_romfs(input: str, output: str): + + print(f"Compressing {input} -> {output}") + inputSize: int =os.path.getsize(input) + with open(file=input, mode="rb") as inputFile: + + inputBuffer: bytes = inputFile.read() + outputBuffer: bytes = zlib.compress(inputBuffer, 9) + + with open(file=output, mode="wb") as outputFile: + + inputSizeBytes: bytes = inputSize.to_bytes(length=4, byteorder="little") + outputSizeBytes: bytes = len(outputBuffer).to_bytes(length=4, byteorder="little") + + outputFile.write(inputSizeBytes) + outputFile.write(outputSizeBytes) + outputFile.write(outputBuffer) + + + +def main() -> int: + + # This is run from the makefile. All paths must be relative to it, not the script. + textInput: str = "./Assets/Text" + textOutput: str = "./romfs/Text" + + ensure_fresh_dir(textOutput) + + for entry in os.listdir(textInput): + + inputPath: str = f"{textInput}/{entry}" + outputPath: str = f"{textOutput}/{entry}.z" + + compress_to_romfs(inputPath, outputPath) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Libraries/SDLLib b/Libraries/SDLLib index 7fdd7dc..07b5658 160000 --- a/Libraries/SDLLib +++ b/Libraries/SDLLib @@ -1 +1 @@ -Subproject commit 7fdd7dc872270ebd375e7571dbb83a3e9b16de82 +Subproject commit 07b5658c0056d2ebd65d51a97d4fb994aaee6fe1 diff --git a/Makefile b/Makefile index b9e0cdf..2b794e4 100644 --- a/Makefile +++ b/Makefile @@ -146,12 +146,12 @@ ifneq ($(ROMFS),) export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) endif -.PHONY: $(BUILD) FsLib SDLLib clean all +.PHONY: $(BUILD) FsLib SDLLib Assets clean all #--------------------------------------------------------------------------------- -all: FsLib SDLLib TextFiles $(BUILD) +all: FsLib SDLLib Assets $(BUILD) -$(BUILD): FsLib SDLLib TextFiles +$(BUILD): FsLib SDLLib Assets @[ -d $@ ] || mkdir -p $@ @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @@ -167,8 +167,9 @@ SDLLib: #--------------------------------------------------------------------------------- -TextFiles: - @python ./Text/compress_text.py +Assets: + @echo python script + @python ./Assets/compress_assets.py #--------------------------------------------------------------------------------- diff --git a/Text/compress_text.py b/Text/compress_text.py deleted file mode 100644 index d525e61..0000000 --- a/Text/compress_text.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python3 -# coding: utf-8 - -import zlib -import os -import sys -import shutil - -def main() -> int: - # This is run from the makefile. All paths must be relative to it, not the script. - inputDir: str = "./Text/Files" - outputDir: str = "./romfs/Text" - - if os.path.exists(outputDir): - shutil.rmtree(outputDir) - - os.mkdir(outputDir) - - for entry in os.listdir(inputDir): - - inputPath: str = f"{inputDir}/{entry}" - inputSize: int = os.path.getsize(inputPath) - with open(file=inputPath, mode="rb") as inputFile: - - inputBuffer: bytes = inputFile.read() - outputBuffer: bytes = zlib.compress(inputBuffer, 9) - - outputPath: str = f"{outputDir}/{entry}.z" - with open(file=outputPath, mode="wb") as outputFile: - - inputSizeBytes: bytes = inputSize.to_bytes(4, byteorder="little") - outputSizeBytes: bytes = len(outputBuffer).to_bytes(4, byteorder="little") - - outputFile.write(inputSizeBytes) - outputFile.write(outputSizeBytes) - outputFile.write(outputBuffer) - - return 0 - -if __name__ == "__main__": - sys.exit(main()) diff --git a/include/appstates/ConfirmState.hpp b/include/appstates/ConfirmState.hpp index 553c3d3..0d42230 100644 --- a/include/appstates/ConfirmState.hpp +++ b/include/appstates/ConfirmState.hpp @@ -56,6 +56,7 @@ class ConfirmState final : public BaseState ConfirmState::center_no(); ConfirmState::center_yes(); ConfirmState::load_holding_strings(); + sm_dialogPop->play(); } /// @brief Returns a new ConfirmState. See constructor. @@ -180,13 +181,21 @@ class ConfirmState final : public BaseState /// @brief Pointer to data struct passed to ^ const sys::Task::TaskData m_taskData{}; + /// @brief This dialog is shared between all instances. static inline std::shared_ptr sm_dialog{}; + /// @brief This sound is shared. + static inline sdl::SharedSound sm_dialogPop{}; + void initialize_static_members() { - if (sm_dialog) { return; } + static constexpr std::string_view POP_SOUND = "ConfirmPop"; + static constexpr const char *POP_PATH = "romfs:/Sound/ConfirmPop.wav"; - sm_dialog = ui::DialogBox::create(0, 0, 0, 0); + if (sm_dialog && sm_dialogPop) { return; } + + sm_dialog = ui::DialogBox::create(0, 0, 0, 0); + sm_dialogPop = sdl::SoundManager::load(POP_SOUND, POP_PATH); sm_dialog->set_from_transition(m_transition, true); } diff --git a/include/appstates/MessageState.hpp b/include/appstates/MessageState.hpp index d9a91ba..00bf08f 100644 --- a/include/appstates/MessageState.hpp +++ b/include/appstates/MessageState.hpp @@ -67,6 +67,9 @@ class MessageState final : public BaseState /// @brief All instances share this. There's no point in constantly allocating a new one. static inline std::shared_ptr sm_dialog{}; + /// @brief This is the same sound that the confirmation uses. + static inline sdl::SharedSound sm_dialogPop{}; + /// @brief Allocates and ensures ^ void initialize_static_members(); diff --git a/include/appstates/SettingsState.hpp b/include/appstates/SettingsState.hpp index b633f75..b0b133b 100644 --- a/include/appstates/SettingsState.hpp +++ b/include/appstates/SettingsState.hpp @@ -55,6 +55,9 @@ class SettingsState final : public BaseState /// @brief Toggles or executes the code to changed the selected menu option. void toggle_options(); + /// @brief Calls config::reset_to_default and refreshes the menu. + void reset_settings(); + /// @brief Creates and pushes the message with the description. void create_push_description_message(); diff --git a/include/appstates/TitleInfoState.hpp b/include/appstates/TitleInfoState.hpp index 4461f20..c02cb41 100644 --- a/include/appstates/TitleInfoState.hpp +++ b/include/appstates/TitleInfoState.hpp @@ -78,6 +78,9 @@ class TitleInfoState final : public BaseState /// @brief This frame is shared by all instances. No point in allocating it over and over. static inline std::shared_ptr sm_frame{}; + /// @brief This is the little chime that plays when this is opened. + static inline sdl::SharedSound sm_openChime{}; + /// @brief Initializes the static members if they haven't been already. void initialize_static_members(); diff --git a/include/ui/Menu.hpp b/include/ui/Menu.hpp index 2e7fa17..3674dda 100644 --- a/include/ui/Menu.hpp +++ b/include/ui/Menu.hpp @@ -124,6 +124,9 @@ namespace ui /// @brief Text scroll for when the current option is too long to on screen. std::shared_ptr m_optionScroll{}; + /// @brief The sound played when the selected/cursor moves. + static inline sdl::SharedSound sm_cursor{}; + /// @brief Calculates the alignment variables. void calculate_alignments() noexcept; @@ -136,6 +139,9 @@ namespace ui /// @brief Initializes the UI elements used within the menu. void initialize_ui_elements(); + /// @brief Ensures the menu cursor sound is loaded. + void initialize_cursor_sound(); + /// @brief Updates the text scroll for the currently highlighted option. void update_scroll_text(); diff --git a/include/ui/TitleView.hpp b/include/ui/TitleView.hpp index f50d61b..5fda390 100644 --- a/include/ui/TitleView.hpp +++ b/include/ui/TitleView.hpp @@ -71,6 +71,12 @@ namespace ui /// @brief Bounding box rendered around the selected title. std::shared_ptr m_bounding{}; + /// @brief Sound that is played when the selected title changes. This is shared with the menu code. + static inline sdl::SharedSound sm_cursor{}; + + /// @brief Ensures static members are initialized properly. + void initialize_static_members(); + /// @brief Performs the input routine. void handle_input(); diff --git a/romfs/Sound/ConfirmPop.wav b/romfs/Sound/ConfirmPop.wav new file mode 100644 index 0000000..26eae17 Binary files /dev/null and b/romfs/Sound/ConfirmPop.wav differ diff --git a/romfs/Sound/MenuCursor.wav b/romfs/Sound/MenuCursor.wav new file mode 100644 index 0000000..028d732 Binary files /dev/null and b/romfs/Sound/MenuCursor.wav differ diff --git a/romfs/Sound/PopMessage.wav b/romfs/Sound/PopMessage.wav index 558a545..36b8be9 100644 Binary files a/romfs/Sound/PopMessage.wav and b/romfs/Sound/PopMessage.wav differ diff --git a/romfs/Sound/TitleInfo.wav b/romfs/Sound/TitleInfo.wav new file mode 100644 index 0000000..56db9de Binary files /dev/null and b/romfs/Sound/TitleInfo.wav differ diff --git a/source/appstates/MessageState.cpp b/source/appstates/MessageState.cpp index 94c6a6b..509b1e1 100644 --- a/source/appstates/MessageState.cpp +++ b/source/appstates/MessageState.cpp @@ -11,6 +11,7 @@ MessageState::MessageState(std::string_view message) , m_transition(0, 0, 32, 32, 0, 0, 720, 256, ui::Transition::DEFAULT_THRESHOLD) { MessageState::initialize_static_members(); + sm_dialogPop->play(); } void MessageState::update() @@ -44,12 +45,16 @@ void MessageState::render() void MessageState::initialize_static_members() { - static constexpr int HALF_WIDTH = 640; - if (sm_okText && sm_dialog) { return; } + static constexpr int HALF_WIDTH = 640; + static constexpr std::string_view POP_SOUND = "ConfirmPop"; + static constexpr const char *POP_PATH = "romfs:/Sound/ConfirmPop.wav"; - sm_okText = strings::get_by_name(strings::names::YES_NO_OK, 2); - sm_okX = HALF_WIDTH - (sdl::text::get_width(22, sm_okText) / 2); - sm_dialog = ui::DialogBox::create(0, 0, 0, 0); + if (sm_okText && sm_dialog && sm_dialogPop) { return; } + + sm_okText = strings::get_by_name(strings::names::YES_NO_OK, 2); + sm_okX = HALF_WIDTH - (sdl::text::get_width(22, sm_okText) / 2); + sm_dialog = ui::DialogBox::create(0, 0, 0, 0); + sm_dialogPop = sdl::SoundManager::load(POP_SOUND, POP_PATH); sm_dialog->set_from_transition(m_transition, true); } diff --git a/source/appstates/SettingsState.cpp b/source/appstates/SettingsState.cpp index 3e1d8bb..c29daf7 100644 --- a/source/appstates/SettingsState.cpp +++ b/source/appstates/SettingsState.cpp @@ -78,10 +78,12 @@ void SettingsState::update() const bool hasFocus = BaseState::has_focus(); const bool aPressed = input::button_pressed(HidNpadButton_A); const bool bPressed = input::button_pressed(HidNpadButton_B); + const bool xPressed = input::button_pressed(HidNpadButton_X); const bool minusPressed = input::button_pressed(HidNpadButton_Minus); m_settingsMenu->update(hasFocus); if (aPressed) { SettingsState::toggle_options(); } + else if (xPressed) { SettingsState::reset_settings(); } else if (minusPressed) { SettingsState::create_push_description_message(); } else if (bPressed) { BaseState::deactivate(); } @@ -231,6 +233,12 @@ void SettingsState::toggle_options() SettingsState::update_menu_options(); } +void SettingsState::reset_settings() +{ + config::reset_to_default(); + SettingsState::update_menu_options(); +} + void SettingsState::create_push_description_message() { const int selected = m_settingsMenu->get_selected(); diff --git a/source/appstates/TitleInfoState.cpp b/source/appstates/TitleInfoState.cpp index 14630fe..0f25607 100644 --- a/source/appstates/TitleInfoState.cpp +++ b/source/appstates/TitleInfoState.cpp @@ -57,6 +57,7 @@ TitleInfoState::TitleInfoState(data::User *user, data::TitleInfo *titleInfo, con { TitleInfoState::initialize_static_members(); TitleInfoState::initialize_info_fields(); + sm_openChime->play(); } void TitleInfoState::update() @@ -95,7 +96,10 @@ void TitleInfoState::render() void TitleInfoState::initialize_static_members() { - if (sm_frame) + static constexpr std::string_view CHIME_NAME = "TitleInfoChime"; + static constexpr const char *CHIME_PATH = "romfs:/Sound/TitleInfo.wav"; + + if (sm_frame && sm_openChime) { sm_frame->set_from_transition(m_transition, true); return; @@ -106,6 +110,7 @@ void TitleInfoState::initialize_static_members() const int width = m_transition.get_width(); const int height = m_transition.get_height(); sm_frame = ui::Frame::create(x, y, width, height); + sm_openChime = sdl::SoundManager::load(CHIME_NAME, CHIME_PATH); } void TitleInfoState::initialize_info_fields() diff --git a/source/ui/Menu.cpp b/source/ui/Menu.cpp index 94650e6..85c18f4 100644 --- a/source/ui/Menu.cpp +++ b/source/ui/Menu.cpp @@ -20,6 +20,7 @@ ui::Menu::Menu(int x, int y, int width, int fontSize, int renderTargetHeight) Menu::initialize_transition(); Menu::initialize_option_target(); Menu::initialize_ui_elements(); + Menu::initialize_cursor_sound(); } void ui::Menu::update(bool hasFocus) @@ -155,6 +156,16 @@ void ui::Menu::initialize_ui_elements() false); } +void ui::Menu::initialize_cursor_sound() +{ + static constexpr std::string_view CURSOR_NAME = "MenuCursor"; + static constexpr const char *CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; + + if (sm_cursor) { return; } + + sm_cursor = sdl::SoundManager::load(CURSOR_NAME, CURSOR_PATH); +} + void ui::Menu::update_scroll_text() { const std::string_view text = m_optionScroll->get_text(); @@ -172,6 +183,7 @@ void ui::Menu::handle_input() const bool rShoulderPressed = input::button_pressed(HidNpadButton_R); const int optionsSize = m_options.size(); + const int previousSelected = m_selected; if (upPressed) { --m_selected; } else if (downPressed) { ++m_selected; } else if (leftPressed) { m_selected -= m_scrollLength; } @@ -181,6 +193,8 @@ void ui::Menu::handle_input() if (m_selected < 0) { m_selected = 0; } else if (m_selected >= optionsSize) { m_selected = optionsSize - 1; } + + if (m_selected != previousSelected) { sm_cursor->play(); } } void ui::Menu::update_scrolling() diff --git a/source/ui/TitleView.cpp b/source/ui/TitleView.cpp index 0fe060c..f0a2bca 100644 --- a/source/ui/TitleView.cpp +++ b/source/ui/TitleView.cpp @@ -20,6 +20,7 @@ ui::TitleView::TitleView(data::User *user) , m_transition(0, UPPER_THRESHOLD, 0, 0, 0, UPPER_THRESHOLD, 0, 0, m_transition.DEFAULT_THRESHOLD) , m_bounding(ui::BoundingBox::create(0, 0, 188, 188)) { + TitleView::initialize_static_members(); TitleView::refresh(); } @@ -111,6 +112,16 @@ void ui::TitleView::reset() for (ui::TitleTile ¤tTile : m_titleTiles) { currentTile.reset(); } } +void ui::TitleView::initialize_static_members() +{ + static constexpr std::string_view CURSOR_NAME = "MenuCursor"; + static constexpr const char *CURSOR_PATH = "romfs:/Sound/MenuCursor.wav"; + + if (sm_cursor) { return; } + + sm_cursor = sdl::SoundManager::load(CURSOR_NAME, CURSOR_PATH); +} + void ui::TitleView::handle_input() { const int totalTiles = m_titleTiles.size() - 1; @@ -121,6 +132,8 @@ void ui::TitleView::handle_input() const bool lShoulderPressed = input::button_pressed(HidNpadButton_L); const bool rShoulderPressed = input::button_pressed(HidNpadButton_R); + const int previousSelected = m_selected; + if (upPressed) { m_selected -= ICON_ROW_SIZE; } else if (leftPressed) { --m_selected; } else if (lShoulderPressed) { m_selected -= ICON_ROW_SIZE * 3; } @@ -130,6 +143,8 @@ void ui::TitleView::handle_input() if (m_selected < 0) { m_selected = 0; } if (m_selected > totalTiles) { m_selected = totalTiles; } + + if (m_selected != previousSelected) { sm_cursor->play(); } } void ui::TitleView::handle_scrolling()