From 1711a30cf7c7769e977140dac14656f990dcca82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romulo=20Leit=C3=A3o?= Date: Sat, 11 Mar 2023 13:35:42 -0300 Subject: [PATCH] Add support for custom boot entries (either vWii or Wiiu) (cherry picked from commit 74316bd54d4d873d9bb453eb12747f84be5ae0d8) --- source/BootUtils.cpp | 42 +++++++++++++++---- source/BootUtils.h | 7 ++++ source/MenuUtils.cpp | 99 ++++++++++++++++++++++++++++++++++---------- source/MenuUtils.h | 29 +++++++++++-- source/main.cpp | 26 +++++++++++- 5 files changed, 167 insertions(+), 36 deletions(-) diff --git a/source/BootUtils.cpp b/source/BootUtils.cpp index 191a884..2c13c8a 100644 --- a/source/BootUtils.cpp +++ b/source/BootUtils.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,18 @@ void bootHomebrewLauncher() { _SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); } +void bootWiiuTitle(const std::string &hexId) { + uint64_t titleId; + std::stringstream ss; + ss << std::hex << hexId; + ss >> titleId; + + handleAccountSelection(); + + titleId = _SYSGetSystemApplicationTitleId(static_cast(titleId)); + _SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); +} + void handleAccountSelection() { nn::act::Initialize(); nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount(); @@ -83,7 +96,7 @@ void handleAccountSelection() { nn::act::Finalize(); } -static void launchvWiiTitle(uint64_t titleId) { +void launchvWiiTitle(uint64_t titleId) { // we need to init kpad for cmpt KPADInit(); @@ -114,7 +127,7 @@ void bootvWiiMenu() { launchvWiiTitle(0); } -uint64_t getVWiiHBLTitleId() { +uint64_t getVWiiTitleId(const std::string& hexId) { // fall back to booting the vWii system menu if anything fails uint64_t titleId = 0; @@ -126,13 +139,15 @@ uint64_t getVWiiHBLTitleId() { if (FSAMount(client, "/dev/slccmpt01", "/vol/storage_slccmpt01", FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0) >= 0) { FSStat stat; - // test if the OHBC or HBC is installed - if (FSAGetStat(client, "/vol/storage_slccmpt01/title/00010001/4f484243/content/00000000.app", &stat) >= 0) { - titleId = 0x000100014F484243L; // 'OHBC' - } else if (FSAGetStat(client, "/vol/storage_slccmpt01/title/00010001/4c554c5a/content/00000000.app", &stat) >= 0) { - titleId = 0x000100014C554C5AL; // 'LULZ' + const std::string hexValue{ hexId.size() > 2 ? hexId.substr(2) : hexId }; + const std::string titleString{ "/vol/storage_slccmpt01/title/00010001/" + hexValue + "/content/00000000.app" }; + if (FSAGetStat(client, titleString.c_str(), &stat) >= 0) { + std::stringstream ss; + ss << std::hex << hexId; + ss >> titleId; + titleId |= 0x0001000100000000L; } else { - DEBUG_FUNCTION_LINE("Cannot find HBC"); + DEBUG_FUNCTION_LINE("Cannot find title 0x%s", hexId.c_str()); } FSAUnmount(client, "/vol/storage_slccmpt01", static_cast(2)); } else { @@ -145,6 +160,17 @@ uint64_t getVWiiHBLTitleId() { } else { DEBUG_FUNCTION_LINE_ERR("Failed to add FSAClient"); } + + return titleId; +} + +uint64_t getVWiiHBLTitleId() { + // Try 'OHBC' first and if it fails, try 'LULZ' + uint64_t titleId = getVWiiTitleId("4f484243"); + if (titleId == 0) { + titleId = getVWiiTitleId("4c554c5a"); + } + return titleId; } diff --git a/source/BootUtils.h b/source/BootUtils.h index 6ac2121..a0e964e 100644 --- a/source/BootUtils.h +++ b/source/BootUtils.h @@ -1,13 +1,20 @@ #pragma once #include +#include + +void launchvWiiTitle(uint64_t titleId); void bootWiiUMenu(); void bootHomebrewLauncher(); +void bootWiiuTitle(const std::string& hexId); + void bootvWiiMenu(); void bootHomebrewChannel(); +uint64_t getVWiiTitleId(const std::string& hexId); + uint64_t getVWiiHBLTitleId(); \ No newline at end of file diff --git a/source/MenuUtils.cpp b/source/MenuUtils.cpp index 06cf9c7..c5c01da 100644 --- a/source/MenuUtils.cpp +++ b/source/MenuUtils.cpp @@ -11,18 +11,17 @@ #include #include #include +#include +#include #include #include #include #define AUTOBOOT_MODULE_VERSION "v0.1.2" -const char *autoboot_config_strings[] = { - "wiiu_menu", - "homebrew_launcher", - "vwii_system_menu", - "vwii_homebrew_channel", -}; +// Initialize with the autoboot_base_config_strings data +std::vector autoboot_config_strings = { autoboot_base_config_strings.begin(), autoboot_base_config_strings.end() }; +std::vector custom_boot_options; template std::string string_format(const std::string &format, Args... args) { @@ -33,36 +32,90 @@ std::string string_format(const std::string &format, Args... 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"); - if (f) { - char buf[128]{}; - fgets(buf, sizeof(buf), f); - fclose(f); +std::string trim(const std::string& str) { + std::string result; + for (auto& ch : str) { + if (!std::isspace(ch)) { + result += ch; + } + } - 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 result; +} + +void readBootOptionsFromSD(const std::string &configPath) { + std::ifstream fileStream(configPath.c_str(), std::ios::in); + if (fileStream.is_open()) { + DEBUG_FUNCTION_LINE("bootOptions.cfg open"); + + constexpr size_t bufSize{ 256 }; + for (char line[bufSize]; fileStream.getline(line, bufSize);) { + DEBUG_FUNCTION_LINE(" Line \"%s\"", line); + std::istringstream parsingLine{std::string{line}}; + memset(line, 0, bufSize); + + std::vector tokens; + for (char tmp[bufSize]; parsingLine.getline(tmp, bufSize, ',');) { + DEBUG_FUNCTION_LINE(" Token \"%s\"", tmp); + tokens.push_back(trim(tmp)); + memset(tmp, 0, bufSize); + } + + if (tokens.size() == 3) + { + const std::string subSystem{ tokens[2] }; + const std::string vWiiStr{ "vwii" }; + const bool vWii{ std::equal(subSystem.begin(), subSystem.end(), + vWiiStr.begin(), vWiiStr.end(), [](unsigned char a, unsigned char b) { + return std::tolower(a) == std::tolower(b); + }) + }; + + custom_boot_options.push_back(BootOption{tokens[0], tokens[1], vWii}); + } + } + } else { + DEBUG_FUNCTION_LINE("Failed to open bootOptions.cfg"); + } +} + +int32_t readAutobootOption(const std::string &configPath) { + std::ifstream fileStream(configPath.c_str(), std::ios::in); + if (fileStream.is_open()) { + std::string readOption; + fileStream >> readOption; + + for (size_t i = 0; i < autoboot_config_strings.size(); ++i) { + if (autoboot_config_strings[i] == readOption) { return i; } } + + for (size_t i = 0; i < custom_boot_options.size(); ++i) { + if (custom_boot_options[i].title == readOption) { + return BOOT_OPTION_MAX_OPTIONS + 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); +void writeAutobootOption(const std::string &configPath, int32_t autobootOption) { + const int32_t customAutoBootOption{ autobootOption - BOOT_OPTION_MAX_OPTIONS }; + std::ofstream outStream(configPath.c_str(), std::ios::out); + if (outStream.is_open()) { + if (customAutoBootOption >= 0) { + outStream << custom_boot_options[customAutoBootOption].title; + } else if (autobootOption >= 0) { + outStream << autoboot_config_strings[autobootOption]; } else { - fputs("none", f); + outStream << "none"; } - - fclose(f); } } -int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map &menu) { +int32_t handleMenuScreen(const std::string &configPath, int32_t autobootOptionInput, const std::map &menu) { auto screenBuffer = DrawUtils::InitOSScreen(); if (!screenBuffer) { OSFatal("Failed to alloc memory for screen"); diff --git a/source/MenuUtils.h b/source/MenuUtils.h index 1129674..265a15f 100644 --- a/source/MenuUtils.h +++ b/source/MenuUtils.h @@ -1,6 +1,7 @@ #pragma once #include "ACTAccountInfo.h" +#include #include #include #include @@ -20,18 +21,38 @@ #define COLOR_BORDER Color(204, 204, 204, 255) #define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff) +struct BootOption { + std::string title; + std::string hexId; + bool vWii; +}; + enum { - BOOT_OPTION_WII_U_MENU, + BOOT_OPTION_WII_U_MENU = 0, BOOT_OPTION_HOMEBREW_LAUNCHER, BOOT_OPTION_VWII_SYSTEM_MENU, BOOT_OPTION_VWII_HOMEBREW_CHANNEL, + + BOOT_OPTION_MAX_OPTIONS }; -int32_t readAutobootOption(std::string &configPath); +constexpr std::array autoboot_base_config_strings{ + "wiiu_menu", + "homebrew_launcher", + "vwii_system_menu", + "vwii_homebrew_channel", +}; -void writeAutobootOption(std::string &configPath, int32_t autobootOption); +extern std::vector autoboot_config_strings; +extern std::vector custom_boot_options; -int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map &menu); +void readBootOptionsFromSD(const std::string &configPath); + +int32_t readAutobootOption(const std::string &configPath); + +void writeAutobootOption(const std::string &configPath, int32_t autobootOption); + +int32_t handleMenuScreen(const std::string &configPath, int32_t autobootOptionInput, const std::map &menu); nn::act::SlotNo handleAccountSelectScreen(const std::vector> &data); diff --git a/source/main.cpp b/source/main.cpp index dfdec30..b17c449 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -81,6 +81,10 @@ int32_t main(int32_t argc, char **argv) { if (stat(hblInstallerPath.c_str(), &st) >= 0) { showHBL = true; } + + readBootOptionsFromSD(std::string(argv[0]) + "/bootOptions.cfg"); + } else { + readBootOptionsFromSD("fs:/vol/external01/wiiu/bootOptions.cfg"); } int32_t bootSelection = readAutobootOption(configPath); @@ -95,6 +99,11 @@ int32_t main(int32_t argc, char **argv) { menu[BOOT_OPTION_VWII_HOMEBREW_CHANNEL] = "vWii Homebrew Channel"; } + for (size_t i = 0; i < custom_boot_options.size(); ++i) { + const BootOption& option{ custom_boot_options[i] }; + menu[BOOT_OPTION_MAX_OPTIONS + i] = option.title + " (" + (option.vWii ? "vWii" : "WiiU") + " " + option.hexId + ")"; + } + if ((bootSelection == -1) || (bootSelection == BOOT_OPTION_HOMEBREW_LAUNCHER && !showHBL) || (bootSelection == BOOT_OPTION_VWII_HOMEBREW_CHANNEL && !showvHBL) || @@ -102,7 +111,22 @@ int32_t main(int32_t argc, char **argv) { bootSelection = handleMenuScreen(configPath, bootSelection, menu); } - if (bootSelection >= 0) { + const int32_t customBootSelection{bootSelection - BOOT_OPTION_MAX_OPTIONS}; + if (customBootSelection >= 0) { + if (static_cast(customBootSelection) < custom_boot_options.size()) { + const BootOption &selectedOption{custom_boot_options[customBootSelection]}; + if (selectedOption.vWii) { + const uint64_t titleId = getVWiiTitleId(selectedOption.hexId); + DEBUG_FUNCTION_LINE("Launching vWii title %016llx", titleId); + launchvWiiTitle(titleId); + } else { + DEBUG_FUNCTION_LINE("Launching WiiU title %s", selectedOption.hexId); + bootWiiuTitle(selectedOption.hexId); + } + } else { + bootWiiUMenu(); + } + } else if (bootSelection >= 0) { switch (bootSelection) { case BOOT_OPTION_WII_U_MENU: bootWiiUMenu();