From 484c218989370b67c96f38ffafe958bd82128af0 Mon Sep 17 00:00:00 2001 From: Maschell Date: Tue, 18 Jan 2022 14:17:15 +0100 Subject: [PATCH] Show a Account Selection screen when no default account has been set --- source/ACTAccountInfo.h | 13 ++++ source/BootUtils.cpp | 60 +++++++++++++++++ source/MenuUtils.cpp | 146 +++++++++++++++++++++++++++++++++++++++- source/MenuUtils.h | 10 ++- source/StorageUtils.cpp | 2 +- 5 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 source/ACTAccountInfo.h diff --git a/source/ACTAccountInfo.h b/source/ACTAccountInfo.h new file mode 100644 index 0000000..a43b31d --- /dev/null +++ b/source/ACTAccountInfo.h @@ -0,0 +1,13 @@ +#pragma once + +class AccountInfo { +public: + AccountInfo() = default; + + nn::act::SlotNo slot{}; + std::string name; + char accountId[nn::act::AccountIdSize]; + bool isNetworkAccount = false; + uint8_t miiImageBuffer[65554]; + uint32_t miiImageSize = 0; +}; diff --git a/source/BootUtils.cpp b/source/BootUtils.cpp index c64cae4..186f6f3 100644 --- a/source/BootUtils.cpp +++ b/source/BootUtils.cpp @@ -1,12 +1,29 @@ #include +#include +#include +#include +#include +#include +#include #include "BootUtils.h" +#include "logger.h" +#include "DrawUtils.h" +#include "MenuUtils.h" +#include "ACTAccountInfo.h" +#include +#include #include #include #include #include #include +#include +#include + + +void handleAccountSelection(); void bootWiiUMenu() { nn::act::Initialize(); @@ -22,10 +39,53 @@ void bootWiiUMenu() { } void bootHomebrewLauncher() { + handleAccountSelection(); + uint64_t titleId = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER); _SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); } +void handleAccountSelection() { + nn::act::Initialize(); + nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount(); + + if (!defaultSlot) { // No default account is set. + std::vector> accountInfoList; + for (int32_t i = 0; i < 13; i++) { + if (!nn::act::IsSlotOccupied(i)) { + continue; + } + char16_t nameOut[nn::act::MiiNameSize]; + std::shared_ptr accountInfo = std::make_shared(); + accountInfo->slot = i; + auto result = nn::act::GetMiiNameEx(reinterpret_cast(nameOut), i); + if (result.IsSuccess()) { + std::u16string source; + std::wstring_convert, char16_t> convert; + accountInfo->name = convert.to_bytes((char16_t *) nameOut); + } else { + accountInfo->name = "[UNKNOWN]"; + } + accountInfo->isNetworkAccount = nn::act::IsNetworkAccountEx(i); + if (accountInfo->isNetworkAccount) { + nn::act::GetAccountIdEx(accountInfo->accountId, i); + } + + uint32_t imageSize = 0; + result = nn::act::GetMiiImageEx(&imageSize, accountInfo->miiImageBuffer, sizeof(accountInfo->miiImageBuffer), 0, i); + if (result.IsSuccess()) { + accountInfo->miiImageSize = imageSize; + } + accountInfoList.push_back(accountInfo); + } + auto slot = handleAccountSelectScreen(accountInfoList); + + DEBUG_FUNCTION_LINE("Load slot %d", slot); + nn::act::LoadConsoleAccount(slot, 0, nullptr, false); + } + nn::act::Finalize(); +} + static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high) { // we need to init kpad for cmpt KPADInit(); diff --git a/source/MenuUtils.cpp b/source/MenuUtils.cpp index 07ba049..a59aa2e 100644 --- a/source/MenuUtils.cpp +++ b/source/MenuUtils.cpp @@ -2,7 +2,7 @@ #include #include #include - +#include #include "MenuUtils.h" #include "DrawUtils.h" @@ -10,8 +10,12 @@ #include #include #include +#include +#include #include "icon_png.h" +#include "ACTAccountInfo.h" +#include "logger.h" const char *menu_options[] = { "Wii U Menu", @@ -27,6 +31,14 @@ const char *autoboot_config_strings[] = { "vwii_homebrew_channel", }; +template +std::string string_format(const std::string &format, Args ... args) { + int size_s = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0' + auto size = static_cast( size_s ); + auto buf = std::make_unique(size); + std::snprintf(buf.get(), size, format.c_str(), args ...); + return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside +} int32_t readAutobootOption(std::string &configPath) { FILE *f = fopen(configPath.c_str(), "r"); @@ -153,4 +165,136 @@ int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput) { } return selected; +} + + +nn::act::SlotNo handleAccountSelectScreen(const std::vector> &data) { + auto screenBuffer = DrawUtils::InitOSScreen(); + if (!screenBuffer) { + OSFatal("Failed to alloc memory for screen"); + } + + uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); + uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC); + + DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize); + DrawUtils::initFont(); + + int32_t selected = 0; + bool redraw = true; + while (true) { + VPADStatus vpad{}; + VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr); + + if (vpad.trigger & VPAD_BUTTON_UP) { + if (selected > 0) { + selected--; + redraw = true; + } + } else if (vpad.trigger & VPAD_BUTTON_DOWN) { + if (selected < data.size() - 1) { + selected++; + redraw = true; + } + } else if (vpad.trigger & VPAD_BUTTON_A) { + break; + } + + if (redraw) { + DrawUtils::beginDraw(); + DrawUtils::clear(COLOR_BACKGROUND); + + // draw buttons + uint32_t index = 8 + 24 + 8 + 4; + int32_t start = (selected / 5) * 5; + auto end = start + 5 < data.size() ? start + 5 : data.size(); + for (int i = start; i < end; i++) { + auto &val = data[i]; + if (val->miiImageSize > 0) { + // Draw Mii + auto width = 128; + auto height = 128; + auto target_height = 64; + auto target_width = 64; + auto xOffset = 20; + auto yOffset = index; + for (uint32_t y = 0; y < target_height; y++) { + for (uint32_t x = 0; x < target_width; x++) { + uint32_t col = (((x) * width / target_width) + ((target_height - y - 1) * height / target_height) * width) * 4; + uint32_t colVal = ((uint32_t *) &val->miiImageBuffer[col + 1])[0]; + if (colVal == 0x00808080) { // Remove the green background. + DrawUtils::drawPixel(x + xOffset, y + yOffset, COLOR_BACKGROUND.r, COLOR_BACKGROUND.g, COLOR_BACKGROUND.b, COLOR_BACKGROUND.a); + } else { + DrawUtils::drawPixel(x + xOffset, y + yOffset, val->miiImageBuffer[col + 1], val->miiImageBuffer[col + 2], val->miiImageBuffer[col + 3], val->miiImageBuffer[col]); + } + } + } + } + + if (i == selected) { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 64, 4, COLOR_BORDER_HIGHLIGHTED); + } + + DrawUtils::setFontSize(24); + DrawUtils::setFontColor(COLOR_TEXT); + + std::string finalStr = val->name + (val->isNetworkAccount ? (std::string(" (NNID: ") + val->accountId + ")") : ""); + DrawUtils::print(72 + 16 * 2, index + 8 + 32, finalStr.c_str()); + + index += 72 + 8; + } + + DrawUtils::setFontColor(COLOR_TEXT); + + // draw top bar + DrawUtils::setFontSize(24); + DrawUtils::print(16, 6 + 24, "Select your Account"); + auto curPage = (selected / 5) + 1; + auto totalPages = data.size() % 5 == 0 ? data.size() / 5 : data.size() / 5 + 1; + DrawUtils::print(SCREEN_WIDTH - 50, 6 + 24, string_format("%d/%d", curPage, totalPages).c_str()); + DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE); + + // draw bottom bar + DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE); + DrawUtils::setFontSize(18); + DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate "); + DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue000 Choose", true); + + if (start > 0) { + DrawUtils::setFontSize(36); + DrawUtils::print(SCREEN_WIDTH - 30, 68, "\uE01B", true); + } + + if (end < data.size()) { + DrawUtils::setFontSize(36); + DrawUtils::print(SCREEN_WIDTH - 30, SCREEN_HEIGHT - 40, "\uE01C", true); + } + + DrawUtils::endDraw(); + + redraw = false; + } + } + + DrawUtils::beginDraw(); + DrawUtils::clear(COLOR_BLACK); + DrawUtils::endDraw(); + + DrawUtils::deinitFont(); + + // Call GX2Init to shut down OSScreen + GX2Init(nullptr); + + free(screenBuffer); + + auto i = 0; + nn::act::SlotNo resultSlot = 0; + for (auto const &val: data) { + if (i == selected) { + resultSlot = val->slot; + } + i++; + } + + return resultSlot; } \ No newline at end of file diff --git a/source/MenuUtils.h b/source/MenuUtils.h index 361ff23..0dab2ef 100644 --- a/source/MenuUtils.h +++ b/source/MenuUtils.h @@ -1,7 +1,11 @@ #pragma once -#include +#include #include +#include +#include +#include +#include "ACTAccountInfo.h" #define COLOR_WHITE Color(0xffffffff) #define COLOR_BLACK Color(0, 0, 0, 255) @@ -23,4 +27,6 @@ int32_t readAutobootOption(std::string &configPath); void writeAutobootOption(std::string &configPath, int32_t autobootOption); -int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput); \ No newline at end of file +int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput); + +nn::act::SlotNo handleAccountSelectScreen(const std::vector> &data); \ No newline at end of file diff --git a/source/StorageUtils.cpp b/source/StorageUtils.cpp index e2dc878..d8abf9d 100644 --- a/source/StorageUtils.cpp +++ b/source/StorageUtils.cpp @@ -135,7 +135,7 @@ void initExternalStorage() { tries++; } if (!found) { - DEBUG_FUNCTION_LINE("USB Storage is connected but either it's not connected or we ran into a timeout."); + DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout."); InitEmptyExternalStorage(); }