diff --git a/troposphere/daybreak/source/main.cpp b/troposphere/daybreak/source/main.cpp index ba7656873..268808366 100644 --- a/troposphere/daybreak/source/main.cpp +++ b/troposphere/daybreak/source/main.cpp @@ -26,10 +26,6 @@ extern "C" { void userAppInit(void) { Result rc = 0; - if (R_FAILED(rc = amssuInitialize())) { - fatalThrow(rc); - } - if (R_FAILED(rc = romfsInit())) { fatalThrow(rc); } @@ -42,6 +38,10 @@ extern "C" { fatalThrow(rc); } + if (R_FAILED(rc = splInitialize())) { + fatalThrow(rc); + } + if (R_FAILED(rc = hiddbgInitialize())) { fatalThrow(rc); } @@ -50,6 +50,7 @@ extern "C" { void userAppExit(void) { hiddbgExit(); + splExit(); plExit(); spsmExit(); romfsExit(); diff --git a/troposphere/daybreak/source/ui.cpp b/troposphere/daybreak/source/ui.cpp index d055efbaf..71ee72e74 100644 --- a/troposphere/daybreak/source/ui.cpp +++ b/troposphere/daybreak/source/ui.cpp @@ -26,6 +26,10 @@ namespace dbk { namespace { + static constexpr u32 ExosphereApiVersionConfigItem = 65000; + static constexpr u32 ExosphereHasRcmBugPatch = 65004; + static constexpr u32 ExosphereEmummcType = 65007; + u32 g_screen_width; u32 g_screen_height; @@ -313,6 +317,132 @@ namespace dbk { return m_prev_menu; } + ErrorMenu::ErrorMenu(const char *text, const char *subtext, Result rc) : Menu(nullptr), m_text{}, m_subtext{}, m_result_text{}, m_rc(rc) { + const float window_height = WindowHeight + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; + const float y = g_screen_height / 2.0f - window_height / 2.0f; + + /* Copy the input text. */ + strncpy(m_text, text, sizeof(m_text)-1); + strncpy(m_subtext, subtext, sizeof(m_subtext)-1); + + /* Copy result text if there is a result. */ + if (R_FAILED(rc)) { + snprintf(m_result_text, sizeof(m_result_text)-1, "Result: 0x%08x", rc); + } + + const float button_y = y + TitleGap + SubTextHeight + VerticalGap + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + this->AddButton(ExitButtonId, "Exit", x + HorizontalGap, button_y, ButtonWidth, ButtonHeight); + this->SetButtonSelected(ExitButtonId, true); + } + + void ErrorMenu::Update(u64 ns) { + u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO); + + /* Go back if B is pressed. */ + if (k_down & KEY_B) { + g_exit_requested = true; + return; + } + + /* Take action if a button has been activated. */ + if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) { + switch (activated_button->id) { + case ExitButtonId: + g_exit_requested = true; + break; + } + } + + this->UpdateButtons(); + + /* Fallback on selecting the exfat button. */ + if (const Button *selected_button = this->GetSelectedButton(); k_down && selected_button == nullptr) { + this->SetButtonSelected(ExitButtonId, true); + } + } + + void ErrorMenu::Draw(NVGcontext *vg, u64 ns) { + const float window_height = WindowHeight + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; + const float y = g_screen_height / 2.0f - window_height / 2.0f; + + DrawWindow(vg, m_text, x, y, WindowWidth, window_height); + DrawText(vg, x + HorizontalGap, y + TitleGap, WindowWidth - HorizontalGap * 2.0f, m_subtext); + + /* Draw the result if there is one. */ + if (R_FAILED(m_rc)) { + DrawText(vg, x + HorizontalGap, y + TitleGap + SubTextHeight, WindowWidth - HorizontalGap * 2.0f, m_result_text); + } + + this->DrawButtons(vg, ns); + } + + WarningMenu::WarningMenu(std::shared_ptr prev_menu, std::shared_ptr next_menu, const char *text, const char *subtext, Result rc) : Menu(prev_menu), m_next_menu(next_menu), m_text{}, m_subtext{}, m_result_text{}, m_rc(rc) { + const float window_height = WindowHeight + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; + const float y = g_screen_height / 2.0f - window_height / 2.0f; + + /* Copy the input text. */ + strncpy(m_text, text, sizeof(m_text)-1); + strncpy(m_subtext, subtext, sizeof(m_subtext)-1); + + /* Copy result text if there is a result. */ + if (R_FAILED(rc)) { + snprintf(m_result_text, sizeof(m_result_text)-1, "Result: 0x%08x", rc); + } + + const float button_y = y + TitleGap + SubTextHeight + VerticalGap + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + this->AddButton(BackButtonId, "Back", x + HorizontalGap, button_y, ButtonWidth, ButtonHeight); + this->AddButton(ContinueButtonId, "Continue", x + HorizontalGap + ButtonWidth + ButtonHorizontalGap, button_y, ButtonWidth, ButtonHeight); + this->SetButtonSelected(ContinueButtonId, true); + } + + void WarningMenu::Update(u64 ns) { + u64 k_down = hidKeysDown(CONTROLLER_P1_AUTO); + + /* Go back if B is pressed. */ + if (k_down & KEY_B) { + ReturnToPreviousMenu(); + return; + } + + /* Take action if a button has been activated. */ + if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) { + switch (activated_button->id) { + case BackButtonId: + ReturnToPreviousMenu(); + return; + case ContinueButtonId: + ChangeMenu(m_next_menu); + return; + } + } + + this->UpdateButtons(); + + /* Fallback on selecting the exfat button. */ + if (const Button *selected_button = this->GetSelectedButton(); k_down && selected_button == nullptr) { + this->SetButtonSelected(ContinueButtonId, true); + } + } + + void WarningMenu::Draw(NVGcontext *vg, u64 ns) { + const float window_height = WindowHeight + (R_FAILED(m_rc) ? SubTextHeight : 0.0f); + const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; + const float y = g_screen_height / 2.0f - window_height / 2.0f; + + DrawWindow(vg, m_text, x, y, WindowWidth, window_height); + DrawText(vg, x + HorizontalGap, y + TitleGap, WindowWidth - HorizontalGap * 2.0f, m_subtext); + + /* Draw the result if there is one. */ + if (R_FAILED(m_rc)) { + DrawText(vg, x + HorizontalGap, y + TitleGap + SubTextHeight, WindowWidth - HorizontalGap * 2.0f, m_result_text); + } + + this->DrawButtons(vg, ns); + } + MainMenu::MainMenu() : Menu(nullptr) { const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; const float y = g_screen_height / 2.0f - WindowHeight / 2.0f; @@ -333,11 +463,42 @@ namespace dbk { if (const Button *activated_button = this->GetActivatedButton(); activated_button != nullptr) { switch (activated_button->id) { case InstallButtonId: - ChangeMenu(std::make_shared(g_current_menu, "/")); - break; + { + const auto file_menu = std::make_shared(g_current_menu, "/"); + + Result rc = 0; + u64 hardware_type; + u64 has_rcm_bug_patch; + u64 is_emummc; + + if (R_FAILED(rc = splGetConfig(SplConfigItem_HardwareType, &hardware_type))) { + ChangeMenu(std::make_shared("An error has occurred", "Failed to get hardware type.", rc)); + return; + } + + if (R_FAILED(rc = splGetConfig(static_cast(ExosphereHasRcmBugPatch), &has_rcm_bug_patch))) { + ChangeMenu(std::make_shared("An error has occurred", "Failed to check RCM bug status.", rc)); + return; + } + + if (R_FAILED(rc = splGetConfig(static_cast(ExosphereEmummcType), &is_emummc))) { + ChangeMenu(std::make_shared("An error has occurred", "Failed to chech emuMMC status.", rc)); + return; + } + + /* Warn if we're working with a patched unit. */ + const bool is_erista = hardware_type == 0 || hardware_type == 1; + if (is_erista && has_rcm_bug_patch && !is_emummc) { + ChangeMenu(std::make_shared(g_current_menu, file_menu, "Warning: Patched unit detected", "You may burn fuses or render your switch inoperable.")); + } else { + ChangeMenu(file_menu); + } + + return; + } case ExitButtonId: g_exit_requested = true; - break; + return; } } @@ -737,9 +898,18 @@ namespace dbk { const float x = g_screen_width / 2.0f - WindowWidth / 2.0f; const float y = g_screen_height / 2.0f - WindowHeight / 2.0f; - this->AddButton(Fat32ButtonId, "FAT32", x + ButtonHorizontalInset, y + TitleGap, ButtonWidth, ButtonHeight); - this->AddButton(ExFatButtonId, "exFAT", x + ButtonHorizontalInset + ButtonWidth + ButtonHorizontalGap, y + TitleGap, ButtonWidth, ButtonHeight); - this->SetButtonSelected(ExFatButtonId, true); + this->AddButton(Fat32ButtonId, "Install (FAT32)", x + ButtonHorizontalInset, y + TitleGap, ButtonWidth, ButtonHeight); + this->AddButton(ExFatButtonId, "Install (exFAT/FAT32)", x + ButtonHorizontalInset + ButtonWidth + ButtonHorizontalGap, y + TitleGap, ButtonWidth, ButtonHeight); + + /* Set the default selected button based on the user's current install. We aren't particularly concerned if fsIsExFatSupported fails. */ + bool exfat_supported = false; + fsIsExFatSupported(&exfat_supported); + + if (exfat_supported) { + this->SetButtonSelected(ExFatButtonId, true); + } else { + this->SetButtonSelected(Fat32ButtonId, true); + } } void ChooseExfatMenu::Update(u64 ns) { @@ -930,15 +1100,40 @@ namespace dbk { } void InitializeMenu(u32 screen_width, u32 screen_height) { + Result rc = 0; + /* Set the screen width and height. */ g_screen_width = screen_width; g_screen_height = screen_height; - /* Change the current menu to the main menu. */ - g_current_menu = std::make_shared(); - /* Mark as initialized. */ g_initialized = true; + + /* Attempt to get the exosphere version. */ + u64 version; + if (R_FAILED(rc = splGetConfig(static_cast(ExosphereApiVersionConfigItem), &version))) { + ChangeMenu(std::make_shared("Atmosphere not found", "Daybreak requires Atmosphere to be installed.", rc)); + return; + } + + const u32 version_micro = (version >> 40) & 0xff; + const u32 version_minor = (version >> 48) & 0xff; + const u32 version_major = (version >> 56) & 0xff; + + /* Validate the exosphere version. */ + const bool ams_supports_sysupdate_api = version_major >= 0 && version_minor >= 14 && version_micro >= 0; + if (!ams_supports_sysupdate_api) { + ChangeMenu(std::make_shared("Outdated Atmosphere version", "Daybreak requires Atmosphere 0.14.0 or later.", rc)); + return; + } + + /* Initialize ams:su. */ + if (R_FAILED(rc = amssuInitialize())) { + fatalThrow(rc); + } + + /* Change the current menu to the main menu. */ + g_current_menu = std::make_shared(); } void UpdateMenu(u64 ns) { diff --git a/troposphere/daybreak/source/ui.hpp b/troposphere/daybreak/source/ui.hpp index 8afa43b16..277f36834 100644 --- a/troposphere/daybreak/source/ui.hpp +++ b/troposphere/daybreak/source/ui.hpp @@ -82,6 +82,58 @@ namespace dbk { virtual void Draw(NVGcontext *vg, u64 ns) = 0; }; + class ErrorMenu : public Menu { + private: + static constexpr u32 ExitButtonId = 0; + + static constexpr float WindowWidth = 600.0f; + static constexpr float WindowHeight = 214.0f; + static constexpr float TitleGap = 90.0f; + static constexpr float SubTextHeight = 24.0f; + static constexpr float HorizontalGap = 20.0f; + static constexpr float VerticalGap = 20.0f; + static constexpr float ButtonHeight = 60.0f; + static constexpr float ButtonHorizontalGap = 10.0f; + static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f); + private: + char m_text[0x100]; + char m_subtext[0x100]; + char m_result_text[0x20]; + Result m_rc; + public: + ErrorMenu(const char *text, const char *subtext, Result rc = 0); + + virtual void Update(u64 ns) override; + virtual void Draw(NVGcontext *vg, u64 ns) override; + }; + + class WarningMenu : public Menu { + private: + static constexpr u32 BackButtonId = 0; + static constexpr u32 ContinueButtonId = 1; + + static constexpr float WindowWidth = 600.0f; + static constexpr float WindowHeight = 214.0f; + static constexpr float TitleGap = 90.0f; + static constexpr float SubTextHeight = 24.0f; + static constexpr float HorizontalGap = 20.0f; + static constexpr float VerticalGap = 20.0f; + static constexpr float ButtonHeight = 60.0f; + static constexpr float ButtonHorizontalGap = 10.0f; + static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f) / 2.0f - ButtonHorizontalGap; + private: + std::shared_ptr m_next_menu; + char m_text[0x100]; + char m_subtext[0x100]; + char m_result_text[0x20]; + Result m_rc; + public: + WarningMenu(std::shared_ptr prev_menu, std::shared_ptr next_menu, const char *text, const char *subtext, Result rc = 0); + + virtual void Update(u64 ns) override; + virtual void Draw(NVGcontext *vg, u64 ns) override; + }; + class MainMenu : public Menu { private: static constexpr u32 InstallButtonId = 0; @@ -148,8 +200,8 @@ namespace dbk { static constexpr float BottomGap = 20.0f; static constexpr float HorizontalGap = 20.0f; static constexpr float TextAreaHeight = 410.0f; - static constexpr float TextHorizontalInset = 6.0f; - static constexpr float TextVerticalInset = 6.0f; + static constexpr float TextHorizontalInset = 8.0f; + static constexpr float TextVerticalInset = 8.0f; static constexpr float ButtonHeight = 60.0f; static constexpr float ButtonHorizontalGap = 10.0f; static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f) / 2.0f - ButtonHorizontalGap; @@ -211,8 +263,8 @@ namespace dbk { static constexpr float ProgressBarHeight = 30.0f; static constexpr float VerticalGap = 10.0f; static constexpr float TextAreaHeight = 320.0f; - static constexpr float TextHorizontalInset = 6.0f; - static constexpr float TextVerticalInset = 6.0f; + static constexpr float TextHorizontalInset = 8.0f; + static constexpr float TextVerticalInset = 8.0f; static constexpr float ButtonHeight = 60.0f; static constexpr float ButtonHorizontalGap = 10.0f; static constexpr float ButtonWidth = (WindowWidth - HorizontalGap * 2.0f) / 2.0f - ButtonHorizontalGap; diff --git a/troposphere/daybreak/source/ui_util.cpp b/troposphere/daybreak/source/ui_util.cpp index aa88c4971..3ed6a66f8 100644 --- a/troposphere/daybreak/source/ui_util.cpp +++ b/troposphere/daybreak/source/ui_util.cpp @@ -30,9 +30,9 @@ namespace dbk { /* Calculate the rgb values for the breathing colour effect. */ const double t = static_cast(ns) / 1'000'000'000.0d; const float d = -0.5 * cos(3.0f*t) + 0.5f; - const int r2 = 83 + (float)(144 - 83) * (d * 0.7f + 0.3f); - const int g2 = 71 + (float)(185 - 71) * (d * 0.7f + 0.3f); - const int b2 = 185 + (float)(217 - 185) * (d * 0.7f + 0.3f); + const int r2 = 83 + (float)(128 - 83) * (d * 0.7f + 0.3f); + const int g2 = 71 + (float)(126 - 71) * (d * 0.7f + 0.3f); + const int b2 = 185 + (float)(230 - 185) * (d * 0.7f + 0.3f); return nvgRGB(r2, g2, b2); } @@ -135,6 +135,16 @@ namespace dbk { nvgFill(vg); } + void DrawText(NVGcontext *vg, float x, float y, float w, const char *text) { + nvgFontSize(vg, 20.0f); + nvgFontFace(vg, SwitchStandardFont); + nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); + nvgFillColor(vg, nvgRGB(0, 0, 0)); + + const float tw = nvgTextBounds(vg, 0, 0, text, nullptr, nullptr); + nvgText(vg, x + w * 0.5f - tw * 0.5f, y, text, nullptr); + } + void DrawProgressText(NVGcontext *vg, float x, float y, float progress) { char progress_text[32] = {}; snprintf(progress_text, sizeof(progress_text)-1, "%d%% complete", static_cast(progress * 100.0f)); @@ -155,7 +165,7 @@ namespace dbk { /* Draw the progress bar fill. */ if (progress > 0.0f) { - NVGpaint progress_fill_paint = nvgLinearGradient(vg, x, y + 0.5f * h, x + w, y + 0.5f * h, nvgRGB(83, 71, 185), nvgRGB(144, 185, 217)); + NVGpaint progress_fill_paint = nvgLinearGradient(vg, x, y + 0.5f * h, x + w, y + 0.5f * h, nvgRGB(83, 71, 185), nvgRGB(128, 126, 230)); nvgBeginPath(vg); nvgRoundedRect(vg, x, y, WindowCornerRadius + (w - WindowCornerRadius) * progress, h, WindowCornerRadius); nvgFillPaint(vg, progress_fill_paint); @@ -171,6 +181,7 @@ namespace dbk { /* Configure the text. */ nvgFontSize(vg, 18.0f); nvgFontFace(vg, SwitchStandardFont); + nvgTextLineHeight(vg, 1.3f); nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); nvgFillColor(vg, nvgRGB(0, 0, 0)); diff --git a/troposphere/daybreak/source/ui_util.hpp b/troposphere/daybreak/source/ui_util.hpp index 4522760ab..224f36c91 100644 --- a/troposphere/daybreak/source/ui_util.hpp +++ b/troposphere/daybreak/source/ui_util.hpp @@ -32,6 +32,7 @@ namespace dbk { void DrawWindow(NVGcontext *vg, const char *title, float x, float y, float w, float h); void DrawButton(NVGcontext *vg, const char *text, float x, float y, float w, float h, ButtonStyle style, u64 ns); void DrawTextBackground(NVGcontext *vg, float x, float y, float w, float h); + void DrawText(NVGcontext *vg, float x, float y, float w, const char *text); void DrawProgressText(NVGcontext *vg, float x, float y, float progress); void DrawProgressBar(NVGcontext *vg, float x, float y, float w, float h, float progress); void DrawTextBlock(NVGcontext *vg, const char *text, float x, float y, float w, float h);