diff --git a/source/BootUtils.cpp b/source/BootUtils.cpp new file mode 100644 index 0000000..c64cae4 --- /dev/null +++ b/source/BootUtils.cpp @@ -0,0 +1,62 @@ +#include + +#include "BootUtils.h" + +#include +#include +#include +#include +#include + +void bootWiiUMenu() { + nn::act::Initialize(); + nn::act::SlotNo slot = nn::act::GetSlotNo(); + nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount(); + nn::act::Finalize(); + + if (defaultSlot) { //normal menu boot + SYSLaunchMenu(); + } else { //show mii select + _SYSLaunchMenuWithCheckingAccount(slot); + } +} + +void bootHomebrewLauncher() { + uint64_t titleId = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER); + _SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); +} + +static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high) { + // we need to init kpad for cmpt + KPADInit(); + + // Try to find a screen type that works + CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_BOTH); + if (CMPTCheckScreenState() < 0) { + CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_DRC); + if (CMPTCheckScreenState() < 0) { + CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_TV); + } + } + + uint32_t dataSize = 0; + CMPTGetDataSize(&dataSize); + + void *dataBuffer = memalign(0x40, dataSize); + + if (titleId_low == 0 && titleId_high == 0) { + CMPTLaunchMenu(dataBuffer, dataSize); + } else { + CMPTLaunchTitle(dataBuffer, dataSize, titleId_low, titleId_high); + } + + free(dataBuffer); +} + +void bootvWiiMenu() { + launchvWiiTitle(0, 0); +} + +void bootHomebrewChannel() { + launchvWiiTitle(0x00010001, 0x4f484243); // 'OHBC' +} diff --git a/source/BootUtils.h b/source/BootUtils.h new file mode 100644 index 0000000..4eeb473 --- /dev/null +++ b/source/BootUtils.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +void bootWiiUMenu(); + +void bootHomebrewLauncher(); + +void bootvWiiMenu(); + +void bootHomebrewChannel(); \ No newline at end of file diff --git a/source/DrawUtils.cpp b/source/DrawUtils.cpp index 511232b..e3496ca 100644 --- a/source/DrawUtils.cpp +++ b/source/DrawUtils.cpp @@ -1,4 +1,5 @@ #include "DrawUtils.h" +#include #include #include @@ -7,6 +8,7 @@ #include #include #include FT_FREETYPE_H +#include "MenuUtils.h" // buffer width #define TV_WIDTH 0x500 @@ -24,6 +26,25 @@ static FT_Library ft_lib = nullptr; static FT_Face ft_face = nullptr; static Color font_col = {0xFFFFFFFF}; +void* DrawUtils::InitOSScreen(){ + OSScreenInit(); + + uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); + uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC); + + auto *screenBuffer = (uint8_t *) memalign(0x100, tvBufferSize + drcBufferSize); + if (screenBuffer == nullptr) { + return nullptr; + } + + OSScreenSetBufferEx(SCREEN_TV, screenBuffer); + OSScreenSetBufferEx(SCREEN_DRC, screenBuffer + tvBufferSize); + + OSScreenEnableEx(SCREEN_TV, TRUE); + OSScreenEnableEx(SCREEN_DRC, TRUE); + return screenBuffer; +} + void DrawUtils::initBuffers(void* tvBuffer, uint32_t tvSize, void* drcBuffer, uint32_t drcSize) { DrawUtils::tvBuffer = (uint8_t*) tvBuffer; diff --git a/source/DrawUtils.h b/source/DrawUtils.h index dee4a87..995a254 100644 --- a/source/DrawUtils.h +++ b/source/DrawUtils.h @@ -7,11 +7,11 @@ #define SCREEN_HEIGHT 480 union Color { - Color(uint32_t color) { - this->color = color; + Color(uint32_t color) { + this->color = color; } - Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - this->r = r; this->g = g; this->b = b; this->a = a; + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + this->r = r; this->g = g; this->b = b; this->a = a; } uint32_t color; @@ -25,6 +25,7 @@ union Color { class DrawUtils { public: + static void* InitOSScreen(); static void initBuffers(void* tvBuffer, uint32_t tvSize, void* drcBuffer, uint32_t drcSize); static void beginDraw(); static void endDraw(); diff --git a/source/MenuUtils.cpp b/source/MenuUtils.cpp new file mode 100644 index 0000000..07ba049 --- /dev/null +++ b/source/MenuUtils.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include + +#include "MenuUtils.h" +#include "DrawUtils.h" + +#include +#include +#include +#include + +#include "icon_png.h" + +const char *menu_options[] = { + "Wii U Menu", + "Homebrew Launcher", + "vWii System Menu", + "vWii Homebrew Channel", +}; + +const char *autoboot_config_strings[] = { + "wiiu_menu", + "homebrew_launcher", + "vwii_system_menu", + "vwii_homebrew_channel", +}; + + +int32_t readAutobootOption(std::string &configPath) { + FILE *f = fopen(configPath.c_str(), "r"); + if (f) { + char buf[128]{}; + fgets(buf, sizeof(buf), f); + fclose(f); + + for (uint32_t i = 0; i < sizeof(autoboot_config_strings) / sizeof(char *); i++) { + if (strncmp(autoboot_config_strings[i], buf, strlen(autoboot_config_strings[i])) == 0) { + return i; + } + } + } + return -1; +} + +void writeAutobootOption(std::string &configPath, int32_t autobootOption) { + FILE *f = fopen(configPath.c_str(), "w"); + if (f) { + if (autobootOption >= 0) { + fputs(autoboot_config_strings[autobootOption], f); + } else { + fputs("none", f); + } + + fclose(f); + } +} + +int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput) { + 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, screenBuffer + tvBufferSize, drcBufferSize); + DrawUtils::initFont(); + + uint32_t selected = autobootOptionInput > 0 ? autobootOptionInput : 0; + int autoboot = autobootOptionInput; + 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 < sizeof(menu_options) / sizeof(char *) - 1) { + selected++; + redraw = true; + } + } else if (vpad.trigger & VPAD_BUTTON_A) { + break; + } else if (vpad.trigger & VPAD_BUTTON_X) { + autoboot = -1; + redraw = true; + } else if (vpad.trigger & VPAD_BUTTON_Y) { + autoboot = selected; + redraw = true; + } + + if (redraw) { + DrawUtils::beginDraw(); + DrawUtils::clear(COLOR_BACKGROUND); + + // draw buttons + uint32_t index = 8 + 24 + 8 + 4; + for (uint32_t i = 0; i < sizeof(menu_options) / sizeof(char *); i++) { + if (i == selected) { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED); + } else { + DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_BORDER); + } + + DrawUtils::setFontSize(24); + DrawUtils::setFontColor((i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_TEXT); + DrawUtils::print(16 * 2, index + 8 + 24, menu_options[i]); + index += 42 + 8; + } + + DrawUtils::setFontColor(COLOR_TEXT); + + // draw top bar + DrawUtils::setFontSize(24); + DrawUtils::drawPNG(16, 2, icon_png); + DrawUtils::print(64 + 2, 6 + 24, "Tiramisu Boot Selector"); + 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); + const char *autobootHints = "\ue002 Clear Autoboot / \ue003 Select Autoboot"; + DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, 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); + + if (autoboot != autobootOptionInput) { + writeAutobootOption(configPath, autoboot); + } + + return selected; +} \ No newline at end of file diff --git a/source/MenuUtils.h b/source/MenuUtils.h new file mode 100644 index 0000000..361ff23 --- /dev/null +++ b/source/MenuUtils.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#define COLOR_WHITE Color(0xffffffff) +#define COLOR_BLACK Color(0, 0, 0, 255) +#define COLOR_BACKGROUND COLOR_BLACK +#define COLOR_TEXT COLOR_WHITE +#define COLOR_TEXT2 Color(0xB3ffffff) +#define COLOR_AUTOBOOT Color(0xaeea00ff) +#define COLOR_BORDER Color(204, 204, 204, 255) +#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff) + +enum { + BOOT_OPTION_WII_U_MENU, + BOOT_OPTION_HOMEBREW_LAUNCHER, + BOOT_OPTION_VWII_SYSTEM_MENU, + BOOT_OPTION_VWII_HOMEBREW_CHANNEL, +}; + +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 diff --git a/source/QuickStartUtils.cpp b/source/QuickStartUtils.cpp new file mode 100644 index 0000000..70f0071 --- /dev/null +++ b/source/QuickStartUtils.cpp @@ -0,0 +1,221 @@ +#include + +#include "QuickStartUtils.h" +#include "logger.h" +#include "BootUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" void __fini_wut(); + +static void StartAppletAndExit() { + DEBUG_FUNCTION_LINE("Wait for applet"); + ProcUIInit(OSSavesDone_ReadyToRelease); + + bool doProcUi = true; + bool launchWiiUMenuOnNextForeground = false; + while (true) { + switch (ProcUIProcessMessages(true)) { + case PROCUI_STATUS_EXITING: { + doProcUi = false; + break; + } + case PROCUI_STATUS_RELEASE_FOREGROUND: { + ProcUIDrawDoneRelease(); + launchWiiUMenuOnNextForeground = true; + break; + } + case PROCUI_STATUS_IN_FOREGROUND: { + if (launchWiiUMenuOnNextForeground) { + bootWiiUMenu(); + launchWiiUMenuOnNextForeground = false; + } + break; + } + case PROCUI_STATUS_IN_BACKGROUND: { + break; + } + default: + break; + } + OSSleepTicks(OSMillisecondsToTicks(1)); + if (!doProcUi) { + break; + } + } + ProcUIShutdown(); + + DEBUG_FUNCTION_LINE("Exit to Wii U Menu"); + + deinitLogging(); + __fini_wut(); + _Exit(0); +} + +extern "C" int32_t SYSSwitchToBrowser(void *); +extern "C" int32_t SYSSwitchToEShop(void *); +extern "C" int32_t _SYSSwitchTo(uint32_t pfid); + +void loadConsoleAccount(const char *data_uuid) { + nn::act::Initialize(); + for (int32_t i = 0; i < 13; i++) { + char uuid[16]; + auto result = nn::act::GetUuidEx(uuid, i); + if (result.IsSuccess()) { + if (memcmp(uuid, data_uuid, 8) == 0) { + DEBUG_FUNCTION_LINE("Load Console account %d", i); + nn::act::LoadConsoleAccount(i, 0, nullptr, false); + break; + } + } + } + nn::act::Finalize(); +} + +bool getQuickBoot() { + auto bootCheck = CCRSysCaffeineBootCheck(); + if (bootCheck == 0) { + nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap); + char path[0x80]; + nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process + FSCmdBlock cmdBlock; + FSInitCmdBlock(&cmdBlock); + + auto fileStream = new nn::sl::FileStream; + auto *fsClient = (FSClient *) memalign(0x40, sizeof(FSClient)); + memset(fsClient, 0, sizeof(*fsClient)); + FSAddClient(fsClient, FS_ERROR_FLAG_NONE); + + fileStream->Initialize(fsClient, &cmdBlock, path, "r"); + + auto database = new nn::sl::LaunchInfoDatabase; + database->Load(fileStream, nn::sl::REGION_EUR); + + CCRAppLaunchParam data; // load sys caffeine data + // load app launch param + CCRSysCaffeineGetAppLaunchParam(&data); + + // get launch info for id + nn::sl::LaunchInfo info; + auto result = database->GetLaunchInfoById(&info, data.titleId); + + delete database; + delete fileStream; + + FSDelClient(fsClient, FS_ERROR_FLAG_NONE); + + nn::sl::Finalize(); + + if (!result.IsSuccess()) { + DEBUG_FUNCTION_LINE("GetLaunchInfoById failed."); + return false; + } + + DEBUG_FUNCTION_LINE("Trying to autoboot for titleId %016llX", info.titleId); + + if (info.titleId == 0x0005001010040000L || + info.titleId == 0x0005001010040100L || + info.titleId == 0x0005001010040200L) { + DEBUG_FUNCTION_LINE("Skip quick starting into the Wii U Menu"); + return false; + } + + if (info.titleId == 0x000500301001220AL || + info.titleId == 0x000500301001210AL || + info.titleId == 0x000500301001200AL) { + DEBUG_FUNCTION_LINE("Launching the browser"); + loadConsoleAccount(data.uuid); + SYSSwitchToBrowser(nullptr); + + StartAppletAndExit(); + + return true; + } + if (info.titleId == 0x000500301001400AL || + info.titleId == 0x000500301001410AL || + info.titleId == 0x000500301001420AL) { + DEBUG_FUNCTION_LINE("Launching the Eshop"); + loadConsoleAccount(data.uuid); + SYSSwitchToEShop(nullptr); + + StartAppletAndExit(); + + return true; + } + if (info.titleId == 0x000500301001800AL || + info.titleId == 0x000500301001810AL || + info.titleId == 0x000500301001820AL) { + DEBUG_FUNCTION_LINE("Launching the Download Management"); + loadConsoleAccount(data.uuid); + _SYSSwitchTo(12); + + StartAppletAndExit(); + + return true; + } + if (info.titleId == 0x000500301001600AL || + info.titleId == 0x000500301001610AL || + info.titleId == 0x000500301001620AL) { + DEBUG_FUNCTION_LINE("Launching Miiverse"); + loadConsoleAccount(data.uuid); + _SYSSwitchTo(9); + + StartAppletAndExit(); + + return true; + } + if (info.titleId == 0x000500301001500AL || + info.titleId == 0x000500301001510AL || + info.titleId == 0x000500301001520AL) { + DEBUG_FUNCTION_LINE("Launching Friendlist"); + loadConsoleAccount(data.uuid); + _SYSSwitchTo(11); + + StartAppletAndExit(); + + return true; + } + if (info.titleId == 0x000500301001300AL || + info.titleId == 0x000500301001310AL || + info.titleId == 0x000500301001320AL) { + DEBUG_FUNCTION_LINE("Launching TVii"); + loadConsoleAccount(data.uuid); + _SYSSwitchTo(3); + + StartAppletAndExit(); + + return true; + } + + if (!SYSCheckTitleExists(info.titleId)) { + DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", info.titleId); + return false; + } + + MCPTitleListType titleInfo; + int32_t handle = MCP_Open(); + auto err = MCP_GetTitleInfo(handle, info.titleId, &titleInfo); + MCP_Close(handle); + if (err == 0) { + loadConsoleAccount(data.uuid); + ACPAssignTitlePatch(&titleInfo); + _SYSLaunchTitleWithStdArgsInNoSplash(info.titleId, nullptr); + return true; + } + + return false; + } else { + DEBUG_FUNCTION_LINE("No quick start"); + } + return false; +} \ No newline at end of file diff --git a/source/QuickStartUtils.h b/source/QuickStartUtils.h new file mode 100644 index 0000000..a9052ca --- /dev/null +++ b/source/QuickStartUtils.h @@ -0,0 +1,3 @@ +#pragma once + +bool getQuickBoot(); \ No newline at end of file diff --git a/source/StorageUtils.cpp b/source/StorageUtils.cpp new file mode 100644 index 0000000..e2dc878 --- /dev/null +++ b/source/StorageUtils.cpp @@ -0,0 +1,144 @@ +#include +#include + +#include "StorageUtils.h" +#include "logger.h" + +#include +#include +#include +#include +#include +#include + +static void InitEmptyExternalStorage() { + DEBUG_FUNCTION_LINE("Fallback to empty ExtendedStorage"); + nn::spm::VolumeId empty{}; + nn::spm::SetDefaultExtendedStorageVolumeId(empty); + + nn::spm::StorageIndex storageIndex = 0; + nn::spm::SetExtendedStorage(&storageIndex); +} + +static int numberUSBStorageDevicesConnected() { + DEBUG_FUNCTION_LINE("Check if USB Storage is connected"); + auto *handle = (UhsHandle *) memalign(0x40, sizeof(UhsHandle)); + if (!handle) { + return -1; + } + memset(handle, 0, sizeof(UhsHandle)); + auto *config = (UhsConfig *) memalign(0x40, sizeof(UhsConfig)); + if (!config) { + free(config); + return -2; + } + memset(config, 0, sizeof(UhsConfig)); + + config->controller_num = 0; + uint32_t size = 5120; + void *buffer = memalign(0x40, size); + if (buffer == nullptr) { + free(handle); + free(config); + } + memset(buffer, 0, size); + + config->buffer = buffer; + config->buffer_size = size; + + if (UhsClientOpen(handle, config) != UHS_STATUS_OK) { + DEBUG_FUNCTION_LINE("UhsClient failed"); + free(handle); + free(config); + free(buffer); + return -3; + } + + UhsInterfaceProfile profiles[10]; + UhsInterfaceFilter filter = { + .match_params = MATCH_ANY + }; + + int result = 0; + if ((result = UhsQueryInterfaces(handle, &filter, profiles, 10)) <= UHS_STATUS_OK) { + DEBUG_FUNCTION_LINE("UhsQueryInterfaces failed"); + UhsClientClose(handle); + free(handle); + free(config); + free(buffer); + return -4; + } + + auto found = 0; + for (int i = 0; i < result; i++) { + if (profiles[i].if_desc.bInterfaceClass == USBCLASS_STORAGE) { + DEBUG_FUNCTION_LINE("Found USBCLASS_STORAGE"); + found++; + } + } + + UhsClientClose(handle); + free(handle); + free(config); + free(config->buffer); + return found; +} + +void initExternalStorage() { + if (OSGetTitleID() == _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER)) { + // nn::spm functions always call OSFatal when they fail, so we make sure have permission to use + // the lib before actually using it. + return; + } + int connectedStorage = 0; + if ((connectedStorage = numberUSBStorageDevicesConnected()) <= 0) { + nn::spm::Initialize(); + InitEmptyExternalStorage(); + nn::spm::Finalize(); + return; + } + + DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", connectedStorage); + + nn::spm::Initialize(); + + nn::spm::StorageListItem items[0x20]; + int tries = 0; + bool found = false; + + while (tries < 600) { + int32_t numItems = nn::spm::GetStorageList(items, 0x20); + + DEBUG_FUNCTION_LINE("Number of items: %d", numItems); + + for (int32_t i = 0; i < numItems; i++) { + if (items[i].type == nn::spm::STORAGE_TYPE_WFS) { + nn::spm::StorageInfo info{}; + if (nn::spm::GetStorageInfo(&info, &items[i].index) == 0) { + DEBUG_FUNCTION_LINE("Using %s for extended storage", info.path); + + nn::spm::SetExtendedStorage(&items[i].index); + ACPMountExternalStorage(); + + nn::spm::SetDefaultExtendedStorageVolumeId(info.volumeId); + + found = true; + break; + } + } + } + if (found || (connectedStorage == numItems)) { + DEBUG_FUNCTION_LINE("Found all expected items, breaking."); + break; + } + OSSleepTicks(OSMillisecondsToTicks(16)); + tries++; + } + if (!found) { + DEBUG_FUNCTION_LINE("USB Storage is connected but either it's not connected or we ran into a timeout."); + InitEmptyExternalStorage(); + } + + nn::spm::Finalize(); +} + diff --git a/source/StorageUtils.h b/source/StorageUtils.h new file mode 100644 index 0000000..9be42ed --- /dev/null +++ b/source/StorageUtils.h @@ -0,0 +1,3 @@ +#pragma once + +void initExternalStorage(); \ No newline at end of file diff --git a/source/logger.cpp b/source/logger.cpp new file mode 100644 index 0000000..3169c74 --- /dev/null +++ b/source/logger.cpp @@ -0,0 +1,38 @@ +#include "logger.h" + +#ifdef DEBUG +#include +#include +#include +#include + +uint32_t moduleLogInit = false; +uint32_t cafeLogInit = false; +uint32_t udpLogInit = false; +#endif + +void initLogging() { +#ifdef DEBUG + if (!(moduleLogInit = WHBLogModuleInit())) { + cafeLogInit = WHBLogCafeInit(); + udpLogInit = WHBLogUdpInit(); + } +#endif // DEBUG +} + +void deinitLogging() { +#ifdef DEBUG + if (moduleLogInit) { + WHBLogModuleDeinit(); + moduleLogInit = false; + } + if (cafeLogInit) { + WHBLogCafeDeinit(); + cafeLogInit = false; + } + if (udpLogInit) { + WHBLogUdpDeinit(); + udpLogInit = false; + } +#endif // DEBUG +} \ No newline at end of file diff --git a/source/logger.h b/source/logger.h index 7e11c21..5b1bd51 100644 --- a/source/logger.h +++ b/source/logger.h @@ -28,6 +28,10 @@ extern "C" { #endif +void initLogging(); + +void deinitLogging(); + #ifdef __cplusplus } #endif diff --git a/source/main.cpp b/source/main.cpp index ba5def9..2f443c8 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,524 +1,55 @@ #include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - +#include +#include +#include "StorageUtils.h" +#include "QuickStartUtils.h" #include "DrawUtils.h" #include "logger.h" -#include "icon_png.h" -#define COLOR_WHITE Color(0xffffffff) -#define COLOR_BLACK Color(0, 0, 0, 255) -#define COLOR_BACKGROUND COLOR_BLACK -#define COLOR_TEXT COLOR_WHITE -#define COLOR_TEXT2 Color(0xB3ffffff) -#define COLOR_AUTOBOOT Color(0xaeea00ff) -#define COLOR_BORDER Color(204, 204, 204, 255) -#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff) +#include "BootUtils.h" +#include "MenuUtils.h" -void bootWiiUMenu(); - -void StartAppletAndExit() { - DEBUG_FUNCTION_LINE("Wait for applet"); - ProcUIInit(OSSavesDone_ReadyToRelease); - - bool doProcUi = true; - bool launchWiiUMenuOnNextForeground = false; - while (true) { - switch (ProcUIProcessMessages(true)) { - case PROCUI_STATUS_EXITING: { - doProcUi = false; - break; - } - case PROCUI_STATUS_RELEASE_FOREGROUND: { - ProcUIDrawDoneRelease(); - launchWiiUMenuOnNextForeground = true; - break; - } - case PROCUI_STATUS_IN_FOREGROUND: { - if (launchWiiUMenuOnNextForeground) { - bootWiiUMenu(); - launchWiiUMenuOnNextForeground = false; - } - break; - } - case PROCUI_STATUS_IN_BACKGROUND: { - break; - } - default: - break; - } - OSSleepTicks(OSMillisecondsToTicks(1)); - if (!doProcUi) { - break; - } +void clearScreen() { + auto buffer = DrawUtils::InitOSScreen(); + if (!buffer) { + OSFatal("Failed to alloc memory for screen"); } - ProcUIShutdown(); - DEBUG_FUNCTION_LINE("Exit to Wii U Menu"); - _Exit(0); -} - -enum { - BOOT_OPTION_WII_U_MENU, - BOOT_OPTION_HOMEBREW_LAUNCHER, - BOOT_OPTION_VWII_SYSTEM_MENU, - BOOT_OPTION_VWII_HOMEBREW_CHANNEL, -}; - -static const char *menu_options[] = { - "Wii U Menu", - "Homebrew Launcher", - "vWii System Menu", - "vWii Homebrew Channel", -}; - -static const char *autoboot_config_strings[] = { - "wiiu_menu", - "homebrew_launcher", - "vwii_system_menu", - "vwii_homebrew_channel", -}; - -std::string configPath; - -int32_t readAutobootOption() { - FILE *f = fopen(configPath.c_str(), "r"); - if (f) { - char buf[128]{}; - fgets(buf, sizeof(buf), f); - fclose(f); - - for (uint32_t i = 0; i < sizeof(autoboot_config_strings) / sizeof(char *); i++) { - if (strncmp(autoboot_config_strings[i], buf, strlen(autoboot_config_strings[i])) == 0) { - return i; - } - } - } - return -1; -} - -void writeAutobootOption(int32_t autobootOption) { - FILE *f = fopen(configPath.c_str(), "w"); - if (f) { - if (autobootOption >= 0) { - fputs(autoboot_config_strings[autobootOption], f); - } else { - fputs("none", f); - } - - fclose(f); - } -} - -extern "C" int32_t SYSSwitchToBrowser(void *); -extern "C" int32_t SYSSwitchToEShop(void *); -extern "C" int32_t _SYSSwitchTo(uint32_t pfid); - -void loadConsoleAccount(const char *data_uuid) { - nn::act::Initialize(); - for (int32_t i = 0; i < 13; i++) { - char uuid[16]; - auto result = nn::act::GetUuidEx(uuid, i); - if (result.IsSuccess()) { - if (memcmp(uuid, data_uuid, 8) == 0) { - DEBUG_FUNCTION_LINE("Load Console account %d", i); - nn::act::LoadConsoleAccount(i, 0, nullptr, false); - break; - } - } - } - nn::act::Finalize(); -} - -bool getQuickBoot() { - auto bootCheck = CCRSysCaffeineBootCheck(); - if (bootCheck == 0) { - nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap); - char path[0x80]; - nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process - FSCmdBlock cmdBlock; - FSInitCmdBlock(&cmdBlock); - - auto fileStream = new nn::sl::FileStream; - auto *fsClient = (FSClient *) memalign(0x40, sizeof(FSClient)); - memset(fsClient, 0, sizeof(*fsClient)); - FSAddClient(fsClient, FS_ERROR_FLAG_NONE); - - fileStream->Initialize(fsClient, &cmdBlock, path, "r"); - - auto database = new nn::sl::LaunchInfoDatabase; - database->Load(fileStream, nn::sl::REGION_EUR); - - CCRAppLaunchParam data; // load sys caffeine data - // load app launch param - CCRSysCaffeineGetAppLaunchParam(&data); - - // get launch info for id - nn::sl::LaunchInfo info; - auto result = database->GetLaunchInfoById(&info, data.titleId); - - delete database; - delete fileStream; - - FSDelClient(fsClient, FS_ERROR_FLAG_NONE); - - nn::sl::Finalize(); - - if (!result.IsSuccess()) { - DEBUG_FUNCTION_LINE("GetLaunchInfoById failed."); - return false; - } - - DEBUG_FUNCTION_LINE("Trying to autoboot for titleId %016llX", info.titleId); - - if (info.titleId == 0x0005001010040000L || - info.titleId == 0x0005001010040100L || - info.titleId == 0x0005001010040200L) { - DEBUG_FUNCTION_LINE("Skip quick booting into the Wii U Menu"); - return false; - } - - if (info.titleId == 0x000500301001220AL || - info.titleId == 0x000500301001210AL || - info.titleId == 0x000500301001200AL) { - DEBUG_FUNCTION_LINE("Launching the browser"); - loadConsoleAccount(data.uuid); - SYSSwitchToBrowser(nullptr); - - StartAppletAndExit(); - - return true; - } - if (info.titleId == 0x000500301001400AL || - info.titleId == 0x000500301001410AL || - info.titleId == 0x000500301001420AL) { - DEBUG_FUNCTION_LINE("Launching the Eshop"); - loadConsoleAccount(data.uuid); - SYSSwitchToEShop(nullptr); - - StartAppletAndExit(); - - return true; - } - if (info.titleId == 0x000500301001800AL || - info.titleId == 0x000500301001810AL || - info.titleId == 0x000500301001820AL) { - DEBUG_FUNCTION_LINE("Launching the Download Management"); - loadConsoleAccount(data.uuid); - _SYSSwitchTo(12); - - StartAppletAndExit(); - - return true; - } - if (info.titleId == 0x000500301001600AL || - info.titleId == 0x000500301001610AL || - info.titleId == 0x000500301001620AL) { - DEBUG_FUNCTION_LINE("Launching Miiverse"); - loadConsoleAccount(data.uuid); - _SYSSwitchTo(9); - - StartAppletAndExit(); - - return true; - } - if (info.titleId == 0x000500301001500AL || - info.titleId == 0x000500301001510AL || - info.titleId == 0x000500301001520AL) { - DEBUG_FUNCTION_LINE("Launching Friendlist"); - loadConsoleAccount(data.uuid); - _SYSSwitchTo(11); - - StartAppletAndExit(); - - return true; - } - if (info.titleId == 0x000500301001300AL || - info.titleId == 0x000500301001310AL || - info.titleId == 0x000500301001320AL) { - DEBUG_FUNCTION_LINE("Launching TVii"); - loadConsoleAccount(data.uuid); - _SYSSwitchTo(3); - - StartAppletAndExit(); - - return true; - } - - if (!SYSCheckTitleExists(info.titleId)) { - DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", info.titleId); - return false; - } - - MCPTitleListType titleInfo; - int32_t handle = MCP_Open(); - auto err = MCP_GetTitleInfo(handle, info.titleId, &titleInfo); - MCP_Close(handle); - if (err == 0) { - loadConsoleAccount(data.uuid); - ACPAssignTitlePatch(&titleInfo); - _SYSLaunchTitleWithStdArgsInNoSplash(info.titleId, nullptr); - return true; - } - - return false; - } else { - DEBUG_FUNCTION_LINE("No quick boot"); - } - return false; -} - -static void initExternalStorage() { - if (OSGetTitleID() == _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER)) { - // nn::spm functions always call OSFatal when they fail, so we make sure have permission to use - // the lib before actually using it. - return; - } - - nn::spm::Initialize(); - - nn::spm::StorageListItem items[0x20]; - int32_t numItems = nn::spm::GetStorageList(items, 0x20); - - bool found = false; - for (int32_t i = 0; i < numItems; i++) { - if (items[i].type == nn::spm::STORAGE_TYPE_WFS) { - nn::spm::StorageInfo info{}; - if (nn::spm::GetStorageInfo(&info, &items[i].index) == 0) { - DEBUG_FUNCTION_LINE("Using %s for extended storage", info.path); - - nn::spm::SetExtendedStorage(&items[i].index); - ACPMountExternalStorage(); - - nn::spm::SetDefaultExtendedStorageVolumeId(info.volumeId); - - found = true; - break; - } - } - } - if (!found) { - DEBUG_FUNCTION_LINE("Fallback to empty ExtendedStorage"); - nn::spm::VolumeId empty{}; - nn::spm::SetDefaultExtendedStorageVolumeId(empty); - - nn::spm::StorageIndex storageIndex = 0; - nn::spm::SetExtendedStorage(&storageIndex); - } - - nn::spm::Finalize(); -} - -void bootWiiUMenu() { - nn::act::Initialize(); - nn::act::SlotNo slot = nn::act::GetSlotNo(); - nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount(); - nn::act::Finalize(); - - if (defaultSlot) { //normal menu boot - SYSLaunchMenu(); - } else { //show mii select - _SYSLaunchMenuWithCheckingAccount(slot); - } -} - -void bootHomebrewLauncher() { - uint64_t titleId = _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER); - _SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); -} - -static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high) { - // we need to init kpad for cmpt - KPADInit(); - - // Try to find a screen type that works - CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_BOTH); - if (CMPTCheckScreenState() < 0) { - CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_DRC); - if (CMPTCheckScreenState() < 0) { - CMPTAcctSetScreenType(CMPT_SCREEN_TYPE_TV); - } - } - - uint32_t dataSize = 0; - CMPTGetDataSize(&dataSize); - - void *dataBuffer = memalign(0x40, dataSize); - - if (titleId_low == 0 && titleId_high == 0) { - CMPTLaunchMenu(dataBuffer, dataSize); - } else { - CMPTLaunchTitle(dataBuffer, dataSize, titleId_low, titleId_high); - } - - free(dataBuffer); -} - -void bootvWiiMenu() { - launchvWiiTitle(0, 0); -} - -void bootHomebrewChannel() { - launchvWiiTitle(0x00010001, 0x4f484243); // 'OHBC' -} - -int32_t handleMenuScreen(int32_t autobootOptionInput) { - OSScreenInit(); - - uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV); - uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC); - - auto *screenBuffer = (uint8_t *) memalign(0x100, tvBufferSize + drcBufferSize); - if (screenBuffer == nullptr) { - OSFatal("Failed to allocate screenBuffer"); - } - - OSScreenSetBufferEx(SCREEN_TV, screenBuffer); - OSScreenSetBufferEx(SCREEN_DRC, screenBuffer + tvBufferSize); - - OSScreenEnableEx(SCREEN_TV, TRUE); - OSScreenEnableEx(SCREEN_DRC, TRUE); - - DrawUtils::initBuffers(screenBuffer, tvBufferSize, screenBuffer + tvBufferSize, drcBufferSize); - DrawUtils::initFont(); - - uint32_t selected = autobootOptionInput > 0 ? autobootOptionInput : 0; - int autoboot = autobootOptionInput; - 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 < sizeof(menu_options) / sizeof(char *) - 1) { - selected++; - redraw = true; - } - } else if (vpad.trigger & VPAD_BUTTON_A) { - break; - } else if (vpad.trigger & VPAD_BUTTON_X) { - autoboot = -1; - redraw = true; - } else if (vpad.trigger & VPAD_BUTTON_Y) { - autoboot = selected; - redraw = true; - } - - if (redraw) { - DrawUtils::beginDraw(); - DrawUtils::clear(COLOR_BACKGROUND); - - // draw buttons - uint32_t index = 8 + 24 + 8 + 4; - for (uint32_t i = 0; i < sizeof(menu_options) / sizeof(char *); i++) { - if (i == selected) { - DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED); - } else { - DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_BORDER); - } - - DrawUtils::setFontSize(24); - DrawUtils::setFontColor((i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_TEXT); - DrawUtils::print(16 * 2, index + 8 + 24, menu_options[i]); - index += 42 + 8; - } - - DrawUtils::setFontColor(COLOR_TEXT); - - // draw top bar - DrawUtils::setFontSize(24); - DrawUtils::drawPNG(16, 2, icon_png); - DrawUtils::print(64 + 2, 6 + 24, "Tiramisu Boot Selector"); - 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); - const char *autobootHints = "\ue002 Clear Autoboot / \ue003 Select Autoboot"; - DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true); - - DrawUtils::endDraw(); - - redraw = false; - } - } - - DrawUtils::beginDraw(); - DrawUtils::clear(COLOR_BLACK); - DrawUtils::endDraw(); - - DrawUtils::deinitFont(); + DrawUtils::clear(COLOR_BACKGROUND); // Call GX2Init to shut down OSScreen GX2Init(nullptr); - free(screenBuffer); - - if (autoboot != autobootOptionInput) { - writeAutobootOption(autoboot); - } - - return selected; + free(buffer); } - int32_t main(int32_t argc, char **argv) { -#ifdef DEBUG - if (!WHBLogModuleInit()) { - WHBLogCafeInit(); - WHBLogUdpInit(); - } -#endif // DEBUG - DEBUG_FUNCTION_LINE("Hello from Autoboot"); + initLogging(); + DEBUG_FUNCTION_LINE("Hello from Autoboot Module"); + + // Clear screen to avoid screen corruptions when loading the Wii U Menu + clearScreen(); initExternalStorage(); if (getQuickBoot()) { + deinitLogging(); return 0; } - configPath = "fs:/vol/exernal01/wiiu/autoboot.cfg"; + std::string configPath = "fs:/vol/exernal01/wiiu/autoboot.cfg"; if (argc >= 1) { configPath = std::string(argv[0]) + "/autoboot.cfg"; } - int32_t bootSelection = readAutobootOption(); + int32_t bootSelection = readAutobootOption(configPath); VPADStatus vpad{}; VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr); if ((bootSelection == -1) || (vpad.hold & VPAD_BUTTON_PLUS)) { - bootSelection = handleMenuScreen(bootSelection); + bootSelection = handleMenuScreen(configPath, bootSelection); } if (bootSelection >= 0) { @@ -543,5 +74,6 @@ int32_t main(int32_t argc, char **argv) { bootWiiUMenu(); } + deinitLogging(); return 0; } diff --git a/source/main.h b/source/main.h new file mode 100644 index 0000000..6f70f09 --- /dev/null +++ b/source/main.h @@ -0,0 +1 @@ +#pragma once