From c8644c80cdd3c5b0e3653396b34008342e20d113 Mon Sep 17 00:00:00 2001 From: ITotalJustice <47043333+ITotalJustice@users.noreply.github.com> Date: Wed, 30 Jul 2025 23:26:15 +0100 Subject: [PATCH] add more info boxes to options, merge option.cpp changes from totalsms. --- sphaira/include/app.hpp | 2 +- sphaira/include/option.hpp | 4 +- sphaira/include/ui/menus/main_menu.hpp | 1 + sphaira/source/app.cpp | 56 ++++++++++++----- sphaira/source/option.cpp | 51 +++++++++------- sphaira/source/ui/menus/main_menu.cpp | 85 ++++++++++++++++++++------ 6 files changed, 142 insertions(+), 57 deletions(-) diff --git a/sphaira/include/app.hpp b/sphaira/include/app.hpp index 9fddba6..2b4b6fd 100644 --- a/sphaira/include/app.hpp +++ b/sphaira/include/app.hpp @@ -325,7 +325,7 @@ public: option::OptionBool m_dump_append_folder_with_xci{"dump", "append_folder_with_xci", true}; option::OptionBool m_dump_trim_xci{"dump", "trim_xci", false}; option::OptionBool m_dump_label_trim_xci{"dump", "label_trim_xci", false}; - option::OptionBool m_dump_usb_transfer_stream{"dump", "usb_transfer_stream", true}; + option::OptionBool m_dump_usb_transfer_stream{"dump", "usb_transfer_stream", true, false}; option::OptionBool m_dump_convert_to_common_ticket{"dump", "convert_to_common_ticket", true}; // todo: move this into it's own menu diff --git a/sphaira/include/option.hpp b/sphaira/include/option.hpp index 4eb6204..35df275 100644 --- a/sphaira/include/option.hpp +++ b/sphaira/include/option.hpp @@ -7,10 +7,11 @@ namespace sphaira::option { template struct OptionBase { - OptionBase(const std::string& section, const std::string& name, T default_value) + OptionBase(const std::string& section, const std::string& name, T default_value, bool file = true) : m_section{section} , m_name{name} , m_default_value{default_value} + , m_file{file} {} auto Get() -> T; @@ -29,6 +30,7 @@ private: const std::string m_section; const std::string m_name; const T m_default_value; + const bool m_file; std::optional m_value; }; diff --git a/sphaira/include/ui/menus/main_menu.hpp b/sphaira/include/ui/menus/main_menu.hpp index 0df97e7..5b2038a 100644 --- a/sphaira/include/ui/menus/main_menu.hpp +++ b/sphaira/include/ui/menus/main_menu.hpp @@ -31,6 +31,7 @@ struct MiscMenuEntry { const char* title; MiscMenuFunction func; u8 flag; + const char* info; auto IsShortcut() const -> bool { return flag & MiscMenuFlag_Shortcut; diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index dddc06c..52d745f 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -1619,11 +1619,13 @@ void App::DisplayThemeOptions(bool left_side) { options->Add("Music"_i18n, App::GetThemeMusicEnable(), [](bool& enable){ App::SetThemeMusicEnable(enable); - }); + }, "Enable background music.\n"\ + "Each theme can have it's own music file."\ + "If a theme does not set a music file, then /config/sphaira/themes/default_music.bfstm is loaded instead (if it exists)."_i18n); options->Add("12 Hour Time"_i18n, App::Get12HourTimeEnable(), [](bool& enable){ App::Set12HourTimeEnable(enable); - }); + }, "Changes the clock to 12 hour"_i18n); options->Add("Download Default Music"_i18n, [](){ // check if we already have music @@ -1640,7 +1642,7 @@ void App::DisplayThemeOptions(bool left_side) { } else { download_default_music(); } - }); + }, "Downloads the default background music for sphaira to /config/sphaira/themes/default_music.bfstm"_i18n); } void App::DisplayNetworkOptions(bool left_side) { @@ -1662,7 +1664,7 @@ void App::DisplayMiscOptions(bool left_side) { options->Add(i18n::get(e.title), [e](){ App::Push(e.func(ui::menu::MenuFlag_None)); - }); + }, i18n::get(e.info)); } if (App::IsApplication()) { @@ -1690,7 +1692,9 @@ void App::DisplayMiscOptions(bool left_side) { } } ); - }); + }, + "Launch the built-in web browser.\n\n", + "NOTE: The browser is very limted, some websites will fail to load and there's a 30 minute timeout which closes the browser"_i18n); } } @@ -1733,7 +1737,7 @@ void App::DisplayAdvancedOptions(bool left_side) { options->Add("Text scroll speed"_i18n, text_scroll_speed_items, [](s64& index_out){ App::SetTextScrollSpeed(index_out); - }, App::GetTextScrollSpeed()); + }, App::GetTextScrollSpeed(), "Change how fast the scrolling text updates"_i18n); options->Add("Set left-side menu"_i18n, menu_items, [menu_names](s64& index_out){ const auto e = menu_names[index_out]; @@ -1750,7 +1754,7 @@ void App::DisplayAdvancedOptions(bool left_side) { } ); } - }, i18n::get(g_app->m_left_menu.Get())); + }, i18n::get(g_app->m_left_menu.Get()), "Set the menu that appears on the left tab."_i18n); options->Add("Set right-side menu"_i18n, menu_items, [menu_names](s64& index_out){ const auto e = menu_names[index_out]; @@ -1767,15 +1771,16 @@ void App::DisplayAdvancedOptions(bool left_side) { } ); } - }, i18n::get(g_app->m_right_menu.Get())); + }, i18n::get(g_app->m_right_menu.Get()), "Set the menu that appears on the right tab."_i18n); options->Add("Install options"_i18n, [left_side](){ App::DisplayInstallOptions(left_side); - }); + }, "Change the install options.\n"\ + "You can enable installing from here."_i18n); options->Add("Dump options"_i18n, [left_side](){ App::DisplayDumpOptions(left_side); - }); + }, "Change the dump options."_i18n); static const char* erpt_path = "/atmosphere/erpt_reports"; options->Add("Disable erpt_reports"_i18n, fs::FsNativeSd().FileExists(erpt_path), [](bool& enable){ @@ -1891,12 +1896,31 @@ void App::DisplayDumpOptions(bool left_side) { auto options = std::make_unique("Dump Options"_i18n, left_side ? ui::Sidebar::Side::LEFT : ui::Sidebar::Side::RIGHT); ON_SCOPE_EXIT(App::Push(std::move(options))); - options->Add("Created nested folder"_i18n, App::GetApp()->m_dump_app_folder); - options->Add("Append folder with .xci"_i18n, App::GetApp()->m_dump_append_folder_with_xci); - options->Add("Trim XCI"_i18n, App::GetApp()->m_dump_trim_xci); - options->Add("Label trimmed XCI"_i18n, App::GetApp()->m_dump_label_trim_xci); - options->Add("Multi-threaded USB transfer"_i18n, App::GetApp()->m_dump_usb_transfer_stream); - options->Add("Convert to common ticket"_i18n, App::GetApp()->m_dump_convert_to_common_ticket); + options->Add( + "Created nested folder"_i18n, App::GetApp()->m_dump_app_folder, + "Creates a folder using the name of the game.\n"\ + "For example, /dumps/XCI/name/name.xci"\ + "Disabling this would use /dumps/XCI/name.xci"_i18n + ); + options->Add( + "Append folder with .xci"_i18n, App::GetApp()->m_dump_append_folder_with_xci, + "XCI dumps will name the folder with the .xci extension.\n"\ + "For example, /dumps/XCI/name.xci/name.xci\n\n" + "Some devices only function is the xci folder is named exactly the same as the xci."_i18n + ); + options->Add( + "Trim XCI"_i18n, App::GetApp()->m_dump_trim_xci, + "Removes the unused data at the end of the XCI, making the output smaller."_i18n + ); + options->Add( + "Label trimmed XCI"_i18n, App::GetApp()->m_dump_label_trim_xci, + "Names the trimmed xci.\n" + "For example, /dumps/XCI/name/name (trimmed).xci"_i18n + ); + options->Add( + "Convert to common ticket"_i18n, App::GetApp()->m_dump_convert_to_common_ticket, + "Converts personalised ticket to a fake common ticket."_i18n + ); } App::~App() { diff --git a/sphaira/source/option.cpp b/sphaira/source/option.cpp index 6d2a093..c4b8f44 100644 --- a/sphaira/source/option.cpp +++ b/sphaira/source/option.cpp @@ -34,16 +34,21 @@ bool getbool(const char* LocalBuffer, bool def) { template auto OptionBase::GetInternal(const char* name) -> T { if (!m_value.has_value()) { - if constexpr(std::is_same_v) { - m_value = ini_getbool(m_section.c_str(), name, m_default_value, App::CONFIG_PATH); - } else if constexpr(std::is_same_v) { - m_value = ini_getl(m_section.c_str(), name, m_default_value, App::CONFIG_PATH); - } else if constexpr(std::is_same_v) { - char buf[FS_MAX_PATH]; - ini_gets(m_section.c_str(), name, m_default_value.c_str(), buf, sizeof(buf), App::CONFIG_PATH); - m_value = buf; + if (m_file) { + if constexpr(std::is_same_v) { + m_value = ini_getbool(m_section.c_str(), name, m_default_value, App::CONFIG_PATH); + } else if constexpr(std::is_same_v) { + m_value = ini_getl(m_section.c_str(), name, m_default_value, App::CONFIG_PATH); + } else if constexpr(std::is_same_v) { + char buf[FS_MAX_PATH]; + ini_gets(m_section.c_str(), name, m_default_value.c_str(), buf, sizeof(buf), App::CONFIG_PATH); + m_value = buf; + } + } else { + m_value = m_default_value; } } + return m_value.value(); } @@ -54,7 +59,7 @@ auto OptionBase::Get() -> T { template auto OptionBase::GetOr(const char* name) -> T { - if (ini_haskey(m_section.c_str(), m_name.c_str(), App::CONFIG_PATH)) { + if (m_file && ini_haskey(m_section.c_str(), m_name.c_str(), App::CONFIG_PATH)) { return Get(); } else { return GetInternal(name); @@ -64,12 +69,14 @@ auto OptionBase::GetOr(const char* name) -> T { template void OptionBase::Set(T value) { m_value = value; - if constexpr(std::is_same_v) { - ini_putl(m_section.c_str(), m_name.c_str(), value, App::CONFIG_PATH); - } else if constexpr(std::is_same_v) { - ini_putl(m_section.c_str(), m_name.c_str(), value, App::CONFIG_PATH); - } else if constexpr(std::is_same_v) { - ini_puts(m_section.c_str(), m_name.c_str(), value.c_str(), App::CONFIG_PATH); + if (m_file) { + if constexpr(std::is_same_v) { + ini_putl(m_section.c_str(), m_name.c_str(), value, App::CONFIG_PATH); + } else if constexpr(std::is_same_v) { + ini_putl(m_section.c_str(), m_name.c_str(), value, App::CONFIG_PATH); + } else if constexpr(std::is_same_v) { + ini_puts(m_section.c_str(), m_name.c_str(), value.c_str(), App::CONFIG_PATH); + } } } @@ -81,12 +88,14 @@ auto OptionBase::LoadFrom(const char* section, const char* name, const char* template auto OptionBase::LoadFrom(const char* name, const char* value) -> bool { if (m_name == name) { - if constexpr(std::is_same_v) { - m_value = getbool(value, m_default_value); - } else if constexpr(std::is_same_v) { - m_value = getl(value, m_default_value); - } else if constexpr(std::is_same_v) { - m_value = value; + if (m_file) { + if constexpr(std::is_same_v) { + m_value = getbool(value, m_default_value); + } else if constexpr(std::is_same_v) { + m_value = getl(value, m_default_value); + } else if constexpr(std::is_same_v) { + m_value = value; + } } return true; diff --git a/sphaira/source/ui/menus/main_menu.cpp b/sphaira/source/ui/menus/main_menu.cpp index ebfcece..e633cdb 100644 --- a/sphaira/source/ui/menus/main_menu.cpp +++ b/sphaira/source/ui/menus/main_menu.cpp @@ -49,19 +49,59 @@ auto MiscMenuFuncGenerator(u32 flags) { } const MiscMenuEntry MISC_MENU_ENTRIES[] = { - { .name = "Appstore", .title = "Appstore", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "Games", .title = "Games", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "FileBrowser", .title = "FileBrowser", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "Saves", .title = "Saves", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "Themezer", .title = "Themezer", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "GitHub", .title = "GitHub", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "Appstore", .title = "Appstore", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "Download and update apps.\n\n"\ + "Internet connection required." }, + + { .name = "Games", .title = "Games", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "View all installed games."\ + "In this menu you can launch, backup, create savedata and much more." }, + + { .name = "FileBrowser", .title = "FileBrowser", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "Browse files on you SD Card."\ + "You can move, copy, delete, extract zip, create zip, upload and much more.\n\n"\ + "A connected USB/HDD can be opened by mounting it in the advanced options." }, + + { .name = "Saves", .title = "Saves", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "View save data for each user."\ + "You can backup and restore saves."\ + "Experimental support for backing up system saves is possible." }, + + { .name = "Themezer", .title = "Themezer", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "Download themes from https://themezer.net."\ + "Themes are downloaded to /themes/sphaira"\ + "To install the themes, NXThemesInstaller needs to be installed (can be downloaded via the AppStore)." }, + + { .name = "GitHub", .title = "GitHub", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "Download releases directly from GitHub."\ + "Custom entries can be added to /config/sphaira/github" }, + #if ENABLE_NETWORK_INSTALL - { .name = "FTP", .title = "FTP Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install }, - { .name = "MTP", .title = "MTP Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install }, - { .name = "USB", .title = "USB Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install }, + { .name = "FTP", .title = "FTP Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install, .info = + "Install apps via FTP.\n\n"\ + "NOTE: This feature does not always work, use at your own risk."\ + "If you encounter an issue, do not open an issue, it will not be fixed." }, + + { .name = "MTP", .title = "MTP Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install, .info = + "Install apps via MTP.\n\n"\ + "NOTE: This feature does not always work, use at your own risk."\ + "If you encounter an issue, do not open an issue, it will not be fixed." }, + + { .name = "USB", .title = "USB Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install, .info = + "Install apps via USB.\n\n"\ + "A USB client is required on PC, such as ns-usbloader and fluffy.\n\n"\ + "NOTE: This feature does not always work, use at your own risk."\ + "If you encounter an issue, do not open an issue, it will not be fixed." }, + #endif - { .name = "GameCard", .title = "GameCard", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, - { .name = "IRS", .title = "IRS (Infrared Joycon Camera)", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "GameCard", .title = "GameCard", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "View info on the inserted Game Card (GC)."\ + "You can backup and install the inserted GC."\ + "To swap GC's, simply remove the old GC and insert the new one."\ + "You do not need to exit the menu." }, + + { .name = "IRS", .title = "IRS (Infrared Joycon Camera)", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut, .info = + "InfraRed Sensor (IRS) is the small camera found on right JoyCon." }, }; auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string version) -> Result { @@ -288,29 +328,37 @@ MainMenu::MainMenu() { options->Add("Ftp"_i18n, App::GetFtpEnable(), [](bool& enable){ App::SetFtpEnable(enable); - }); + }, "Enable FTP server to run in the background.\n\n"\ + "The default port is 5000 with no user/pass set.\n"\ + "You can change this behaviour in /config/ftpsrv/config.ini"_i18n); options->Add("Mtp"_i18n, App::GetMtpEnable(), [](bool& enable){ App::SetMtpEnable(enable); - }); + }, "Enable MTP server to run in the background."_i18n); options->Add("Nxlink"_i18n, App::GetNxlinkEnable(), [](bool& enable){ App::SetNxlinkEnable(enable); - }); + }, "Enable NXlink server to run in the background."\ + "NXlink is used to send .nro's from PC to the switch\n\n"\ + "If you are not a developer, you can disable this option."_i18n); options->Add("Hdd"_i18n, App::GetHddEnable(), [](bool& enable){ App::SetHddEnable(enable); - }); + }, "Enable mounting of connected USB/HDD devices."\ + "Connected devices can be used in the FileBrowser, as well as a backup location when dumping games and saves."_i18n); options->Add("Hdd write protect"_i18n, App::GetWriteProtect(), [](bool& enable){ App::SetWriteProtect(enable); - }); + }, "Makes the connected HDD read-only."_i18n); }, "Toggle FTP, MTP, HDD and NXlink\n\n" \ "If Sphaira has a update available, you can download it from this menu"_i18n); options->Add("Language"_i18n, language_items, [](s64& index_out){ App::SetLanguage(index_out); - }, (s64)App::GetLanguage()); + }, (s64)App::GetLanguage(), + "Change the language.\n\n" + "If your language isn't found, or translations are missing, please consider opening a PR at "\ + "https://github.com/ITotalJustice/sphaira/pulls"_i18n); options->Add("Misc"_i18n, [](){ App::DisplayMiscOptions(); @@ -318,7 +366,8 @@ MainMenu::MainMenu() { options->Add("Advanced"_i18n, [](){ App::DisplayAdvancedOptions(); - }); + }, "Change the advanced options."\ + "Please view the info boxes to better understand each option."_i18n); }} ));