From 73a220205fdff816367f9cb6af2c1f08014c5066 Mon Sep 17 00:00:00 2001 From: XorTroll Date: Sun, 11 Aug 2024 21:54:06 +0200 Subject: [PATCH] Add basic lockscreen, verify support, fix several bugs --- README.md | 17 +- .../default-theme/Main/EntryIcon/Amiibo.xcf | Bin 0 -> 27412 bytes .../default-theme/Main/OverIcon/Corrupted.xcf | Bin 0 -> 7459 bytes .../default-theme/Main/OverIcon/Gamecard.xcf | Bin 0 -> 21617 bytes .../Main/OverIcon/NeedsUpdate.xcf | Bin 0 -> 6739 bytes .../Main/OverIcon/NotLaunchable.xcf | Bin 0 -> 8063 bytes assets/orig/Empty.png | Bin 0 -> 1089 bytes assets/orig/Update.png | Bin 0 -> 781 bytes default-theme/ui/Main/EntryIcon/Amiibo.png | Bin 0 -> 7414 bytes default-theme/ui/Main/OverIcon/Corrupted.png | Bin 0 -> 6815 bytes default-theme/ui/Main/OverIcon/Gamecard.png | Bin 0 -> 7099 bytes .../ui/Main/OverIcon/NeedsUpdate.png | Bin 0 -> 7259 bytes .../ui/Main/OverIcon/NotLaunchable.png | Bin 0 -> 7974 bytes default-theme/ui/UI.json | 42 +- docs/udesigner.data | Bin 2603524 -> 2626318 bytes docs/udesigner.js | 14 +- docs/udesigner.wasm | Bin 1168430 -> 1168246 bytes libs/uCommon/include/ul/cfg/cfg_Config.hpp | 31 +- libs/uCommon/include/ul/menu/menu_Cache.hpp | 2 +- libs/uCommon/include/ul/menu/menu_Entries.hpp | 50 +- .../uCommon/include/ul/os/os_Applications.hpp | 10 +- libs/uCommon/include/ul/smi/smi_Protocol.hpp | 30 +- .../include/ul/system/system_Message.hpp | 2 +- libs/uCommon/include/ul/ul_Result.hpp | 6 +- libs/uCommon/include/ul/ul_Results.rc.hpp | 1 + libs/uCommon/source/ul/menu/menu_Cache.cpp | 16 +- libs/uCommon/source/ul/menu/menu_Entries.cpp | 229 +++- libs/uCommon/source/ul/os/os_Applications.cpp | 19 +- projects/uDesigner/source/main.cpp | 12 +- .../ul/loader/loader_ProgramIdUtils.cpp | 2 +- projects/uManager/source/main.cpp | 7 + .../include/ul/menu/smi/smi_Commands.hpp | 65 ++ .../ul/menu/smi/smi_MenuMessageHandler.hpp | 2 +- .../uMenu/include/ul/menu/ui/ui_Common.hpp | 102 +- .../uMenu/include/ul/menu/ui/ui_EntryMenu.hpp | 34 +- .../include/ul/menu/ui/ui_IMenuLayout.hpp | 13 +- .../ul/menu/ui/ui_LockscreenMenuLayout.hpp | 26 + .../include/ul/menu/ui/ui_MainMenuLayout.hpp | 20 +- .../include/ul/menu/ui/ui_MenuApplication.hpp | 140 +-- .../uMenu/include/ul/menu/ui/ui_QuickMenu.hpp | 2 +- .../ul/menu/ui/ui_SettingsMenuLayout.hpp | 55 +- projects/uMenu/romfs/lang/en.json | 71 +- projects/uMenu/romfs/lang/es.json | 69 +- projects/uMenu/romfs/lang/it.json | 69 +- projects/uMenu/romfs/lang/ko.json | 69 +- projects/uMenu/romfs/lang/pt-BR.json | 71 +- projects/uMenu/source/main.cpp | 154 ++- .../ul/menu/am/am_LibnxLibappletWrap.cpp | 2 +- .../uMenu/source/ul/menu/ui/ui_Common.cpp | 37 +- .../uMenu/source/ul/menu/ui/ui_EntryMenu.cpp | 208 +++- .../source/ul/menu/ui/ui_IMenuLayout.cpp | 115 +- .../uMenu/source/ul/menu/ui/ui_InputBar.cpp | 1 - .../ul/menu/ui/ui_LockscreenMenuLayout.cpp | 76 ++ .../source/ul/menu/ui/ui_MainMenuLayout.cpp | 238 ++-- .../source/ul/menu/ui/ui_MenuApplication.cpp | 90 +- .../uMenu/source/ul/menu/ui/ui_QuickMenu.cpp | 2 +- .../ul/menu/ui/ui_SettingsMenuLayout.cpp | 1004 ++++++++++++----- .../ul/menu/ui/ui_StartupMenuLayout.cpp | 5 +- .../source/ul/menu/ui/ui_ThemesMenuLayout.cpp | 21 +- projects/uSystem/source/main.cpp | 465 ++++++-- .../source/ul/system/app/app_Application.cpp | 92 +- .../source/ul/system/la/la_LibraryApplet.cpp | 50 +- .../ul/system/sf/sf_IPrivateService.cpp | 2 +- 63 files changed, 2974 insertions(+), 886 deletions(-) create mode 100644 assets/default-theme/Main/EntryIcon/Amiibo.xcf create mode 100644 assets/default-theme/Main/OverIcon/Corrupted.xcf create mode 100644 assets/default-theme/Main/OverIcon/Gamecard.xcf create mode 100644 assets/default-theme/Main/OverIcon/NeedsUpdate.xcf create mode 100644 assets/default-theme/Main/OverIcon/NotLaunchable.xcf create mode 100644 assets/orig/Empty.png create mode 100644 assets/orig/Update.png create mode 100644 default-theme/ui/Main/EntryIcon/Amiibo.png create mode 100644 default-theme/ui/Main/OverIcon/Corrupted.png create mode 100644 default-theme/ui/Main/OverIcon/Gamecard.png create mode 100644 default-theme/ui/Main/OverIcon/NeedsUpdate.png create mode 100644 default-theme/ui/Main/OverIcon/NotLaunchable.png create mode 100644 projects/uMenu/include/ul/menu/ui/ui_LockscreenMenuLayout.hpp create mode 100644 projects/uMenu/source/ul/menu/ui/ui_LockscreenMenuLayout.cpp diff --git a/README.md b/README.md index 2c8abc5..b8d3a1f 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@

