Add support for custom boot entries (either vWii or Wiiu)

(cherry picked from commit 74316bd54d4d873d9bb453eb12747f84be5ae0d8)
This commit is contained in:
Romulo Leitão 2023-03-11 13:35:42 -03:00
parent 2d023be0a2
commit 1711a30cf7
5 changed files with 167 additions and 36 deletions

View File

@ -12,6 +12,7 @@
#include <nn/act.h> #include <nn/act.h>
#include <nn/cmpt/cmpt.h> #include <nn/cmpt/cmpt.h>
#include <padscore/kpad.h> #include <padscore/kpad.h>
#include <sstream>
#include <string> #include <string>
#include <sysapp/launch.h> #include <sysapp/launch.h>
#include <sysapp/title.h> #include <sysapp/title.h>
@ -39,6 +40,18 @@ void bootHomebrewLauncher() {
_SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr); _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<SYSTEM_APP_ID>(titleId));
_SYSLaunchTitleWithStdArgsInNoSplash(titleId, nullptr);
}
void handleAccountSelection() { void handleAccountSelection() {
nn::act::Initialize(); nn::act::Initialize();
nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount(); nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount();
@ -83,7 +96,7 @@ void handleAccountSelection() {
nn::act::Finalize(); nn::act::Finalize();
} }
static void launchvWiiTitle(uint64_t titleId) { void launchvWiiTitle(uint64_t titleId) {
// we need to init kpad for cmpt // we need to init kpad for cmpt
KPADInit(); KPADInit();
@ -114,7 +127,7 @@ void bootvWiiMenu() {
launchvWiiTitle(0); launchvWiiTitle(0);
} }
uint64_t getVWiiHBLTitleId() { uint64_t getVWiiTitleId(const std::string& hexId) {
// fall back to booting the vWii system menu if anything fails // fall back to booting the vWii system menu if anything fails
uint64_t titleId = 0; 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) { if (FSAMount(client, "/dev/slccmpt01", "/vol/storage_slccmpt01", FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0) >= 0) {
FSStat stat; FSStat stat;
// test if the OHBC or HBC is installed const std::string hexValue{ hexId.size() > 2 ? hexId.substr(2) : hexId };
if (FSAGetStat(client, "/vol/storage_slccmpt01/title/00010001/4f484243/content/00000000.app", &stat) >= 0) { const std::string titleString{ "/vol/storage_slccmpt01/title/00010001/" + hexValue + "/content/00000000.app" };
titleId = 0x000100014F484243L; // 'OHBC' if (FSAGetStat(client, titleString.c_str(), &stat) >= 0) {
} else if (FSAGetStat(client, "/vol/storage_slccmpt01/title/00010001/4c554c5a/content/00000000.app", &stat) >= 0) { std::stringstream ss;
titleId = 0x000100014C554C5AL; // 'LULZ' ss << std::hex << hexId;
ss >> titleId;
titleId |= 0x0001000100000000L;
} else { } 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<FSAUnmountFlags>(2)); FSAUnmount(client, "/vol/storage_slccmpt01", static_cast<FSAUnmountFlags>(2));
} else { } else {
@ -145,6 +160,17 @@ uint64_t getVWiiHBLTitleId() {
} else { } else {
DEBUG_FUNCTION_LINE_ERR("Failed to add FSAClient"); 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; return titleId;
} }

View File

@ -1,13 +1,20 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <string>
void launchvWiiTitle(uint64_t titleId);
void bootWiiUMenu(); void bootWiiUMenu();
void bootHomebrewLauncher(); void bootHomebrewLauncher();
void bootWiiuTitle(const std::string& hexId);
void bootvWiiMenu(); void bootvWiiMenu();
void bootHomebrewChannel(); void bootHomebrewChannel();
uint64_t getVWiiTitleId(const std::string& hexId);
uint64_t getVWiiHBLTitleId(); uint64_t getVWiiHBLTitleId();

View File

@ -11,18 +11,17 @@
#include <malloc.h> #include <malloc.h>
#include <memory> #include <memory>
#include <nn/act/client_cpp.h> #include <nn/act/client_cpp.h>
#include <fstream>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <vpad/input.h> #include <vpad/input.h>
#define AUTOBOOT_MODULE_VERSION "v0.1.2" #define AUTOBOOT_MODULE_VERSION "v0.1.2"
const char *autoboot_config_strings[] = { // Initialize with the autoboot_base_config_strings data
"wiiu_menu", std::vector<std::string> autoboot_config_strings = { autoboot_base_config_strings.begin(), autoboot_base_config_strings.end() };
"homebrew_launcher", std::vector<BootOption> custom_boot_options;
"vwii_system_menu",
"vwii_homebrew_channel",
};
template<typename... Args> template<typename... Args>
std::string string_format(const std::string &format, Args... args) { 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 return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
} }
int32_t readAutobootOption(std::string &configPath) { std::string trim(const std::string& str) {
FILE *f = fopen(configPath.c_str(), "r"); std::string result;
if (f) { for (auto& ch : str) {
char buf[128]{}; if (!std::isspace(ch)) {
fgets(buf, sizeof(buf), f); result += ch;
fclose(f); }
}
for (uint32_t i = 0; i < sizeof(autoboot_config_strings) / sizeof(char *); i++) { return result;
if (strncmp(autoboot_config_strings[i], buf, strlen(autoboot_config_strings[i])) == 0) { }
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<std::string> 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; 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; return -1;
} }
void writeAutobootOption(std::string &configPath, int32_t autobootOption) { void writeAutobootOption(const std::string &configPath, int32_t autobootOption) {
FILE *f = fopen(configPath.c_str(), "w"); const int32_t customAutoBootOption{ autobootOption - BOOT_OPTION_MAX_OPTIONS };
if (f) { std::ofstream outStream(configPath.c_str(), std::ios::out);
if (autobootOption >= 0) { if (outStream.is_open()) {
fputs(autoboot_config_strings[autobootOption], f); if (customAutoBootOption >= 0) {
outStream << custom_boot_options[customAutoBootOption].title;
} else if (autobootOption >= 0) {
outStream << autoboot_config_strings[autobootOption];
} else { } else {
fputs("none", f); outStream << "none";
} }
fclose(f);
} }
} }
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu) { int32_t handleMenuScreen(const std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu) {
auto screenBuffer = DrawUtils::InitOSScreen(); auto screenBuffer = DrawUtils::InitOSScreen();
if (!screenBuffer) { if (!screenBuffer) {
OSFatal("Failed to alloc memory for screen"); OSFatal("Failed to alloc memory for screen");

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "ACTAccountInfo.h" #include "ACTAccountInfo.h"
#include <array>
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <memory> #include <memory>
@ -20,18 +21,38 @@
#define COLOR_BORDER Color(204, 204, 204, 255) #define COLOR_BORDER Color(204, 204, 204, 255)
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff) #define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff)
struct BootOption {
std::string title;
std::string hexId;
bool vWii;
};
enum { enum {
BOOT_OPTION_WII_U_MENU, BOOT_OPTION_WII_U_MENU = 0,
BOOT_OPTION_HOMEBREW_LAUNCHER, BOOT_OPTION_HOMEBREW_LAUNCHER,
BOOT_OPTION_VWII_SYSTEM_MENU, BOOT_OPTION_VWII_SYSTEM_MENU,
BOOT_OPTION_VWII_HOMEBREW_CHANNEL, BOOT_OPTION_VWII_HOMEBREW_CHANNEL,
BOOT_OPTION_MAX_OPTIONS
}; };
int32_t readAutobootOption(std::string &configPath); constexpr std::array<const char*, BOOT_OPTION_MAX_OPTIONS> 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<std::string> autoboot_config_strings;
extern std::vector<BootOption> custom_boot_options;
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &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<uint32_t, std::string> &menu);
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data); nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data);

View File

@ -81,6 +81,10 @@ int32_t main(int32_t argc, char **argv) {
if (stat(hblInstallerPath.c_str(), &st) >= 0) { if (stat(hblInstallerPath.c_str(), &st) >= 0) {
showHBL = true; showHBL = true;
} }
readBootOptionsFromSD(std::string(argv[0]) + "/bootOptions.cfg");
} else {
readBootOptionsFromSD("fs:/vol/external01/wiiu/bootOptions.cfg");
} }
int32_t bootSelection = readAutobootOption(configPath); 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"; 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) || if ((bootSelection == -1) ||
(bootSelection == BOOT_OPTION_HOMEBREW_LAUNCHER && !showHBL) || (bootSelection == BOOT_OPTION_HOMEBREW_LAUNCHER && !showHBL) ||
(bootSelection == BOOT_OPTION_VWII_HOMEBREW_CHANNEL && !showvHBL) || (bootSelection == BOOT_OPTION_VWII_HOMEBREW_CHANNEL && !showvHBL) ||
@ -102,7 +111,22 @@ int32_t main(int32_t argc, char **argv) {
bootSelection = handleMenuScreen(configPath, bootSelection, menu); bootSelection = handleMenuScreen(configPath, bootSelection, menu);
} }
if (bootSelection >= 0) { const int32_t customBootSelection{bootSelection - BOOT_OPTION_MAX_OPTIONS};
if (customBootSelection >= 0) {
if (static_cast<size_t>(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) { switch (bootSelection) {
case BOOT_OPTION_WII_U_MENU: case BOOT_OPTION_WII_U_MENU:
bootWiiUMenu(); bootWiiUMenu();