-### Want to find **themes** for uLaunch? Check the [r/uLaunchThemes subreddit](https://www.reddit.com/r/uLaunchThemes/) or the [Discord server](https://discord.gg/3KpFyaH)! +### Want to find **themes** for uLaunch? Check the `ulaunch-themes` channel on our [Discord server](https://discord.gg/3KpFyaH)! ### Want to make your own uLaunch **themes**? Check our [web theme editor](https://xortroll.github.io/uLaunch/) or the [wiki](https://github.com/XorTroll/uLaunch/wiki)! @@ -56,6 +56,7 @@ - [Installing uLaunch](#installing-ulaunch) - [Removing uLaunch](#removing-ulaunch) - [FAQ](#faq) +- [Translating](#translating) - [Components](#components) - [uSystem](#usystem) - [uMenu](#umenu) @@ -240,6 +241,18 @@ List of not implemented official HOME menu features: - Aside from the two excuses above, there is always room for further optimizations in uLaunch's code. Feel free to submit any issues of excessive lag/slowdowns, I'll do my best to improve it :) +## Translating + +Translations for uLaunch are always welcome! + +The files to be translated are [uMenu translations](https://github.com/XorTroll/uLaunch/blob/unew/projects/uMenu/romfs/lang/en.json) and [uManager translations](https://github.com/XorTroll/uLaunch/blob/unew/projects/uManager/romfs/lang/en.json). They are pretty straightforward to understand, just JSON files with English sentences meant to be translated to **any language officially supported by the Nintendo Switch**. + +Character `\n` is a new-line escape (indicates a new line) and must remain unchanged. Punctuation marks (mainly dots at the end of sentences, questions, double dots) must be respected, with perhaphs the exception of commas in the middle of sentences. + +Feel free to open pull requests for translations, but it's better to keep contact with me through [Discord](https://discord.gg/3KpFyaH). + +As I work on new features and new strings are needed, I will manually add them as English on other language files to keep support. + ## Components ### uSystem @@ -354,6 +367,6 @@ In order to only build a certain subproject, you can run `make` plus the subproj - Several scene developers for their help with small issues or features. -- uMenu/uManager translations: [DDinghoya](https://github.com/DDinghoya) for Korean, [NedcloarBR](https://github.com/NedcloarBR) for Brazilian Portuguese +- uMenu/uManager translations: [DDinghoya](https://github.com/DDinghoya) for Korean, [NedcloarBR](https://github.com/NedcloarBR) for Brazilian Portuguese, [Gabriele73](https://github.com/Gabriele73) for Italian - Everyone from my Discord and other places whose suggestions made this project a little bit better! Specially all the testers for being essential in reporting bugs and helping a lot with the project's development <3 diff --git a/assets/default-theme/Main/EntryIcon/Amiibo.xcf b/assets/default-theme/Main/EntryIcon/Amiibo.xcf new file mode 100644 index 0000000000000000000000000000000000000000..bf04931f0f86281aeef0ead9a63fca27e9facaca GIT binary patch literal 27412 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 ZjB5>vP0+uoyzbjW)!? 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 IM^$3~1v4`JSO5S3 literal 0 HcmV?d00001 diff --git a/assets/default-theme/Main/OverIcon/NeedsUpdate.xcf b/assets/default-theme/Main/OverIcon/NeedsUpdate.xcf new file mode 100644 index 0000000000000000000000000000000000000000..c58859207268030e77e8214c8e64f914cf58ebb1 GIT binary patch literal 6739 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 literal 0 HcmV?d00001 diff --git a/default-theme/ui/Main/EntryIcon/Amiibo.png b/default-theme/ui/Main/EntryIcon/Amiibo.png new file mode 100644 index 0000000000000000000000000000000000000000..8d4bccdece2c862c71f858ccfbb0318790d954aa GIT binary patch literal 7414 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 Msp+cLD8GpOKiQjT@Bjb+ literal 0 HcmV?d00001 diff --git a/default-theme/ui/Main/OverIcon/Corrupted.png b/default-theme/ui/Main/OverIcon/Corrupted.png new file mode 100644 index 0000000000000000000000000000000000000000..89c81f87488edd45c43143d327317c1ddc13ffa7 GIT binary patch literal 6815 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 BNjv}m literal 0 HcmV?d00001 diff --git a/default-theme/ui/Main/OverIcon/NotLaunchable.png b/default-theme/ui/Main/OverIcon/NotLaunchable.png new file mode 100644 index 0000000000000000000000000000000000000000..e822a5106289f660abac2bf3c27b7c60a84b1f24 GIT binary patch literal 7974 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 D#?Nzg delta 2189 zcmY+EeOMD$7RG0C6TU(M315X^ASH8Wpb(0oEUiUcj1)ww_TkGfDo9mOic1x(t`DeV zDe8*W##?JuU~AQ_Emo`}v5HEusDLPy77T5U%t}}X){%8$omm%F%DS>LR?fPy?yQ1UvL37_>&1GrKI}`Z zFYCvuSbugBt7Ze(z*8FiS&-gX%0->k7)Y?_w~x?}0qVIEKWIqBI3kQn?)PTRUU~6O zSx?5`08wy{B!~+x1mQyX6KitAQxkQ09~8Wwn&M1=&Ry(Myynt1>!buJ?gs=R{4}TD zDEvCmedG1?)%`013?AA4(YyYus)>m%S$n(lVV%#-Q^`&b`UmTcT$g^?^@EFs@<;AX z&4K#OD=R8j`Um@bGW_ZAlH#9Q&C~bAY}_3BL&p5CGjn}rl$TiU`)@q#rQe&@bis=W zw>bY$VCXeb_{rux{#ie@9%>L!tpVOFH!~SIv?QdSqYasga%RcDUman=$;BKfbtbgr6 z__{jBA$MA5%zK~@O?IVx#ylT9kuBZxx(eoottyAJ9W_BIgo_Z`j1O|t1qfF{E$;Suc)531+W^BKftun_O&%P3$ z6WUhf7gQ(G)Xsl>c*%2Uz1vEI+s6m|(6#iU%if(AiQVfLEDXLBd#Adq^>t%z`ZjfM zaC4Y6)1Rv5@A@nI6cXuI77LdcotsXZ#xUY=x7n@OX1Zsqv&sEn$ z3&~ZQ;AX(TSA=;omvJ7ZNQ4)Tam{#SB{}LE3wk-J8h=L@`*Yd#u%L1&5Xt}|vb*TlI4aVC4r{O9;A~oDvhJ$L49k+}rDs$dp_;=^`z14p}~(+XUc r&?Z3 { if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?'; }); - // end include: /tmp/tmpx50pwn30.js + // end include: /tmp/tmpiu8r_zac.js // Sometimes an existing Module object exists with properties diff --git a/docs/udesigner.wasm b/docs/udesigner.wasm index 55e74714f2e293107d72e1eeda6c4882f2e95814..22cd0bb7f1b8a339d731258e5284b2d065c4d565 100644 GIT binary patch delta 177439 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 H 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 z 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 diff --git a/libs/uCommon/include/ul/cfg/cfg_Config.hpp b/libs/uCommon/include/ul/cfg/cfg_Config.hpp index bddfe5b..ddaf8fc 100644 --- a/libs/uCommon/include/ul/cfg/cfg_Config.hpp +++ b/libs/uCommon/include/ul/cfg/cfg_Config.hpp @@ -34,9 +34,10 @@ namespace ul::cfg { MenuTakeoverProgramId, HomebrewAppletTakeoverProgramId, HomebrewApplicationTakeoverApplicationId, - ViewerUsbEnabled, + UsbScreenCaptureEnabled, ActiveThemeName, - MenuEntryHeightCount + MenuEntryHeightCount, + LockscreenEnabled }; enum class ConfigEntryType : u8 { @@ -165,7 +166,7 @@ namespace ul::cfg { return false; } } - case ConfigEntryId::ViewerUsbEnabled: { + case ConfigEntryId::UsbScreenCaptureEnabled: { if constexpr(std::is_same_v) { new_entry.header.type = ConfigEntryType::Bool; new_entry.header.size = sizeof(t); @@ -198,6 +199,17 @@ namespace ul::cfg { return false; } } + case ConfigEntryId::LockscreenEnabled: { + if constexpr(std::is_same_v) { + new_entry.header.type = ConfigEntryType::Bool; + new_entry.header.size = sizeof(t); + new_entry.bool_value = t; + break; + } + else { + return false; + } + } } this->entries.push_back(std::move(new_entry)); return true; @@ -243,7 +255,7 @@ namespace ul::cfg { return false; } } - case ConfigEntryId::ViewerUsbEnabled: { + case ConfigEntryId::UsbScreenCaptureEnabled: { if constexpr(std::is_same_v) { // Disabled by default, it might interfer with other homebrews out_t = false; @@ -255,7 +267,7 @@ namespace ul::cfg { } case ConfigEntryId::ActiveThemeName: { if constexpr(std::is_same_v) { - // Empty by default + // None (empty) by default out_t = ""; return true; } @@ -272,6 +284,15 @@ namespace ul::cfg { return false; } } + case ConfigEntryId::LockscreenEnabled: { + if constexpr(std::is_same_v) { + out_t = false; + return true; + } + else { + return false; + } + } } return false; } diff --git a/libs/uCommon/include/ul/menu/menu_Cache.hpp b/libs/uCommon/include/ul/menu/menu_Cache.hpp index 13ed1eb..28cd55b 100644 --- a/libs/uCommon/include/ul/menu/menu_Cache.hpp +++ b/libs/uCommon/include/ul/menu/menu_Cache.hpp @@ -8,7 +8,7 @@ namespace ul::menu { void CacheHomebrew(const std::string &hb_base_path = RootHomebrewPath); void CacheApplications(const std::vector &records); - void CacheSingleApplication(const u64 app_id); + bool CacheSingleApplication(const u64 app_id); inline std::string GetApplicationCacheIconPath(const u64 app_id) { return fs::JoinPath(ApplicationCachePath, util::FormatProgramId(app_id) + ".jpg"); diff --git a/libs/uCommon/include/ul/menu/menu_Entries.hpp b/libs/uCommon/include/ul/menu/menu_Entries.hpp index 112891b..5a3407f 100644 --- a/libs/uCommon/include/ul/menu/menu_Entries.hpp +++ b/libs/uCommon/include/ul/menu/menu_Entries.hpp @@ -1,6 +1,7 @@ #pragma once #include
    +#include
      #include
        #include
          #include @@ -18,35 +19,25 @@ namespace ul::menu { SpecialEntrySettings, SpecialEntryThemes, SpecialEntryControllers, - SpecialEntryAlbum + SpecialEntryAlbum, + SpecialEntryAmiibo }; struct EntryApplicationInfo { u64 app_id; NsApplicationRecord record; - NsApplicationContentMetaStatus meta_status; + NsApplicationView view; + u32 version; + u32 launch_required_version; - inline bool IsInstalledNew() const { - return this->record.type == 0x03; + template + inline constexpr bool HasViewFlag() const { + return (view.flags & static_cast(Flag)) != 0; } - inline bool IsInstalled() const { - return this->record.type == 0x10; + inline bool NeedsUpdate() const { + return this->launch_required_version > this->version; } - - inline bool IsRunning() const { - return this->record.type == 0x0; // Not really an ideal state, might be after a uLaunch crash, but whatever - } - - inline bool IsLaunchable() const { - return this->IsInstalled() || this->IsInstalledNew() || this->IsRunning(); - } - - /* TODO (new) - inline bool IsGamecard() const { - return this->meta_status.storageID == NcmStorageId_GameCard; - } - */ }; struct EntryHomebrewInfo { @@ -97,7 +88,8 @@ namespace ul::menu { || this->Is() || this->Is() || this->Is() - || this->Is(); + || this->Is() + || this->Is(); } inline bool operator<(const Entry &other) const { @@ -109,7 +101,7 @@ namespace ul::menu { } void TryLoadControlData(); - void ReloadApplicationInfo(); + void ReloadApplicationInfo(const bool force_reload_records_views = true); void MoveTo(const std::string &new_folder_path); bool MoveToIndex(const u32 new_index); @@ -128,13 +120,17 @@ namespace ul::menu { std::vector Remove(); }; - void InitializeEntries(); - void EnsureApplicationEntry(const NsApplicationRecord &app_record); + void SetLoadApplicationEntryVersions(const bool load); + void InitializeEntries(); std::vector LoadEntries(const std::string &path); - Entry CreateFolderEntry(const std::string &base_path, const std::string &folder_name, const u32 index); - Entry CreateHomebrewEntry(const std::string &base_path, const std::string &nro_path, const std::string &nro_argv, const u32 index); - void DeleteApplicationEntry(const u64 app_id, const std::string &path); + void EnsureApplicationEntry(const NsApplicationRecord &app_record); + Entry CreateFolderEntry(const std::string &base_path, const std::string &folder_name, const s32 index = -1); + Entry CreateHomebrewEntry(const std::string &base_path, const std::string &nro_path, const std::string &nro_argv, const s32 index = -1); + Entry CreateSpecialEntry(const std::string &base_path, const EntryType type, const s32 index = -1); + void DeleteApplicationEntryRecursively(const u64 app_id, const std::string &path); + + void ReloadApplicationEntryInfos(std::vector &entries); } diff --git a/libs/uCommon/include/ul/os/os_Applications.hpp b/libs/uCommon/include/ul/os/os_Applications.hpp index 07dd1c0..4b49846 100644 --- a/libs/uCommon/include/ul/os/os_Applications.hpp +++ b/libs/uCommon/include/ul/os/os_Applications.hpp @@ -5,9 +5,15 @@ namespace ul::os { - constexpr u32 MaxApplicationCount = 64000; + enum class ApplicationViewFlag : u32 { + Valid = BIT(0), + GameCardApplication = BIT(6), + GameCardApplicationAccessible = BIT(7), + Launchable = BIT(8), + NeedsVerify = BIT(13) + }; std::vector ListApplicationRecords(); - Result GetApplicationContentMetaStatus(const u64 app_id, NsApplicationContentMetaStatus &out_status); + std::vector ListApplicationViews(const std::vector &base_records); } diff --git a/libs/uCommon/include/ul/smi/smi_Protocol.hpp b/libs/uCommon/include/ul/smi/smi_Protocol.hpp index 483cf9c..b8f69ed 100644 --- a/libs/uCommon/include/ul/smi/smi_Protocol.hpp +++ b/libs/uCommon/include/ul/smi/smi_Protocol.hpp @@ -8,7 +8,7 @@ namespace ul::smi { enum class MenuStartMode : u32 { Invalid, - StartupMenu, + Start, MainMenu, MainMenuApplicationSuspended, SettingsMenu @@ -20,7 +20,11 @@ namespace ul::smi { SdCardEjected, GameCardMountFailure, PreviousLaunchFailure, - ChosenHomebrew + ChosenHomebrew, + FinishedSleep, + ApplicationRecordsChanged, + ApplicationVerifyProgress, + ApplicationVerifyResult }; struct MenuMessageContext { @@ -33,6 +37,19 @@ namespace ul::smi { struct { char nro_path[FS_MAX_PATH]; } chosen_hb; + struct { + bool records_added_or_deleted; + } app_records_changed; + struct { + u64 app_id; + u64 done; + u64 total; + } app_verify_progress; + struct { + u64 app_id; + Result rc; + Result detail_rc; + } app_verify_rc; }; }; @@ -55,7 +72,11 @@ namespace ul::smi { OpenMiiEdit, OpenAddUser, OpenNetConnect, - ReloadThemeCache + ListAddedApplications, + ListDeletedApplications, + OpenCabinet, + StartVerifyApplication, + ListInVerifyApplications }; struct SystemStatus { @@ -66,6 +87,9 @@ namespace ul::smi { char last_menu_path[FS_MAX_PATH]; u32 last_menu_index; bool reload_theme_cache; + u32 last_added_app_count; + u32 last_deleted_app_count; + u32 in_verify_app_count; }; using CommandFunction = Result(*)(void*, const size_t, const bool); diff --git a/libs/uCommon/include/ul/system/system_Message.hpp b/libs/uCommon/include/ul/system/system_Message.hpp index e12c7cf..914973a 100644 --- a/libs/uCommon/include/ul/system/system_Message.hpp +++ b/libs/uCommon/include/ul/system/system_Message.hpp @@ -48,7 +48,7 @@ namespace ul::system { Unk_OverlayBrightValueChanged = 13, Unk_OverlayAutoBrightnessChanged = 14, Unk_OverlayAirplaneModeChanged = 15, - Unk_HomeButtonHold = 16, + Unk_OverlayShown = 16, Unk_OverlayHidden = 17, RequestToLaunchApplication = 32, RequestJumpToStory = 33 diff --git a/libs/uCommon/include/ul/ul_Result.hpp b/libs/uCommon/include/ul/ul_Result.hpp index 611e37d..2f2a9e9 100644 --- a/libs/uCommon/include/ul/ul_Result.hpp +++ b/libs/uCommon/include/ul/ul_Result.hpp @@ -64,10 +64,10 @@ namespace ul { const char *mod_name; \ const char *rc_name; \ if(rc::GetResultNameAny(_tmp_rc, mod_name, rc_name)) { \ - ::ul::OnAssertionFailed(_tmp_rc, log " asserted %04d-%04d/0x%X/%s::%s...\n", R_MODULE(_tmp_rc) + 2000, R_DESCRIPTION(_tmp_rc), R_VALUE(_tmp_rc), mod_name, rc_name); \ + ::ul::OnAssertionFailed(_tmp_rc, log " asserted %04d-%04d/0x%X/%s::%s...", R_MODULE(_tmp_rc) + 2000, R_DESCRIPTION(_tmp_rc), R_VALUE(_tmp_rc), mod_name, rc_name); \ } \ else { \ - ::ul::OnAssertionFailed(_tmp_rc, log " asserted %04d-%04d/0x%X...\n", R_MODULE(_tmp_rc) + 2000, R_DESCRIPTION(_tmp_rc), R_VALUE(_tmp_rc)); \ + ::ul::OnAssertionFailed(_tmp_rc, log " asserted %04d-%04d/0x%X...", R_MODULE(_tmp_rc) + 2000, R_DESCRIPTION(_tmp_rc), R_VALUE(_tmp_rc)); \ } \ } \ }) @@ -77,7 +77,7 @@ namespace ul { #define UL_ASSERT_TRUE(expr) ({ \ const auto _tmp_expr = (expr); \ if(!_tmp_expr) { \ - ::ul::OnAssertionFailed(::rc::ulaunch::ResultAssertionFailed, #expr " asserted to be false...\n"); \ + ::ul::OnAssertionFailed(::rc::ulaunch::ResultAssertionFailed, #expr " asserted to be false..."); \ } \ }) diff --git a/libs/uCommon/include/ul/ul_Results.rc.hpp b/libs/uCommon/include/ul/ul_Results.rc.hpp index abcfd65..55bbfd5 100644 --- a/libs/uCommon/include/ul/ul_Results.rc.hpp +++ b/libs/uCommon/include/ul/ul_Results.rc.hpp @@ -28,6 +28,7 @@ namespace ulaunch { R_DEFINE_ERROR_RESULT(AlreadyQueued, 403); R_DEFINE_ERROR_RESULT(ApplicationNotActive, 404); R_DEFINE_ERROR_RESULT(NoHomebrewTakeoverApplication, 405); + R_DEFINE_ERROR_RESULT(InvalidApplicationListCount, 406); R_DEFINE_ERROR_RANGE(Util, 501, 599); R_DEFINE_ERROR_RESULT(InvalidJson, 501); diff --git a/libs/uCommon/source/ul/menu/menu_Cache.cpp b/libs/uCommon/source/ul/menu/menu_Cache.cpp index 4f699c4..1a8fce1 100644 --- a/libs/uCommon/source/ul/menu/menu_Cache.cpp +++ b/libs/uCommon/source/ul/menu/menu_Cache.cpp @@ -1,4 +1,5 @@ #include
            +#include
              namespace ul::menu { @@ -78,11 +79,17 @@ namespace ul::menu { }); } - void CacheApplicationEntry(const u64 app_id, NsApplicationControlData *tmp_control_data) { + bool CacheApplicationEntry(const u64 app_id, NsApplicationControlData *tmp_control_data) { const auto cache_icon_path = GetApplicationCacheIconPath(app_id); fs::DeleteFile(cache_icon_path); - if(R_SUCCEEDED(nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, tmp_control_data, sizeof(NsApplicationControlData), nullptr))) { + const auto rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, tmp_control_data, sizeof(NsApplicationControlData), nullptr); + if(R_SUCCEEDED(rc)) { fs::WriteFile(cache_icon_path, tmp_control_data->icon, sizeof(tmp_control_data->icon), true); + return true; + } + else { + UL_LOG_WARN("Application cache failed: %s", util::FormatResultDisplay(rc).c_str()); + return false; } } @@ -107,10 +114,11 @@ namespace ul::menu { CacheApplicationEntries(records); } - void CacheSingleApplication(const u64 app_id) { + bool CacheSingleApplication(const u64 app_id) { auto tmp_control_data = new NsApplicationControlData(); - CacheApplicationEntry(app_id, tmp_control_data); + const auto ok = CacheApplicationEntry(app_id, tmp_control_data); delete tmp_control_data; + return ok; } std::string GetHomebrewCacheIconPath(const std::string &nro_path) { diff --git a/libs/uCommon/source/ul/menu/menu_Entries.cpp b/libs/uCommon/source/ul/menu/menu_Entries.cpp index 689f1f3..6cf7dcf 100644 --- a/libs/uCommon/source/ul/menu/menu_Entries.cpp +++ b/libs/uCommon/source/ul/menu/menu_Entries.cpp @@ -10,12 +10,19 @@ namespace ul::menu { namespace { + bool g_LoadApplicationEntryVersions = true; + + inline u64 GetUpdateApplicationId(const u64 app_id) { + return (app_id & ~0x800) | 0x800; + } + std::vector g_ApplicationRecords = {}; + std::vector g_ApplicationViews = {}; void LoadControlDataStrings(EntryControlData &out_control, NacpStruct *nacp) { NacpLanguageEntry *lang_entry = nullptr; - nacpGetLanguageEntry(nacp, &lang_entry); - if(lang_entry == nullptr) { + const auto rc = nsGetApplicationDesiredLanguage(nacp, &lang_entry); + if(R_FAILED(rc) || (lang_entry == nullptr)) { for(u32 i = 0; i < 16; i++) { lang_entry = &nacp->lang[i]; if((lang_entry->name[0] > 0) && (lang_entry->author[0] > 0)) { @@ -24,6 +31,7 @@ namespace ul::menu { lang_entry = nullptr; } } + UL_ASSERT_TRUE(lang_entry != nullptr); if(lang_entry != nullptr) { if(!out_control.custom_name) { @@ -54,9 +62,10 @@ namespace ul::menu { } } - inline void EnsureApplicationRecords(const bool reload = false) { + inline void EnsureApplicationRecordsAndViews(const bool reload = false) { if(reload || g_ApplicationRecords.empty()) { g_ApplicationRecords = os::ListApplicationRecords(); + g_ApplicationViews = os::ListApplicationViews(g_ApplicationRecords); } } @@ -160,6 +169,7 @@ namespace ul::menu { _UL_MENU_ADD_SPECIAL_ENTRY(EntryType::SpecialEntryThemes); _UL_MENU_ADD_SPECIAL_ENTRY(EntryType::SpecialEntryControllers); _UL_MENU_ADD_SPECIAL_ENTRY(EntryType::SpecialEntryAlbum); + _UL_MENU_ADD_SPECIAL_ENTRY(EntryType::SpecialEntryAmiibo); // Add remaining app entries for(const auto &app_record : remaining_apps) { @@ -183,7 +193,7 @@ namespace ul::menu { fs::CreateDirectory(MenuPath); } - EnsureApplicationRecords(); + EnsureApplicationRecordsAndViews(); auto apps_copy = g_ApplicationRecords; UL_FS_FOR(OldMenuPath, old_entry_name, old_entry_path, is_dir, is_file, { @@ -231,12 +241,40 @@ namespace ul::menu { if(find_rec != apps_copy.end()) { entry.app_info.record = *find_rec; apps_copy.erase(find_rec); + entry.Save(); + } + else { + UL_LOG_WARN("Found old menu application that could not be matched to an existing application record..."); + } + const auto find_view = std::find_if(g_ApplicationViews.begin(), g_ApplicationViews.end(), [&](const NsApplicationView &view) -> bool { + return view.application_id == application_id; + }); + if(find_view != g_ApplicationViews.end()) { + entry.app_info.view = *find_view; entry.Save(); } else { - UL_LOG_WARN("Found old menu application entry whose application is not present..."); + UL_LOG_WARN("Found old menu application that could not be matched to an existing application view..."); } + + if(g_LoadApplicationEntryVersions) { + auto rc = avmGetHighestAvailableVersion(application_id, GetUpdateApplicationId(application_id), &entry.app_info.version); + if(R_FAILED(rc)) { + entry.app_info.version = 0; + UL_LOG_WARN("Found old menu application whose version could not be retrieved..."); + } + rc = avmGetLaunchRequiredVersion(application_id, &entry.app_info.launch_required_version); + if(R_FAILED(rc)) { + entry.app_info.launch_required_version = 0; + UL_LOG_WARN("Found old menu application whose launch required version could not be retrieved..."); + } + } + else { + entry.app_info.version = 0; + entry.app_info.launch_required_version = 0; + } + break; } case EntryType::Homebrew: { @@ -273,7 +311,7 @@ namespace ul::menu { if(!this->control.IsLoaded()) { switch(this->type) { case EntryType::Application: { - LoadApplicationControlData(this->app_info.record.application_id, this->control); + LoadApplicationControlData(this->app_info.app_id, this->control); break; } case EntryType::Homebrew: { @@ -287,16 +325,12 @@ namespace ul::menu { } } - void Entry::ReloadApplicationInfo() { + void Entry::ReloadApplicationInfo(const bool force_reload_records_views) { if(this->Is()) { - const auto app_id = this->app_info.record.application_id; - - if(R_FAILED(os::GetApplicationContentMetaStatus(app_id, this->app_info.meta_status))) { - UL_LOG_WARN("Unable to reload application meta status (not found...?)"); - } + const auto app_id = this->app_info.app_id; // Assume we need to reload here - EnsureApplicationRecords(true); + EnsureApplicationRecordsAndViews(force_reload_records_views); const auto find_rec = std::find_if(g_ApplicationRecords.begin(), g_ApplicationRecords.end(), [&](const NsApplicationRecord &rec) -> bool { return rec.application_id == app_id; @@ -305,7 +339,17 @@ namespace ul::menu { this->app_info.record = *find_rec; } else { - UL_LOG_WARN("Unable to reload application record (not found...?)"); + UL_LOG_WARN("Unable to reload application record: not found?"); + } + + const auto find_view = std::find_if(g_ApplicationViews.begin(), g_ApplicationViews.end(), [&](const NsApplicationView &view) -> bool { + return view.application_id == app_id; + }); + if(find_view != g_ApplicationViews.end()) { + this->app_info.view = *find_view; + } + else { + UL_LOG_WARN("Unable to reload application view: not found?"); } } } @@ -373,7 +417,7 @@ namespace ul::menu { switch(this->type) { case EntryType::Application: { - entry_json["application_id"] = this->app_info.record.application_id; + entry_json["application_id"] = this->app_info.app_id; break; } case EntryType::Homebrew: { @@ -413,10 +457,14 @@ namespace ul::menu { return {}; } + void SetLoadApplicationEntryVersions(const bool load) { + g_LoadApplicationEntryVersions = load; + } + void InitializeEntries() { u32 entry_idx = 0; - EnsureApplicationRecords(); + EnsureApplicationRecordsAndViews(); ConvertOldMenu(entry_idx); @@ -427,22 +475,9 @@ namespace ul::menu { } } - void EnsureApplicationEntry(const NsApplicationRecord &app_record) { - // Just fill enough fields needed to save the path - const auto entry_idx = FindNextEntryIndex(MenuPath); - Entry app_entry = { - .type = EntryType::Application, - .entry_path = MakeEntryPath(MenuPath, entry_idx), - - .app_info = { - .app_id = app_record.application_id, - .record = app_record - } - }; - app_entry.Save(); - } - std::vector LoadEntries(const std::string &path) { + EnsureApplicationRecordsAndViews(); + std::vector entries; UL_FS_FOR(path, entry_name, entry_path, is_dir, is_file, { if(is_file && util::StringEndsWith(entry_name, ".m.json")) { @@ -451,10 +486,10 @@ namespace ul::menu { const auto type = static_cast(entry_json.value("type", static_cast(EntryType::Invalid))); const auto entry_index_str = entry_name.substr(0, entry_name.length() - __builtin_strlen(".m.json")); const u32 entry_index = std::strtoul(entry_index_str.c_str(), nullptr, 10); - const auto custom_name = entry_json.value("custom_name", ""); - const auto custom_author = entry_json.value("custom_author", ""); - const auto custom_version = entry_json.value("custom_version", ""); - const auto custom_icon_path = entry_json.value("custom_icon_path", ""); + const auto custom_name_str = entry_json.value("custom_name", ""); + const auto custom_author_str = entry_json.value("custom_author", ""); + const auto custom_version_str = entry_json.value("custom_version", ""); + const auto custom_icon_path_str = entry_json.value("custom_icon_path", ""); Entry entry = { .type = type, @@ -462,14 +497,14 @@ namespace ul::menu { .index = entry_index, .control = { - .name = custom_name, - .custom_name = !custom_name.empty(), - .author = custom_author, - .custom_author = !custom_name.empty(), - .version = custom_version, - .custom_version = !custom_name.empty(), - .icon_path = custom_icon_path, - .custom_icon_path = !custom_name.empty(), + .name = custom_name_str, + .custom_name = !custom_name_str.empty(), + .author = custom_author_str, + .custom_author = !custom_author_str.empty(), + .version = custom_version_str, + .custom_version = !custom_version_str.empty(), + .icon_path = custom_icon_path_str, + .custom_icon_path = !custom_icon_path_str.empty(), } }; @@ -488,9 +523,6 @@ namespace ul::menu { entry.app_info = { .app_id = application_id }; - if(R_FAILED(os::GetApplicationContentMetaStatus(application_id, entry.app_info.meta_status))) { - UL_LOG_WARN("Invalid application entry with no application meta status"); - } const auto find_rec = std::find_if(g_ApplicationRecords.begin(), g_ApplicationRecords.end(), [&](const NsApplicationRecord &rec) -> bool { return rec.application_id == application_id; @@ -499,7 +531,34 @@ namespace ul::menu { entry.app_info.record = *find_rec; } else { - UL_LOG_WARN("Invalid application entry with no application record"); + UL_LOG_WARN("Potentially invalid application entry: unable to match to application record"); + } + + const auto find_view = std::find_if(g_ApplicationViews.begin(), g_ApplicationViews.end(), [&](const NsApplicationView &view) -> bool { + return view.application_id == application_id; + }); + if(find_view != g_ApplicationViews.end()) { + entry.app_info.view = *find_view; + } + else { + UL_LOG_WARN("Potentially invalid application entry: unable to match to application view"); + } + + if(g_LoadApplicationEntryVersions) { + auto rc = avmGetHighestAvailableVersion(application_id, GetUpdateApplicationId(application_id), &entry.app_info.version); + if(R_FAILED(rc)) { + entry.app_info.version = 0; + UL_LOG_WARN("Potentially invalid application entry: unable to retrieve version"); + } + rc = avmGetLaunchRequiredVersion(application_id, &entry.app_info.launch_required_version); + if(R_FAILED(rc)) { + entry.app_info.launch_required_version = 0; + UL_LOG_WARN("Potentially invalid application entry: unable to retrieve launch required version"); + } + } + else { + entry.app_info.version = 0; + entry.app_info.launch_required_version = 0; } entries.push_back(entry); @@ -550,6 +609,7 @@ namespace ul::menu { case EntryType::SpecialEntryThemes: case EntryType::SpecialEntryControllers: case EntryType::SpecialEntryAlbum: + case EntryType::SpecialEntryAmiibo: entries.push_back(entry); break; default: @@ -562,14 +622,37 @@ namespace ul::menu { return entries; } - Entry CreateFolderEntry(const std::string &base_path, const std::string &folder_name, const u32 index) { + void EnsureApplicationEntry(const NsApplicationRecord &app_record) { + // Just fill enough fields needed to save the path + const auto entry_idx = FindNextEntryIndex(MenuPath); + Entry app_entry = { + .type = EntryType::Application, + .entry_path = MakeEntryPath(MenuPath, entry_idx), + + .app_info = { + .app_id = app_record.application_id, + .record = app_record + } + }; + app_entry.Save(); + } + + Entry CreateFolderEntry(const std::string &base_path, const std::string &folder_name, const s32 index) { + u32 actual_index; + if(index < 0) { + actual_index = FindNextEntryIndex(base_path); + } + else { + actual_index = (u32)index; + } + const auto folder_path = MakeNextFolderPath(base_path, folder_name); fs::CreateDirectory(folder_path); Entry folder_entry = { .type = EntryType::Folder, - .entry_path = MakeEntryPath(base_path, index), - .index = index, + .entry_path = MakeEntryPath(base_path, actual_index), + .index = actual_index, .control = { .custom_name = false, @@ -587,11 +670,19 @@ namespace ul::menu { return folder_entry; } - Entry CreateHomebrewEntry(const std::string &base_path, const std::string &nro_path, const std::string &nro_argv, const u32 index) { + Entry CreateHomebrewEntry(const std::string &base_path, const std::string &nro_path, const std::string &nro_argv, const s32 index) { + u32 actual_index; + if(index < 0) { + actual_index = FindNextEntryIndex(base_path); + } + else { + actual_index = (u32)index; + } + Entry hb_entry = { .type = EntryType::Homebrew, - .entry_path = MakeEntryPath(base_path, index), - .index = index, + .entry_path = MakeEntryPath(base_path, actual_index), + .index = actual_index, .control = { .custom_name = false, @@ -617,7 +708,26 @@ namespace ul::menu { return hb_entry; } - void DeleteApplicationEntry(const u64 app_id, const std::string &path) { + Entry CreateSpecialEntry(const std::string &base_path, const EntryType type, const s32 index) { + u32 actual_index; + if(index < 0) { + actual_index = FindNextEntryIndex(base_path); + } + else { + actual_index = (u32)index; + } + + Entry special_entry = { + .type = type, + .entry_path = MakeEntryPath(base_path, actual_index), + .index = actual_index + }; + + special_entry.Save(); + return special_entry; + } + + void DeleteApplicationEntryRecursively(const u64 app_id, const std::string &path) { auto cur_entries = LoadEntries(path); for(auto &entry: cur_entries) { if(entry.Is() && (entry.app_info.app_id == app_id)) { @@ -625,9 +735,18 @@ namespace ul::menu { } else if(entry.Is()) { const auto new_path = fs::JoinPath(path, entry.folder_info.fs_name); - DeleteApplicationEntry(app_id, new_path); + DeleteApplicationEntryRecursively(app_id, new_path); } } } + void ReloadApplicationEntryInfos(std::vector &entries) { + // Helper to only reload records/views once + EnsureApplicationRecordsAndViews(true); + + for(auto &entry: entries) { + entry.ReloadApplicationInfo(false); + } + } + } diff --git a/libs/uCommon/source/ul/os/os_Applications.cpp b/libs/uCommon/source/ul/os/os_Applications.cpp index daf2cca..fb621f5 100644 --- a/libs/uCommon/source/ul/os/os_Applications.cpp +++ b/libs/uCommon/source/ul/os/os_Applications.cpp @@ -35,10 +35,21 @@ namespace ul::os { return records; } - Result GetApplicationContentMetaStatus(const u64 app_id, NsApplicationContentMetaStatus &out_status) { - s32 tmp_count; - UL_RC_TRY(nsListApplicationContentMetaStatus(app_id, 0, std::addressof(out_status), 1, &tmp_count)); - return ResultSuccess; + std::vector ListApplicationViews(const std::vector &base_records) { + const auto count = base_records.size(); + auto app_id_buf = new u64[count](); + for(u32 i = 0; i < count; i++) { + app_id_buf[i] = base_records.at(i).application_id; + } + + auto app_view_buf = new NsApplicationView[count](); + UL_RC_ASSERT(nsGetApplicationView(app_view_buf, app_id_buf, count)); + + std::vector views(app_view_buf, app_view_buf + count); + + delete[] app_view_buf; + delete[] app_id_buf; + return views; } } diff --git a/projects/uDesigner/source/main.cpp b/projects/uDesigner/source/main.cpp index f3d36b5..b9f5e00 100644 --- a/projects/uDesigner/source/main.cpp +++ b/projects/uDesigner/source/main.cpp @@ -455,7 +455,8 @@ struct Element { } } - inline void SavePositionAndAlignmentSettings(nlohmann::json &el_json) { + inline void SavePositionAndAlignmentSettings() { + auto &el_json = this->GetSettingsReference(); el_json["x"] = this->x; el_json["y"] = this->y; @@ -719,8 +720,13 @@ struct Element { return true; } + inline nlohmann::json &GetSettingsReference() { + return g_UiSettings[ul::design::MenuSettingsNames[static_cast(this->menu)]][this->ui_name]; + } + inline bool SaveAsText(zip_t *theme_zip) { - auto text_json = g_UiSettings[ul::design::MenuSettingsNames[static_cast(this->menu)]][this->ui_name]; + // All texts must have UI settings + auto &text_json = this->GetSettingsReference(); switch(static_cast(this->el_text.font_size)) { case ul::design::FontSize::Small: @@ -747,7 +753,7 @@ struct Element { inline bool Save(zip_t *theme_zip) { if(!this->ui_name.empty() && (this->menu != ul::design::MenuType::None)) { - this->SavePositionAndAlignmentSettings(g_UiSettings[ul::design::MenuSettingsNames[static_cast(this->menu)]][this->ui_name]); + this->SavePositionAndAlignmentSettings(); } switch(this->type) { diff --git a/projects/uLoader/source/ul/loader/loader_ProgramIdUtils.cpp b/projects/uLoader/source/ul/loader/loader_ProgramIdUtils.cpp index ddafd83..34cf870 100644 --- a/projects/uLoader/source/ul/loader/loader_ProgramIdUtils.cpp +++ b/projects/uLoader/source/ul/loader/loader_ProgramIdUtils.cpp @@ -11,7 +11,7 @@ namespace ul::loader { void DetermineSelfAppletType(const u64 self_program_id) { __nx_applet_type = GetAppletType(self_program_id); - UL_LOG_INFO("Program ID: 0x%016X --> applet type: %d", self_program_id, __nx_applet_type); + UL_LOG_INFO("Program ID: 0x%016lX --> applet type: %d", self_program_id, __nx_applet_type); } AppletType GetSelfAppletType() { diff --git a/projects/uManager/source/main.cpp b/projects/uManager/source/main.cpp index 8b82594..69da302 100644 --- a/projects/uManager/source/main.cpp +++ b/projects/uManager/source/main.cpp @@ -30,10 +30,17 @@ int main() { Initialize(); auto renderer_opts = pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags); + renderer_opts.UseImage(pu::ui::render::IMGAllFlags); renderer_opts.AddDefaultAllSharedFonts(); renderer_opts.AddExtraDefaultFontSize(35); renderer_opts.UseRomfs(); + + renderer_opts.SetInputPlayerCount(1); + renderer_opts.AddInputNpadStyleTag(HidNpadStyleSet_NpadStandard); + renderer_opts.AddInputNpadIdType(HidNpadIdType_Handheld); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No1); + auto renderer = pu::ui::render::Renderer::New(renderer_opts); g_MainApplication = ul::man::ui::MainApplication::New(renderer); diff --git a/projects/uMenu/include/ul/menu/smi/smi_Commands.hpp b/projects/uMenu/include/ul/menu/smi/smi_Commands.hpp index e357113..ab58062 100644 --- a/projects/uMenu/include/ul/menu/smi/smi_Commands.hpp +++ b/projects/uMenu/include/ul/menu/smi/smi_Commands.hpp @@ -223,4 +223,69 @@ namespace ul::menu::smi { ); } + inline Result ListAddedApplications(const u32 count, u64 *out_app_buf) { + return SendCommand(SystemMessage::ListAddedApplications, + [&](ScopedStorageWriter &writer) { + writer.Push(count); + return ResultSuccess; + }, + [&](ScopedStorageReader &reader) { + reader.PopData(out_app_buf, sizeof(u64) * count); + return ResultSuccess; + } + ); + } + + inline Result ListDeletedApplications(const u32 count, u64 *out_app_buf) { + return SendCommand(SystemMessage::ListDeletedApplications, + [&](ScopedStorageWriter &writer) { + writer.Push(count); + return ResultSuccess; + }, + [&](ScopedStorageReader &reader) { + reader.PopData(out_app_buf, sizeof(u64) * count); + return ResultSuccess; + } + ); + } + + inline Result OpenCabinet(const NfpLaStartParamTypeForAmiiboSettings type) { + return SendCommand(SystemMessage::OpenCabinet, + [&](ScopedStorageWriter &writer) { + writer.Push((u8)type); + return ResultSuccess; + }, + [](ScopedStorageReader &reader) { + // ... + return ResultSuccess; + } + ); + } + + inline Result StartVerifyApplication(const u64 app_id) { + return SendCommand(SystemMessage::StartVerifyApplication, + [&](ScopedStorageWriter &writer) { + writer.Push(app_id); + return ResultSuccess; + }, + [](ScopedStorageReader &reader) { + // ... + return ResultSuccess; + } + ); + } + + inline Result ListInVerifyApplications(const u32 count, u64 *out_app_buf) { + return SendCommand(SystemMessage::ListInVerifyApplications, + [&](ScopedStorageWriter &writer) { + writer.Push(count); + return ResultSuccess; + }, + [&](ScopedStorageReader &reader) { + reader.PopData(out_app_buf, sizeof(u64) * count); + return ResultSuccess; + } + ); + } + } diff --git a/projects/uMenu/include/ul/menu/smi/smi_MenuMessageHandler.hpp b/projects/uMenu/include/ul/menu/smi/smi_MenuMessageHandler.hpp index 4b16776..fac655d 100644 --- a/projects/uMenu/include/ul/menu/smi/smi_MenuMessageHandler.hpp +++ b/projects/uMenu/include/ul/menu/smi/smi_MenuMessageHandler.hpp @@ -7,7 +7,7 @@ namespace ul::menu::smi { using namespace ul::smi; - using OnMessageCallback = std::function; + using OnMessageCallback = std::function; Result InitializeMenuMessageHandler(); void FinalizeMenuMessageHandler(); diff --git a/projects/uMenu/include/ul/menu/ui/ui_Common.hpp b/projects/uMenu/include/ul/menu/ui/ui_Common.hpp index 9c98319..1f4e2db 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_Common.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_Common.hpp @@ -1,7 +1,10 @@ #pragma once #include
                +#include
                  +#include
                    #include +#include
                      namespace ul::menu::ui { @@ -24,7 +27,103 @@ namespace ul::menu::ui { void LoadSelectedUserIconTexture(); pu::sdl2::TextureHandle::Ref GetSelectedUserIconTexture(); - void SaveConfig(); + struct GlobalSettings { + SetSysFirmwareVersion fw_version; + SetSysSerialNumber serial_no; + SetSysSleepSettings sleep_settings; + SetRegion region; + SetSysPrimaryAlbumStorage album_storage; + bool nfc_enabled; + bool usb30_enabled; + bool bluetooth_enabled; + bool wireless_lan_enabled; + bool auto_update_enabled; + bool auto_app_download_enabled; + bool console_info_upload_enabled; + u64 available_language_codes[os::LanguageNameCount]; + SetLanguage language; + SetSysDeviceNickName nickname; + TimeLocationName timezone; + SetBatteryLot battery_lot; + + ul::Version ams_version; + bool ams_is_emummc; + smi::SystemStatus system_status; + std::string initial_last_menu_fs_path; + u32 initial_last_menu_index; + ul::cfg::Config config; + ul::cfg::Theme active_theme; + ul::util::JSON default_lang; + ul::util::JSON main_lang; + u64 cache_hb_takeover_app_id; + + std::vector added_app_ids; + std::vector deleted_app_ids; + std::vector in_verify_app_ids; + + inline bool IsTitleSuspended() { + return this->system_status.suspended_app_id != 0; + } + + inline bool IsHomebrewSuspended() { + return strlen(this->system_status.suspended_hb_target_ipt.nro_path) > 0; + } + + inline bool IsSuspended() { + return this->IsTitleSuspended() || this->IsHomebrewSuspended(); + } + + inline void ResetSuspendedApplication() { + // Blanking the whole status would also blank the selected user, thus we only blank the relevant params + this->system_status.suspended_app_id = {}; + this->system_status.suspended_hb_target_ipt = {}; + } + + inline void UpdateMenuIndex(const u32 idx) { + UL_RC_ASSERT(smi::UpdateMenuIndex(idx)); + this->system_status.last_menu_index = idx; + } + + inline bool IsEntrySuspended(const Entry &entry) { + if(entry.Is()) { + return entry.app_info.record.application_id == this->system_status.suspended_app_id; + } + else if(entry.Is()) { + // Enough to compare the NRO path + return memcmp(this->system_status.suspended_hb_target_ipt.nro_path, entry.hb_info.nro_target.nro_path, sizeof(entry.hb_info.nro_target.nro_path)) == 0; + } + + return false; + } + + inline void SaveConfig() { + cfg::SaveConfig(this->config); + UL_RC_ASSERT(smi::ReloadConfig()); + } + + inline void SetHomebrewTakeoverApplicationId(const u64 app_id) { + this->cache_hb_takeover_app_id = app_id; + + UL_ASSERT_TRUE(this->config.SetEntry(cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, this->cache_hb_takeover_app_id)); + this->SaveConfig(); + } + + inline void SetSelectedUser(const AccountUid user_id) { + this->system_status.selected_user = user_id; + UL_RC_ASSERT(ul::menu::smi::SetSelectedUser(user_id)); + LoadSelectedUserIconTexture(); + } + + inline void SetActiveTheme(const ul::cfg::Theme &theme) { + this->active_theme = theme; + UL_ASSERT_TRUE(this->config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, this->active_theme.name)); + this->SaveConfig(); + } + + inline void UpdateSleepSettings() { + UL_RC_ASSERT(setsysSetSleepSettings(&this->sleep_settings)); + } + }; void RebootSystem(); void ShutdownSystem(); @@ -40,6 +139,7 @@ namespace ul::menu::ui { void ShowAlbum(); void ShowMiiEdit(); void ShowNetConnect(); + void ShowCabinet(); void ShowPowerDialog(); diff --git a/projects/uMenu/include/ul/menu/ui/ui_EntryMenu.hpp b/projects/uMenu/include/ul/menu/ui/ui_EntryMenu.hpp index bb36c03..de2f3bc 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_EntryMenu.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_EntryMenu.hpp @@ -24,6 +24,10 @@ namespace ul::menu::ui { static constexpr u32 CursorTransitionIncrementSteps = 6; static constexpr u32 DefaultEntrySize = 256; static constexpr u32 MinScalableEntrySize = DefaultEntrySize * 1.5; // Equivalent to keeping 256x256 in 1280x720 resolution (otherwise they are be too small in 1080p) + static constexpr u32 DefaultOverTextSideMargin = 15; + static constexpr u32 DefaultProgressBarHeight = 25; + static constexpr pu::ui::Color ProgressBackgroundColor = { 0, 0, 0, 215 }; + static constexpr pu::ui::Color ProgressForegroundColor = { 0, 210, 210, 215 }; static constexpr u8 MoveOverIconAlpha = 220; static constexpr u32 DefaultMoveOverIconOffset = 8; static constexpr u32 EntryShowIncrementSteps = 4; @@ -60,7 +64,17 @@ namespace ul::menu::ui { u32 selected_size; pu::sdl2::Texture hb_takeover_app_over_icon; u32 hb_takeover_app_size; + pu::sdl2::Texture gamecard_over_icon; + u32 gamecard_size; + pu::sdl2::Texture corrupted_over_icon; + u32 corrupted_size; + pu::sdl2::Texture not_launchable_over_icon; + u32 not_launchable_size; + pu::sdl2::Texture needs_update_over_icon; + u32 needs_update_size; std::vector entry_icons; + std::vector entry_over_texts; + std::vector entry_progresses; std::vector load_img_entry_alphas; std::vector> load_img_entry_incrs; std::string cur_path; @@ -93,6 +107,10 @@ namespace ul::menu::ui { return this->swipe_mode == SwipeMode::None; } + inline bool IsNotInLargeSwipe() { + return (this->swipe_mode != SwipeMode::LeftPage) && (this->swipe_mode != SwipeMode::RightPage) && (this->swipe_mode != SwipeMode::Rewind); + } + void SwipeSingleLeft(); void SwipeSingleRight(); void SwipeToPreviousPage(); @@ -100,6 +118,8 @@ namespace ul::menu::ui { void SwipeRewind(); pu::sdl2::TextureHandle::Ref LoadEntryIconTexture(const Entry &entry); + void CleanOverTexts(); + void LoadOverText(const u32 idx); bool LoadEntry(const u32 idx); void NotifyFocusedEntryChanged(const s32 prev_entry_idx, const s32 is_prev_entry_suspended_override = -1); @@ -123,8 +143,8 @@ namespace ul::menu::ui { this->entry_total_count = ((this->cur_entries.size() / this->entry_page_count) + 1) * this->entry_page_count; #define _COMPUTE_OVER_SIZE(name) { \ - const auto over_def_size = pu::ui::render::GetTextureWidth(this->name ## _over_icon); \ - this->name ## _size = (u32)((double)this->entry_size * ((double)over_def_size / (double)DefaultEntrySize)); \ + const auto over_def_size = pu::ui::render::GetTextureWidth(this->name##_over_icon); \ + this->name##_size = (u32)((double)this->entry_size * ((double)over_def_size / (double)DefaultEntrySize)); \ } _COMPUTE_OVER_SIZE(cursor) @@ -132,6 +152,10 @@ namespace ul::menu::ui { _COMPUTE_OVER_SIZE(suspended) _COMPUTE_OVER_SIZE(selected) _COMPUTE_OVER_SIZE(hb_takeover_app) + _COMPUTE_OVER_SIZE(gamecard) + _COMPUTE_OVER_SIZE(corrupted) + _COMPUTE_OVER_SIZE(not_launchable) + _COMPUTE_OVER_SIZE(needs_update) } void SetFocusedEntryIndex(const u32 idx); @@ -208,6 +232,12 @@ namespace ul::menu::ui { return this->GetEntry(this->cur_entry_idx); } + void UpdateEntryProgress(const u32 idx, const float progress = NAN); + + inline bool IsFocusedEntryInProgress() { + return !std::isnan(this->entry_progresses.at(this->cur_entry_idx)); + } + void IncrementEntryHeightCount(); void DecrementEntryHeightCount(); diff --git a/projects/uMenu/include/ul/menu/ui/ui_IMenuLayout.hpp b/projects/uMenu/include/ul/menu/ui/ui_IMenuLayout.hpp index 71cc674..7ce078b 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_IMenuLayout.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_IMenuLayout.hpp @@ -10,11 +10,22 @@ namespace ul::menu::ui { RecursiveMutex msg_queue_lock; std::queue msg_queue; + bool last_has_connection; + u32 last_connection_strength; + u32 last_battery_level; + bool last_battery_is_charging; + + protected: + void UpdateConnectionTopIcon(pu::ui::elm::Image::Ref &icon); + void UpdateDateText(pu::ui::elm::TextBlock::Ref &text); + void UpdateTimeText(pu::ui::elm::TextBlock::Ref &text); + void UpdateBatteryTextAndTopIcons(pu::ui::elm::TextBlock::Ref &text, pu::ui::elm::Image::Ref &base_top_icon, pu::ui::elm::Image::Ref &charging_top_icon); + public: IMenuLayout(); void OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos); - void NotifyMessageContext(const smi::MenuMessageContext msg_ctx); + void NotifyMessageContext(const smi::MenuMessageContext &msg_ctx); virtual void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) = 0; virtual bool OnHomeButtonPress() = 0; virtual void DisposeAudio() = 0; diff --git a/projects/uMenu/include/ul/menu/ui/ui_LockscreenMenuLayout.hpp b/projects/uMenu/include/ul/menu/ui/ui_LockscreenMenuLayout.hpp new file mode 100644 index 0000000..8a84d17 --- /dev/null +++ b/projects/uMenu/include/ul/menu/ui/ui_LockscreenMenuLayout.hpp @@ -0,0 +1,26 @@ + +#pragma once +#include
                        + +namespace ul::menu::ui { + + class LockscreenMenuLayout : public IMenuLayout { + private: + pu::ui::elm::TextBlock::Ref info_text; + pu::ui::elm::Image::Ref connection_top_icon; + pu::ui::elm::TextBlock::Ref time_text; + pu::ui::elm::TextBlock::Ref date_text; + pu::ui::elm::TextBlock::Ref battery_text; + pu::ui::elm::Image::Ref battery_top_icon; + pu::ui::elm::Image::Ref battery_charging_top_icon; + + public: + LockscreenMenuLayout(); + PU_SMART_CTOR(LockscreenMenuLayout) + + void OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) override; + bool OnHomeButtonPress() override; + void DisposeAudio() override; + }; + +} diff --git a/projects/uMenu/include/ul/menu/ui/ui_MainMenuLayout.hpp b/projects/uMenu/include/ul/menu/ui/ui_MainMenuLayout.hpp index ead68df..0d27c9a 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_MainMenuLayout.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_MainMenuLayout.hpp @@ -16,7 +16,7 @@ namespace ul::menu::ui { public: // TODO (new): config in theme? static constexpr u8 SuspendedScreenAlphaIncrement = 10; - static constexpr s64 MessagesWaitTimeSeconds = 2; + static constexpr s64 MessagesWaitTimeSeconds = 1; static constexpr s64 TimeDotsDisplayChangeWaitTimeSeconds = 1; static constexpr u32 LogoSize = 90; @@ -30,10 +30,6 @@ namespace ul::menu::ui { HidingLostFocus = 5 }; - bool last_has_connection; - u32 last_connection_strength; - u32 last_battery_lvl; - bool last_is_charging; bool last_quick_menu_on; pu::ui::elm::Image::Ref top_menu_default_bg; pu::ui::elm::Image::Ref top_menu_folder_bg; @@ -146,6 +142,7 @@ namespace ul::menu::ui { inline void MoveToRoot(const bool fade, std::function action = nullptr) { this->cur_folder_path = ""; + this->cur_path_text->SetText(this->cur_folder_path); this->MoveTo(MenuPath, fade, action); } @@ -184,7 +181,18 @@ namespace ul::menu::ui { this->mode = SuspendedImageMode::HidingForResume; } - void NotifyLoad(const AccountUid user); + inline void UpdateApplicationVerifyProgress(const u64 app_id, const float progress) { + const auto &cur_entries = this->entry_menu->GetEntries(); + for(u32 i = 0; i < cur_entries.size(); i++) { + const auto &entry = cur_entries.at(i); + if(entry.Is() && (entry.app_info.app_id == app_id)) { + this->entry_menu->UpdateEntryProgress(i, progress); + break; + } + } + } + + void NotifyLoad(); void HandleCloseSuspended(); void HandleHomebrewLaunch(const Entry &entry); void StopSelection(); diff --git a/projects/uMenu/include/ul/menu/ui/ui_MenuApplication.hpp b/projects/uMenu/include/ul/menu/ui/ui_MenuApplication.hpp index daedea3..a52a4a2 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_MenuApplication.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_MenuApplication.hpp @@ -1,9 +1,10 @@ #pragma once -#include
                          #include
                            +#include
                              #include
                                #include
                                  +#include
                                    #include
                                      namespace ul::menu::ui { @@ -14,7 +15,8 @@ namespace ul::menu::ui { Main, Startup, Themes, - Settings + Settings, + Lockscreen }; struct MenuBgmEntry { @@ -24,7 +26,7 @@ namespace ul::menu::ui { pu::audio::Music bgm; }; - void OnMessage(const smi::MenuMessageContext msg_ctx); + void OnMessage(const smi::MenuMessageContext &msg_ctx); class MenuApplication : public pu::ui::Application { public: @@ -33,6 +35,9 @@ namespace ul::menu::ui { static constexpr u32 DefaultBgmFadeOutMs = 500; using MenuFadeCallback = std::function; + static constexpr u8 DefaultFadeAlphaIncrementSteps = 20; + static constexpr u8 FastFadeAlphaIncrementSteps = 12; + private: smi::MenuStartMode start_mode; MainMenuLayout::Ref main_menu_lyt; @@ -43,12 +48,17 @@ namespace ul::menu::ui { MenuBgmEntry themes_menu_bgm; SettingsMenuLayout::Ref settings_menu_lyt; MenuBgmEntry settings_menu_bgm; + LockscreenMenuLayout::Ref lockscreen_menu_lyt; + MenuBgmEntry lockscreen_menu_bgm; pu::ui::extras::Toast::Ref notif_toast; - smi::SystemStatus system_status; bool launch_failed; Result pending_gc_mount_rc; + bool needs_app_records_reload; + bool needs_app_entries_reload; char chosen_hb[FS_MAX_PATH]; - u64 takeover_app_id; + u64 verify_finished_app_id; + Result verify_rc; + Result verify_detail_rc; MenuType loaded_menu; util::JSON ui_json; util::JSON bgm_json; @@ -71,6 +81,8 @@ namespace ul::menu::ui { return this->themes_menu_bgm; case MenuType::Settings: return this->settings_menu_bgm; + case MenuType::Lockscreen: + return this->lockscreen_menu_bgm; } UL_ASSERT_TRUE(false && "Invalid current menu?"); @@ -90,9 +102,8 @@ namespace ul::menu::ui { void OnLoad() override; - inline void Initialize(const smi::MenuStartMode start_mode, const smi::SystemStatus system_status, const util::JSON ui_json) { + inline void Initialize(const smi::MenuStartMode start_mode, const util::JSON ui_json) { this->start_mode = start_mode; - this->system_status = system_status; this->ui_json = ui_json; } @@ -100,7 +111,7 @@ namespace ul::menu::ui { this->StopPlayBgm(); this->ResetFade(); this->DisposeAllAudio(); - this->CloseWithFadeOut(); + this->CloseWithFadeOut(true); } void SetBackgroundFade(); @@ -111,26 +122,8 @@ namespace ul::menu::ui { void LoadMenuByType(const MenuType type, const bool fade = true, MenuFadeCallback fade_cb = nullptr); - inline bool IsTitleSuspended() { - return this->system_status.suspended_app_id != 0; - } - - inline bool IsHomebrewSuspended() { - return strlen(this->system_status.suspended_hb_target_ipt.nro_path) > 0; - } - - inline bool IsSuspended() { - return this->IsTitleSuspended() || this->IsHomebrewSuspended(); - } - - inline smi::SystemStatus &GetStatus() { - return this->system_status; - } - - inline void ResetSuspendedApplication() { - // Blanking the whole status would also blank the selected user, thus we only blank the params - this->system_status.suspended_app_id = {}; - this->system_status.suspended_hb_target_ipt = {}; + inline void NotifyLaunchFailed() { + this->launch_failed = true; } inline bool GetConsumeLastLaunchFailed() { @@ -139,8 +132,8 @@ namespace ul::menu::ui { return res; } - inline void NotifyLaunchFailed() { - this->launch_failed = true; + inline void NotifyHomebrewChosen(const char (&chosen_hb_path)[FS_MAX_PATH]) { + util::CopyToStringBuffer(this->chosen_hb, chosen_hb_path); } inline bool HasChosenHomebrew() { @@ -153,16 +146,6 @@ namespace ul::menu::ui { return res; } - inline void NotifyHomebrewChosen(const char (&chosen_hb_path)[FS_MAX_PATH]) { - util::CopyToStringBuffer(this->chosen_hb, chosen_hb_path); - } - - inline u64 GetTakeoverApplicationId() { - return this->takeover_app_id; - } - - void SetTakeoverApplicationId(const u64 app_id); - inline void NotifyGameCardMountFailure(const Result rc) { this->pending_gc_mount_rc = rc; } @@ -177,9 +160,58 @@ namespace ul::menu::ui { return gc_rc; } - inline void UpdateMenuIndex(const u32 idx) { - UL_RC_ASSERT(smi::UpdateMenuIndex(idx)); - this->system_status.last_menu_index = idx; + inline void NotifyApplicationRecordReloadNeeded() { + this->needs_app_records_reload = true; + } + + inline bool GetConsumeApplicationRecordReloadNeeded() { + const auto needs_reload = this->needs_app_records_reload; + this->needs_app_records_reload = false; + return needs_reload; + } + + inline void NotifyApplicationEntryReloadNeeded() { + this->needs_app_entries_reload = true; + } + + inline bool GetConsumeApplicationEntryReloadNeeded() { + const auto needs_reload = this->needs_app_entries_reload; + this->needs_app_entries_reload = false; + return needs_reload; + } + + inline void NotifyVerifyFinished(const u64 app_id, const Result rc, const Result detail_rc) { + this->verify_finished_app_id = app_id; + this->verify_rc = rc; + this->verify_detail_rc = detail_rc; + } + + inline bool HasVerifyFinishedPending() { + return this->verify_finished_app_id != 0; + } + + inline u64 GetConsumeVerifyFinishedApplicationId() { + const auto app_id = this->verify_finished_app_id; + this->verify_finished_app_id = 0; + return app_id; + } + + inline Result GetConsumeVerifyResult() { + const auto rc = this->verify_rc; + this->verify_rc = ResultSuccess; + return rc; + } + + inline Result GetConsumeVerifyDetailResult() { + const auto rc = this->verify_detail_rc; + this->verify_detail_rc = ResultSuccess; + return rc; + } + + inline void NotifyApplicationVerifyProgress(const u64 app_id, const float progress = NAN) { + if(this->loaded_menu == MenuType::Main) { + this->GetLayout()->UpdateApplicationVerifyProgress(app_id, progress); + } } void ShowNotification(const std::string &text, const u64 timeout = 1500); @@ -312,28 +344,6 @@ namespace ul::menu::ui { this->settings_menu_lyt->DisposeAudio(); } - void SetSelectedUser(const AccountUid user_id); - - inline bool IsEntrySuspended(const Entry &entry) { - if(entry.Is()) { - return entry.app_info.record.application_id == this->system_status.suspended_app_id; - } - else if(entry.Is()) { - // Enough to compare the NRO path - return memcmp(this->system_status.suspended_hb_target_ipt.nro_path, entry.hb_info.nro_target.nro_path, sizeof(entry.hb_info.nro_target.nro_path)) == 0; - } - - return false; - } - - inline bool IsEntryHomebrewTakeoverApplication(const Entry &entry) { - return entry.Is() && (entry.app_info.record.application_id == this->takeover_app_id); - } - - inline AccountUid GetSelectedUser() { - return this->system_status.selected_user; - } - int DisplayDialog(const std::string &title, const std::string &content, const std::vector &opts, const bool use_last_opt_as_cancel, pu::sdl2::TextureHandle::Ref icon = {}); }; diff --git a/projects/uMenu/include/ul/menu/ui/ui_QuickMenu.hpp b/projects/uMenu/include/ul/menu/ui/ui_QuickMenu.hpp index a4371ef..583fcfd 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_QuickMenu.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_QuickMenu.hpp @@ -34,7 +34,7 @@ namespace ul::menu::ui { pu::ui::elm::MenuItem::Ref settings_menu_item; pu::ui::elm::MenuItem::Ref mii_menu_item; - static void OnHomeButtonDetection(const smi::MenuMessageContext _); + static void OnHomeButtonDetection(const smi::MenuMessageContext &_); public: QuickMenu(); diff --git a/projects/uMenu/include/ul/menu/ui/ui_SettingsMenuLayout.hpp b/projects/uMenu/include/ul/menu/ui/ui_SettingsMenuLayout.hpp index 65d7c67..26a2c83 100644 --- a/projects/uMenu/include/ul/menu/ui/ui_SettingsMenuLayout.hpp +++ b/projects/uMenu/include/ul/menu/ui/ui_SettingsMenuLayout.hpp @@ -5,18 +5,67 @@ namespace ul::menu::ui { + enum class SettingMenu { + System, + uLaunch, + Bluetooth, + Network, + Screen, + Dev, + + Count + }; + + enum class SettingSubmenu { + None + }; + + enum class Setting { + ConsoleFirmware, + AtmosphereFirmware, + AtmosphereEmummc, + ConsoleNickname, + ConsoleTimezone, + UsbScreenCaptureEnabled, + ConnectionSsid, + ConsoleLanguage, + ConsoleInformationUploadEnabled, + AutomaticApplicationDownloadEnabled, + ConsoleAutoUpdateEnabled, + WirelessLanEnabled, + BluetoothEnabled, + Usb30Enabled, + NfcEnabled, + ConsoleSerialNumber, + MacAddress, + IpAddress, + + ConsoleRegion, + SystemUpdateStatus, + LockscreenEnabled, + PrimaryAlbumStorage, + HandheldSleepPlan, + ConsoleSleepPlan, + SleepWhilePlayingMedia, + SleepWakesAtPowerStateChange, + BatteryLot, + }; + class SettingsMenuLayout : public IMenuLayout { public: static constexpr u32 SettingsMenuWidth = 1770; static constexpr u32 SettingsMenuItemSize = 150; - static constexpr u32 SettingsMenuItemsToShow = 6; + static constexpr u32 SettingsMenuItemsToShow = 5; private: - pu::ui::elm::TextBlock::Ref info_text; + pu::ui::elm::TextBlock::Ref menu_text; + pu::ui::elm::TextBlock::Ref submenu_text; pu::ui::elm::Menu::Ref settings_menu; pu::audio::Sfx setting_edit_sfx; pu::audio::Sfx setting_save_sfx; pu::audio::Sfx back_sfx; + SettingMenu cur_menu; + SettingSubmenu cur_submenu; void PushSettingItem(const std::string &name, const std::string &value_display, const int id); void setting_DefaultKey(const u32 id); @@ -29,7 +78,7 @@ namespace ul::menu::ui { bool OnHomeButtonPress() override; void DisposeAudio() override; - void Reload(const bool reset_idx); + void Reload(const bool soft); }; } diff --git a/projects/uMenu/romfs/lang/en.json b/projects/uMenu/romfs/lang/en.json index 0260509..c273fed 100644 --- a/projects/uMenu/romfs/lang/en.json +++ b/projects/uMenu/romfs/lang/en.json @@ -31,7 +31,7 @@ "power_power_off": "Power off", "power_reboot": "Reboot", "app_launch": "Application launch", - "app_not_launchable": "This application is not launchable (gamecard not inserted, archived, not downloaded, needs an update, etc.)", + "app_not_launchable": "This application is not launchable: contents are missing (might be archived, not downloaded, etc.)", "app_take_over": "Select as homebrew donor/take-over application", "app_no_take_over_app": "There is no application specified for homebrew to take over it.\nPlease select an application from its options.", "app_take_over_select": "If selected, homebrew will be launched using this application.\nThe application will not be affected in any way, and this can be disabled in any moment.\n\nWould you like to select this application for homebrew launching?", @@ -57,12 +57,12 @@ "set_ams_emummc": "EmuMMC present", "set_console_nickname": "Console nickname", "set_console_timezone": "Console timezone location", - "set_viewer_enabled": "USB screen capture enabled", + "set_usb_screen_capture_enabled": "USB screen capture enabled", "set_wifi_none": "none (no WiFi connection)", "set_wifi_name": "Connected WiFi network", "set_console_lang": "Console language", "set_console_info_upload": "Console information upload enabled", - "set_auto_titles_dl": "Automatic application download enabled", + "set_auto_app_download": "Automatic application download enabled", "set_auto_update": "Console auto-update enabled", "set_wireless_lan": "Wireless LAN enabled", "set_bluetooth": "Bluetooth enabled", @@ -72,9 +72,9 @@ "set_mac_addr": "MAC address", "set_ip_addr": "IP address", "swkbd_console_nick_guide": "Enter console nickname", - "set_viewer_info": "Only enable USB screen capture support if you wish to use the PC screen viewer (uScreen) via USB cable.", - "set_viewer_enable_conf": "Would you like to enable USB screen capture support?", - "set_viewer_disable_conf": "Would you like to disable USB screen capture support?", + "set_usb_screen_capture_info": "Only enable USB screen capture support if you wish to use the PC screen viewer (uScreen) via USB cable.", + "set_usb_screen_capture_enable_conf": "Would you like to enable USB screen capture support?", + "set_usb_screen_capture_disable_conf": "Would you like to disable USB screen capture support?", "set_changed_reboot": "USB screen capture enabled. (A reboot is required, the option won't be used until then)", "startup_welcome_info": "Welcome! Please select a user.", "startup_add_user": "Add user", @@ -133,5 +133,60 @@ "special_entry_text_settings": "Console & uLaunch settings", "special_entry_text_themes": "uLaunch themes", "special_entry_text_controllers": "Controllers", - "special_entry_text_album": "Album" -} \ No newline at end of file + "special_entry_text_album": "Album", + "special_entry_text_amiibo": "Amiibo options", + "set_enum_options": "Select option:", + "set_menu_system": "System", + "set_menu_ulaunch": "uLaunch", + "set_menu_bluetooth": "Bluetooth", + "set_menu_network": "Network", + "set_menu_screen": "Screen", + "set_menu_dev": "Dev", + "set_region": "Console region", + "set_region_jpn": "Japan", + "set_region_usa": "United States", + "set_region_eur": "Europe", + "set_region_aus": "Australia/New Zealand", + "set_region_htk": "Hong Kong/Taiwan/Korea", + "set_region_chn": "China", + "set_update": "System update status", + "set_update_updated": "Up to date", + "set_update_ready_install": "Downloaded and ready to install", + "set_update_needs_download": "Needs to download", + "set_update_no_connection": "No connection...", + "set_album": "Primary album storage", + "set_album_nand": "System memory (NAND)", + "set_album_sd": "SD card", + "set_sleep_console": "Sleep settings (docked)", + "set_sleep_console_1h": "1 hour", + "set_sleep_console_2h": "2 hours", + "set_sleep_console_3h": "3 hours", + "set_sleep_console_6h": "6 hours", + "set_sleep_console_12h": "12 hours", + "set_sleep_console_never": "Never", + "set_sleep_handheld": "Sleep settings (handheld)", + "set_sleep_handheld_1min": "1 minute", + "set_sleep_handheld_3min": "3 minutes", + "set_sleep_handheld_5min": "5 minutes", + "set_sleep_handheld_10min": "10 minutes", + "set_sleep_handheld_30min": "30 minutes", + "set_sleep_handheld_never": "Never", + "set_lockscreen": "Lockscreen enabled", + "set_sleep_media_play": "Sleeps while playing media", + "set_sleep_wake_power_state": "Wakes at power state change", + "set_battery_lot": "Battery lot", + "app_corrupted": "This application might be corrupted.\nIt must be checked (might take some minutes), would you like it to be checked in the background?", + "app_verify": "Application data check", + "app_verify_ok": "No corrupted data was found.", + "app_verify_error": "Application corrupt data check failed", + "app_no_gamecard": "This application's gamecard is not inserted.", + "app_needs_update": "This application expects an update in order to be launched.\nWould you like to force reset the requirement and launch it anyway?", + "app_launch_version_reset_error": "An error ocurred while resetting the required application launch version", + "app_verify_wait": "This application is currently being verified...", + "amiibo_entry_info": "What would you like to do?", + "amiibo_entry_nickname_owner_settings": "Edit nickname/owner", + "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_restore": "Restore corrupted", + "amiibo_entry_format": "Format", + "lockscreen_info": "Press any key to unlock" +} diff --git a/projects/uMenu/romfs/lang/es.json b/projects/uMenu/romfs/lang/es.json index db45bbd..403f4cc 100644 --- a/projects/uMenu/romfs/lang/es.json +++ b/projects/uMenu/romfs/lang/es.json @@ -31,7 +31,7 @@ "power_power_off": "Apagado", "power_reboot": "Reinicio", "app_launch": "Ejecución de aplicación", - "app_not_launchable": "Esta aplicación no es ejecutable (cartucho no insertado, archivada, no descargada, necesita ser actualizada, etc.)", + "app_not_launchable": "This application is not launchable: contents are missing (might be archived, not downloaded, etc.)", "app_take_over": "Seleccionar como aplicación donante para homebrew", "app_no_take_over_app": "No hay una aplicación donante para homebrew especificada.\nPor favor, escoja una aplicación desde sus opciones.", "app_take_over_select": "Si es seleccionada, el homebrew será ejecutado sobre esta aplicación.\nLa aplicación no se verá afectada de ningún modo, y puede ser deshabilitado en cualquier momento.\n\n¿Quiere seleccionar esta aplicación para ejecutar homebrew?", @@ -57,12 +57,12 @@ "set_ams_emummc": "EmuMMC presente", "set_console_nickname": "Apodo de la consola", "set_console_timezone": "Huso horario", - "set_viewer_enabled": "Captura de pantalla por USB activada", + "set_usb_screen_capture_enabled": "Captura de pantalla por USB activada", "set_wifi_none": "no (sin conexión WiFi)", "set_wifi_name": "Red WiFi conectada", "set_console_lang": "Idioma del sistema", "set_console_info_upload": "Compartir información del sistema", - "set_auto_titles_dl": "Descarga automática de aplicaciones", + "set_auto_app_download": "Descarga automática de aplicaciones", "set_auto_update": "Actualización automática del sistema", "set_wireless_lan": "LAN inalámbrica activada", "set_bluetooth": "Bluetooth activado", @@ -72,9 +72,9 @@ "set_mac_addr": "Dirección MAC", "set_ip_addr": "Dirección IP", "swkbd_console_nick_guide": "Introduzca un apodo", - "set_viewer_info": "Sólamente active la captura de pantalla por USB si desea utilizar el software de captura (uScreen) via cable USB.", - "set_viewer_enable_conf": "¿Le gustaría activar la captura de pantalla por USB?", - "set_viewer_disable_conf": "¿Le gustaría desactivar la captura de pantalla por USB?", + "set_usb_screen_capture_info": "Sólamente active la captura de pantalla por USB si desea utilizar el software de captura (uScreen) via cable USB.", + "set_usb_screen_capture_enable_conf": "¿Le gustaría activar la captura de pantalla por USB?", + "set_usb_screen_capture_disable_conf": "¿Le gustaría desactivar la captura de pantalla por USB?", "set_changed_reboot": "Captura de pantalla por USB activada. (Requiere un reinicio, hasta entonces no será utilizable)", "startup_welcome_info": "Bienvenido! Por favor, escoja un usuario.", "startup_add_user": "Añadir usuario", @@ -133,5 +133,60 @@ "special_entry_text_settings": "Ajustes del sistema y uLaunch", "special_entry_text_themes": "Temas de uLaunch", "special_entry_text_controllers": "Mandos", - "special_entry_text_album": "Álbum" + "special_entry_text_album": "Álbum", + "special_entry_text_amiibo": "Amiibo options", + "set_enum_options": "Select option:", + "set_menu_system": "System", + "set_menu_ulaunch": "uLaunch", + "set_menu_bluetooth": "Bluetooth", + "set_menu_network": "Network", + "set_menu_screen": "Screen", + "set_menu_dev": "Dev", + "set_region": "Console region", + "set_region_jpn": "Japan", + "set_region_usa": "United States", + "set_region_eur": "Europe", + "set_region_aus": "Australia/New Zealand", + "set_region_htk": "Hong Kong/Taiwan/Korea", + "set_region_chn": "China", + "set_update": "System update status", + "set_update_updated": "Up to date", + "set_update_ready_install": "Downloaded and ready to install", + "set_update_needs_download": "Needs to download", + "set_update_no_connection": "No connection...", + "set_album": "Primary album storage", + "set_album_nand": "System memory (NAND)", + "set_album_sd": "SD card", + "set_sleep_console": "Sleep settings (docked)", + "set_sleep_console_1h": "1 hour", + "set_sleep_console_2h": "2 hours", + "set_sleep_console_3h": "3 hours", + "set_sleep_console_6h": "6 hours", + "set_sleep_console_12h": "12 hours", + "set_sleep_console_never": "Never", + "set_sleep_handheld": "Sleep settings (handheld)", + "set_sleep_handheld_1min": "1 minute", + "set_sleep_handheld_3min": "3 minutes", + "set_sleep_handheld_5min": "5 minutes", + "set_sleep_handheld_10min": "10 minutes", + "set_sleep_handheld_30min": "30 minutes", + "set_sleep_handheld_never": "Never", + "set_lockscreen": "Lockscreen enabled", + "set_sleep_media_play": "Sleeps while playing media", + "set_sleep_wake_power_state": "Wakes at power state change", + "set_battery_lot": "Battery lot", + "app_corrupted": "This application might be corrupted.\nIt must be checked (might take some minutes), would you like it to be checked in the background?", + "app_verify": "Application data check", + "app_verify_ok": "No corrupted data was found.", + "app_verify_error": "Application corrupt data check failed", + "app_no_gamecard": "This application's gamecard is not inserted.", + "app_needs_update": "This application expects an update in order to be launched.\nWould you like to force reset the requirement and launch it anyway?", + "app_launch_version_reset_error": "An error ocurred while resetting the required application launch version", + "app_verify_wait": "This application is currently being verified...", + "amiibo_entry_info": "What would you like to do?", + "amiibo_entry_nickname_owner_settings": "Edit nickname/owner", + "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_restore": "Restore corrupted", + "amiibo_entry_format": "Format", + "lockscreen_info": "Press any key to unlock" } \ No newline at end of file diff --git a/projects/uMenu/romfs/lang/it.json b/projects/uMenu/romfs/lang/it.json index 151fe44..eb4a487 100644 --- a/projects/uMenu/romfs/lang/it.json +++ b/projects/uMenu/romfs/lang/it.json @@ -31,7 +31,7 @@ "power_power_off": "Spegni", "power_reboot": "Riavvia", "app_launch": "Avvio applicazione", - "app_not_launchable": "Questa applicazione non può essere avviata (cartuccia non inserita, archiviata, non scaricata, richiede aggiornamento, etc.)", + "app_not_launchable": "This application is not launchable: contents are missing (might be archived, not downloaded, etc.)", "app_take_over": "Seleziona come applicazione homebrew donor/take-over", "app_no_take_over_app": "Non è stata specificata un'app da sfruttare per il take-over delle homebrew.\nPerfavore seleziona un'applicazione dalle sue opzioni.", "app_take_over_select": "Se selezionato, l'homebrew sarà avviata utilizzando questa app.\nL'Applicazione non sarà danneggiata in alcun modo, e puoi disabilitarlo in qualsiasi momento.\n\nVorresti selezionare questa applicazione per il lancio delle homebrew?", @@ -57,12 +57,12 @@ "set_ams_emummc": "EmuMMC presente", "set_console_nickname": "Nickname Console", "set_console_timezone": "Fuso orario Console", - "set_viewer_enabled": "Cattura schermo USB abilitato", + "set_usb_screen_capture_enabled": "Cattura schermo USB abilitato", "set_wifi_none": "nessuna (no connessione WiFi)", "set_wifi_name": "WiFi Connesso", "set_console_lang": "Lingua Console", "set_console_info_upload": "Caricamento delle informazioni sulla Console abilitato", - "set_auto_titles_dl": "Download automatico delle applicazioni abilitato", + "set_auto_app_download": "Download automatico delle applicazioni abilitato", "set_auto_update": "Aggiornamento automatico della Console abilitato", "set_wireless_lan": "LAN Wireless abilitato", "set_bluetooth": "Bluetooth abilitato", @@ -72,9 +72,9 @@ "set_mac_addr": "Indirizzo MAC", "set_ip_addr": "Indirizzo IP", "swkbd_console_nick_guide": "Inserisci nickname Console", - "set_viewer_info": "Abilita la cattura schermo USB solo se desideri utilizzare il visualizzatore schermo per PC (uScreen) via cavo USB.", - "set_viewer_enable_conf": "Vorresti abilitare la cattura schermo USB?", - "set_viewer_disable_conf": "Vorresti disabilitare la cattura schermo USB?", + "set_usb_screen_capture_info": "Abilita la cattura schermo USB solo se desideri utilizzare il visualizzatore schermo per PC (uScreen) via cavo USB.", + "set_usb_screen_capture_enable_conf": "Vorresti abilitare la cattura schermo USB?", + "set_usb_screen_capture_disable_conf": "Vorresti disabilitare la cattura schermo USB?", "set_changed_reboot": "Cattura schermo USB abilitata. (Un riavvio è richiesto, altrimenti l'opzione non sarà attiva)", "startup_welcome_info": "Benvenuto/a! Seleziona un utente.", "startup_add_user": "Aggiungi utente", @@ -133,5 +133,60 @@ "special_entry_text_settings": "Impostazioni Console & uLaunch", "special_entry_text_themes": "Temi uLaunch", "special_entry_text_controllers": "Controllers", - "special_entry_text_album": "Album" + "special_entry_text_album": "Album", + "special_entry_text_amiibo": "Amiibo options", + "set_enum_options": "Select option:", + "set_menu_system": "System", + "set_menu_ulaunch": "uLaunch", + "set_menu_bluetooth": "Bluetooth", + "set_menu_network": "Network", + "set_menu_screen": "Screen", + "set_menu_dev": "Dev", + "set_region": "Console region", + "set_region_jpn": "Japan", + "set_region_usa": "United States", + "set_region_eur": "Europe", + "set_region_aus": "Australia/New Zealand", + "set_region_htk": "Hong Kong/Taiwan/Korea", + "set_region_chn": "China", + "set_update": "System update status", + "set_update_updated": "Up to date", + "set_update_ready_install": "Downloaded and ready to install", + "set_update_needs_download": "Needs to download", + "set_update_no_connection": "No connection...", + "set_album": "Primary album storage", + "set_album_nand": "System memory (NAND)", + "set_album_sd": "SD card", + "set_sleep_console": "Sleep settings (docked)", + "set_sleep_console_1h": "1 hour", + "set_sleep_console_2h": "2 hours", + "set_sleep_console_3h": "3 hours", + "set_sleep_console_6h": "6 hours", + "set_sleep_console_12h": "12 hours", + "set_sleep_console_never": "Never", + "set_sleep_handheld": "Sleep settings (handheld)", + "set_sleep_handheld_1min": "1 minute", + "set_sleep_handheld_3min": "3 minutes", + "set_sleep_handheld_5min": "5 minutes", + "set_sleep_handheld_10min": "10 minutes", + "set_sleep_handheld_30min": "30 minutes", + "set_sleep_handheld_never": "Never", + "set_lockscreen": "Lockscreen enabled", + "set_sleep_media_play": "Sleeps while playing media", + "set_sleep_wake_power_state": "Wakes at power state change", + "set_battery_lot": "Battery lot", + "app_corrupted": "This application might be corrupted.\nIt must be checked (might take some minutes), would you like it to be checked in the background?", + "app_verify": "Application data check", + "app_verify_ok": "No corrupted data was found.", + "app_verify_error": "Application corrupt data check failed", + "app_no_gamecard": "This application's gamecard is not inserted.", + "app_needs_update": "This application expects an update in order to be launched.\nWould you like to force reset the requirement and launch it anyway?", + "app_launch_version_reset_error": "An error ocurred while resetting the required application launch version", + "app_verify_wait": "This application is currently being verified...", + "amiibo_entry_info": "What would you like to do?", + "amiibo_entry_nickname_owner_settings": "Edit nickname/owner", + "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_restore": "Restore corrupted", + "amiibo_entry_format": "Format", + "lockscreen_info": "Press any key to unlock" } diff --git a/projects/uMenu/romfs/lang/ko.json b/projects/uMenu/romfs/lang/ko.json index 469c4cc..1916c76 100644 --- a/projects/uMenu/romfs/lang/ko.json +++ b/projects/uMenu/romfs/lang/ko.json @@ -31,7 +31,7 @@ "power_power_off": "전원 끄기", "power_reboot": "재부팅", "app_launch": "응용 프로그램 실행", - "app_not_launchable": "이 애플리케이션을 실행할 수 없습니다(게임 카드가 삽입되지 않음, 보관됨, 다운로드되지 않음, 업데이트가 필요함 등)", + "app_not_launchable": "This application is not launchable: contents are missing (might be archived, not downloaded, etc.)", "app_take_over": "홀브류 기증자/인계 신청으로 선택", "app_no_take_over_app": "홈브류가 이를 인수하도록 지정된 응용 프로그램이 없습니다.\n해당 옵션에서 응용 프로그램을 선택하세요.", "app_take_over_select": "선택하면 이 응용 프로그램을 사용하여 홈브류가 실행됩니다.\n응용 프로그램은 어떤 방식으로든 영향을 받지 않으며 언제든지 비활성화할 수 있습니다.\n\n홈브류 실행을 위해 이 응용 프로그램을 선택하시겠습니까?", @@ -57,12 +57,12 @@ "set_ams_emummc": "EmuMMC 제공", "set_console_nickname": "콘솔 별명", "set_console_timezone": "콘솔 시간대 위치", - "set_viewer_enabled": "USB 화면 뷰어가 활성화됨", + "set_usb_screen_capture_enabled": "USB 화면 뷰어가 활성화됨", "set_wifi_none": "없음 (WiFi 연결 없음)", "set_wifi_name": "연결된 WiFi 네트워크", "set_console_lang": "콘솔 언어", "set_console_info_upload": "콘솔 정보 업로드 활성화됨", - "set_auto_titles_dl": "자동 응용프로그램 다운로드 활성화됨", + "set_auto_app_download": "자동 응용프로그램 다운로드 활성화됨", "set_auto_update": "콘솔 자동 업데이트 활성화됨", "set_wireless_lan": "무선 LAN 활성화됨", "set_bluetooth": "블루투스 활성화됨", @@ -72,9 +72,9 @@ "set_mac_addr": "MAC 주소", "set_ip_addr": "IP 주소", "swkbd_console_nick_guide": "새 콘솔 별명 입력", - "set_viewer_info": "USB 케이블을 통해 PC 화면 뷰어(uScreen)를 사용하려는 경우에만 USB 화면 뷰어 지원을 활성화하세요.", - "set_viewer_enable_conf": "USB 화면 뷰어 지원을 활성화하겠습니까?", - "set_viewer_disable_conf": "USB 화면 뷰어 지원을 비활성화하겠습니까?", + "set_usb_screen_capture_info": "USB 케이블을 통해 PC 화면 뷰어(uScreen)를 사용하려는 경우에만 USB 화면 뷰어 지원을 활성화하세요.", + "set_usb_screen_capture_enable_conf": "USB 화면 뷰어 지원을 활성화하겠습니까?", + "set_usb_screen_capture_disable_conf": "USB 화면 뷰어 지원을 비활성화하겠습니까?", "set_changed_reboot": "USB 화면 뷰어가 활성화되었습니다. (재부팅이 필요하며 그때까지는 해당 옵션이 사용되지 않습니다.)", "startup_welcome_info": "환영합니다! 사용자를 선택해 주세요.", "startup_add_user": "사용자 추가", @@ -133,5 +133,60 @@ "special_entry_text_settings": "콘솔 & uLaunch 설정", "special_entry_text_themes": "uLaunch 테마", "special_entry_text_controllers": "콘트롤러", - "special_entry_text_album": "앨범" + "special_entry_text_album": "앨범", + "special_entry_text_amiibo": "Amiibo options", + "set_enum_options": "Select option:", + "set_menu_system": "System", + "set_menu_ulaunch": "uLaunch", + "set_menu_bluetooth": "Bluetooth", + "set_menu_network": "Network", + "set_menu_screen": "Screen", + "set_menu_dev": "Dev", + "set_region": "Console region", + "set_region_jpn": "Japan", + "set_region_usa": "United States", + "set_region_eur": "Europe", + "set_region_aus": "Australia/New Zealand", + "set_region_htk": "Hong Kong/Taiwan/Korea", + "set_region_chn": "China", + "set_update": "System update status", + "set_update_updated": "Up to date", + "set_update_ready_install": "Downloaded and ready to install", + "set_update_needs_download": "Needs to download", + "set_update_no_connection": "No connection...", + "set_album": "Primary album storage", + "set_album_nand": "System memory (NAND)", + "set_album_sd": "SD card", + "set_sleep_console": "Sleep settings (docked)", + "set_sleep_console_1h": "1 hour", + "set_sleep_console_2h": "2 hours", + "set_sleep_console_3h": "3 hours", + "set_sleep_console_6h": "6 hours", + "set_sleep_console_12h": "12 hours", + "set_sleep_console_never": "Never", + "set_sleep_handheld": "Sleep settings (handheld)", + "set_sleep_handheld_1min": "1 minute", + "set_sleep_handheld_3min": "3 minutes", + "set_sleep_handheld_5min": "5 minutes", + "set_sleep_handheld_10min": "10 minutes", + "set_sleep_handheld_30min": "30 minutes", + "set_sleep_handheld_never": "Never", + "set_lockscreen": "Lockscreen enabled", + "set_sleep_media_play": "Sleeps while playing media", + "set_sleep_wake_power_state": "Wakes at power state change", + "set_battery_lot": "Battery lot", + "app_corrupted": "This application might be corrupted.\nIt must be checked (might take some minutes), would you like it to be checked in the background?", + "app_verify": "Application data check", + "app_verify_ok": "No corrupted data was found.", + "app_verify_error": "Application corrupt data check failed", + "app_no_gamecard": "This application's gamecard is not inserted.", + "app_needs_update": "This application expects an update in order to be launched.\nWould you like to force reset the requirement and launch it anyway?", + "app_launch_version_reset_error": "An error ocurred while resetting the required application launch version", + "app_verify_wait": "This application is currently being verified...", + "amiibo_entry_info": "What would you like to do?", + "amiibo_entry_nickname_owner_settings": "Edit nickname/owner", + "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_restore": "Restore corrupted", + "amiibo_entry_format": "Format", + "lockscreen_info": "Press any key to unlock" } diff --git a/projects/uMenu/romfs/lang/pt-BR.json b/projects/uMenu/romfs/lang/pt-BR.json index b31f8c0..d2b6b2d 100644 --- a/projects/uMenu/romfs/lang/pt-BR.json +++ b/projects/uMenu/romfs/lang/pt-BR.json @@ -31,7 +31,7 @@ "power_power_off": "Desligar", "power_reboot": "Reiniciar", "app_launch": "Iniciar aplicação", - "app_not_launchable": "Esta aplicação não pode ser iniciada (cartucho não inserido, arquivado, não instalado,precisa ser atualizado, etc.)", + "app_not_launchable": "This application is not launchable: contents are missing (might be archived, not downloaded, etc.)", "app_take_over": "Selecionar como aplicativo doador para homebrew", "app_no_take_over_app": "Não há um aplicativo definido como doador para homebrew\n Por favor selecione um aplicativo para ser doador.", "app_take_over_select": "Se selecionado, qualquer homebrew será iniciado usando esta aplicação.\nA aplicação não será afetada de forma alguma, isso pode ser desativado a qualquer momento.\nGostaria de selecionar esta aplicação para iniciar homebrews?", @@ -57,12 +57,12 @@ "set_ams_emummc": "EmuMMC presente", "set_console_nickname": "Apelido do Console", "set_console_timezone": "Fuso-horário do Console", - "set_viewer_enabled": "Visualizador de tela USB habilitado", + "set_usb_screen_capture_enabled": "Visualizador de tela USB habilitado", "set_wifi_none": "nenhum (sem conexão Wi-Fi )", "set_wifi_name": "Conectado a rede Wi-Fi", "set_console_lang": "Idioma do Console", "set_console_info_upload": "Envio de informações do console habilitado", - "set_auto_titles_dl": "Download automático de aplicativos habilitado", + "set_auto_app_download": "Download automático de aplicativos habilitado", "set_auto_update": "Atualização automática do console habilitado", "set_wireless_lan": "Wireless LAN habilitado", "set_bluetooth": "Bluetooth habilitado", @@ -72,9 +72,9 @@ "set_mac_addr": "Endereço MAC", "set_ip_addr": "Endereço IP", "swkbd_console_nick_guide": "Digite o novo apelido do console", - "set_viewer_info": "Habilite isso somente se você desejar usar o visualizador de tela no PC (uScreen) via cabo USB.", - "set_viewer_enable_conf": "Você gostaria de habilitar o suporte do visualizador de tela USB?", - "set_viewer_disable_conf": "Você gostaria de desabilitar o suporte do visualizador de tela USB?", + "set_usb_screen_capture_info": "Habilite isso somente se você desejar usar o visualizador de tela no PC (uScreen) via cabo USB.", + "set_usb_screen_capture_enable_conf": "Você gostaria de habilitar o suporte do visualizador de tela USB?", + "set_usb_screen_capture_disable_conf": "Você gostaria de desabilitar o suporte do visualizador de tela USB?", "set_changed_reboot": "Visualizador de tela USB habilitado. É necessário reiniciar o Console (a opção não será utilizada antes disso)", "startup_welcome_info": "Bem-vindo(a)! Por favor selecione um usuário.", "startup_add_user": "Adicionar usuário", @@ -133,5 +133,60 @@ "special_entry_text_settings": "Configurações do Console & uLaunch", "special_entry_text_themes": "Temas do uLaunch", "special_entry_text_controllers": "Controles", - "special_entry_text_album": "Álbum" -} \ No newline at end of file + "special_entry_text_album": "Álbum", + "special_entry_text_amiibo": "Amiibo options", + "set_enum_options": "Select option:", + "set_menu_system": "System", + "set_menu_ulaunch": "uLaunch", + "set_menu_bluetooth": "Bluetooth", + "set_menu_network": "Network", + "set_menu_screen": "Screen", + "set_menu_dev": "Dev", + "set_region": "Console region", + "set_region_jpn": "Japan", + "set_region_usa": "United States", + "set_region_eur": "Europe", + "set_region_aus": "Australia/New Zealand", + "set_region_htk": "Hong Kong/Taiwan/Korea", + "set_region_chn": "China", + "set_update": "System update status", + "set_update_updated": "Up to date", + "set_update_ready_install": "Downloaded and ready to install", + "set_update_needs_download": "Needs to download", + "set_update_no_connection": "No connection...", + "set_album": "Primary album storage", + "set_album_nand": "System memory (NAND)", + "set_album_sd": "SD card", + "set_sleep_console": "Sleep settings (docked)", + "set_sleep_console_1h": "1 hour", + "set_sleep_console_2h": "2 hours", + "set_sleep_console_3h": "3 hours", + "set_sleep_console_6h": "6 hours", + "set_sleep_console_12h": "12 hours", + "set_sleep_console_never": "Never", + "set_sleep_handheld": "Sleep settings (handheld)", + "set_sleep_handheld_1min": "1 minute", + "set_sleep_handheld_3min": "3 minutes", + "set_sleep_handheld_5min": "5 minutes", + "set_sleep_handheld_10min": "10 minutes", + "set_sleep_handheld_30min": "30 minutes", + "set_sleep_handheld_never": "Never", + "set_lockscreen": "Lockscreen enabled", + "set_sleep_media_play": "Sleeps while playing media", + "set_sleep_wake_power_state": "Wakes at power state change", + "set_battery_lot": "Battery lot", + "app_corrupted": "This application might be corrupted.\nIt must be checked (might take some minutes), would you like it to be checked in the background?", + "app_verify": "Application data check", + "app_verify_ok": "No corrupted data was found.", + "app_verify_error": "Application corrupt data check failed", + "app_no_gamecard": "This application's gamecard is not inserted.", + "app_needs_update": "This application expects an update in order to be launched.\nWould you like to force reset the requirement and launch it anyway?", + "app_launch_version_reset_error": "An error ocurred while resetting the required application launch version", + "app_verify_wait": "This application is currently being verified...", + "amiibo_entry_info": "What would you like to do?", + "amiibo_entry_nickname_owner_settings": "Edit nickname/owner", + "amiibo_entry_game_data_erase": "Erase game data", + "amiibo_entry_restore": "Restore corrupted", + "amiibo_entry_format": "Format", + "lockscreen_info": "Press any key to unlock" +} diff --git a/projects/uMenu/source/main.cpp b/projects/uMenu/source/main.cpp index 1fded51..085af3a 100644 --- a/projects/uMenu/source/main.cpp +++ b/projects/uMenu/source/main.cpp @@ -10,12 +10,19 @@ using namespace ul::util::size; -SetSysFirmwareVersion g_FwVersion; +ul::menu::ui::GlobalSettings g_GlobalSettings; + +namespace { + + constexpr u32 ExosphereApiVersionConfigItem = 65000; + constexpr u32 ExosphereEmummcType = 65007; + +} extern "C" { AppletType __nx_applet_type = AppletType_LibraryApplet; // Explicitly declare we're a library applet (need to do so for non-hbloader homebrew) - TimeServiceType __nx_time_service_type = TimeServiceType_User; + TimeServiceType __nx_time_service_type = TimeServiceType_Menu; u32 __nx_fs_num_sessions = 1; size_t __nx_heap_size = 296_MB; @@ -31,33 +38,78 @@ extern "C" { UL_RC_ASSERT(fsInitialize()); UL_RC_ASSERT(fsdevMountSdmc()); + UL_RC_ASSERT(splInitialize()); + // Since we rely on ams for uLaunch to work, it *must* be present + u64 raw_ams_ver; + UL_RC_ASSERT(splGetConfig(static_cast(ExosphereApiVersionConfigItem), &raw_ams_ver)); + g_GlobalSettings.ams_version = { + .major = static_cast((raw_ams_ver >> 56) & 0xFF), + .minor = static_cast((raw_ams_ver >> 48) & 0xFF), + .micro = static_cast((raw_ams_ver >> 40) & 0xFF) + }; + u64 emummc_type; + UL_RC_ASSERT(splGetConfig(static_cast(ExosphereEmummcType), &emummc_type)); + g_GlobalSettings.ams_is_emummc = emummc_type != 0; + splExit(); + UL_RC_ASSERT(setsysInitialize()); - UL_RC_ASSERT(setsysGetFirmwareVersion(&g_FwVersion)); - hosversionSet(MAKEHOSVERSION(g_FwVersion.major, g_FwVersion.minor, g_FwVersion.micro) | BIT(31)); - setsysExit(); + UL_RC_ASSERT(setInitialize()); + + UL_RC_ASSERT(setsysGetFirmwareVersion(&g_GlobalSettings.fw_version)); + hosversionSet(MAKEHOSVERSION(g_GlobalSettings.fw_version.major, g_GlobalSettings.fw_version.minor, g_GlobalSettings.fw_version.micro) | BIT(31)); + + UL_RC_ASSERT(setsysGetSerialNumber(&g_GlobalSettings.serial_no)); + UL_RC_ASSERT(setsysGetSleepSettings(&g_GlobalSettings.sleep_settings)); + UL_RC_ASSERT(setGetRegionCode(&g_GlobalSettings.region)); + UL_RC_ASSERT(setsysGetPrimaryAlbumStorage(&g_GlobalSettings.album_storage)); + UL_RC_ASSERT(setsysGetNfcEnableFlag(&g_GlobalSettings.nfc_enabled)); + UL_RC_ASSERT(setsysGetUsb30EnableFlag(&g_GlobalSettings.usb30_enabled)); + UL_RC_ASSERT(setsysGetBluetoothEnableFlag(&g_GlobalSettings.bluetooth_enabled)); + UL_RC_ASSERT(setsysGetWirelessLanEnableFlag(&g_GlobalSettings.wireless_lan_enabled)); + UL_RC_ASSERT(setsysGetAutoUpdateEnableFlag(&g_GlobalSettings.auto_update_enabled)); + UL_RC_ASSERT(setsysGetAutomaticApplicationDownloadFlag(&g_GlobalSettings.auto_app_download_enabled)); + UL_RC_ASSERT(setsysGetConsoleInformationUploadFlag(&g_GlobalSettings.console_info_upload_enabled)); + u64 lang_code; + UL_RC_ASSERT(setGetSystemLanguage(&lang_code)); + UL_RC_ASSERT(setMakeLanguage(lang_code, &g_GlobalSettings.language)); + s32 tmp; + UL_RC_ASSERT(setGetAvailableLanguageCodes(&tmp, g_GlobalSettings.available_language_codes, ul::os::LanguageNameCount)); + UL_RC_ASSERT(setsysGetDeviceNickname(&g_GlobalSettings.nickname)); + UL_RC_ASSERT(setsysGetBatteryLot(&g_GlobalSettings.battery_lot)); UL_RC_ASSERT(appletInitialize()); UL_RC_ASSERT(hidInitialize()); + UL_RC_ASSERT(timeInitialize()); __libnx_init_time(); + UL_RC_ASSERT(timeGetDeviceLocationName(&g_GlobalSettings.timezone)); UL_RC_ASSERT(accountInitialize(AccountServiceType_System)); UL_RC_ASSERT(nsInitialize()); + UL_RC_ASSERT(nssuInitialize()); + UL_RC_ASSERT(avmInitialize()); UL_RC_ASSERT(ul::net::Initialize()); UL_RC_ASSERT(psmInitialize()); - UL_RC_ASSERT(setsysInitialize()); - UL_RC_ASSERT(setInitialize()); __nx_win_init(); } void __appExit() { + ul::menu::smi::FinalizeMenuMessageHandler(); + + // Exit RomFs manually, since we also initialized it manually + romfsExit(); + + UL_LOG_INFO("Goodbye!"); + __nx_win_exit(); setExit(); setsysExit(); psmExit(); ul::net::Finalize(); + avmExit(); + nssuExit(); nsExit(); accountExit(); @@ -77,41 +129,36 @@ extern "C" { ul::menu::ui::MenuApplication::Ref g_MenuApplication; -ul::cfg::Config g_Config; -ul::cfg::Theme g_ActiveTheme; - -ul::util::JSON g_DefaultLanguage; -ul::util::JSON g_MainLanguage; - namespace { ul::smi::MenuStartMode g_StartMode; - ul::smi::SystemStatus g_SystemStatus; void MainLoop() { // After initializing RomFs, start initializing the rest of stuff here ul::menu::InitializeEntries(); // Load menu config - g_Config = ul::cfg::LoadConfig(); + g_GlobalSettings.config = ul::cfg::LoadConfig(); // Cache active theme if needed - if(g_SystemStatus.reload_theme_cache) { - ul::cfg::CacheActiveTheme(g_Config); + if(g_GlobalSettings.system_status.reload_theme_cache) { + ul::cfg::CacheActiveTheme(g_GlobalSettings.config); } + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(ul::cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, g_GlobalSettings.cache_hb_takeover_app_id)); + // Load active theme if set std::string active_theme_name; - UL_ASSERT_TRUE(g_Config.GetEntry(ul::cfg::ConfigEntryId::ActiveThemeName, active_theme_name)); + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(ul::cfg::ConfigEntryId::ActiveThemeName, active_theme_name)); if(!active_theme_name.empty()) { - const auto rc = ul::cfg::TryLoadTheme(active_theme_name, g_ActiveTheme); + const auto rc = ul::cfg::TryLoadTheme(active_theme_name, g_GlobalSettings.active_theme); if(R_SUCCEEDED(rc)) { - ul::cfg::EnsureCacheActiveTheme(g_Config); + ul::cfg::EnsureCacheActiveTheme(g_GlobalSettings.config); } else { - g_ActiveTheme = {}; + g_GlobalSettings.active_theme = {}; UL_LOG_WARN("Unable to load active theme '%s': %s, resetting to default theme...", active_theme_name.c_str(), ul::util::FormatResultDisplay(rc).c_str()); - UL_ASSERT_TRUE(g_Config.SetEntry(ul::cfg::ConfigEntryId::ActiveThemeName, g_ActiveTheme.name)); + UL_ASSERT_TRUE(g_GlobalSettings.config.SetEntry(ul::cfg::ConfigEntryId::ActiveThemeName, g_GlobalSettings.active_theme.name)); ul::cfg::RemoveActiveThemeCache(); } } @@ -119,8 +166,43 @@ namespace { UL_LOG_INFO("No active theme set..."); } + // List added/removed/in verify applications + + UL_LOG_INFO("Added app count: %d", g_GlobalSettings.system_status.last_added_app_count); + if(g_GlobalSettings.system_status.last_added_app_count > 0) { + auto app_buf = new u64[g_GlobalSettings.system_status.last_added_app_count](); + UL_RC_ASSERT(ul::menu::smi::ListAddedApplications(g_GlobalSettings.system_status.last_added_app_count, app_buf)); + for(u32 i = 0; i < g_GlobalSettings.system_status.last_added_app_count; i++) { + UL_LOG_INFO("> Added app: 0x%016lX", app_buf[i]); + g_GlobalSettings.added_app_ids.push_back(app_buf[i]); + } + delete[] app_buf; + } + + UL_LOG_INFO("Deleted app count: %d", g_GlobalSettings.system_status.last_deleted_app_count); + if(g_GlobalSettings.system_status.last_deleted_app_count > 0) { + auto app_buf = new u64[g_GlobalSettings.system_status.last_deleted_app_count](); + UL_RC_ASSERT(ul::menu::smi::ListDeletedApplications(g_GlobalSettings.system_status.last_deleted_app_count, app_buf)); + for(u32 i = 0; i < g_GlobalSettings.system_status.last_deleted_app_count; i++) { + UL_LOG_INFO("> Deleted app: 0x%016lX", app_buf[i]); + g_GlobalSettings.deleted_app_ids.push_back(app_buf[i]); + } + delete[] app_buf; + } + + UL_LOG_INFO("In verify app count: %d", g_GlobalSettings.system_status.in_verify_app_count); + if(g_GlobalSettings.system_status.in_verify_app_count > 0) { + auto app_buf = new u64[g_GlobalSettings.system_status.in_verify_app_count](); + UL_RC_ASSERT(ul::menu::smi::ListInVerifyApplications(g_GlobalSettings.system_status.in_verify_app_count, app_buf)); + for(u32 i = 0; i < g_GlobalSettings.system_status.in_verify_app_count; i++) { + UL_LOG_INFO("> App being verified: 0x%016lX", app_buf[i]); + g_GlobalSettings.in_verify_app_ids.push_back(app_buf[i]); + } + delete[] app_buf; + } + // Get system language and load translations (default one if not present) - ul::cfg::LoadLanguageJsons(ul::MenuLanguagesPath, g_MainLanguage, g_DefaultLanguage); + ul::cfg::LoadLanguageJsons(ul::MenuLanguagesPath, g_GlobalSettings.main_lang, g_GlobalSettings.default_lang); // Get the text sizes to initialize default fonts auto ui_json = ul::util::JSON::object(); @@ -128,6 +210,18 @@ namespace { auto renderer_opts = pu::ui::render::RendererInitOptions(SDL_INIT_EVERYTHING, pu::ui::render::RendererHardwareFlags); + renderer_opts.SetInputPlayerCount(8); + renderer_opts.AddInputNpadStyleTag(HidNpadStyleSet_NpadStandard); + renderer_opts.AddInputNpadIdType(HidNpadIdType_Handheld); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No1); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No2); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No3); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No4); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No5); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No6); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No7); + renderer_opts.AddInputNpadIdType(HidNpadIdType_No8); + const auto default_font_path = ul::menu::ui::TryGetActiveThemeResource("ui/Font.ttf"); if(!default_font_path.empty()) { renderer_opts.AddDefaultFontPath(default_font_path); @@ -147,7 +241,7 @@ namespace { auto renderer = pu::ui::render::Renderer::New(renderer_opts); g_MenuApplication = ul::menu::ui::MenuApplication::New(renderer); - g_MenuApplication->Initialize(g_StartMode, g_SystemStatus, ui_json); + g_MenuApplication->Initialize(g_StartMode, ui_json); g_MenuApplication->Prepare(); // With the handlers ready, initialize uSystem message handling @@ -160,6 +254,8 @@ namespace { g_MenuApplication->ShowWithFadeIn(); } + UL_LOG_WARN("googdggogo"); + g_MenuApplication = {}; } @@ -177,7 +273,9 @@ int main() { UL_LOG_INFO("Start mode: %d", (u32)g_StartMode); // Information sent as an extra storage to uMenu - UL_RC_ASSERT(ul::menu::am::ReadFromInputStorage(&g_SystemStatus, sizeof(g_SystemStatus))); + UL_RC_ASSERT(ul::menu::am::ReadFromInputStorage(&g_GlobalSettings.system_status, sizeof(g_GlobalSettings.system_status))); + g_GlobalSettings.initial_last_menu_index = g_GlobalSettings.system_status.last_menu_index; + g_GlobalSettings.initial_last_menu_fs_path = g_GlobalSettings.system_status.last_menu_fs_path; // Check if our RomFs data exists... if(!ul::fs::ExistsFile(ul::MenuRomfsFile)) { @@ -194,11 +292,5 @@ int main() { MainLoop(); - ul::menu::smi::FinalizeMenuMessageHandler(); - - // Exit RomFs manually, since we also initialized it manually - romfsExit(); - - UL_LOG_INFO("Goodbye!"); return 0; } diff --git a/projects/uMenu/source/ul/menu/am/am_LibnxLibappletWrap.cpp b/projects/uMenu/source/ul/menu/am/am_LibnxLibappletWrap.cpp index c61bd2a..35c2e25 100644 --- a/projects/uMenu/source/ul/menu/am/am_LibnxLibappletWrap.cpp +++ b/projects/uMenu/source/ul/menu/am/am_LibnxLibappletWrap.cpp @@ -13,7 +13,7 @@ namespace { namespace ul::menu::am { - void OnHomeButtonDetection(const smi::MenuMessageContext _) { + void OnHomeButtonDetection(const smi::MenuMessageContext &_) { if(g_IsAppletRunning) { g_DetectionHomePressed = true; } diff --git a/projects/uMenu/source/ul/menu/ui/ui_Common.cpp b/projects/uMenu/source/ul/menu/ui/ui_Common.cpp index da7457c..4de4437 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_Common.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_Common.cpp @@ -8,9 +8,8 @@ #include
                                        #include
                                          +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; -extern ul::cfg::Theme g_ActiveTheme; namespace ul::menu::ui { @@ -41,13 +40,12 @@ namespace ul::menu::ui { std::string TryGetActiveThemeResource(const std::string &resource_base) { std::string path; - if(g_ActiveTheme.IsValid()) { + if(g_GlobalSettings.active_theme.IsValid()) { path = cfg::GetActiveThemeResource(resource_base); } - else { + if(!fs::ExistsFile(path)) { path = fs::JoinPath(DefaultThemePath, resource_base); } - if(!fs::ExistsFile(path)) { path = ""; } @@ -106,7 +104,7 @@ namespace ul::menu::ui { } void LoadSelectedUserIconTexture() { - const auto icon_path = acc::GetIconCacheImagePath(g_MenuApplication->GetSelectedUser()); + const auto icon_path = acc::GetIconCacheImagePath(g_GlobalSettings.system_status.selected_user); g_UserIconTexture = pu::sdl2::TextureHandle::New(pu::ui::render::LoadImage(icon_path)); } @@ -114,11 +112,6 @@ namespace ul::menu::ui { return g_UserIconTexture; } - void SaveConfig() { - cfg::SaveConfig(g_Config); - UL_RC_ASSERT(smi::ReloadConfig()); - } - void RebootSystem() { PushPowerSystemAppletMessage(system::GeneralChannelMessage::Unk_Reboot); } @@ -194,6 +187,28 @@ namespace ul::menu::ui { g_MenuApplication->Finalize(); } + void ShowCabinet() { + const auto option = g_MenuApplication->DisplayDialog(GetLanguageString("special_entry_text_amiibo"), GetLanguageString("amiibo_entry_info"), { GetLanguageString("amiibo_entry_nickname_owner_settings"), GetLanguageString("amiibo_entry_game_data_erase"), GetLanguageString("amiibo_entry_restore"), GetLanguageString("amiibo_entry_format"), GetLanguageString("cancel") }, true); + switch(option) { + case 0: + UL_RC_ASSERT(ul::menu::smi::OpenCabinet(NfpLaStartParamTypeForAmiiboSettings_NicknameAndOwnerSettings)); + g_MenuApplication->Finalize(); + break; + case 1: + UL_RC_ASSERT(ul::menu::smi::OpenCabinet(NfpLaStartParamTypeForAmiiboSettings_GameDataEraser)); + g_MenuApplication->Finalize(); + break; + case 2: + UL_RC_ASSERT(ul::menu::smi::OpenCabinet(NfpLaStartParamTypeForAmiiboSettings_Restorer)); + g_MenuApplication->Finalize(); + break; + case 3: + UL_RC_ASSERT(ul::menu::smi::OpenCabinet(NfpLaStartParamTypeForAmiiboSettings_Formatter)); + g_MenuApplication->Finalize(); + break; + } + } + void ShowPowerDialog() { auto msg = ul::system::GeneralChannelMessage::Unk_Invalid; diff --git a/projects/uMenu/source/ul/menu/ui/ui_EntryMenu.cpp b/projects/uMenu/source/ul/menu/ui/ui_EntryMenu.cpp index 593c3f3..65d8968 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_EntryMenu.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_EntryMenu.cpp @@ -5,8 +5,8 @@ #include
                                            #include
                                              +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; namespace ul::menu::ui { @@ -15,12 +15,36 @@ namespace ul::menu::ui { u32 g_EntryHeightCount = 0; void SetEntryHeightCount(const u32 count) { - UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::MenuEntryHeightCount, static_cast(count))); - SaveConfig(); + UL_ASSERT_TRUE(g_GlobalSettings.config.SetEntry(cfg::ConfigEntryId::MenuEntryHeightCount, static_cast(count))); + g_GlobalSettings.SaveConfig(); g_EntryHeightCount = count; } + inline bool IsEntryHomebrewTakeoverApplication(const Entry &entry) { + return entry.Is() && (entry.app_info.record.application_id == g_GlobalSettings.cache_hb_takeover_app_id); + } + + inline bool IsEntryGamecardInserted(const Entry &entry) { + return entry.Is() && entry.app_info.HasViewFlag() && entry.app_info.HasViewFlag(); + } + + inline bool IsEntryGamecardNotInserted(const Entry &entry) { + return entry.Is() && entry.app_info.HasViewFlag() && !entry.app_info.HasViewFlag(); + } + + inline bool IsEntryCorrupted(const Entry &entry) { + return entry.Is() && entry.app_info.HasViewFlag(); + } + + inline bool EntryNeedsUpdate(const Entry &entry) { + return entry.Is() && entry.app_info.NeedsUpdate(); + } + + inline bool IsEntryNotLaunchable(const Entry &entry) { + return entry.Is() && !entry.app_info.HasViewFlag(); + } + } void EntryMenu::SwipeSingleLeft() { @@ -112,6 +136,9 @@ namespace ul::menu::ui { else if(entry.Is()) { icon_path = "ui/Main/EntryIcon/Album"; } + else if(entry.Is()) { + icon_path = "ui/Main/EntryIcon/Amiibo"; + } return TryFindLoadImageHandle(icon_path); } else { @@ -119,11 +146,31 @@ namespace ul::menu::ui { } } + void EntryMenu::CleanOverTexts() { + for(auto &over_text_tex: this->entry_over_texts) { + pu::ui::render::DeleteTexture(over_text_tex); + } + } + + void EntryMenu::LoadOverText(const u32 idx) { + if(idx < this->cur_entries.size()) { + const auto &entry = this->cur_entries.at(idx); + if(entry.Is()) { + auto &over_text_tex = this->entry_over_texts.at(idx); + if(over_text_tex == nullptr) { + const auto side_margin = (u32)(((double)this->entry_size / (double)DefaultEntrySize) * (double)DefaultOverTextSideMargin); + this->entry_over_texts.at(idx) = pu::ui::render::RenderText(pu::ui::GetDefaultFont(pu::ui::DefaultFontSize::Medium), entry.folder_info.name, g_MenuApplication->GetTextColor(), this->entry_size - 2 * side_margin); + } + } + } + } + bool EntryMenu::LoadEntry(const u32 idx) { if(idx < this->cur_entries.size()) { const auto &entry = this->cur_entries.at(idx); if(!entry.Is()) { this->entry_icons.at(idx) = this->LoadEntryIconTexture(entry); + this->LoadOverText(idx); auto &entry_alpha = this->load_img_entry_alphas.at(idx); entry_alpha = 0; @@ -147,12 +194,12 @@ namespace ul::menu::ui { if(((u32)prev_entry_idx < this->cur_entries.size())) { const auto &prev_entry = this->cur_entries.at(prev_entry_idx); if(prev_entry.type != EntryType::Invalid) { - is_prev_entry_suspended = g_MenuApplication->IsEntrySuspended(prev_entry); + is_prev_entry_suspended = g_GlobalSettings.IsEntrySuspended(prev_entry); } } } - const auto is_cur_entry_suspended = this->IsFocusedNonemptyEntry() && g_MenuApplication->IsEntrySuspended(this->GetFocusedEntry()); + const auto is_cur_entry_suspended = this->IsFocusedNonemptyEntry() && g_GlobalSettings.IsEntrySuspended(this->GetFocusedEntry()); this->cur_entry_changed_cb(has_prev_entry, is_prev_entry_suspended, is_cur_entry_suspended); } @@ -185,6 +232,14 @@ namespace ul::menu::ui { this->selected_size = 0; this->hb_takeover_app_over_icon = TryFindLoadImage("ui/Main/OverIcon/HomebrewTakeoverApplication"); this->hb_takeover_app_size = 0; + this->gamecard_over_icon = TryFindLoadImage("ui/Main/OverIcon/Gamecard"); + this->gamecard_size = 0; + this->corrupted_over_icon = TryFindLoadImage("ui/Main/OverIcon/Corrupted"); + this->corrupted_size = 0; + this->not_launchable_over_icon = TryFindLoadImage("ui/Main/OverIcon/NotLaunchable"); + this->not_launchable_size = 0; + this->needs_update_over_icon = TryFindLoadImage("ui/Main/OverIcon/NeedsUpdate"); + this->needs_update_size = 0; this->empty_entry_icon = TryFindLoadImageHandle("ui/Main/EntryIcon/Empty"); this->folder_entry_icon = TryFindLoadImageHandle("ui/Main/EntryIcon/Folder"); @@ -202,7 +257,7 @@ namespace ul::menu::ui { void EntryMenu::Initialize(const u32 last_idx) { u64 entry_height_count; - UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::MenuEntryHeightCount, entry_height_count)); + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::MenuEntryHeightCount, entry_height_count)); g_EntryHeightCount = entry_height_count; this->ComputeSizes(g_EntryHeightCount); @@ -288,33 +343,62 @@ namespace ul::menu::ui { auto &entry_alpha_incr = this->load_img_entry_incrs.at(entry_i); entry_alpha_incr.Increment(entry_alpha); - if(entry_icon != nullptr) { - drawer->RenderTexture(entry_icon->Get(), (s32)entry_x - (s32)this->entries_base_swipe_neg_offset, entry_y, pu::ui::render::TextureRenderOptions::WithCustomAlphaAndDimensions(entry_alpha, this->entry_size, this->entry_size)); - } - - if(entry.Is() || entry.Is() || entry.IsSpecial()) { - // Only for valid non-folder entries - const auto border_x = entry_x - ((this->border_size - this->entry_size) / 2); - const auto border_y = entry_y - ((this->border_size - this->entry_size) / 2); - drawer->RenderTexture(this->border_over_icon, (s32)border_x - (s32)this->entries_base_swipe_neg_offset, border_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->border_size, this->border_size)); + #define _DRAW_OVER_ICON(name) { \ + const auto over_icon_x = (s32)(entry_x - ((this->name##_size - this->entry_size) / 2)) - (s32)this->entries_base_swipe_neg_offset; \ + const auto over_icon_y = entry_y - ((this->name##_size - this->entry_size) / 2); \ + drawer->RenderTexture(this->name##_over_icon, over_icon_x, over_icon_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->name##_size, this->name##_size)); \ } - if(g_MenuApplication->IsEntryHomebrewTakeoverApplication(entry)) { - const auto takeover_x = (s32)(entry_x - ((this->hb_takeover_app_size - this->entry_size) / 2)) - (s32)this->entries_base_swipe_neg_offset; - const auto takeover_y = entry_y - ((this->hb_takeover_app_size - this->entry_size) / 2); - drawer->RenderTexture(this->hb_takeover_app_over_icon, takeover_x, takeover_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->hb_takeover_app_size, this->hb_takeover_app_size)); - } - - if(g_MenuApplication->IsEntrySuspended(entry)) { - const auto suspended_x = (s32)(entry_x - ((this->suspended_size - this->entry_size) / 2)) - (s32)this->entries_base_swipe_neg_offset; - const auto suspended_y = entry_y - ((this->suspended_size - this->entry_size) / 2); - drawer->RenderTexture(this->suspended_over_icon, suspended_x, suspended_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->suspended_size, this->suspended_size)); - } + if(entry_icon != nullptr) { + drawer->RenderTexture(entry_icon->Get(), (s32)entry_x - (s32)this->entries_base_swipe_neg_offset, entry_y, pu::ui::render::TextureRenderOptions::WithCustomAlphaAndDimensions(entry_alpha, this->entry_size, this->entry_size)); - if(this->IsEntrySelected(entry_i)) { - const auto selected_x = (s32)(entry_x - ((this->selected_size - this->entry_size) / 2)) - (s32)this->entries_base_swipe_neg_offset; - const auto selected_y = entry_y - ((this->selected_size - this->entry_size) / 2); - drawer->RenderTexture(this->selected_over_icon, selected_x, selected_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->selected_size, this->selected_size)); + const auto progress = this->entry_progresses.at(entry_i); + if(!std::isnan(progress)) { + const auto bar_width = this->entry_size; + const auto bar_height = (u32)(((double)this->entry_size / (double)DefaultEntrySize) * (double)DefaultProgressBarHeight); + drawer->RenderRectangleFill(ProgressBackgroundColor, (s32)entry_x - (s32)this->entries_base_swipe_neg_offset, entry_y + this->entry_size - bar_height, bar_width, bar_height); + + const auto progress_width = (u32)((float)bar_width * progress); + drawer->RenderRectangleFill(ProgressForegroundColor, (s32)entry_x - (s32)this->entries_base_swipe_neg_offset, entry_y + this->entry_size - bar_height, progress_width, bar_height); + } + + if(entry.Is() || entry.Is() || entry.IsSpecial()) { + _DRAW_OVER_ICON(border); + } + + if(IsEntryHomebrewTakeoverApplication(entry)) { + _DRAW_OVER_ICON(hb_takeover_app); + } + + if(IsEntryGamecardInserted(entry)) { + _DRAW_OVER_ICON(gamecard); + } + + if(IsEntryCorrupted(entry)) { + _DRAW_OVER_ICON(corrupted); + } + else if(IsEntryNotLaunchable(entry)) { + _DRAW_OVER_ICON(not_launchable); + } + else if(EntryNeedsUpdate(entry)) { + _DRAW_OVER_ICON(needs_update); + } + + if(g_GlobalSettings.IsEntrySuspended(entry)) { + _DRAW_OVER_ICON(suspended); + } + + this->LoadOverText(entry_i); + auto over_text_tex = this->entry_over_texts.at(entry_i); + if(over_text_tex != nullptr) { + const auto text_width = pu::ui::render::GetTextureWidth(over_text_tex); + const auto text_height = pu::ui::render::GetTextureHeight(over_text_tex); + drawer->RenderTexture(over_text_tex, (s32)entry_x - (s32)this->entries_base_swipe_neg_offset + (u32)((double)(this->entry_size - text_width) / 2.0f), entry_y + (u32)((double)(this->entry_size - text_height) / 2.0f)); + } + + if(this->IsEntrySelected(entry_i)) { + _DRAW_OVER_ICON(selected); + } } } @@ -331,6 +415,7 @@ namespace ul::menu::ui { const auto cur_actual_entry_idx_x = (cur_actual_entry_idx / g_EntryHeightCount) - this->base_entry_idx_x; const auto cur_actual_entry_idx_y = cur_actual_entry_idx % g_EntryHeightCount; + // Cursor is an special over icon const auto cursor_x = x + HorizontalSideMargin + cur_actual_entry_idx_x * (this->entry_size + this->entry_h_margin) - ((this->cursor_size - this->entry_size) / 2) + (this->IsCursorInTransition() ? this->cursor_transition_x : 0); const auto cursor_y = y + this->v_side_margin + cur_actual_entry_idx_y * (this->entry_size + EntryVerticalMargin) - ((this->cursor_size - this->entry_size) / 2) + (this->IsCursorInTransition() ? this->cursor_transition_y : 0); drawer->RenderTexture(this->cursor_over_icon, cursor_x, cursor_y, pu::ui::render::TextureRenderOptions::WithCustomDimensions(this->cursor_size, this->cursor_size)); @@ -377,28 +462,34 @@ namespace ul::menu::ui { const auto cur_entry_idx_y = this->cur_entry_idx % g_EntryHeightCount; if(cur_entry_idx_y > 0) { - this->pre_transition_entry_idx = this->cur_entry_idx; - this->after_transition_entry_idx = this->cur_entry_idx - 1; - this->cursor_transition_y_incr.Start(CursorTransitionIncrementSteps, 0, -cursor_transition_abs_incr_y); - this->cur_entry_change_started_cb(); + if(this->IsNotInLargeSwipe()) { + this->pre_transition_entry_idx = this->cur_entry_idx; + this->after_transition_entry_idx = this->cur_entry_idx - 1; + this->cursor_transition_y_incr.Start(CursorTransitionIncrementSteps, 0, -cursor_transition_abs_incr_y); + this->cur_entry_change_started_cb(); + } } } else if((keys_down & HidNpadButton_Down) || (keys_held & HidNpadButton_StickLDown) || (keys_held & HidNpadButton_StickRDown)) { const auto cur_entry_idx_y = this->cur_entry_idx % g_EntryHeightCount; if((cur_entry_idx_y + 1) < g_EntryHeightCount) { - this->pre_transition_entry_idx = this->cur_entry_idx; - this->after_transition_entry_idx = this->cur_entry_idx + 1; - this->cursor_transition_y_incr.Start(CursorTransitionIncrementSteps, 0, cursor_transition_abs_incr_y); - this->cur_entry_change_started_cb(); + if(this->IsNotInLargeSwipe()) { + this->pre_transition_entry_idx = this->cur_entry_idx; + this->after_transition_entry_idx = this->cur_entry_idx + 1; + this->cursor_transition_y_incr.Start(CursorTransitionIncrementSteps, 0, cursor_transition_abs_incr_y); + this->cur_entry_change_started_cb(); + } } } else if((keys_down & HidNpadButton_Left) || (keys_held & HidNpadButton_StickLLeft) || (keys_held & HidNpadButton_StickRLeft)) { const auto rel_entry_idx_x = (this->cur_entry_idx / g_EntryHeightCount) - this->base_entry_idx_x; if(rel_entry_idx_x > 0) { - this->pre_transition_entry_idx = this->cur_entry_idx; - this->after_transition_entry_idx = this->cur_entry_idx - g_EntryHeightCount; - this->cursor_transition_x_incr.Start(CursorTransitionIncrementSteps, 0, -cursor_transition_abs_incr_x); - this->cur_entry_change_started_cb(); + if(this->IsNotInLargeSwipe()) { + this->pre_transition_entry_idx = this->cur_entry_idx; + this->after_transition_entry_idx = this->cur_entry_idx - g_EntryHeightCount; + this->cursor_transition_x_incr.Start(CursorTransitionIncrementSteps, 0, -cursor_transition_abs_incr_x); + this->cur_entry_change_started_cb(); + } } else if((rel_entry_idx_x == 0) && (this->base_entry_idx_x > 0)) { if(this->IsNotInSwipe()) { @@ -413,10 +504,12 @@ namespace ul::menu::ui { else if((keys_down & HidNpadButton_Right) || (keys_held & HidNpadButton_StickLRight) || (keys_held & HidNpadButton_StickRRight)) { const auto rel_entry_idx_x = (this->cur_entry_idx / g_EntryHeightCount) - this->base_entry_idx_x; if((rel_entry_idx_x + 1) < this->entry_width_count) { - this->pre_transition_entry_idx = this->cur_entry_idx; - this->after_transition_entry_idx = this->cur_entry_idx + g_EntryHeightCount; - this->cursor_transition_x_incr.Start(CursorTransitionIncrementSteps, 0, cursor_transition_abs_incr_x); - this->cur_entry_change_started_cb(); + if(this->IsNotInLargeSwipe()) { + this->pre_transition_entry_idx = this->cur_entry_idx; + this->after_transition_entry_idx = this->cur_entry_idx + g_EntryHeightCount; + this->cursor_transition_x_incr.Start(CursorTransitionIncrementSteps, 0, cursor_transition_abs_incr_x); + this->cur_entry_change_started_cb(); + } } else if((rel_entry_idx_x + 1) == this->entry_width_count) { if(this->IsNotInSwipe()) { @@ -439,7 +532,7 @@ namespace ul::menu::ui { s32 is_prev_entry_suspended_override = -1; if(!reloading) { if(this->IsFocusedNonemptyEntry()) { - is_prev_entry_suspended_override = static_cast(g_MenuApplication->IsEntrySuspended(this->GetFocusedEntry())); + is_prev_entry_suspended_override = static_cast(g_GlobalSettings.IsEntrySuspended(this->GetFocusedEntry())); } this->cur_path = new_path; @@ -463,12 +556,19 @@ namespace ul::menu::ui { entry_icon = {}; } } + for(auto &over_text : this->entry_over_texts) { + pu::ui::render::DeleteTexture(over_text); + } this->entry_icons.clear(); + this->entry_over_texts.clear(); + this->entry_progresses.clear(); this->load_img_entry_alphas.clear(); this->load_img_entry_incrs.clear(); for(u32 i = 0; i < this->entry_total_count; i++) { this->entry_icons.push_back(nullptr); + this->entry_over_texts.push_back(nullptr); + this->entry_progresses.push_back(NAN); this->load_img_entry_alphas.push_back(0xFF); this->load_img_entry_incrs.push_back({}); } @@ -506,13 +606,13 @@ namespace ul::menu::ui { tmp_entry.type = EntryType::Invalid; } this->entry_icons.at(rem_entry.index) = {}; + pu::ui::render::DeleteTexture(this->entry_over_texts.at(rem_entry.index)); } for(const auto &add_entry: this->entries_to_add) { UL_LOG_WARN("Adding entry of type %d at %d", (u32)add_entry.type, add_entry.index); if(add_entry.type != EntryType::Invalid) { while(tmp_entries.size() < (add_entry.index + 1)) { - UL_LOG_WARN("pushpush"); auto &tmp_entry = tmp_entries.emplace_back(); tmp_entry.type = EntryType::Invalid; } @@ -527,6 +627,8 @@ namespace ul::menu::ui { this->ComputeSizes(g_EntryHeightCount); while(this->entry_icons.size() < this->entry_total_count) { this->entry_icons.push_back(nullptr); + this->entry_over_texts.push_back(nullptr); + this->entry_progresses.push_back(NAN); this->load_img_entry_alphas.push_back(0xFF); this->load_img_entry_incrs.push_back({}); } @@ -558,6 +660,12 @@ namespace ul::menu::ui { return (this->cur_entry_idx < this->cur_entries.size()) && !this->cur_entries.at(this->cur_entry_idx).Is(); } + void EntryMenu::UpdateEntryProgress(const u32 idx, const float progress) { + UL_ASSERT_TRUE(idx < this->cur_entries.size()); + + this->entry_progresses.at(idx) = progress; + } + void EntryMenu::IncrementEntryHeightCount() { auto h_count = g_EntryHeightCount; h_count++; @@ -573,6 +681,7 @@ namespace ul::menu::ui { this->base_entry_idx_x = 0; this->cur_entry_idx = 0; + this->CleanOverTexts(); this->NotifyFocusedEntryChanged(-1); g_MenuApplication->FadeIn(); } @@ -600,6 +709,7 @@ namespace ul::menu::ui { this->base_entry_idx_x = 0; this->cur_entry_idx = 0; + this->CleanOverTexts(); this->NotifyFocusedEntryChanged(-1); } g_MenuApplication->FadeIn(); @@ -667,7 +777,7 @@ namespace ul::menu::ui { return false; } else { - return g_MenuApplication->IsEntrySuspended(this->GetFocusedEntry()); + return g_GlobalSettings.IsEntrySuspended(this->GetFocusedEntry()); } } diff --git a/projects/uMenu/source/ul/menu/ui/ui_IMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_IMenuLayout.cpp index 5ba5647..8d9e259 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_IMenuLayout.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_IMenuLayout.cpp @@ -1,7 +1,9 @@ #include
                                                #include
                                                  #include
                                                    +#include
                                                      +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; namespace ul::menu::ui { @@ -10,11 +12,74 @@ namespace ul::menu::ui { s32 g_HomeButtonPressHandleCount = 0; + std::vector g_WeekdayList; + + void EnsureWeekdayList() { + if(g_WeekdayList.empty()) { + for(u32 i = 0; i < 7; i++) { + g_WeekdayList.push_back(GetLanguageString("week_day_short_" + std::to_string(i))); + } + } + } + } - IMenuLayout::IMenuLayout() : Layout(), msg_queue_lock(), msg_queue() { + void IMenuLayout::UpdateConnectionTopIcon(pu::ui::elm::Image::Ref &icon) { + u32 conn_strength; + const auto has_conn = net::HasConnection(conn_strength); + if((this->last_has_connection != has_conn) || (this->last_connection_strength != conn_strength)) { + this->last_has_connection = has_conn; + this->last_connection_strength = conn_strength; + if(has_conn) { + icon->SetImage(TryFindLoadImageHandle("ui/Main/TopIcon/Connection/" + std::to_string(conn_strength))); + } + else { + icon->SetImage(TryFindLoadImageHandle("ui/Main/TopIcon/Connection/None")); + } + } + } + + void IMenuLayout::UpdateDateText(pu::ui::elm::TextBlock::Ref &text) { + // TODO (low priority): do not set the date all the time? + const auto cur_date = os::GetCurrentDate(g_WeekdayList); + text->SetText(cur_date); + } + + void IMenuLayout::UpdateTimeText(pu::ui::elm::TextBlock::Ref &text) { + u32 cur_h; + u32 cur_min; + u32 cur_sec; + os::GetCurrentTime(cur_h, cur_min, cur_sec); + char cur_time_str[0x10] = {}; + sprintf(cur_time_str, "%02d:%02d", cur_h, cur_min); + text->SetText(cur_time_str); + } + + void IMenuLayout::UpdateBatteryTextAndTopIcons(pu::ui::elm::TextBlock::Ref &text, pu::ui::elm::Image::Ref &base_top_icon, pu::ui::elm::Image::Ref &charging_top_icon) { + const auto battery_level = os::GetBatteryLevel(); + const auto is_charging = os::IsConsoleCharging(); + if((this->last_battery_level != battery_level) || (this->last_battery_is_charging != is_charging)) { + this->last_battery_level = battery_level; + this->last_battery_is_charging = is_charging; + + const auto battery_str = std::to_string(battery_level) + "%"; + text->SetText(battery_str); + + auto battery_lvl_norm = (1 + (battery_level / 10)) * 10; // Converts it to 10, 20, ..., 100 + if(battery_lvl_norm > 100) { + battery_lvl_norm = 100; + } + const auto battery_img = "ui/Main/TopIcon/Battery/" + std::to_string(battery_lvl_norm); + base_top_icon->SetImage(TryFindLoadImageHandle(battery_img)); + charging_top_icon->SetVisible(is_charging); + } + } + + IMenuLayout::IMenuLayout() : Layout(), msg_queue_lock(), msg_queue(), last_has_connection(false), last_connection_strength(0), last_battery_level(0), last_battery_is_charging(false) { this->SetBackgroundImage(GetBackgroundTexture()); this->SetOnInput(std::bind(&IMenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + + EnsureWeekdayList(); } void IMenuLayout::OnInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) { @@ -42,7 +107,6 @@ namespace ul::menu::ui { } case smi::MenuMessage::SdCardEjected: { this->msg_queue.pop(); - while(true) { const auto option = g_MenuApplication->DisplayDialog(GetLanguageString("sd_card"), GetLanguageString("sd_card_ejected"), { GetLanguageString("shutdown"), GetLanguageString("reboot") }, false); if(option == 0) { @@ -57,13 +121,43 @@ namespace ul::menu::ui { case smi::MenuMessage::PreviousLaunchFailure: { g_MenuApplication->NotifyLaunchFailed(); this->msg_queue.pop(); - break; } case smi::MenuMessage::ChosenHomebrew: { g_MenuApplication->NotifyHomebrewChosen(first_msg.chosen_hb.nro_path); this->msg_queue.pop(); + break; + } + case smi::MenuMessage::FinishedSleep: { + this->msg_queue.pop(); + bool lockscreen_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::LockscreenEnabled, lockscreen_enabled)); + if(lockscreen_enabled) { + g_MenuApplication->LoadMenuByType(MenuType::Lockscreen, false); + } + break; + } + case smi::MenuMessage::ApplicationRecordsChanged: { + g_MenuApplication->NotifyApplicationRecordReloadNeeded(); + if(first_msg.app_records_changed.records_added_or_deleted) { + // Need to also reload entries as well + g_MenuApplication->NotifyApplicationEntryReloadNeeded(); + } + this->msg_queue.pop(); + break; + } + case smi::MenuMessage::ApplicationVerifyProgress: { + const auto progress = (float)first_msg.app_verify_progress.done / (float)first_msg.app_verify_progress.total; + g_MenuApplication->NotifyApplicationVerifyProgress(first_msg.app_verify_progress.app_id, progress); + this->msg_queue.pop(); + break; + } + case smi::MenuMessage::ApplicationVerifyResult: { + g_MenuApplication->NotifyApplicationVerifyProgress(first_msg.app_verify_rc.app_id, NAN); + g_MenuApplication->NotifyVerifyFinished(first_msg.app_verify_rc.app_id, first_msg.app_verify_rc.rc, first_msg.app_verify_rc.detail_rc); + + this->msg_queue.pop(); break; } default: { @@ -74,16 +168,10 @@ namespace ul::menu::ui { } } - /* TODO (new) - if(!hidIsControllerConnected(CONTROLLER_HANDHELD) && !hidIsControllerConnected(CONTROLLER_PLAYER_1)) { - ShowControllerSupport(); - } - */ - this->OnMenuInput(keys_down, keys_up, keys_held, touch_pos); } - void IMenuLayout::NotifyMessageContext(const smi::MenuMessageContext msg_ctx) { + void IMenuLayout::NotifyMessageContext(const smi::MenuMessageContext &msg_ctx) { ScopedLock lk(this->msg_queue_lock); // Remove consequent homemenu requests @@ -94,6 +182,13 @@ namespace ul::menu::ui { } } } + else if(msg_ctx.msg == smi::MenuMessage::ApplicationVerifyProgress) { + if(!this->msg_queue.empty()) { + if(this->msg_queue.front().msg == smi::MenuMessage::ApplicationVerifyProgress) { + this->msg_queue.pop(); + } + } + } this->msg_queue.push(msg_ctx); } diff --git a/projects/uMenu/source/ul/menu/ui/ui_InputBar.cpp b/projects/uMenu/source/ul/menu/ui/ui_InputBar.cpp index 8243ea3..d620e9e 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_InputBar.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_InputBar.cpp @@ -2,7 +2,6 @@ #include
                                                        #include
                                                          -extern ul::cfg::Config g_Config; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; namespace ul::menu::ui { diff --git a/projects/uMenu/source/ul/menu/ui/ui_LockscreenMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_LockscreenMenuLayout.cpp new file mode 100644 index 0000000..cacd4f6 --- /dev/null +++ b/projects/uMenu/source/ul/menu/ui/ui_LockscreenMenuLayout.cpp @@ -0,0 +1,76 @@ +#include
                                                            +#include
                                                              + +extern ul::menu::ui::GlobalSettings g_GlobalSettings; +extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; + +namespace ul::menu::ui { + + namespace { + + inline void Unlock() { + if(accountUidIsValid(&g_GlobalSettings.system_status.selected_user)) { + g_MenuApplication->LoadMenuByType(MenuType::Main); + } + else { + g_MenuApplication->LoadMenuByType(MenuType::Startup); + } + } + + } + + LockscreenMenuLayout::LockscreenMenuLayout() : IMenuLayout() { + this->info_text = pu::ui::elm::TextBlock::New(0, 0, GetLanguageString("lockscreen_info")); + this->info_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "info_text", this->info_text); + this->Add(this->info_text); + + this->connection_top_icon = pu::ui::elm::Image::New(0, 0, TryFindLoadImageHandle("ui/Main/TopIcon/Connection/None")); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "connection_top_icon", this->connection_top_icon); + this->Add(this->connection_top_icon); + + this->time_text = pu::ui::elm::TextBlock::New(0, 0, "..."); + this->time_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "time_text", this->time_text); + this->Add(this->time_text); + + this->date_text = pu::ui::elm::TextBlock::New(0, 0, "..."); + this->date_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "date_text", this->date_text); + this->Add(this->date_text); + + this->battery_text = pu::ui::elm::TextBlock::New(0, 0, "..."); + this->battery_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "battery_text", this->battery_text); + this->Add(this->battery_text); + + this->battery_top_icon = pu::ui::elm::Image::New(0, 0, TryFindLoadImageHandle("ui/Main/TopIcon/Battery/100")); + this->battery_charging_top_icon = pu::ui::elm::Image::New(0, 0, TryFindLoadImageHandle("ui/Main/TopIcon/Battery/Charging")); + this->battery_charging_top_icon->SetVisible(false); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "battery_top_icon", this->battery_top_icon); + g_MenuApplication->ApplyConfigForElement("lockscreen_menu", "battery_top_icon", this->battery_charging_top_icon); + this->Add(this->battery_top_icon); + this->Add(this->battery_charging_top_icon); + } + + void LockscreenMenuLayout::OnMenuInput(const u64 keys_down, const u64 keys_up, const u64 keys_held, const pu::ui::TouchPoint touch_pos) { + if(keys_down != 0) { + Unlock(); + } + + this->UpdateConnectionTopIcon(this->connection_top_icon); + this->UpdateTimeText(this->time_text); + this->UpdateDateText(this->date_text); + this->UpdateBatteryTextAndTopIcons(this->battery_text, this->battery_top_icon, this->battery_charging_top_icon); + } + + bool LockscreenMenuLayout::OnHomeButtonPress() { + Unlock(); + return true; + } + + void LockscreenMenuLayout::DisposeAudio() { + + } + +} diff --git a/projects/uMenu/source/ul/menu/ui/ui_MainMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_MainMenuLayout.cpp index a9583d1..c4f4afb 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_MainMenuLayout.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_MainMenuLayout.cpp @@ -9,8 +9,8 @@ #include
                                                                #include
                                                                  +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; namespace ul::menu::ui { @@ -30,12 +30,14 @@ namespace ul::menu::ui { } inline bool IsEntryNonRemovable(const Entry &entry) { + /* if(strcmp(entry.hb_info.nro_target.nro_path, ul::HbmenuPath) == 0) { return true; } if(strcmp(entry.hb_info.nro_target.nro_path, ul::ManagerPath) == 0) { return true; } + */ if(entry.IsSpecial()) { return true; @@ -44,7 +46,6 @@ namespace ul::menu::ui { return false; } - std::vector g_WeekdayList; std::string g_UserName; char g_MenuFsPathBuffer[FS_MAX_PATH] = {}; @@ -74,7 +75,7 @@ namespace ul::menu::ui { const auto option = g_MenuApplication->DisplayDialog(GetLanguageString("user_logoff"), GetLanguageString("user_logoff_opt"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true ); if(option == 0) { auto log_off = false; - if(g_MenuApplication->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { const auto option_2 = g_MenuApplication->DisplayDialog(GetLanguageString("suspended_app"), GetLanguageString("user_logoff_app_suspended"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); if(option_2 == 0) { log_off = true; @@ -85,12 +86,13 @@ namespace ul::menu::ui { } if(log_off) { - if(g_MenuApplication->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { this->DoTerminateApplication(); } pu::audio::PlaySfx(this->logoff_sfx); + g_GlobalSettings.system_status.selected_user = {}; g_MenuApplication->LoadMenuByType(MenuType::Startup, true, [&]() { this->MoveToRoot(false); }); @@ -155,9 +157,9 @@ namespace ul::menu::ui { else if(cur_entry.Is() || cur_entry.Is()) { auto do_launch_entry = true; - if(g_MenuApplication->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { // Play animations, then resume the suspended hb/app - if(g_MenuApplication->IsEntrySuspended(cur_entry)) { + if(g_GlobalSettings.IsEntrySuspended(cur_entry)) { if(this->mode == SuspendedImageMode::Focused) { this->StartResume(); } @@ -169,17 +171,60 @@ namespace ul::menu::ui { if(do_launch_entry && cur_entry.Is()) { do_launch_entry = false; this->HandleCloseSuspended(); - do_launch_entry = !g_MenuApplication->IsSuspended(); + do_launch_entry = !g_GlobalSettings.IsSuspended(); } } - if(do_launch_entry && cur_entry.Is() && !cur_entry.app_info.IsLaunchable()) { - UL_LOG_WARN("Tried to launch application with type 0x%X", cur_entry.app_info.record.type); - pu::audio::PlaySfx(this->error_sfx); - // TODO: gamecard detection? - // TODO: support for "fixing" corrupted apps, like regular homemenu? - g_MenuApplication->ShowNotification(GetLanguageString("app_not_launchable")); - do_launch_entry = false; + if(do_launch_entry && cur_entry.Is()) { + if(cur_entry.app_info.HasViewFlag()) { + pu::audio::PlaySfx(this->error_sfx); + + auto is_being_verified = false; + for(const auto app_id: g_GlobalSettings.in_verify_app_ids) { + if(app_id == cur_entry.app_info.app_id) { + g_MenuApplication->ShowNotification(GetLanguageString("app_verify_wait")); + is_being_verified = true; + break; + } + } + + if(!is_being_verified) { + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("app_launch"), GetLanguageString("app_corrupted"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); + if(opt == 0) { + UL_RC_ASSERT(smi::StartVerifyApplication(cur_entry.app_info.app_id)); + g_GlobalSettings.in_verify_app_ids.push_back(cur_entry.app_info.app_id); + } + } + + do_launch_entry = false; + } + else if(cur_entry.app_info.HasViewFlag() && !cur_entry.app_info.HasViewFlag()) { + pu::audio::PlaySfx(this->error_sfx); + g_MenuApplication->ShowNotification(GetLanguageString("app_no_gamecard")); + do_launch_entry = false; + } + else if(!cur_entry.app_info.HasViewFlag()) { + UL_LOG_WARN("Tried to launch non-launchable application 0x%016lX with record type 0x%X and view flags 0x%D", cur_entry.app_info.app_id, cur_entry.app_info.record.type, cur_entry.app_info.view.flags); + pu::audio::PlaySfx(this->error_sfx); + g_MenuApplication->ShowNotification(GetLanguageString("app_not_launchable")); + do_launch_entry = false; + } + else { + if(cur_entry.app_info.NeedsUpdate()) { + do_launch_entry = false; + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("app_launch"), "launch req ver " + std::to_string(cur_entry.app_info.launch_required_version) + " VS actual ver " + std::to_string(cur_entry.app_info.version) + "\n\n" + GetLanguageString("app_needs_update"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); + if(opt == 0) { + const auto rc = avmPushLaunchVersion(cur_entry.app_info.app_id, cur_entry.app_info.version); + if(R_SUCCEEDED(rc)) { + cur_entry.app_info.launch_required_version = cur_entry.app_info.version; + do_launch_entry = true; + } + else { + g_MenuApplication->DisplayDialog(GetLanguageString("app_launch"), GetLanguageString("app_launch_version_reset_error") + ": " + util::FormatResultDisplay(rc), { GetLanguageString("ok") }, true); + } + } + } + } } if(do_launch_entry) { @@ -189,7 +234,7 @@ namespace ul::menu::ui { else { pu::audio::PlaySfx(this->launch_app_sfx); - const auto rc = smi::LaunchApplication(cur_entry.app_info.record.application_id); + const auto rc = smi::LaunchApplication(cur_entry.app_info.app_id); if(R_SUCCEEDED(rc)) { g_MenuApplication->Finalize(); return; @@ -237,6 +282,12 @@ namespace ul::menu::ui { ShowAlbum(); break; } + case EntryType::SpecialEntryAmiibo: { + // pu::audio::PlaySfx(this->open_album_sfx); + + ShowCabinet(); + break; + } default: break; } @@ -265,10 +316,15 @@ namespace ul::menu::ui { SwkbdConfig cfg; UL_RC_ASSERT(swkbdCreate(&cfg, 0)); swkbdConfigSetGuideText(&cfg, GetLanguageString("swkbd_folder_name_guide").c_str()); - char new_folder_name[500] = {}; - const auto rc = swkbdShow(&cfg, new_folder_name, sizeof(new_folder_name)); + char new_folder_name_buf[500] = {}; + const auto rc = swkbdShow(&cfg, new_folder_name_buf, sizeof(new_folder_name_buf)); swkbdClose(&cfg); - if(R_SUCCEEDED(rc)) { + + std::string new_folder_name(new_folder_name_buf); + while(!new_folder_name.empty() && new_folder_name.at(0) == ' ') { + new_folder_name.erase(new_folder_name.begin()); + } + if(R_SUCCEEDED(rc) && !new_folder_name.empty()) { pu::audio::PlaySfx(this->create_folder_sfx); const auto folder_entry = CreateFolderEntry(this->entry_menu->GetPath(), new_folder_name, this->entry_menu->GetFocusedEntryIndex()); @@ -299,7 +355,7 @@ namespace ul::menu::ui { else if(this->entry_menu->IsFocusedNonemptyEntry()) { auto &cur_entry = this->entry_menu->GetFocusedEntry(); - if(g_MenuApplication->IsSuspended() && g_MenuApplication->IsEntrySuspended(cur_entry)) { + if(g_GlobalSettings.IsSuspended() && g_GlobalSettings.IsEntrySuspended(cur_entry)) { this->HandleCloseSuspended(); } else { @@ -379,7 +435,7 @@ namespace ul::menu::ui { if(option == 0) { const auto option_2 = g_MenuApplication->DisplayDialog(GetLanguageString("app_launch"), GetLanguageString("app_take_over_select"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); if(option_2 == 0) { - g_MenuApplication->SetTakeoverApplicationId(cur_entry.app_info.record.application_id); + g_GlobalSettings.SetHomebrewTakeoverApplicationId(cur_entry.app_info.record.application_id); g_MenuApplication->ShowNotification(GetLanguageString("app_take_over_done")); } } @@ -424,7 +480,7 @@ namespace ul::menu::ui { this->entry_menu_left_icon->SetVisible(!this->entry_menu->IsMenuStart()); - g_MenuApplication->UpdateMenuIndex(this->entry_menu->GetFocusedEntryIndex()); + g_GlobalSettings.UpdateMenuIndex(this->entry_menu->GetFocusedEntryIndex()); if(this->entry_menu->IsFocusedNonemptyEntry()) { auto &cur_entry = this->entry_menu->GetFocusedEntry(); @@ -470,6 +526,11 @@ namespace ul::menu::ui { this->cur_entry_main_text->SetText(GetLanguageString("special_entry_text_album")); this->cur_entry_sub_text->SetVisible(false); } + else if(cur_entry.Is()) { + this->SetTopMenuDefault(); + this->cur_entry_main_text->SetText(GetLanguageString("special_entry_text_amiibo")); + this->cur_entry_sub_text->SetVisible(false); + } else { if(cur_entry.Is()) { this->SetTopMenuApplication(); @@ -477,16 +538,31 @@ namespace ul::menu::ui { else { this->SetTopMenuHomebrew(); } - + cur_entry.TryLoadControlData(); - if(cur_entry.control.IsLoaded()) { + + if(!cur_entry.control.name.empty()) { this->cur_entry_main_text->SetText(cur_entry.control.name); - this->cur_entry_sub_text->SetText(cur_entry.control.version + ", " + cur_entry.control.author); } else { - // TODO (low priority): anything better to show when control data doesn't load? (really shouldn't happen anyway) this->cur_entry_main_text->SetText("???"); - this->cur_entry_sub_text->SetText("???"); + } + + if(!cur_entry.control.author.empty()) { + if(!cur_entry.control.version.empty()) { + this->cur_entry_sub_text->SetText(cur_entry.control.version + ", " + cur_entry.control.author); + } + else { + this->cur_entry_sub_text->SetText(cur_entry.control.author); + } + } + else { + if(!cur_entry.control.version.empty()) { + this->cur_entry_sub_text->SetText(cur_entry.control.version); + } + else { + this->cur_entry_sub_text->SetText("???"); + } } } } @@ -496,7 +572,7 @@ namespace ul::menu::ui { this->cur_entry_sub_text->SetVisible(false); } - if(g_MenuApplication->IsSuspended() && has_prev_entry) { + if(g_GlobalSettings.IsSuspended() && has_prev_entry) { if(is_prev_entry_suspended && !is_cur_entry_suspended) { this->mode = SuspendedImageMode::HidingLostFocus; } @@ -506,14 +582,9 @@ namespace ul::menu::ui { } } - MainMenuLayout::MainMenuLayout(const u8 *captured_screen_buf, const u8 min_alpha) : IMenuLayout(), last_has_connection(false), last_connection_strength(0), last_battery_lvl(0), last_is_charging(false), last_quick_menu_on(false), start_time_elapsed(false), min_alpha(min_alpha), mode(SuspendedImageMode::ShowingAfterStart), suspended_screen_alpha(0xFF) { + MainMenuLayout::MainMenuLayout(const u8 *captured_screen_buf, const u8 min_alpha) : IMenuLayout(), last_quick_menu_on(false), start_time_elapsed(false), min_alpha(min_alpha), mode(SuspendedImageMode::ShowingAfterStart), suspended_screen_alpha(0xFF) { // TODO (low priority): like nxlink but for sending themes and quickly being able to test them? - this->cur_folder_path = g_MenuApplication->GetStatus().last_menu_path; - - g_WeekdayList.clear(); - for(u32 i = 0; i < 7; i++) { - g_WeekdayList.push_back(GetLanguageString("week_day_short_" + std::to_string(i))); - } + this->cur_folder_path = g_GlobalSettings.system_status.last_menu_path; if(captured_screen_buf != nullptr) { this->suspended_screen_img = RawRgbaImage::New(0, 0, captured_screen_buf, 1280, 720, 4); @@ -608,7 +679,7 @@ namespace ul::menu::ui { this->Add(this->cur_path_text); - this->entry_menu = EntryMenu::New(0, 0, g_MenuApplication->GetStatus().last_menu_fs_path, std::bind(&MainMenuLayout::menu_EntryInputPressed, this, std::placeholders::_1), std::bind(&MainMenuLayout::menu_FocusedEntryChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), [&]() { + this->entry_menu = EntryMenu::New(0, 0, g_GlobalSettings.system_status.last_menu_fs_path, std::bind(&MainMenuLayout::menu_EntryInputPressed, this, std::placeholders::_1), std::bind(&MainMenuLayout::menu_FocusedEntryChanged, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), [&]() { pu::audio::PlaySfx(this->cursor_move_sfx); }); g_MenuApplication->ApplyConfigForElement("main_menu", "entry_menu", this->entry_menu); @@ -773,7 +844,7 @@ namespace ul::menu::ui { this->input_bar->AddSetInput(HidNpadButton_B, GetLanguageString("input_logoff")); } - if(g_MenuApplication->IsSuspended() && !this->entry_menu->IsFocusedEntrySuspended()) { + if(g_GlobalSettings.IsSuspended() && !this->entry_menu->IsFocusedEntrySuspended()) { this->input_bar->AddSetInput(InputBar::MetaHomeNpadButton, GetLanguageString("input_resume_suspended")); } @@ -785,48 +856,10 @@ namespace ul::menu::ui { const auto now_tp = std::chrono::steady_clock::now(); - u32 conn_strength; - const auto has_conn = net::HasConnection(conn_strength); - if((this->last_has_connection != has_conn) || (this->last_connection_strength != conn_strength)) { - this->last_has_connection = has_conn; - this->last_connection_strength = conn_strength; - if(has_conn) { - this->connection_top_icon->SetImage(TryFindLoadImageHandle("ui/Main/TopIcon/Connection/" + std::to_string(conn_strength))); - } - else { - this->connection_top_icon->SetImage(TryFindLoadImageHandle("ui/Main/TopIcon/Connection/None")); - } - } - - u32 cur_h; - u32 cur_min; - u32 cur_sec; - os::GetCurrentTime(cur_h, cur_min, cur_sec); - char cur_time_str[0x10] = {}; - sprintf(cur_time_str, "%02d:%02d", cur_h, cur_min); - this->time_text->SetText(cur_time_str); - - // TODO (low priority): do not set the date all the time? - const auto cur_date = os::GetCurrentDate(g_WeekdayList); - this->date_text->SetText(cur_date); - - const auto battery_lvl = os::GetBatteryLevel(); - const auto is_charging = os::IsConsoleCharging(); - if((this->last_battery_lvl != battery_lvl) || (this->last_is_charging != is_charging)) { - this->last_battery_lvl = battery_lvl; - this->last_is_charging = is_charging; - - const auto battery_str = std::to_string(battery_lvl) + "%"; - this->battery_text->SetText(battery_str); - - auto battery_lvl_norm = (1 + (battery_lvl / 10)) * 10; // Converts it to 10, 20, ..., 100 - if(battery_lvl_norm > 100) { - battery_lvl_norm = 100; - } - const auto battery_img = "ui/Main/TopIcon/Battery/" + std::to_string(battery_lvl_norm); - this->battery_top_icon->SetImage(TryFindLoadImageHandle(battery_img)); - this->battery_charging_top_icon->SetVisible(is_charging); - } + this->UpdateConnectionTopIcon(this->connection_top_icon); + this->UpdateTimeText(this->time_text); + this->UpdateDateText(this->date_text); + this->UpdateBatteryTextAndTopIcons(this->battery_text, this->battery_top_icon, this->battery_charging_top_icon); if(!this->start_time_elapsed) { // Wait a bit before handling sent messages @@ -835,7 +868,9 @@ namespace ul::menu::ui { } } - if(this->start_time_elapsed) { + const auto can_show_stuff = this->start_time_elapsed && ((this->mode == SuspendedImageMode::Focused) || (this->mode == SuspendedImageMode::NotFocused)); + + if(can_show_stuff) { if(g_MenuApplication->GetConsumeLastLaunchFailed()) { pu::audio::PlaySfx(this->error_sfx); g_MenuApplication->DisplayDialog(GetLanguageString("app_launch"), GetLanguageString("app_unexpected_error"), { GetLanguageString("ok") }, true); @@ -845,7 +880,7 @@ namespace ul::menu::ui { pu::audio::PlaySfx(this->create_hb_entry_sfx); // TODO (low priority): custom argv option? - const auto hb_entry = CreateHomebrewEntry(this->entry_menu->GetPath(), nro_path, nro_path, this->entry_menu->GetFocusedEntryIndex()); + const auto hb_entry = CreateHomebrewEntry(g_GlobalSettings.initial_last_menu_fs_path, nro_path, nro_path, g_GlobalSettings.initial_last_menu_index); this->entry_menu->NotifyEntryAdded(hb_entry); this->entry_menu->OrganizeUpdateEntries(); g_MenuApplication->ShowNotification(GetLanguageString("menu_chosen_hb_added")); @@ -858,6 +893,31 @@ namespace ul::menu::ui { } } + if(g_MenuApplication->GetConsumeApplicationRecordReloadNeeded()) { + // Reload just entry infos + ReloadApplicationEntryInfos(this->entry_menu->GetEntries()); + } + + if(g_MenuApplication->GetConsumeApplicationEntryReloadNeeded()) { + // Reload entries + this->MoveTo("", true); + } + + if(g_MenuApplication->HasVerifyFinishedPending()) { + const auto app_id = g_MenuApplication->GetConsumeVerifyFinishedApplicationId(); + const auto rc = g_MenuApplication->GetConsumeVerifyResult(); + const auto detail_rc = g_MenuApplication->GetConsumeVerifyDetailResult(); + + if(R_SUCCEEDED(rc) && R_SUCCEEDED(detail_rc)) { + g_MenuApplication->DisplayDialog(GetLanguageString("app_verify"), GetLanguageString("app_verify_ok"), { GetLanguageString("ok") }, true, pu::sdl2::TextureHandle::New(pu::ui::render::LoadImage(GetApplicationCacheIconPath(app_id)))); + + ReloadApplicationEntryInfos(this->entry_menu->GetEntries()); + } + else { + g_MenuApplication->DisplayDialog(GetLanguageString("app_verify"), GetLanguageString("app_verify_error") + ":\n\n" + util::FormatResultDisplay(rc) + "\n" + util::FormatResultDisplay(detail_rc), { GetLanguageString("ok") }, true); + } + } + if(this->suspended_screen_img) { switch(this->mode) { case SuspendedImageMode::ShowingAfterStart: { @@ -941,7 +1001,7 @@ namespace ul::menu::ui { bool MainMenuLayout::OnHomeButtonPress() { pu::audio::PlaySfx(this->home_press_sfx); - if(g_MenuApplication->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { this->StartResume(); } else { @@ -972,9 +1032,9 @@ namespace ul::menu::ui { } } - void MainMenuLayout::NotifyLoad(const AccountUid user) { - UL_RC_ASSERT(acc::GetAccountName(user, g_UserName)); - this->entry_menu->Initialize(g_MenuApplication->GetStatus().last_menu_index); + void MainMenuLayout::NotifyLoad() { + UL_RC_ASSERT(acc::GetAccountName(g_GlobalSettings.system_status.selected_user, g_UserName)); + this->entry_menu->Initialize(g_GlobalSettings.system_status.last_menu_index); this->quick_menu->UpdateItems(); } @@ -996,12 +1056,12 @@ namespace ul::menu::ui { g_MenuApplication->Finalize(); } else if(option == 1) { - if(g_MenuApplication->GetTakeoverApplicationId() != 0) { + if(g_GlobalSettings.cache_hb_takeover_app_id != 0) { auto launch = true; - if(g_MenuApplication->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { launch = false; this->HandleCloseSuspended(); - if(!g_MenuApplication->IsSuspended()) { + if(!g_GlobalSettings.IsSuspended()) { launch = true; } } @@ -1036,7 +1096,7 @@ namespace ul::menu::ui { auto &entries = this->entry_menu->GetEntries(); u32 i = 0; for(const auto &entry : entries) { - if(g_MenuApplication->IsEntrySuspended(entry)) { + if(g_GlobalSettings.IsEntrySuspended(entry)) { break; } i++; @@ -1050,7 +1110,7 @@ namespace ul::menu::ui { entries.at(i).ReloadApplicationInfo(); } - g_MenuApplication->ResetSuspendedApplication(); + g_GlobalSettings.ResetSuspendedApplication(); this->mode = SuspendedImageMode::NotFocused; if(this->suspended_screen_img) { diff --git a/projects/uMenu/source/ul/menu/ui/ui_MenuApplication.cpp b/projects/uMenu/source/ul/menu/ui/ui_MenuApplication.cpp index 9ab1156..e94b0d5 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_MenuApplication.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_MenuApplication.cpp @@ -1,13 +1,9 @@ #include
                                                                    #include
                                                                      +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; - -extern ul::util::JSON g_DefaultLanguage; -extern ul::util::JSON g_MainLanguage; - namespace ul::menu::ui { namespace { @@ -17,21 +13,30 @@ namespace ul::menu::ui { } std::string GetLanguageString(const std::string &name) { - return cfg::GetLanguageString(g_MainLanguage, g_DefaultLanguage, name); + return cfg::GetLanguageString(g_GlobalSettings.main_lang, g_GlobalSettings.default_lang, name); } - void OnMessage(const smi::MenuMessageContext msg_ctx) { + void OnMessage(const smi::MenuMessageContext &msg_ctx) { g_MenuApplication->GetLayout()->NotifyMessageContext(msg_ctx); } void MenuApplication::OnLoad() { + this->launch_failed = false; this->pending_gc_mount_rc = ResultSuccess; - LoadCommonTextures(); + this->needs_app_records_reload = false; + this->needs_app_entries_reload = false; + memset(this->chosen_hb, 0, sizeof(this->chosen_hb)); + this->verify_finished_app_id = 0; + this->verify_rc = ResultSuccess; + this->verify_detail_rc = ResultSuccess; - UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, this->takeover_app_id)); + // TODO: customize + this->SetFadeAlphaIncrementStepCount(FastFadeAlphaIncrementSteps); + + LoadCommonTextures(); u8 *screen_capture_buf = nullptr; - if(this->IsSuspended()) { + if(g_GlobalSettings.IsSuspended()) { screen_capture_buf = new u8[RawScreenRgbaBufferSize](); bool flag; appletGetLastApplicationCaptureImageEx(screen_capture_buf, RawScreenRgbaBufferSize, &flag); @@ -47,11 +52,12 @@ namespace ul::menu::ui { _LOAD_MENU_BGM(startup_menu, "Startup") _LOAD_MENU_BGM(themes_menu, "Themes") _LOAD_MENU_BGM(settings_menu, "Settings") + _LOAD_MENU_BGM(lockscreen_menu, "Lockscreen") this->bgm_json = ul::util::JSON::object(); const auto rc = ul::util::LoadJSONFromFile(this->bgm_json, TryGetActiveThemeResource("sound/BGM.json")); if(R_SUCCEEDED(rc)) { - #define _LOAD_MENU_BGM_SETTINGS(menu, bgm_name) { \ + #define _LOAD_MENU_BGM_SETTINGS(menu) { \ if(this->bgm_json.count(#menu)) { \ const auto menu_json = this->bgm_json[#menu]; \ this->menu##_bgm.bgm_loop = menu_json.value("bgm_loop", DefaultBgmLoop); \ @@ -59,10 +65,11 @@ namespace ul::menu::ui { this->menu##_bgm.bgm_fade_out_ms = menu_json.value("bgm_fade_out_ms", DefaultBgmFadeOutMs); \ } \ } - _LOAD_MENU_BGM_SETTINGS(main_menu, "Main") - _LOAD_MENU_BGM_SETTINGS(startup_menu, "Startup") - _LOAD_MENU_BGM_SETTINGS(themes_menu, "Themes") - _LOAD_MENU_BGM_SETTINGS(settings_menu, "Settings") + _LOAD_MENU_BGM_SETTINGS(main_menu) + _LOAD_MENU_BGM_SETTINGS(startup_menu) + _LOAD_MENU_BGM_SETTINGS(themes_menu) + _LOAD_MENU_BGM_SETTINGS(settings_menu) + _LOAD_MENU_BGM_SETTINGS(lockscreen_menu) if(this->bgm_json.count("bgm_loop")) { const auto global_loop = this->bgm_json.value("bgm_loop", DefaultBgmLoop); @@ -70,6 +77,7 @@ namespace ul::menu::ui { this->startup_menu_bgm.bgm_loop = global_loop; this->themes_menu_bgm.bgm_loop = global_loop; this->settings_menu_bgm.bgm_loop = global_loop; + this->lockscreen_menu_bgm.bgm_loop = global_loop; } if(this->bgm_json.count("bgm_fade_in_ms")) { const auto global_fade_in_ms = this->bgm_json.value("bgm_fade_in_ms", DefaultBgmFadeInMs); @@ -77,6 +85,7 @@ namespace ul::menu::ui { this->startup_menu_bgm.bgm_fade_in_ms = global_fade_in_ms; this->themes_menu_bgm.bgm_fade_in_ms = global_fade_in_ms; this->settings_menu_bgm.bgm_fade_in_ms = global_fade_in_ms; + this->lockscreen_menu_bgm.bgm_fade_in_ms = global_fade_in_ms; } if(this->bgm_json.count("bgm_fade_out_ms")) { const auto global_fade_out_ms = this->bgm_json.value("bgm_fade_out_ms", DefaultBgmFadeOutMs); @@ -84,6 +93,7 @@ namespace ul::menu::ui { this->startup_menu_bgm.bgm_fade_out_ms = global_fade_out_ms; this->themes_menu_bgm.bgm_fade_out_ms = global_fade_out_ms; this->settings_menu_bgm.bgm_fade_out_ms = global_fade_out_ms; + this->lockscreen_menu_bgm.bgm_fade_out_ms = global_fade_out_ms; } } else { @@ -123,14 +133,11 @@ namespace ul::menu::ui { _GET_UI_COLOR("dialog_color", this->dialog_clr); _GET_UI_COLOR("dialog_over_color", this->dialog_over_clr); - this->launch_failed = false; - memset(this->chosen_hb, 0, sizeof(this->chosen_hb)); - u32 suspended_app_final_alpha; _GET_UI_VALUE("suspended_app_final_alpha", u32, suspended_app_final_alpha); switch(this->start_mode) { - case smi::MenuStartMode::StartupMenu: { + case smi::MenuStartMode::Start: { break; } default: { @@ -144,10 +151,19 @@ namespace ul::menu::ui { this->main_menu_lyt = MainMenuLayout::New(screen_capture_buf, static_cast(suspended_app_final_alpha)); this->themes_menu_lyt = ThemesMenuLayout::New(); this->settings_menu_lyt = SettingsMenuLayout::New(); + this->lockscreen_menu_lyt = LockscreenMenuLayout::New(); + this->loaded_menu = MenuType::Main; switch(this->start_mode) { - case smi::MenuStartMode::StartupMenu: { - this->LoadMenuByType(MenuType::Startup, false); + case smi::MenuStartMode::Start: { + bool lockscreen_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::LockscreenEnabled, lockscreen_enabled)); + if(lockscreen_enabled) { + this->LoadMenuByType(MenuType::Lockscreen, false); + } + else { + this->LoadMenuByType(MenuType::Startup, false); + } break; } case smi::MenuStartMode::SettingsMenu: { @@ -187,12 +203,12 @@ namespace ul::menu::ui { break; } case MenuType::Main: { - this->main_menu_lyt->NotifyLoad(this->system_status.selected_user); + this->main_menu_lyt->NotifyLoad(); this->LoadLayout(this->main_menu_lyt); break; } case MenuType::Settings: { - this->settings_menu_lyt->Reload(true); + this->settings_menu_lyt->Reload(false); this->LoadLayout(this->settings_menu_lyt); break; } @@ -201,6 +217,10 @@ namespace ul::menu::ui { this->LoadLayout(this->themes_menu_lyt); break; } + case MenuType::Lockscreen: { + this->LoadLayout(this->lockscreen_menu_lyt); + break; + } } this->loaded_menu = type; @@ -212,14 +232,6 @@ namespace ul::menu::ui { } } - void MenuApplication::SetTakeoverApplicationId(const u64 app_id) { - this->takeover_app_id = app_id; - - // No need to save config, uSystem deals with that - UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::HomebrewApplicationTakeoverApplicationId, this->takeover_app_id)); - SaveConfig(); - } - void MenuApplication::ShowNotification(const std::string &text, const u64 timeout) { this->EndOverlay(); this->notif_toast->SetText(text); @@ -231,16 +243,10 @@ namespace ul::menu::ui { if(bgm.bgm != nullptr) { const int loops = bgm.bgm_loop ? -1 : 1; if(bgm.bgm_fade_in_ms > 0) { - auto tt = Mix_FadeInMusic(bgm.bgm, loops, bgm.bgm_fade_in_ms); - if(tt < 0) { - UL_LOG_WARN("MP3 fadein play error: '%s'", Mix_GetError()); - } + pu::audio::PlayMusicWithFadeIn(bgm.bgm, loops, bgm.bgm_fade_in_ms); } else { - auto tt = Mix_PlayMusic(bgm.bgm, loops); - if(tt < 0) { - UL_LOG_WARN("MP3 play error: '%s'", Mix_GetError()); - } + pu::audio::PlayMusic(bgm.bgm, loops); } } } @@ -254,12 +260,6 @@ namespace ul::menu::ui { pu::audio::StopMusic(); } } - - void MenuApplication::SetSelectedUser(const AccountUid user_id) { - this->system_status.selected_user = user_id; - UL_RC_ASSERT(ul::menu::smi::SetSelectedUser(user_id)); - LoadSelectedUserIconTexture(); - } int MenuApplication::DisplayDialog(const std::string &title, const std::string &content, const std::vector &opts, const bool use_last_opt_as_cancel, pu::sdl2::TextureHandle::Ref icon) { return this->CreateShowDialog(title, content, opts, use_last_opt_as_cancel, icon, [&](pu::ui::Dialog::Ref &dialog) { diff --git a/projects/uMenu/source/ul/menu/ui/ui_QuickMenu.cpp b/projects/uMenu/source/ul/menu/ui/ui_QuickMenu.cpp index bab4bf2..8a3584c 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_QuickMenu.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_QuickMenu.cpp @@ -13,7 +13,7 @@ namespace ul::menu::ui { } - void QuickMenu::OnHomeButtonDetection(const smi::MenuMessageContext _) { + void QuickMenu::OnHomeButtonDetection(const smi::MenuMessageContext &_) { g_HomePressed = true; } diff --git a/projects/uMenu/source/ul/menu/ui/ui_SettingsMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_SettingsMenuLayout.cpp index 1acd9ff..9d8c690 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_SettingsMenuLayout.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_SettingsMenuLayout.cpp @@ -3,80 +3,698 @@ #include
                                                                        #include
                                                                          #include
                                                                            -#include
                                                                              #include
                                                                                -extern SetSysFirmwareVersion g_FwVersion; - +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; namespace ul::menu::ui { namespace { - template - inline std::string EncodeForSettings(const T &t) { - return GetLanguageString("set_unknown_value"); + std::string GetMenuName(const SettingMenu menu) { + switch(menu) { + case SettingMenu::System: return GetLanguageString("set_menu_system"); + case SettingMenu::uLaunch: return GetLanguageString("set_menu_ulaunch"); + case SettingMenu::Bluetooth: return GetLanguageString("set_menu_bluetooth"); + case SettingMenu::Network: return GetLanguageString("set_menu_network"); + case SettingMenu::Screen: return GetLanguageString("set_menu_screen"); + case SettingMenu::Dev: return GetLanguageString("set_menu_dev"); + + default: return GetLanguageString("set_unknown_value"); + } } - template<> - inline std::string EncodeForSettings(const std::string &t) { - return "\"" + t + "\""; + inline std::string FormatBoolean(const bool &t) { + return t ? GetLanguageString("set_true_value") : GetLanguageString("set_false_value"); } - - template<> - inline std::string EncodeForSettings(const u32 &t) { - return "\"" + std::to_string(t) + "\""; + + inline std::string FormatRegion(const SetRegion region) { + switch(region) { + case SetRegion_JPN: return GetLanguageString("set_region_jpn"); + case SetRegion_USA: return GetLanguageString("set_region_usa"); + case SetRegion_EUR: return GetLanguageString("set_region_eur"); + case SetRegion_AUS: return GetLanguageString("set_region_aus"); + case SetRegion_HTK: return GetLanguageString("set_region_htk"); + case SetRegion_CHN: return GetLanguageString("set_region_chn"); + default: return GetLanguageString("set_unknown_value"); + } } - template<> - inline std::string EncodeForSettings(const bool &t) { - return t ? GetLanguageString("set_true_value") : GetLanguageString("set_false_value"); + inline Result GetLatestSystemUpdate(u8 &out_value) { + NsSystemUpdateControl ctrl; + UL_RC_TRY(nssuOpenSystemUpdateControl(&ctrl)); + UL_ON_SCOPE_EXIT({ + nssuControlClose(&ctrl); + }); + + AsyncValue av; + UL_RC_TRY(nssuControlRequestCheckLatestUpdate(&ctrl, &av)); + UL_ON_SCOPE_EXIT({ + asyncValueClose(&av); + }); + + UL_RC_TRY(asyncValueWait(&av, UINT64_MAX)); + + u8 latest_upd_value; + UL_RC_TRY(asyncValueGet(&av, &latest_upd_value, sizeof(latest_upd_value))); + + out_value = latest_upd_value; + UL_RC_SUCCEED; + } + + inline std::string FormatLatestSystemUpdate(const u8 upd) { + switch(upd) { + case 0: return GetLanguageString("set_update_updated"); + case 1: return GetLanguageString("set_update_ready_install"); + case 2: return GetLanguageString("set_update_needs_download"); + default: return GetLanguageString("set_unknown_value"); + } + } + + inline std::string FormatPrimaryAlbumStorage(const SetSysPrimaryAlbumStorage storage) { + switch(storage) { + case SetSysPrimaryAlbumStorage_Nand: return GetLanguageString("set_album_nand"); + case SetSysPrimaryAlbumStorage_SdCard: return GetLanguageString("set_album_sd"); + default: return GetLanguageString("set_unknown_value"); + } } - constexpr u32 ExosphereApiVersionConfigItem = 65000; - constexpr u32 ExosphereEmummcType = 65007; - constexpr u32 ExosphereSupportedHosVersion = 65011; - - bool g_AmsEmummcInfoLoaded = false; - ul::Version g_AmsVersion; - bool g_AmsIsEmummc; - - void LoadAmsEmummcInfo() { - if(!g_AmsEmummcInfoLoaded) { - UL_RC_ASSERT(splInitialize()); - UL_ON_SCOPE_EXIT( - splExit(); - ); - - // Since we rely on ams for uLaunch to work, it *must* be present - - u64 raw_ams_ver; - UL_RC_ASSERT(splGetConfig(static_cast(ExosphereApiVersionConfigItem), &raw_ams_ver)); - g_AmsVersion = { - .major = static_cast((raw_ams_ver >> 56) & 0xFF), - .minor = static_cast((raw_ams_ver >> 48) & 0xFF), - .micro = static_cast((raw_ams_ver >> 40) & 0xFF) - }; - - u64 emummc_type; - UL_RC_ASSERT(splGetConfig(static_cast(ExosphereEmummcType), &emummc_type)); - g_AmsIsEmummc = emummc_type != 0; - g_AmsEmummcInfoLoaded = true; + inline std::string FormatConsoleSleepPlan(const SetSysConsoleSleepPlan plan) { + switch(plan) { + case SetSysConsoleSleepPlan_1Hour: return GetLanguageString("set_sleep_console_1h"); + case SetSysConsoleSleepPlan_2Hour: return GetLanguageString("set_sleep_console_2h"); + case SetSysConsoleSleepPlan_3Hour: return GetLanguageString("set_sleep_console_3h"); + case SetSysConsoleSleepPlan_6Hour: return GetLanguageString("set_sleep_console_6h"); + case SetSysConsoleSleepPlan_12Hour: return GetLanguageString("set_sleep_console_12h"); + case SetSysConsoleSleepPlan_Never: return GetLanguageString("set_sleep_console_never"); + default: return GetLanguageString("set_unknown_value"); } } + inline std::string FormatHandheldSleepPlan(const SetSysHandheldSleepPlan plan) { + switch(plan) { + case SetSysHandheldSleepPlan_1Min: return GetLanguageString("set_sleep_handheld_1min"); + case SetSysHandheldSleepPlan_3Min: return GetLanguageString("set_sleep_handheld_3min"); + case SetSysHandheldSleepPlan_5Min: return GetLanguageString("set_sleep_handheld_5min"); + case SetSysHandheldSleepPlan_10Min: return GetLanguageString("set_sleep_handheld_10min"); + case SetSysHandheldSleepPlan_30Min: return GetLanguageString("set_sleep_handheld_30min"); + case SetSysHandheldSleepPlan_Never: return GetLanguageString("set_sleep_handheld_never"); + default: return GetLanguageString("set_unknown_value"); + } + } + + constexpr auto SleepFlag_SleepsWhilePlayingMedia = BIT(0); + constexpr auto SleepFlag_WakesAtPowerStateChange = BIT(1); + + struct SettingInfo { + SettingMenu menu; + SettingSubmenu submenu; + Setting setting; + bool editable; + pu::ui::elm::MenuItem::Ref menu_item; + + std::string static_description; + std::function dyn_description_cb; + + std::string static_value; + std::function dyn_value_cb; + + std::function select_cb; + + inline std::string GetDescription() { + if(!this->static_description.empty()) { + return this->static_description; + } + else if(this->dyn_description_cb != nullptr) { + return this->dyn_description_cb(); + } + else { + return ""; + } + } + + inline std::string GetValue() { + if(!this->static_value.empty()) { + return this->static_value; + } + else { + return this->dyn_value_cb(); + } + } + + inline std::string Format() { + const auto &desc = this->GetDescription(); + const auto &val = this->GetValue(); + if(desc.empty()) { + return val; + } + else { + return desc + ": " + val; + } + } + }; + + std::vector g_Settings; + + void LoadSettings() { + g_Settings.clear(); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleRegion, + .editable = false, + .static_description = GetLanguageString("set_region"), + .static_value = FormatRegion(g_GlobalSettings.region) + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::SystemUpdateStatus, + .editable = false, + .static_description = GetLanguageString("set_update"), + .dyn_value_cb = []() { + u8 latest_upd_value = 0xFF; + if(R_SUCCEEDED(GetLatestSystemUpdate(latest_upd_value))) { + return FormatLatestSystemUpdate(latest_upd_value); + } + else { + return GetLanguageString("set_update_no_connection"); + } + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::LockscreenEnabled, + .editable = true, + .static_description = GetLanguageString("set_lockscreen"), + .dyn_value_cb = []() { + bool lockscreen_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::LockscreenEnabled, lockscreen_enabled)); + return FormatBoolean(lockscreen_enabled); + }, + .select_cb = []() { + bool lockscreen_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::LockscreenEnabled, lockscreen_enabled)); + UL_ASSERT_TRUE(g_GlobalSettings.config.SetEntry(cfg::ConfigEntryId::LockscreenEnabled, !lockscreen_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::PrimaryAlbumStorage, + .editable = true, + .static_description = GetLanguageString("set_album"), + .dyn_value_cb = []() { + return FormatPrimaryAlbumStorage(g_GlobalSettings.album_storage); + }, + .select_cb = []() { + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_album"), GetLanguageString("set_enum_options"), { + GetLanguageString("set_album_sd"), + GetLanguageString("set_album_nand"), + GetLanguageString("cancel") + }, true); + switch(opt) { + case 0: + g_GlobalSettings.album_storage = SetSysPrimaryAlbumStorage_SdCard; + UL_RC_ASSERT(setsysSetPrimaryAlbumStorage(g_GlobalSettings.album_storage)); + return true; + case 1: + g_GlobalSettings.album_storage = SetSysPrimaryAlbumStorage_Nand; + UL_RC_ASSERT(setsysSetPrimaryAlbumStorage(g_GlobalSettings.album_storage)); + return true; + } + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleSleepPlan, + .editable = true, + .static_description = GetLanguageString("set_sleep_console"), + .dyn_value_cb = []() { + return FormatConsoleSleepPlan((SetSysConsoleSleepPlan)g_GlobalSettings.sleep_settings.console_sleep_plan); + }, + .select_cb = []() { + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_sleep_console"), GetLanguageString("set_enum_option"), { + GetLanguageString("set_sleep_console_never"), + GetLanguageString("set_sleep_console_1h"), + GetLanguageString("set_sleep_console_2h"), + GetLanguageString("set_sleep_console_3h"), + GetLanguageString("set_sleep_console_6h"), + GetLanguageString("set_sleep_console_12h"), + GetLanguageString("cancel") + }, true); + switch(opt) { + case 0: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_Never; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 1: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_1Hour; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 2: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_2Hour; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 3: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_3Hour; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 4: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_6Hour; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 5: + g_GlobalSettings.sleep_settings.console_sleep_plan = SetSysConsoleSleepPlan_12Hour; + g_GlobalSettings.UpdateSleepSettings(); + return true; + } + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::HandheldSleepPlan, + .editable = true, + .static_description = GetLanguageString("set_sleep_handheld"), + .dyn_value_cb = []() { + return FormatHandheldSleepPlan((SetSysHandheldSleepPlan)g_GlobalSettings.sleep_settings.handheld_sleep_plan); + }, + .select_cb = []() { + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_sleep_handheld"), GetLanguageString("set_enum_options"), { + GetLanguageString("set_sleep_handheld_never"), + GetLanguageString("set_sleep_handheld_1min"), + GetLanguageString("set_sleep_handheld_3min"), + GetLanguageString("set_sleep_handheld_5min"), + GetLanguageString("set_sleep_handheld_10min"), + GetLanguageString("set_sleep_handheld_30min"), + GetLanguageString("cancel") + }, true); + switch(opt) { + case 0: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_Never; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 1: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_1Min; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 2: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_3Min; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 3: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_5Min; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 4: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_10Min; + g_GlobalSettings.UpdateSleepSettings(); + return true; + case 5: + g_GlobalSettings.sleep_settings.handheld_sleep_plan = SetSysHandheldSleepPlan_30Min; + g_GlobalSettings.UpdateSleepSettings(); + return true; + } + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::SleepWhilePlayingMedia, + .editable = true, + .static_description = GetLanguageString("set_sleep_media_play"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.sleep_settings.flags & SleepFlag_SleepsWhilePlayingMedia); + }, + .select_cb = []() { + if(g_GlobalSettings.sleep_settings.flags & SleepFlag_SleepsWhilePlayingMedia) { + g_GlobalSettings.sleep_settings.flags &= ~SleepFlag_SleepsWhilePlayingMedia; + } + else { + g_GlobalSettings.sleep_settings.flags |= SleepFlag_SleepsWhilePlayingMedia; + } + g_GlobalSettings.UpdateSleepSettings(); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Screen, + .submenu = SettingSubmenu::None, + .setting = Setting::SleepWakesAtPowerStateChange, + .editable = true, + .static_description = GetLanguageString("set_sleep_wake_power_state"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.sleep_settings.flags & SleepFlag_WakesAtPowerStateChange); + }, + .select_cb = []() { + if(g_GlobalSettings.sleep_settings.flags & SleepFlag_WakesAtPowerStateChange) { + g_GlobalSettings.sleep_settings.flags &= ~SleepFlag_WakesAtPowerStateChange; + } + else { + g_GlobalSettings.sleep_settings.flags |= SleepFlag_WakesAtPowerStateChange; + } + g_GlobalSettings.UpdateSleepSettings(); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Dev, + .submenu = SettingSubmenu::None, + .setting = Setting::BatteryLot, + .editable = false, + .static_description = GetLanguageString("set_battery_lot"), + .static_value = g_GlobalSettings.battery_lot.lot + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleFirmware, + .editable = false, + .static_description = GetLanguageString("set_console_fw"), + .static_value = std::string(g_GlobalSettings.fw_version.display_version) + " (" + g_GlobalSettings.fw_version.display_title + ")" + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::AtmosphereFirmware, + .editable = false, + .static_description = GetLanguageString("set_ams_fw"), + .static_value = g_GlobalSettings.ams_version.Format() + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::AtmosphereEmummc, + .editable = false, + .static_description = GetLanguageString("set_ams_emummc"), + .static_value = FormatBoolean(g_GlobalSettings.ams_is_emummc) + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleNickname, + .editable = true, + .static_description = GetLanguageString("set_console_nickname"), + .dyn_value_cb = []() { + return std::string(g_GlobalSettings.nickname.nickname); + }, + .select_cb = []() { + SwkbdConfig swkbd; + if(R_SUCCEEDED(swkbdCreate(&swkbd, 0))) { + swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_console_nick_guide").c_str()); + swkbdConfigSetInitialText(&swkbd, g_GlobalSettings.nickname.nickname); + swkbdConfigSetStringLenMax(&swkbd, 32); + SetSysDeviceNickName new_name = {}; + auto rc = swkbdShow(&swkbd, new_name.nickname, sizeof(new_name.nickname)); + swkbdClose(&swkbd); + if(R_SUCCEEDED(rc)) { + g_GlobalSettings.nickname = new_name; + UL_RC_ASSERT(setsysSetDeviceNickname(&g_GlobalSettings.nickname)); + return true; + } + } + + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleTimezone, + .editable = false, + .static_description = GetLanguageString("set_console_timezone"), + .static_value = std::string(g_GlobalSettings.timezone.name) + }); + + g_Settings.push_back({ + .menu = SettingMenu::uLaunch, + .submenu = SettingSubmenu::None, + .setting = Setting::UsbScreenCaptureEnabled, + .editable = true, + .static_description = GetLanguageString("set_usb_screen_capture_enabled"), + .dyn_value_cb = []() { + bool usb_capture_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::UsbScreenCaptureEnabled, usb_capture_enabled)); + return FormatBoolean(usb_capture_enabled); + }, + .select_cb = []() { + bool usb_capture_enabled; + UL_ASSERT_TRUE(g_GlobalSettings.config.GetEntry(cfg::ConfigEntryId::UsbScreenCaptureEnabled, usb_capture_enabled)); + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_usb_screen_capture_enabled"), GetLanguageString("set_usb_screen_capture_info") + "\n" + (usb_capture_enabled ? GetLanguageString("set_usb_screen_capture_disable_conf") : GetLanguageString("set_usb_screen_capture_enable_conf")), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); + if(opt == 0) { + UL_ASSERT_TRUE(g_GlobalSettings.config.SetEntry(cfg::ConfigEntryId::UsbScreenCaptureEnabled, !usb_capture_enabled)); + g_MenuApplication->ShowNotification(GetLanguageString("set_changed_reboot")); + return true; + } + + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::ConnectionSsid, + .editable = true, + .static_description = GetLanguageString("set_wifi_name"), + .dyn_value_cb = []() { + auto connected_wifi_name = GetLanguageString("set_wifi_none"); + NifmNetworkProfileData prof_data = {}; + if(R_SUCCEEDED(nifmGetCurrentNetworkProfile(&prof_data))) { + connected_wifi_name = prof_data.wireless_setting_data.ssid; + } + return connected_wifi_name; + }, + .select_cb = []() { + // Close uMenu and launch the connections applet + ShowNetConnect(); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleLanguage, + .editable = true, + .static_description = GetLanguageString("set_console_lang"), + .dyn_value_cb = []() { + return os::LanguageNameList[static_cast(g_GlobalSettings.language)]; + }, + .select_cb = []() { + std::vector lang_opts = {}; + for(u32 i = 0; i < os::LanguageNameCount; i++) { + lang_opts.push_back(os::LanguageNameList[i]); + } + lang_opts.push_back(GetLanguageString("cancel")); + + const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_lang_select"), GetLanguageString("set_lang_conf"), lang_opts, true); + if(opt >= 0) { + const auto sys_lang = os::GetSystemLanguage(); + if(sys_lang == opt) { + g_MenuApplication->ShowNotification(GetLanguageString("set_lang_active")); + } + else { + const auto lang_code = g_GlobalSettings.available_language_codes[opt]; + const auto rc = setsysSetLanguageCode(lang_code); + g_MenuApplication->DisplayDialog(GetLanguageString("set_lang"), R_SUCCEEDED(rc) ? GetLanguageString("set_lang_select_ok") : GetLanguageString("set_lang_select_error") + ": " + util::FormatResultDisplay(rc), { GetLanguageString("ok") }, true); + if(R_SUCCEEDED(rc)) { + // No need to change our global settings + RebootSystem(); + // Never reached anyway + return true; + } + } + } + + return false; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleInformationUploadEnabled, + .editable = true, + .static_description = GetLanguageString("set_console_info_upload"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.console_info_upload_enabled); + }, + .select_cb = []() { + g_GlobalSettings.console_info_upload_enabled = !g_GlobalSettings.console_info_upload_enabled; + UL_RC_ASSERT(setsysSetConsoleInformationUploadFlag(g_GlobalSettings.console_info_upload_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::AutomaticApplicationDownloadEnabled, + .editable = true, + .static_description = GetLanguageString("set_auto_app_download"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.auto_app_download_enabled); + }, + .select_cb = []() { + g_GlobalSettings.auto_app_download_enabled = !g_GlobalSettings.auto_app_download_enabled; + UL_RC_ASSERT(setsysSetAutomaticApplicationDownloadFlag(g_GlobalSettings.auto_app_download_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleAutoUpdateEnabled, + .editable = true, + .static_description = GetLanguageString("set_auto_update"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.auto_update_enabled); + }, + .select_cb = []() { + g_GlobalSettings.auto_update_enabled = !g_GlobalSettings.auto_update_enabled; + UL_RC_ASSERT(setsysSetAutoUpdateEnableFlag(g_GlobalSettings.auto_update_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::WirelessLanEnabled, + .editable = true, + .static_description = GetLanguageString("set_wireless_lan"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.wireless_lan_enabled); + }, + .select_cb = []() { + g_GlobalSettings.wireless_lan_enabled = !g_GlobalSettings.wireless_lan_enabled; + UL_RC_ASSERT(setsysSetWirelessLanEnableFlag(g_GlobalSettings.wireless_lan_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Bluetooth, + .submenu = SettingSubmenu::None, + .setting = Setting::BluetoothEnabled, + .editable = true, + .static_description = GetLanguageString("set_bluetooth"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.bluetooth_enabled); + }, + .select_cb = []() { + g_GlobalSettings.bluetooth_enabled = !g_GlobalSettings.bluetooth_enabled; + UL_RC_ASSERT(setsysSetBluetoothEnableFlag(g_GlobalSettings.bluetooth_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::Usb30Enabled, + .editable = true, + .static_description = GetLanguageString("set_usb_30"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.usb30_enabled); + }, + .select_cb = []() { + g_GlobalSettings.usb30_enabled = !g_GlobalSettings.usb30_enabled; + UL_RC_ASSERT(setsysSetUsb30EnableFlag(g_GlobalSettings.usb30_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::System, + .submenu = SettingSubmenu::None, + .setting = Setting::NfcEnabled, + .editable = true, + .static_description = GetLanguageString("set_nfc"), + .dyn_value_cb = []() { + return FormatBoolean(g_GlobalSettings.nfc_enabled); + }, + .select_cb = []() { + g_GlobalSettings.nfc_enabled = !g_GlobalSettings.nfc_enabled; + UL_RC_ASSERT(setsysSetNfcEnableFlag(g_GlobalSettings.nfc_enabled)); + return true; + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Dev, + .submenu = SettingSubmenu::None, + .setting = Setting::ConsoleSerialNumber, + .editable = false, + .static_description = GetLanguageString("set_serial_no"), + .static_value = std::string(g_GlobalSettings.serial_no.number) + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::MacAddress, + .editable = false, + .static_description = GetLanguageString("set_mac_addr"), + .dyn_value_cb = []() { + net::WlanMacAddress mac_addr; + UL_RC_ASSERT(net::GetMacAddress(mac_addr)); + return net::FormatMacAddress(mac_addr); + } + }); + + g_Settings.push_back({ + .menu = SettingMenu::Network, + .submenu = SettingSubmenu::None, + .setting = Setting::IpAddress, + .editable = false, + .static_description = GetLanguageString("set_ip_addr"), + .dyn_value_cb = []() { + return net::GetConsoleIpAddress(); + } + }); + } + } SettingsMenuLayout::SettingsMenuLayout() : IMenuLayout() { - LoadAmsEmummcInfo(); + LoadSettings(); + this->cur_menu = static_cast(0); + this->cur_submenu = SettingSubmenu::None; - this->info_text = pu::ui::elm::TextBlock::New(0, 0, GetLanguageString("set_info_text")); + this->menu_text = pu::ui::elm::TextBlock::New(0, 0, "..."); + this->menu_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("settings_menu", "menu_text", this->menu_text); + this->Add(this->menu_text); - this->info_text->SetColor(g_MenuApplication->GetTextColor()); - g_MenuApplication->ApplyConfigForElement("settings_menu", "info_text", this->info_text); - this->Add(this->info_text); + this->submenu_text = pu::ui::elm::TextBlock::New(0, 0, "..."); + this->submenu_text->SetColor(g_MenuApplication->GetTextColor()); + g_MenuApplication->ApplyConfigForElement("settings_menu", "submenu_text", this->submenu_text); + this->Add(this->submenu_text); this->settings_menu = pu::ui::elm::Menu::New(0, 0, SettingsMenuWidth, g_MenuApplication->GetMenuBackgroundColor(), g_MenuApplication->GetMenuFocusColor(), SettingsMenuItemSize, SettingsMenuItemsToShow); g_MenuApplication->ApplyConfigForElement("settings_menu", "settings_menu", this->settings_menu); @@ -97,7 +715,40 @@ namespace ul::menu::ui { if(keys_down & HidNpadButton_B) { pu::audio::PlaySfx(this->back_sfx); - g_MenuApplication->LoadMenuByType(MenuType::Main); + if(this->cur_submenu != SettingSubmenu::None) { + g_MenuApplication->SetBackgroundFade(); + g_MenuApplication->FadeOut(); + + this->cur_submenu = SettingSubmenu::None; + this->Reload(false); + + g_MenuApplication->FadeIn(); + } + else { + g_MenuApplication->LoadMenuByType(MenuType::Main); + } + } + else if(keys_down & HidNpadButton_L) { + if(static_cast(this->cur_menu) > 0) { + g_MenuApplication->SetBackgroundFade(); + g_MenuApplication->FadeOut(); + + this->cur_menu = static_cast(static_cast(this->cur_menu) - 1); + this->Reload(false); + + g_MenuApplication->FadeIn(); + } + } + else if(keys_down & HidNpadButton_R) { + if((static_cast(this->cur_menu) + 1) < static_cast(SettingMenu::Count)) { + g_MenuApplication->SetBackgroundFade(); + g_MenuApplication->FadeOut(); + + this->cur_menu = static_cast(static_cast(this->cur_menu) + 1); + this->Reload(false); + + g_MenuApplication->FadeIn(); + } } } @@ -108,230 +759,47 @@ namespace ul::menu::ui { return true; } - void SettingsMenuLayout::Reload(const bool reset_idx) { - // TODO (long term): implement more settings! - - const auto prev_idx = this->settings_menu->GetSelectedIndex(); - this->settings_menu->ClearItems(); - - this->PushSettingItem(GetLanguageString("set_console_fw"), EncodeForSettings(std::string(g_FwVersion.display_version) + " (" + g_FwVersion.display_title + ")"), -1); - - this->PushSettingItem(GetLanguageString("set_ams_fw"), EncodeForSettings(g_AmsVersion.Format()), -1); - - this->PushSettingItem(GetLanguageString("set_ams_emummc"), EncodeForSettings(g_AmsIsEmummc), -1); - - SetSysDeviceNickName console_name = {}; - UL_RC_ASSERT(setsysGetDeviceNickname(&console_name)); - this->PushSettingItem(GetLanguageString("set_console_nickname"), EncodeForSettings(console_name.nickname), 0); - - TimeLocationName loc = {}; - UL_RC_ASSERT(timeGetDeviceLocationName(&loc)); - this->PushSettingItem(GetLanguageString("set_console_timezone"), EncodeForSettings(loc.name), -1); - - bool viewer_usb_enabled; - UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled)); - this->PushSettingItem(GetLanguageString("set_viewer_enabled"), EncodeForSettings(viewer_usb_enabled), 1); - - auto connected_wifi_name = GetLanguageString("set_wifi_none"); - u32 strength; - if(net::HasConnection(strength)) { - NifmNetworkProfileData prof_data = {}; - if(R_SUCCEEDED(nifmGetCurrentNetworkProfile(&prof_data))) { - connected_wifi_name = prof_data.wireless_setting_data.ssid; - } + void SettingsMenuLayout::Reload(const bool soft) { + if(this->cur_submenu != SettingSubmenu::None) { + /* + this->submenu_text->SetVisible(true); + this->submenu_text->SetText(""); + */ } - this->PushSettingItem(GetLanguageString("set_wifi_name"), EncodeForSettings(connected_wifi_name), 2); - - u64 lang_code = 0; - auto lang_val = SetLanguage_ENUS; - UL_RC_ASSERT(setGetSystemLanguage(&lang_code)); - UL_RC_ASSERT(setMakeLanguage(lang_code, &lang_val)); - const std::string lang_str = os::LanguageNameList[static_cast(lang_val)]; - this->PushSettingItem(GetLanguageString("set_console_lang"), EncodeForSettings(lang_str), 3); - - auto console_info_upload = false; - UL_RC_ASSERT(setsysGetConsoleInformationUploadFlag(&console_info_upload)); - this->PushSettingItem(GetLanguageString("set_console_info_upload"), EncodeForSettings(console_info_upload), 4); - - auto auto_titles_dl = false; - UL_RC_ASSERT(setsysGetAutomaticApplicationDownloadFlag(&auto_titles_dl)); - this->PushSettingItem(GetLanguageString("set_auto_titles_dl"), EncodeForSettings(auto_titles_dl), 5); - - auto auto_update = false; - UL_RC_ASSERT(setsysGetAutoUpdateEnableFlag(&auto_update)); - this->PushSettingItem(GetLanguageString("set_auto_update"), EncodeForSettings(auto_update), 6); - - auto wireless_lan = false; - UL_RC_ASSERT(setsysGetWirelessLanEnableFlag(&wireless_lan)); - this->PushSettingItem(GetLanguageString("set_wireless_lan"), EncodeForSettings(wireless_lan), 7); - - auto bluetooth = false; - UL_RC_ASSERT(setsysGetBluetoothEnableFlag(&bluetooth)); - this->PushSettingItem(GetLanguageString("set_bluetooth"), EncodeForSettings(bluetooth), 8); - - auto usb_30 = false; - UL_RC_ASSERT(setsysGetUsb30EnableFlag(&usb_30)); - this->PushSettingItem(GetLanguageString("set_usb_30"), EncodeForSettings(usb_30), 9); - - auto nfc = false; - UL_RC_ASSERT(setsysGetNfcEnableFlag(&nfc)); - this->PushSettingItem(GetLanguageString("set_nfc"), EncodeForSettings(nfc), 10); - - SetSysSerialNumber serial = {}; - UL_RC_ASSERT(setsysGetSerialNumber(&serial)); - this->PushSettingItem(GetLanguageString("set_serial_no"), EncodeForSettings(serial.number), -1); - - net::WlanMacAddress mac_addr = {}; - UL_RC_ASSERT(net::GetMacAddress(mac_addr)); - const auto mac_addr_str = net::FormatMacAddress(mac_addr); - this->PushSettingItem(GetLanguageString("set_mac_addr"), EncodeForSettings(mac_addr_str), -1); - - const auto ip_str = net::GetConsoleIpAddress(); - this->PushSettingItem(GetLanguageString("set_ip_addr"), EncodeForSettings(ip_str), -1); - - this->settings_menu->SetSelectedIndex(reset_idx ? 0 : prev_idx); - } - - void SettingsMenuLayout::PushSettingItem(const std::string &name, const std::string &value_display, int id) { - const auto is_editable = id >= 0; - auto setting_item = pu::ui::elm::MenuItem::New(name + ": " + value_display); - if(is_editable) { - setting_item->AddOnKey(std::bind(&SettingsMenuLayout::setting_DefaultKey, this, id)); + else { + this->submenu_text->SetVisible(false); } - setting_item->SetIcon(is_editable ? GetEditableSettingIconTexture() : GetNonEditableSettingIconTexture()); - setting_item->SetColor(g_MenuApplication->GetTextColor()); - this->settings_menu->AddItem(setting_item); - } + this->menu_text->SetText(GetMenuName(this->cur_menu)); - void SettingsMenuLayout::setting_DefaultKey(const u32 id) { - pu::audio::PlaySfx(this->setting_edit_sfx); - bool reload_need = false; - switch(id) { - case 0: { - SwkbdConfig swkbd; - if(R_SUCCEEDED(swkbdCreate(&swkbd, 0))) { - swkbdConfigSetGuideText(&swkbd, GetLanguageString("swkbd_console_nick_guide").c_str()); - SetSysDeviceNickName console_name = {}; - UL_RC_ASSERT(setsysGetDeviceNickname(&console_name)); - swkbdConfigSetInitialText(&swkbd, console_name.nickname); - swkbdConfigSetStringLenMax(&swkbd, 32); - SetSysDeviceNickName new_name = {}; - auto rc = swkbdShow(&swkbd, new_name.nickname, sizeof(new_name.nickname)); - swkbdClose(&swkbd); - if(R_SUCCEEDED(rc)) { - setsysSetDeviceNickname(&new_name); - reload_need = true; - } - } - break; - } - case 1: { - bool viewer_usb_enabled; - UL_ASSERT_TRUE(g_Config.GetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled)); - auto sopt = g_MenuApplication->DisplayDialog(GetLanguageString("set_viewer_enabled"), GetLanguageString("set_viewer_info") + "\n" + (viewer_usb_enabled ? GetLanguageString("set_viewer_disable_conf") : GetLanguageString("set_viewer_enable_conf")), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); - if(sopt == 0) { - viewer_usb_enabled = !viewer_usb_enabled; - UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled)); - reload_need = true; - g_MenuApplication->ShowNotification(GetLanguageString("set_changed_reboot")); + if(soft) { + for(auto &setting: g_Settings) { + if((setting.menu == this->cur_menu) && (setting.submenu == this->cur_submenu)) { + setting.menu_item->SetName(setting.Format()); } - break; - } - case 2: { - // Close uMenu and launch the applet - ShowNetConnect(); - break; } - case 3: { - std::vector lang_opts = {}; - for(u32 i = 0; i < os::LanguageNameCount; i++) { - lang_opts.push_back(os::LanguageNameList[i]); - } - lang_opts.push_back(GetLanguageString("cancel")); - - const auto opt = g_MenuApplication->DisplayDialog(GetLanguageString("set_lang_select"), GetLanguageString("set_lang_conf"), lang_opts, true); - if(opt >= 0) { - const auto sys_lang = os::GetSystemLanguage(); - if(sys_lang == opt) { - g_MenuApplication->ShowNotification(GetLanguageString("set_lang_active")); - } - else { - u64 lang_codes[os::LanguageNameCount] = {}; - s32 tmp; - setGetAvailableLanguageCodes(&tmp, lang_codes, os::LanguageNameCount); - const auto lang_code = lang_codes[opt]; - - const auto rc = setsysSetLanguageCode(lang_code); - g_MenuApplication->DisplayDialog(GetLanguageString("set_lang"), R_SUCCEEDED(rc) ? GetLanguageString("set_lang_select_ok") : GetLanguageString("set_lang_select_error") + ": " + util::FormatResultDisplay(rc), { GetLanguageString("ok") }, true); - if(R_SUCCEEDED(rc)) { - RebootSystem(); - } + this->settings_menu->ForceReloadItems(); + } + else { + this->settings_menu->ClearItems(); + for(auto &setting: g_Settings) { + if((setting.menu == this->cur_menu) && (setting.submenu == this->cur_submenu)) { + setting.menu_item = pu::ui::elm::MenuItem::New(setting.Format()); + if(setting.editable) { + setting.menu_item->AddOnKey([&]() { + pu::audio::PlaySfx(this->setting_edit_sfx); + const auto changed = setting.select_cb(); + if(changed) { + pu::audio::PlaySfx(this->setting_save_sfx); + g_GlobalSettings.SaveConfig(); + this->Reload(true); + } + }); } + setting.menu_item->SetIcon(setting.editable ? GetEditableSettingIconTexture() : GetNonEditableSettingIconTexture()); + setting.menu_item->SetColor(g_MenuApplication->GetTextColor()); + this->settings_menu->AddItem(setting.menu_item); } - break; } - case 4: { - auto console_info_upload = false; - UL_RC_ASSERT(setsysGetConsoleInformationUploadFlag(&console_info_upload)); - UL_RC_ASSERT(setsysSetConsoleInformationUploadFlag(!console_info_upload)); - - reload_need = true; - break; - } - case 5: { - auto auto_app_dl = false; - UL_RC_ASSERT(setsysGetAutomaticApplicationDownloadFlag(&auto_app_dl)); - UL_RC_ASSERT(setsysSetAutomaticApplicationDownloadFlag(!auto_app_dl)); - - reload_need = true; - break; - } - case 6: { - auto auto_update = false; - UL_RC_ASSERT(setsysGetAutoUpdateEnableFlag(&auto_update)); - UL_RC_ASSERT(setsysSetAutoUpdateEnableFlag(!auto_update)); - - reload_need = true; - break; - } - case 7: { - auto wireless_lan = false; - UL_RC_ASSERT(setsysGetWirelessLanEnableFlag(&wireless_lan)); - UL_RC_ASSERT(setsysSetWirelessLanEnableFlag(!wireless_lan)); - - reload_need = true; - break; - } - case 8: { - auto bluetooth = false; - UL_RC_ASSERT(setsysGetBluetoothEnableFlag(&bluetooth)); - UL_RC_ASSERT(setsysSetBluetoothEnableFlag(!bluetooth)); - - reload_need = true; - break; - } - case 9: { - auto usb_30 = false; - UL_RC_ASSERT(setsysGetUsb30EnableFlag(&usb_30)); - UL_RC_ASSERT(setsysSetUsb30EnableFlag(!usb_30)); - - reload_need = true; - break; - } - case 10: { - auto nfc = false; - UL_RC_ASSERT(setsysGetNfcEnableFlag(&nfc)); - UL_RC_ASSERT(setsysSetNfcEnableFlag(!nfc)); - - reload_need = true; - break; - } - } - - if(reload_need) { - pu::audio::PlaySfx(this->setting_save_sfx); - SaveConfig(); - this->Reload(false); } } diff --git a/projects/uMenu/source/ul/menu/ui/ui_StartupMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_StartupMenuLayout.cpp index a961c3e..29b0c2a 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_StartupMenuLayout.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_StartupMenuLayout.cpp @@ -4,8 +4,8 @@ #include
                                                                                  #include
                                                                                    +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; namespace ul::menu::ui { @@ -13,9 +13,8 @@ namespace ul::menu::ui { // Note: menu loading is invoked below instead of here so that the main menu doesn't also register the button input which caused this action... this->load_menu = true; pu::audio::PlaySfx(this->user_select_sfx); - g_MenuApplication->SetSelectedUser(uid); + g_GlobalSettings.SetSelectedUser(uid); - g_MenuApplication->StartPlayBgm(); g_MenuApplication->LoadMenuByType(MenuType::Main); } diff --git a/projects/uMenu/source/ul/menu/ui/ui_ThemesMenuLayout.cpp b/projects/uMenu/source/ul/menu/ui/ui_ThemesMenuLayout.cpp index d9b5297..23ec14e 100644 --- a/projects/uMenu/source/ul/menu/ui/ui_ThemesMenuLayout.cpp +++ b/projects/uMenu/source/ul/menu/ui/ui_ThemesMenuLayout.cpp @@ -3,9 +3,8 @@ #include
                                                                                      #include
                                                                                        +extern ul::menu::ui::GlobalSettings g_GlobalSettings; extern ul::menu::ui::MenuApplication::Ref g_MenuApplication; -extern ul::cfg::Config g_Config; -extern ul::cfg::Theme g_ActiveTheme; namespace ul::menu::ui { @@ -53,7 +52,7 @@ namespace ul::menu::ui { // Move active theme to top for(u32 i = 0; i < this->loaded_themes.size(); i++) { const auto theme = this->loaded_themes.at(i); - if(theme.IsSame(g_ActiveTheme)) { + if(theme.IsSame(g_GlobalSettings.active_theme)) { this->loaded_themes.erase(this->loaded_themes.begin() + i); this->loaded_themes.insert(this->loaded_themes.begin(), theme); break; @@ -94,7 +93,7 @@ namespace ul::menu::ui { const auto idx = this->themes_menu->GetSelectedIndex(); const auto &selected_theme = this->loaded_themes.at(idx); if(selected_theme.IsValid()) { - if(selected_theme.IsSame(g_ActiveTheme)) { + if(selected_theme.IsSame(g_GlobalSettings.active_theme)) { g_MenuApplication->ShowNotification(GetLanguageString("theme_active_this")); } else { @@ -105,31 +104,27 @@ namespace ul::menu::ui { const auto option = g_MenuApplication->DisplayDialog(selected_theme.manifest.name, theme_conf_msg, { GetLanguageString("yes"), GetLanguageString("cancel") }, true, this->loaded_theme_icons.at(idx)); if(option == 0) { - g_ActiveTheme = selected_theme; - UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, g_ActiveTheme.name)); - SaveConfig(); + g_GlobalSettings.SetActiveTheme(selected_theme); g_MenuApplication->ShowNotification(GetLanguageString("theme_cache")); pu::audio::PlaySfx(this->theme_change_sfx); g_MenuApplication->ShowNotification(GetLanguageString("theme_changed")); - g_MenuApplication->Finalize(); UL_RC_ASSERT(ul::menu::smi::RestartMenu(true)); + g_MenuApplication->Finalize(); } } } else { - if(g_ActiveTheme.IsValid()) { + if(g_GlobalSettings.active_theme.IsValid()) { const auto option = g_MenuApplication->DisplayDialog(GetLanguageString("theme_reset"), GetLanguageString("theme_reset_conf"), { GetLanguageString("yes"), GetLanguageString("cancel") }, true); if(option == 0) { - g_ActiveTheme = {}; - UL_ASSERT_TRUE(g_Config.SetEntry(cfg::ConfigEntryId::ActiveThemeName, g_ActiveTheme.name)); - SaveConfig(); + g_GlobalSettings.SetActiveTheme({}); g_MenuApplication->ShowNotification(GetLanguageString("theme_cache")); pu::audio::PlaySfx(this->theme_change_sfx); g_MenuApplication->ShowNotification(GetLanguageString("theme_changed")); - g_MenuApplication->Finalize(); UL_RC_ASSERT(ul::menu::smi::RestartMenu(true)); + g_MenuApplication->Finalize(); } } else { diff --git a/projects/uSystem/source/main.cpp b/projects/uSystem/source/main.cpp index 1213a9e..7f5e6f5 100644 --- a/projects/uSystem/source/main.cpp +++ b/projects/uSystem/source/main.cpp @@ -49,7 +49,21 @@ namespace { AppletId_LibraryAppletMyPage, AppletId_LibraryAppletMiiEdit, AppletId_LibraryAppletPlayerSelect, - AppletId_LibraryAppletNetConnect + AppletId_LibraryAppletNetConnect, + AppletId_LibraryAppletCabinet + }; + + constexpr const char ChooseHomebrewCaption[] = "Choose a homebrew for uMenu"; + + struct ApplicationVerifyContext { + static constexpr size_t ThreadStackSize = 64_KB; + + u64 app_id; + Thread thread; + alignas(0x1000) u8 thread_stack[ThreadStackSize]; + bool finished; + + ApplicationVerifyContext(const u64 app_id) : app_id(app_id), thread(), thread_stack(), finished(false) {} }; inline bool IsUsedLibraryApplet(const AppletId applet_id) { @@ -74,6 +88,8 @@ namespace { bool g_MiiEditAppletLaunchFlag = false; bool g_AddUserAppletLaunchFlag = false; bool g_NetConnectAppletLaunchFlag = false; + bool g_CabinetAppletLaunchFlag = false; + NfpLaStartParamTypeForAmiiboSettings g_CabinetAppletLaunchType; bool g_NextMenuLaunchStartupFlag = false; bool g_NextMenuLaunchSettingsFlag = false; bool g_MenuRestartReloadThemeCacheFlag = false; @@ -90,6 +106,8 @@ namespace { alignas(ams::os::MemoryPageSize) constinit u8 g_EventManagerThreadStack[16_KB]; Thread g_EventManagerThread; + std::vector *g_ApplicationVerifyContexts; + enum class UsbMode : u32 { Invalid, Rgba, @@ -120,13 +138,19 @@ namespace { u8 *g_UsbViewerBufferDataOffset = nullptr; std::vector g_CurrentRecords; + + std::vector g_LastDeletedApplications; + std::vector g_LastAddedApplications; constexpr size_t LibstratosphereHeapSize = 4_MB; alignas(ams::os::MemoryPageSize) constinit u8 g_LibstratosphereHeap[LibstratosphereHeapSize]; - constexpr size_t LibnxHeapSize = 4_MB; + constexpr size_t LibnxHeapSize = 8_MB; alignas(ams::os::MemoryPageSize) constinit u8 g_LibnxHeap[LibnxHeapSize]; + constexpr size_t VerifyWorkBufferSize = 0x100000; + constexpr size_t VerifyStepWaitTimeNs = 100'000; + } namespace { @@ -155,7 +179,10 @@ namespace { ul::smi::SystemStatus CreateStatus() { ul::smi::SystemStatus status = { .selected_user = g_SelectedUser, - .last_menu_index = g_CurrentMenuIndex + .last_menu_index = g_CurrentMenuIndex, + .last_added_app_count = (u32)g_LastAddedApplications.size(), + .last_deleted_app_count = (u32)g_LastDeletedApplications.size(), + .in_verify_app_count = (u32)g_ApplicationVerifyContexts->size() }; if(g_MenuRestartReloadThemeCacheFlag) { @@ -195,6 +222,80 @@ namespace { return ecs::RegisterLaunchAsApplet(la::GetMenuProgramId(), static_cast(st_mode), "/ulaunch/bin/uMenu", std::addressof(status), sizeof(status)); } + void ApplicationVerifyMain(void *ctx_raw) { + auto ctx = reinterpret_cast(ctx_raw); + + // Like qlaunch does, same size and align + auto verify_buf = new (std::align_val_t(0x1000)) u8[VerifyWorkBufferSize](); + NsProgressAsyncResult async_rc; + NsSystemUpdateProgress progress; + UL_RC_ASSERT(nsRequestVerifyApplication(&async_rc, ctx->app_id, 0x7, verify_buf, VerifyWorkBufferSize)); + + Result verify_rc; + Result verify_detail_rc; + while(true) { + const auto rc = nsProgressAsyncResultWait(&async_rc, VerifyStepWaitTimeNs); + if(rc == ul::ams::svc::ResultTimedOut) { + // Still not finished + UL_RC_ASSERT(nsProgressAsyncResultGetProgress(&async_rc, &progress, sizeof(progress))); + + if(progress.total_size > 0) { + const auto progress_val = (float)progress.current_size / (float)progress.total_size; + UL_LOG_INFO("[Verify-0x%016lX] done: %lld, total: %lld, prog: %.2f%", ctx->app_id, progress.current_size, progress.total_size, progress_val * 100.0f); + + if(la::IsMenu()) { + const ul::smi::MenuMessageContext menu_ctx = { + .msg = ul::smi::MenuMessage::ApplicationVerifyProgress, + .app_verify_progress = { + .app_id = ctx->app_id, + .done = (u64)progress.current_size, + .total = (u64)progress.total_size + } + }; + PushMenuMessageContext(menu_ctx); + } + } + else { + UL_LOG_INFO("[Verify-0x%016lX] invalid progress...", ctx->app_id); + } + } + else if(R_SUCCEEDED(rc)) { + // Finished + verify_rc = nsProgressAsyncResultGet(&async_rc); + verify_detail_rc = nsProgressAsyncResultGetDetailResult(&async_rc); + break; + } + else { + // Unexpected + UL_LOG_WARN("[Verify-0x%016lX] nsProgressAsyncResultWait failed unexpectedly: %s", ctx->app_id, ul::util::FormatResultDisplay(rc).c_str()); + + verify_rc = rc; + verify_detail_rc = rc; + break; + } + } + + ctx->finished = true; + + nsProgressAsyncResultClose(&async_rc); + delete[] verify_buf; + + if(R_SUCCEEDED(verify_rc) && R_SUCCEEDED(verify_detail_rc)) { + // Note: qlaunch apparently calls this command after verification succeeds, it resets the app record/view so that it can be launchable now + nsClearApplicationTerminateResult(ctx->app_id); + } + + const ul::smi::MenuMessageContext menu_ctx = { + .msg = ul::smi::MenuMessage::ApplicationVerifyResult, + .app_verify_rc = { + .app_id = ctx->app_id, + .rc = verify_rc, + .detail_rc = verify_detail_rc + } + }; + PushMenuMessageContext(menu_ctx); + } + void HandleHomeButton() { if(la::IsActive() && !la::IsMenu()) { // An applet is opened (which is not our menu), thus close it and reopen the menu @@ -212,7 +313,7 @@ namespace { } } - void HandleGeneralChannel() { + void HandleGeneralChannelMessage() { AppletStorage sams_st; if(R_SUCCEEDED(appletPopFromGeneralChannel(&sams_st))) { ul::util::OnScopeExit close_sams_st([&]() { @@ -250,27 +351,27 @@ namespace { break; } case GeneralChannelMessage::RequestJumpToSystemUpdate: { - UL_LOG_WARN("Unimplemented: RequestJumpToSystemUpdate"); + UL_LOG_WARN("Got GeneralChannelMessage: RequestJumpToSystemUpdate"); break; } case GeneralChannelMessage::Unk_OverlayBrightValueChanged: { - UL_LOG_WARN("Unimplemented: Unk_OverlayBrightValueChanged"); + UL_LOG_WARN("Got GeneralChannelMessage: Unk_OverlayBrightValueChanged"); break; } case GeneralChannelMessage::Unk_OverlayAutoBrightnessChanged: { - UL_LOG_WARN("Unimplemented: Unk_OverlayAutoBrightnessChanged"); + UL_LOG_WARN("Got GeneralChannelMessage: Unk_OverlayAutoBrightnessChanged"); break; } case GeneralChannelMessage::Unk_OverlayAirplaneModeChanged: { - UL_LOG_WARN("Unimplemented: Unk_OverlayAirplaneModeChanged"); + UL_LOG_WARN("Got GeneralChannelMessage: Unk_OverlayAirplaneModeChanged"); break; } - case GeneralChannelMessage::Unk_HomeButtonHold: { - UL_LOG_WARN("Unimplemented: Unk_HomeButtonHold"); + case GeneralChannelMessage::Unk_OverlayShown: { + UL_LOG_WARN("Got GeneralChannelMessage: Unk_OverlayShown"); break; } case GeneralChannelMessage::Unk_OverlayHidden: { - UL_LOG_WARN("Unimplemented: Unk_OverlayHidden"); + UL_LOG_WARN("Got GeneralChannelMessage: Unk_OverlayHidden"); break; } case GeneralChannelMessage::RequestToLaunchApplication: { @@ -290,7 +391,7 @@ namespace { auto launch_params_buf = new u8[launch_params_buf_size]; UL_RC_ASSERT(sams_st_reader.ReadBuffer(launch_params_buf, launch_params_buf_size)); - UL_LOG_WARN("Unimplemented: RequestToLaunchApplication { launch_app_request_sender: %d, app_id: 0%16lX, uid: %016lX + %016lX, launch params buf size: 0x%X }", launch_app_request_sender, app_id, uid.uid[0], uid.uid[1], launch_params_buf_size); + UL_LOG_WARN("Got GeneralChannelMessage: RequestToLaunchApplication { launch_app_request_sender: %d, app_id: 0%16lX, uid: %016lX + %016lX, launch params buf size: 0x%X }", launch_app_request_sender, app_id, uid.uid[0], uid.uid[1], launch_params_buf_size); delete[] launch_params_buf; break; @@ -314,38 +415,67 @@ namespace { } } - Result UpdateOperationMode() { + void UpdateOperationMode() { // Thank you so much libnx for not exposing the actual call to get the mode via IPC :P // We're qlaunch, not using appletMainLoop, thus we have to take care of this manually... - u8 raw_mode = 0; - UL_RC_TRY(serviceDispatchOut(appletGetServiceSession_CommonStateGetter(), 5, raw_mode)); - + UL_RC_ASSERT(serviceDispatchOut(appletGetServiceSession_CommonStateGetter(), 5, raw_mode)); g_OperationMode = static_cast(raw_mode); - return ul::ResultSuccess; } void HandleAppletMessage() { + u32 finished_verify_count = 0; + for(auto &ctx: *g_ApplicationVerifyContexts) { + if(ctx.finished) { + UL_RC_ASSERT(threadWaitForExit(&ctx.thread)); + UL_RC_ASSERT(threadClose(&ctx.thread)); + finished_verify_count++; + } + } + + if(finished_verify_count > 0) { + auto new_end = std::remove_if(g_ApplicationVerifyContexts->begin(), g_ApplicationVerifyContexts->end(), [](const ApplicationVerifyContext &ctx) { return ctx.finished; }); + g_ApplicationVerifyContexts->erase(new_end, g_ApplicationVerifyContexts->end()); + } + u32 raw_msg = 0; if(R_SUCCEEDED(appletGetMessage(&raw_msg))) { - /* - Applet messages known to be received by us: - - ChangeIntoForeground - - ChangeIntoBackground - - ApplicationExited - - AutoPowerDown - - DetectShortPressingHomeButton - - DetectShortPressingPowerButton - - FinishedSleepSequence - - OperationModeChanged - - SdCardRemoved - */ - switch(static_cast(raw_msg)) { + case ul::system::AppletMessage::ChangeIntoForeground: { + UL_LOG_INFO("Got AppletMessage: ChangeIntoForeground"); + break; + } + case ul::system::AppletMessage::ChangeIntoBackground: { + UL_LOG_INFO("Got AppletMessage: ChangeIntoBackground"); + break; + } + case ul::system::AppletMessage::ApplicationExited: { + UL_LOG_INFO("Got AppletMessage: ApplicationExited"); + break; + } case ul::system::AppletMessage::DetectShortPressingHomeButton: { HandleHomeButton(); break; } + case ul::system::AppletMessage::DetectShortPressingPowerButton: { + HandleSleep(); + break; + } + case ul::system::AppletMessage::FinishedSleepSequence: { + if(la::IsMenu()) { + PushSimpleMenuMessage(ul::smi::MenuMessage::FinishedSleep); + } + break; + } + case ul::system::AppletMessage::AutoPowerDown: { + // From auto-sleep functionality + HandleSleep(); + break; + } + case ul::system::AppletMessage::OperationModeChanged: { + UpdateOperationMode(); + break; + } case ul::system::AppletMessage::SdCardRemoved: { if(la::IsMenu()) { PushSimpleMenuMessage(ul::smi::MenuMessage::SdCardEjected); @@ -357,14 +487,6 @@ namespace { } break; } - case ul::system::AppletMessage::DetectShortPressingPowerButton: { - HandleSleep(); - break; - } - case ul::system::AppletMessage::OperationModeChanged: { - UL_RC_ASSERT(UpdateOperationMode()); - break; - } default: UL_LOG_WARN("Unimplemented applet message: %d", raw_msg); break; @@ -374,7 +496,9 @@ namespace { void HandleMenuMessage() { if(la::IsMenu()) { - // Note: ignoring result since this won't always work, and would error if no commands were received + u32 app_list_count; + + // Note: ignoring result since this won't always succeed, and would error if no commands were received smi::ReceiveCommand( [&](const ul::smi::SystemMessage msg, smi::ScopedStorageReader &reader) -> Result { switch(msg) { @@ -443,7 +567,7 @@ namespace { break; } case ul::smi::SystemMessage::ChooseHomebrew: { - g_LoaderLaunchFlag = ul::loader::TargetInput::Create(ul::HbmenuPath, ul::HbmenuPath, true, "Choose a homebrew for uMenu"); + g_LoaderLaunchFlag = ul::loader::TargetInput::Create(ul::HbmenuPath, ul::HbmenuPath, true, ChooseHomebrewCaption); g_LoaderChooseFlag = true; break; } @@ -502,6 +626,34 @@ namespace { g_NetConnectAppletLaunchFlag = true; break; } + case ul::smi::SystemMessage::ListAddedApplications: { + UL_RC_TRY(reader.Pop(app_list_count)); + break; + } + case ul::smi::SystemMessage::ListDeletedApplications: { + UL_RC_TRY(reader.Pop(app_list_count)); + break; + } + case ul::smi::SystemMessage::OpenCabinet: { + u8 type; + UL_RC_TRY(reader.Pop(type)); + g_CabinetAppletLaunchFlag = true; + g_CabinetAppletLaunchType = static_cast(type); + break; + } + case ul::smi::SystemMessage::StartVerifyApplication: { + u64 app_id; + UL_RC_TRY(reader.Pop(app_id)); + + auto &ctx = g_ApplicationVerifyContexts->emplace_back(app_id); + UL_RC_ASSERT(threadCreate(&ctx.thread, ApplicationVerifyMain, std::addressof(ctx), ctx.thread_stack, ApplicationVerifyContext::ThreadStackSize, 30, -2)); + UL_RC_ASSERT(threadStart(&ctx.thread)); + break; + } + case ul::smi::SystemMessage::ListInVerifyApplications: { + UL_RC_TRY(reader.Pop(app_list_count)); + break; + } default: { // ... break; @@ -560,10 +712,52 @@ namespace { // ... break; } - case ul::smi::SystemMessage::OpenAddUser: { + case ul::smi::SystemMessage::OpenNetConnect: { + // ... + break; + } + case ul::smi::SystemMessage::ListAddedApplications: { + if(app_list_count > g_LastAddedApplications.size()) { + return ul::ResultInvalidApplicationListCount; + } + + for(u32 i = 0; i < app_list_count; i++) { + writer.Push(g_LastAddedApplications.at(i)); + } + + g_LastAddedApplications.clear(); + break; + } + case ul::smi::SystemMessage::ListDeletedApplications: { + if(app_list_count > g_LastDeletedApplications.size()) { + return ul::ResultInvalidApplicationListCount; + } + + for(u32 i = 0; i < app_list_count; i++) { + writer.Push(g_LastDeletedApplications.at(i)); + } + + g_LastDeletedApplications.clear(); + break; + } + case ul::smi::SystemMessage::OpenCabinet: { + // ... + break; + } + case ul::smi::SystemMessage::StartVerifyApplication: { // ... break; } + case ul::smi::SystemMessage::ListInVerifyApplications: { + if(app_list_count > g_ApplicationVerifyContexts->size()) { + return ul::ResultInvalidApplicationListCount; + } + + for(u32 i = 0; i < app_list_count; i++) { + writer.Push(g_ApplicationVerifyContexts->at(i).app_id); + } + break; + } default: { // ... break; @@ -576,14 +770,16 @@ namespace { } void MainLoop() { - HandleGeneralChannel(); + HandleGeneralChannelMessage(); HandleAppletMessage(); HandleMenuMessage(); auto sth_done = false; + // A valid version will always be >= 0x20000 if(g_WebAppletLaunchFlag.version > 0) { if(!la::IsActive()) { + UL_LOG_INFO("Launching web..."); UL_RC_ASSERT(la::StartWeb(&g_WebAppletLaunchFlag)); sth_done = true; @@ -592,7 +788,8 @@ namespace { } if(g_MenuRestartFlag) { if(!la::IsActive()) { - UL_RC_ASSERT(LaunchMenu(ul::smi::MenuStartMode::StartupMenu, CreateStatus())); + UL_LOG_INFO("Launching menu..."); + UL_RC_ASSERT(LaunchMenu(ul::smi::MenuStartMode::Start, CreateStatus())); sth_done = true; g_MenuRestartFlag = false; @@ -600,6 +797,7 @@ namespace { } if(g_AlbumAppletLaunchFlag) { if(!la::IsActive()) { + UL_LOG_INFO("Launching album..."); const struct { u8 album_arg; } album_data = { AlbumLaArg_ShowAllAlbumFilesForHomeMenu }; @@ -611,6 +809,7 @@ namespace { } if(g_UserPageAppletLaunchFlag) { if(!la::IsActive()) { + UL_LOG_INFO("Launching user page..."); FriendsLaArgHeader arg_hdr = { .type = FriendsLaArgType_ShowMyProfile, .uid = g_SelectedUser @@ -635,6 +834,7 @@ namespace { } if(g_MiiEditAppletLaunchFlag) { if(!la::IsActive()) { + UL_LOG_INFO("Launching mii edit..."); const auto mii_ver = hosversionAtLeast(10,2,0) ? 0x4 : 0x3; MiiLaAppletInput mii_in = { .version = mii_ver, @@ -649,6 +849,7 @@ namespace { } if(g_AddUserAppletLaunchFlag) { if(!la::IsActive()) { + UL_LOG_INFO("Launching add-user page..."); PselUiSettings psel_ui; UL_RC_ASSERT(pselUiCreate(&psel_ui, PselUiMode_UserCreator)); @@ -670,6 +871,7 @@ namespace { } if(g_NetConnectAppletLaunchFlag) { if(!la::IsActive()) { + UL_LOG_INFO("Launching net connect..."); u8 in[28] = {}; // TODO (low priority): 0 = normal, 1 = qlaunch, 2 = starter...? (consider documenting this better, maybe a PR to libnx even) *reinterpret_cast(in) = 1; @@ -685,11 +887,26 @@ namespace { g_NextMenuLaunchSettingsFlag = true; } } - if(g_ApplicationLaunchFlag > 0) { + if(g_CabinetAppletLaunchFlag) { if(!la::IsActive()) { - // Ensure the application is launchable - UL_RC_ASSERT(nsTouchApplication(g_ApplicationLaunchFlag)); + UL_LOG_INFO("Launching cabinet..."); + // Not sending any initial TagInfo/RegisterInfo makes the applet take care of the wait for the user to input amiibos + // No neeed for us/uMenu to handle any amiibo functionality at all ;) + const NfpLaStartParamForAmiiboSettings settings = { + .unk_x0 = 0, + .type = g_CabinetAppletLaunchType, + .flags = 1 + }; + UL_RC_ASSERT(la::Start(AppletId_LibraryAppletCabinet, 1, &settings, sizeof(settings))); + + sth_done = true; + g_CabinetAppletLaunchFlag = false; + } + } + if(g_ApplicationLaunchFlag > 0) { + if(!la::IsActive()) { + UL_LOG_INFO("Launching application 0x%016lX...", g_ApplicationLaunchFlag); if(g_LoaderApplicationLaunchFlag.magic == ul::loader::TargetInput::Magic) { UL_RC_ASSERT(ecs::RegisterLaunchAsApplication(g_ApplicationLaunchFlag, "/ulaunch/bin/uLoader/application", &g_LoaderApplicationLaunchFlag, sizeof(g_LoaderApplicationLaunchFlag), g_SelectedUser)); @@ -706,6 +923,7 @@ namespace { } if(g_LoaderLaunchFlag.magic == ul::loader::TargetInput::Magic) { if(!la::IsActive()) { + UL_LOG_INFO("Launching homebrew '%s' as library applet (once: %d)...", g_LoaderLaunchFlag.nro_path, g_LoaderLaunchFlag.target_once); u64 hb_applet_takeover_program_id; UL_ASSERT_TRUE(g_Config.GetEntry(ul::cfg::ConfigEntryId::HomebrewAppletTakeoverProgramId, hb_applet_takeover_program_id)); @@ -728,6 +946,8 @@ namespace { ul::loader::TargetOutput target_opt; if(g_LoaderChooseFlag) { + UL_LOG_INFO("Getting loader output..."); + AppletStorage target_opt_st; UL_RC_ASSERT(la::Pop(&target_opt_st)); UL_RC_ASSERT(appletStorageRead(&target_opt_st, 0, &target_opt, sizeof(target_opt))); @@ -735,7 +955,7 @@ namespace { auto menu_start_mode = ul::smi::MenuStartMode::MainMenu; if(g_NextMenuLaunchStartupFlag) { - menu_start_mode = ul::smi::MenuStartMode::StartupMenu; + menu_start_mode = ul::smi::MenuStartMode::Start; g_NextMenuLaunchStartupFlag = false; } if(g_NextMenuLaunchSettingsFlag) { @@ -766,7 +986,7 @@ namespace { if(!app::IsActive() && !la::IsActive()) { auto terminate_rc = ul::ResultSuccess; if(R_SUCCEEDED(nsGetApplicationTerminateResult(app::GetId(), &terminate_rc))) { - UL_LOG_WARN("Application 0x%016X terminated with result %s", app::GetId(), ul::util::FormatResultDisplay(terminate_rc).c_str()); + UL_LOG_WARN("Application 0x%016lX terminated with result %s", app::GetId(), ul::util::FormatResultDisplay(terminate_rc).c_str()); } // Reopen uMenu, notify failure @@ -824,40 +1044,64 @@ namespace { } void EventManagerMain(void*) { - UL_LOG_INFO("EventManager: alive!"); + UL_LOG_INFO("[EventManager] alive!"); Event record_ev; UL_RC_ASSERT(nsGetApplicationRecordUpdateSystemEvent(&record_ev)); + UL_LOG_INFO("[EventManager] registered ApplicationRecordUpdateSystemEvent"); Event gc_mount_fail_event; UL_RC_ASSERT(nsGetGameCardMountFailureEvent(&gc_mount_fail_event)); + UL_LOG_INFO("[EventManager] registered GameCardMountFailureEvent"); s32 ev_idx; while(true) { if(R_SUCCEEDED(waitMulti(&ev_idx, UINT64_MAX, waiterForEvent(&record_ev), waiterForEvent(&gc_mount_fail_event)))) { if(ev_idx == 0) { - UL_LOG_INFO("Application records changed! diff:"); + g_LastAddedApplications.clear(); + g_LastDeletedApplications.clear(); + UL_LOG_INFO("[EventManager] Application records changed!"); const auto diff_records = ListChangedRecords(); for(const auto &record: diff_records) { - if(std::find_if(g_CurrentRecords.begin(), g_CurrentRecords.end(), [&](const NsApplicationRecord &rec) -> bool { + if(std::find_if(g_CurrentRecords.begin(), g_CurrentRecords.end(), [record](const NsApplicationRecord &rec) -> bool { return rec.application_id == record.application_id; }) != g_CurrentRecords.end()) { - UL_LOG_INFO("- [new] 0x%lX", record.application_id); - ul::menu::CacheSingleApplication(record.application_id); + UL_LOG_INFO("[EventManager] > Added application 0x%016lX, caching...", record.application_id); + g_LastAddedApplications.push_back(record.application_id); + + while(!ul::menu::CacheSingleApplication(record.application_id)) { + UL_LOG_INFO("[EventManager] > Failed to cache, retrying..."); + svcSleepThread(100'000ul); + } + + UL_LOG_INFO("[EventManager] > cached!"); + ul::menu::EnsureApplicationEntry(record); } else { - UL_LOG_INFO("- [del] 0x%lX", record.application_id); - ul::menu::DeleteApplicationEntry(record.application_id, ul::MenuPath); + UL_LOG_INFO("[EventManager] > Deleted application 0x%016lX", record.application_id); + g_LastDeletedApplications.push_back(record.application_id); + ul::menu::DeleteApplicationEntryRecursively(record.application_id, ul::MenuPath); } } + + if(la::IsMenu()) { + // Only push this if uMenu is currently active + const ul::smi::MenuMessageContext msg_ctx = { + .msg = ul::smi::MenuMessage::ApplicationRecordsChanged, + .app_records_changed = { + .records_added_or_deleted = !diff_records.empty() + } + }; + PushMenuMessageContext(msg_ctx); + } } - if(ev_idx == 1) { + else if(ev_idx == 1) { eventClear(&gc_mount_fail_event); const auto fail_rc = nsGetLastGameCardMountFailureResult(); - UL_LOG_INFO("Gamecard mount failed with rc: 0x%X (sending it to uMenu...)", fail_rc); + UL_LOG_INFO("[EventManager] Gamecard mount failed with rc: 0x%X", fail_rc); const ul::smi::MenuMessageContext msg_ctx = { .msg = ul::smi::MenuMessage::GameCardMountFailure, @@ -913,9 +1157,87 @@ namespace { } } + void LocateApplicationAndSpecialEntries(const std::string &path, std::vector &out_app_entries, u32 &rem_special_entry_mask) { + auto entries = ul::menu::LoadEntries(path); + for(const auto &entry: entries) { + if(entry.Is()) { + out_app_entries.push_back(entry); + } + else if(entry.IsSpecial()) { + // This special entry exists, remove from remaining + rem_special_entry_mask &= ~BITL(static_cast(entry.type)); + } + else if(entry.Is()) { + LocateApplicationAndSpecialEntries(ul::fs::JoinPath(path, entry.folder_info.fs_name), out_app_entries, rem_special_entry_mask); + } + } + } + + void CheckApplicationRecordChanges() { + g_LastDeletedApplications.clear(); + g_LastAddedApplications.clear(); + + std::vector existing_app_entries; + u32 rem_special_entry_mask = + BITL(static_cast(ul::menu::EntryType::SpecialEntryMiiEdit)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryWebBrowser)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryUserPage)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntrySettings)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryThemes)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryControllers)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryAlbum)) | + BITL(static_cast(ul::menu::EntryType::SpecialEntryAmiibo)); + + LocateApplicationAndSpecialEntries(ul::MenuPath, existing_app_entries, rem_special_entry_mask); + + // Ensure all special entries exist + #define _CHECK_HAS_SPECIAL_ENTRY(type) { \ + if((rem_special_entry_mask & BITL(static_cast(type))) != 0) { \ + ul::menu::CreateSpecialEntry(ul::MenuPath, type); \ + } \ + } + + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryMiiEdit); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryWebBrowser); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryUserPage); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntrySettings); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryThemes); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryControllers); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryAlbum); + _CHECK_HAS_SPECIAL_ENTRY(ul::menu::EntryType::SpecialEntryAmiibo); + + // Check applications + + for(auto &app_entry: existing_app_entries) { + if(std::find_if(g_CurrentRecords.begin(), g_CurrentRecords.end(), [app_entry](const NsApplicationRecord &rec) -> bool { + return rec.application_id == app_entry.app_info.app_id; + }) == g_CurrentRecords.end()) { + UL_LOG_INFO("Deleted application 0x%016lX", app_entry.app_info.app_id); + g_LastDeletedApplications.push_back(app_entry.app_info.app_id); + ul::menu::Entry rm_entry(app_entry); + rm_entry.Remove(); + } + } + + for(const auto &record: g_CurrentRecords) { + if(std::find_if(existing_app_entries.begin(), existing_app_entries.end(), [record](const ul::menu::Entry &entry) -> bool { + return entry.app_info.app_id == record.application_id; + }) == existing_app_entries.end()) { + UL_LOG_INFO("Added application 0x%016lX", record.application_id); + g_LastAddedApplications.push_back(record.application_id); + while(!ul::menu::CacheSingleApplication(record.application_id)) { + UL_LOG_INFO("> Failed to cache, retrying..."); + svcSleepThread(100'000ul); + } + ul::menu::CacheSingleApplication(record.application_id); + ul::menu::EnsureApplicationEntry(record); + } + } + } + void Initialize() { UL_RC_ASSERT(appletLoadAndApplyIdlePolicySettings()); - UL_RC_ASSERT(UpdateOperationMode()); + UpdateOperationMode(); // Remove old cache ul::fs::DeleteDirectory(ul::OldApplicationCachePath); @@ -928,19 +1250,21 @@ namespace { CacheAccounts(); + ul::menu::SetLoadApplicationEntryVersions(false); g_CurrentRecords = ul::os::ListApplicationRecords(); ul::menu::CacheApplications(g_CurrentRecords); ul::menu::CacheHomebrew(); + CheckApplicationRecordChanges(); LoadConfig(); UL_RC_ASSERT(sf::Initialize()); - UL_RC_ASSERT(threadCreate(&g_EventManagerThread, EventManagerMain, nullptr, g_EventManagerThreadStack, sizeof(g_EventManagerThreadStack), 0x2C, -2)); + UL_RC_ASSERT(threadCreate(&g_EventManagerThread, EventManagerMain, nullptr, g_EventManagerThreadStack, sizeof(g_EventManagerThreadStack), 34, -2)); UL_RC_ASSERT(threadStart(&g_EventManagerThread)); bool viewer_usb_enabled; - UL_ASSERT_TRUE(g_Config.GetEntry(ul::cfg::ConfigEntryId::ViewerUsbEnabled, viewer_usb_enabled)); + UL_ASSERT_TRUE(g_Config.GetEntry(ul::cfg::ConfigEntryId::UsbScreenCaptureEnabled, viewer_usb_enabled)); if(viewer_usb_enabled) { UL_RC_ASSERT(usbCommsInitialize()); @@ -985,25 +1309,30 @@ namespace ams { namespace init { void InitializeSystemModule() { - ul::InitializeLogging("uSystem"); + __nx_applet_type = AppletType_SystemApplet; UL_RC_ASSERT(sm::Initialize()); - - __nx_applet_type = AppletType_SystemApplet; - UL_RC_ASSERT(appletInitialize()); UL_RC_ASSERT(fsInitialize()); + + UL_RC_ASSERT(appletInitialize()); + UL_RC_ASSERT(nsInitialize()); UL_RC_ASSERT(ldrShellInitialize()); UL_RC_ASSERT(pmshellInitialize()); + UL_RC_ASSERT(setsysInitialize()); + + // FS and log init is intentionally done at the end, otherwise the SD doesn't seem to be always ready...? UL_RC_ASSERT(fsdevMountSdmc()); + ul::InitializeLogging("uSystem"); } void FinalizeSystemModule() { + setsysExit(); capsscExit(); - fsdevUnmountAll(); pmshellExit(); ldrShellExit(); nsExit(); + fsdevUnmountAll(); fsExit(); appletExit(); } @@ -1016,6 +1345,7 @@ namespace ams { fake_heap_end = fake_heap_start + LibnxHeapSize; g_MenuMessageQueue = new std::queue(); + g_ApplicationVerifyContexts = new std::vector(); os::SetThreadNamePointer(os::GetCurrentThread(), "ul.system.Main"); } @@ -1024,7 +1354,8 @@ namespace ams { NORETURN void Exit(int rc) { AMS_UNUSED(rc); - AMS_ABORT("Unexpected exit called by system applet (uSystem)"); + UL_RC_ASSERT(false && "Unexpected exit called by system applet (uSystem)"); + AMS_ABORT(); } // uSystem handles basic qlaunch functionality since it is the back-end of the project, communicating with uMenu when neccessary @@ -1034,7 +1365,7 @@ namespace ams { Initialize(); // After having initialized everything, launch our menu - UL_RC_ASSERT(LaunchMenu(ul::smi::MenuStartMode::StartupMenu, CreateStatus())); + UL_RC_ASSERT(LaunchMenu(ul::smi::MenuStartMode::Start, CreateStatus())); // Loop forever, since qlaunch should NEVER terminate (AM would crash in that case) while(true) { diff --git a/projects/uSystem/source/ul/system/app/app_Application.cpp b/projects/uSystem/source/ul/system/app/app_Application.cpp index 7a6c5b5..7360f68 100644 --- a/projects/uSystem/source/ul/system/app/app_Application.cpp +++ b/projects/uSystem/source/ul/system/app/app_Application.cpp @@ -9,6 +9,41 @@ namespace ul::system::app { AppletApplication g_ApplicationHolder; u64 g_LastApplicationId; + inline void EnsureSaveData(const u64 app_id, const u64 owner_id, const AccountUid user_id, const FsSaveDataType type, const FsSaveDataSpaceId space_id, const u64 savedata_size, const u64 savedata_journal_size) { + if(savedata_size > 0) { + const FsSaveDataAttribute attr = { + .application_id = app_id, + .uid = user_id, + .system_save_data_id = 0, + .save_data_type = type, + .save_data_rank = FsSaveDataRank_Primary, + .save_data_index = 0 + }; + const FsSaveDataCreationInfo cr_info = { + .save_data_size = (s64)savedata_size, + .journal_size = (s64)savedata_journal_size, + .available_size = 0x4000, // Fixed value on all savedata creation in qlaunch + .owner_id = owner_id, + .flags = 0, + .save_data_space_id = (u8)space_id + }; + const FsSaveDataMetaInfo meta_info = { + .size = (type == FsSaveDataType_Bcat) ? 0u : 0x40060u, + .type = (type == FsSaveDataType_Bcat) ? FsSaveDataMetaType_None : FsSaveDataMetaType_Thumbnail + }; + + // Note: qlaunch uses a dedicated command (FindSaveData...), we just check if it exists by trying to open it + FsFileSystem dummy_fs; + if(R_SUCCEEDED(fsOpenSaveDataFileSystem(&dummy_fs, space_id, &attr))) { + fsFsClose(&dummy_fs); + } + else { + // Not yet created, create it then + UL_RC_ASSERT(fsCreateSaveDataFileSystem(&attr, &cr_info, &meta_info)); + } + } + } + } bool g_ApplicationHasFocus; @@ -25,6 +60,7 @@ namespace ul::system::app { } Result Terminate() { + UL_RC_TRY(appletApplicationTerminateAllLibraryApplets(&g_ApplicationHolder)); UL_RC_TRY(appletApplicationRequestExit(&g_ApplicationHolder)); const auto rc = eventWait(&g_ApplicationHolder.StateChangedEvent, 15'000'000'000ul); @@ -47,48 +83,30 @@ namespace ul::system::app { UL_RC_TRY(appletCreateSystemApplication(&g_ApplicationHolder, app_id)); } else { - // Ensure it's launchable + fsDeleteSaveDataFileSystem(app_id); + auto control_data = new NsApplicationControlData; + UL_ON_SCOPE_EXIT({ delete[] control_data; }); + + size_t dummy_size; + UL_RC_TRY(nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, control_data, sizeof(NsApplicationControlData), &dummy_size)); + + // Ensure it's launchable (TODO: does this do anything at all?) UL_RC_TRY(nsTouchApplication(app_id)); - auto ct_data = new NsApplicationControlData; - UL_ON_SCOPE_EXIT({ delete[] ct_data; }); + // Ensure Account savedata + EnsureSaveData(app_id, control_data->nacp.save_data_owner_id, user_id, FsSaveDataType_Account, FsSaveDataSpaceId_User, control_data->nacp.user_account_save_data_size, control_data->nacp.user_account_save_data_journal_size); - size_t dummy_size; - UL_RC_TRY(nsGetApplicationControlData(NsApplicationControlSource_Storage, app_id, ct_data, sizeof(NsApplicationControlData), &dummy_size)); + // Ensure Device savedata + EnsureSaveData(app_id, control_data->nacp.save_data_owner_id, {}, FsSaveDataType_Device, FsSaveDataSpaceId_User, control_data->nacp.device_save_data_size, control_data->nacp.device_save_data_journal_size); + + // Ensure Temporary savedata + EnsureSaveData(app_id, control_data->nacp.save_data_owner_id, {}, FsSaveDataType_Temporary, FsSaveDataSpaceId_Temporary, control_data->nacp.temporary_storage_size, 0); - // Note: why isn't TemporaryStorage automatically created with nsTouchApplication like regular savedata? - // Let's create it ourselves if it doesn't exist yet - if(ct_data->nacp.temporary_storage_size > 0) { - const FsSaveDataAttribute attr = { - .application_id = app_id, - .system_save_data_id = 0, - .save_data_type = FsSaveDataType_Temporary, - .save_data_rank = FsSaveDataRank_Primary, - .save_data_index = 0 - }; - constexpr auto space_id = FsSaveDataSpaceId_Temporary; - const FsSaveDataCreationInfo cr_info = { - .save_data_size = (s64)ct_data->nacp.temporary_storage_size, - .journal_size = 0, - .available_size = 0x4000, - .owner_id = app_id, - .flags = 0, - .save_data_space_id = space_id - }; - const FsSaveDataMetaInfo meta_info = { - .size = 0, - .type = FsSaveDataMetaType_None - }; + // Ensure Cache savedata + EnsureSaveData(app_id, control_data->nacp.save_data_owner_id, {}, FsSaveDataType_Cache, FsSaveDataSpaceId_User, control_data->nacp.cache_storage_size, control_data->nacp.cache_storage_journal_size); - FsFileSystem dummy_fs; - if(R_SUCCEEDED(fsOpenSaveDataFileSystem(&dummy_fs, space_id, &attr))) { - fsFsClose(&dummy_fs); - } - else { - // Not yet created, create it then - UL_RC_TRY(fsCreateSaveDataFileSystem(&attr, &cr_info, &meta_info)); - } - } + // Ensure Bcat savedata + EnsureSaveData(app_id, 0x010000000000000C, {}, FsSaveDataType_Bcat, FsSaveDataSpaceId_User, control_data->nacp.bcat_delivery_cache_storage_size, 0x200000); UL_RC_TRY(appletCreateApplication(&g_ApplicationHolder, app_id)); } diff --git a/projects/uSystem/source/ul/system/la/la_LibraryApplet.cpp b/projects/uSystem/source/ul/system/la/la_LibraryApplet.cpp index f1cbcc6..1be3826 100644 --- a/projects/uSystem/source/ul/system/la/la_LibraryApplet.cpp +++ b/projects/uSystem/source/ul/system/la/la_LibraryApplet.cpp @@ -5,16 +5,18 @@ namespace ul::system::la { namespace { - AppletHolder g_AppletHolder; + AppletHolder g_LibraryAppletHolder; AppletId g_MenuAppletId = AppletId_None; AppletId g_LastAppletId = AppletId_None; - struct AppletInfo { + struct LibraryAppletInfo { u64 program_id; AppletId applet_id; }; - constexpr AppletInfo g_AppletTable[] = { + constexpr u32 AppletTransitionBackgroundColor = RGBA8(0, 0, 0, 0xff); + + constexpr LibraryAppletInfo g_LibraryAppletTable[] = { { 0x0100000000001001, AppletId_LibraryAppletAuth }, { 0x0100000000001002, AppletId_LibraryAppletCabinet }, { 0x0100000000001003, AppletId_LibraryAppletController }, @@ -33,32 +35,36 @@ namespace ul::system::la { { 0x0100000000001011, AppletId_LibraryAppletWifiWebAuth }, { 0x0100000000001013, AppletId_LibraryAppletMyPage } }; - constexpr size_t AppletCount = sizeof(g_AppletTable) / sizeof(AppletInfo); + constexpr size_t LibraryAppletCount = sizeof(g_LibraryAppletTable) / sizeof(LibraryAppletInfo); } bool IsActive() { - if(g_AppletHolder.StateChangedEvent.revent == INVALID_HANDLE) { + if(g_LibraryAppletHolder.StateChangedEvent.revent == INVALID_HANDLE) { return false; } - if(!serviceIsActive(&g_AppletHolder.s)) { + if(!serviceIsActive(&g_LibraryAppletHolder.s)) { return false; } - return !appletHolderCheckFinished(&g_AppletHolder); + return !appletHolderCheckFinished(&g_LibraryAppletHolder); } Result Terminate() { // Give it 15 seconds - return appletHolderRequestExitOrTerminate(&g_AppletHolder, 15'000'000'000ul); + UL_RC_TRY(appletHolderRequestExitOrTerminate(&g_LibraryAppletHolder, 15'000'000'000ul)); + appletHolderClose(&g_LibraryAppletHolder); + + /* UL_RC_ASSERT(appletClearAppletTransitionBuffer(AppletTransitionBackgroundColor)); */ + + UL_RC_SUCCEED; } Result Start(const AppletId id, const s32 la_version, const void *in_data, const size_t in_size) { if(IsActive()) { - Terminate(); + UL_RC_TRY(Terminate()); } - appletHolderClose(&g_AppletHolder); - UL_RC_TRY(appletCreateLibraryApplet(&g_AppletHolder, id, LibAppletMode_AllForeground)); + UL_RC_TRY(appletCreateLibraryApplet(&g_LibraryAppletHolder, id, LibAppletMode_AllForeground)); // Treat -1/any negative pseudovalue as to not push these args if(la_version >= 0) { @@ -66,37 +72,39 @@ namespace ul::system::la { libappletArgsCreate(&la_args, (u32)la_version); // TODO (low priority): does this make any difference? libappletArgsSetPlayStartupSound(&la_args, true); - UL_RC_TRY(libappletArgsPush(&la_args, &g_AppletHolder)); + UL_RC_TRY(libappletArgsPush(&la_args, &g_LibraryAppletHolder)); } if(in_size > 0) { UL_RC_TRY(Send(in_data, in_size)); } - UL_RC_TRY(appletHolderStart(&g_AppletHolder)); + /* UL_RC_ASSERT(appletClearCaptureBuffer(true, AppletCaptureSharedBuffer_LastForeground, AppletTransitionBackgroundColor)); */ + + UL_RC_TRY(appletHolderStart(&g_LibraryAppletHolder)); g_LastAppletId = id; return ResultSuccess; } Result Send(const void *data, const size_t size) { - return libappletPushInData(&g_AppletHolder, data, size); + return libappletPushInData(&g_LibraryAppletHolder, data, size); } Result Read(void *data, const size_t size) { - return libappletPopOutData(&g_AppletHolder, data, size, nullptr); + return libappletPopOutData(&g_LibraryAppletHolder, data, size, nullptr); } Result Push(AppletStorage *st) { - return appletHolderPushInData(&g_AppletHolder, st); + return appletHolderPushInData(&g_LibraryAppletHolder, st); } Result Pop(AppletStorage *st) { - return appletHolderPopOutData(&g_AppletHolder, st); + return appletHolderPopOutData(&g_LibraryAppletHolder, st); } u64 GetProgramIdForAppletId(const AppletId id) { - for(u32 i = 0; i < AppletCount; i++) { - const auto info = g_AppletTable[i]; + for(u32 i = 0; i < LibraryAppletCount; i++) { + const auto info = g_LibraryAppletTable[i]; if(info.applet_id == id) { return info.program_id; } @@ -106,8 +114,8 @@ namespace ul::system::la { } AppletId GetAppletIdForProgramId(const u64 id) { - for(u32 i = 0; i < AppletCount; i++) { - const auto info = g_AppletTable[i]; + for(u32 i = 0; i < LibraryAppletCount; i++) { + const auto info = g_LibraryAppletTable[i]; if(info.program_id == id) { return info.applet_id; } diff --git a/projects/uSystem/source/ul/system/sf/sf_IPrivateService.cpp b/projects/uSystem/source/ul/system/sf/sf_IPrivateService.cpp index 999ab00..d78da72 100644 --- a/projects/uSystem/source/ul/system/sf/sf_IPrivateService.cpp +++ b/projects/uSystem/source/ul/system/sf/sf_IPrivateService.cpp @@ -23,7 +23,7 @@ namespace ul::system::sf { this->initialized = true; } - + return ResultSuccess; }